Import Cobalt 17.179149
Change-Id: I7ac21d1c9985c7fb1bc6ec0c04b37eb7c8a7683f
diff --git a/src/base/base64.cc b/src/base/base64.cc
index 1907978..b496af7 100644
--- a/src/base/base64.cc
+++ b/src/base/base64.cc
@@ -25,13 +25,18 @@
return true;
}
-bool Base64Decode(const StringPiece& input, std::string* output) {
- std::string temp;
+template <class Container>
+bool Base64DecodeInternal(const StringPiece& input, Container* output) {
+ Container temp;
temp.resize(modp_b64_decode_len(input.size()));
- // does not null terminate result since result is binary data!
int input_size = static_cast<int>(input.size());
- int output_size = modp_b64_decode(&(temp[0]), input.data(), input_size);
+ // When using this template for a new type, make sure its content is unsigned
+ // char or char.
+ static_assert(sizeof(typename Container::value_type) == 1,
+ "Input type should be char or equivalent.");
+ int output_size = modp_b64_decode(reinterpret_cast<char*>(&(temp[0])),
+ input.data(), input_size);
if (output_size < 0)
return false;
@@ -40,4 +45,12 @@
return true;
}
+bool Base64Decode(const StringPiece& input, std::string* output) {
+ return Base64DecodeInternal(input, output);
+}
+
+bool Base64Decode(const StringPiece& input, std::vector<uint8_t>* output) {
+ return Base64DecodeInternal(input, output);
+}
+
} // namespace base
diff --git a/src/base/base64.h b/src/base/base64.h
index 983d5e2..ea6f40f 100644
--- a/src/base/base64.h
+++ b/src/base/base64.h
@@ -6,6 +6,7 @@
#define BASE_BASE64_H__
#include <string>
+#include <vector>
#include "base/base_export.h"
#include "base/string_piece.h"
@@ -16,10 +17,15 @@
// otherwise. The output string is only modified if successful.
BASE_EXPORT bool Base64Encode(const StringPiece& input, std::string* output);
-// Decodes the base64 input string. Returns true if successful and false
+// Decodes the base64 input string. Returns true if successful and false
// otherwise. The output string is only modified if successful.
BASE_EXPORT bool Base64Decode(const StringPiece& input, std::string* output);
+// Decodes the base64 input string. Returns true if successful and false
+// otherwise. The output vector is only modified if successful.
+BASE_EXPORT bool Base64Decode(const StringPiece& input,
+ std::vector<uint8_t>* output);
+
} // namespace base
#endif // BASE_BASE64_H__
diff --git a/src/base/file_path.cc b/src/base/file_path.cc
index b5c7900..123132d 100644
--- a/src/base/file_path.cc
+++ b/src/base/file_path.cc
@@ -514,7 +514,8 @@
}
FilePath FilePath::AppendASCII(const base::StringPiece& component) const {
- DCHECK(IsStringASCII(component));
+ DCHECK(IsStringASCII(component))
+ << "invalid component " << component << " being appended to " << value();
#if defined(OS_WIN)
return Append(ASCIIToUTF16(component.as_string()));
#elif defined(OS_POSIX) || defined(OS_STARBOARD)
diff --git a/src/base/file_util_starboard.cc b/src/base/file_util_starboard.cc
index d024542..34aeca3 100644
--- a/src/base/file_util_starboard.cc
+++ b/src/base/file_util_starboard.cc
@@ -347,14 +347,33 @@
last_path = path;
}
+ // Root path is now at index 0.
+ std::reverse(subpaths.begin(), subpaths.end());
+
+ int existing_directory_index = -1;
+
+ // Some platforms disallow access to some parent folders in the hierarchy.
+ for (size_t i = 0; i < subpaths.size(); ++i) {
+ const auto& path = subpaths[i];
+ if (DirectoryExists(path)) {
+ existing_directory_index = static_cast<int>(i);
+ }
+ }
+
+ // No sub-directories existed, including the root.
+ if (existing_directory_index < 0) {
+ return false;
+ }
+
// Iterate through the parents and create the missing ones.
- for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
- i != subpaths.rend(); ++i) {
- if (DirectoryExists(*i)) {
+ for (size_t i = static_cast<size_t>(existing_directory_index);
+ i < subpaths.size(); ++i) {
+ const auto& path = subpaths[i];
+ if (DirectoryExists(path)) {
continue;
}
- if (!SbDirectoryCreate(i->value().c_str())) {
+ if (!SbDirectoryCreate(path.value().c_str())) {
return false;
}
}
diff --git a/src/base/memory/aligned_memory.h b/src/base/memory/aligned_memory.h
index 8ea96b7..fa3b2f2 100644
--- a/src/base/memory/aligned_memory.h
+++ b/src/base/memory/aligned_memory.h
@@ -38,10 +38,10 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
-#if defined(COMPILER_MSVC)
-#include <malloc.h>
-#elif defined(OS_STARBOARD)
+#if defined(OS_STARBOARD)
#include "starboard/memory.h"
+#elif defined(COMPILER_MSVC)
+#include <malloc.h>
#else
#include <stdlib.h>
#endif
@@ -96,10 +96,10 @@
BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment);
inline void AlignedFree(void* ptr) {
-#if defined(COMPILER_MSVC)
- _aligned_free(ptr);
-#elif defined(OS_STARBOARD)
+#if defined(OS_STARBOARD)
SbMemoryDeallocateAligned(ptr);
+#elif defined(COMPILER_MSVC)
+ _aligned_free(ptr);
#else
free(ptr);
#endif
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index f300b74..59b3927 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -2,7 +2,26 @@
This document records all notable changes made to Cobalt since the last release.
+## Version 17
+ - **Improvements and Bug Fixes**
+ - Fix pointer/mouse events not being dispatched to JavaScript in the same
+ order that they are generated by Starboard, relative to other input events
+ like key presses.
+
+ - **Storage format changed from sqlite3 to protobuf**
+
+ Cobalt's internal representation for persistent storage of cookies and local
+ storage entries changed from sqlite3 to protobuf. The header of the
+ SbStorageRecord blob was updated from SAV0 to SAV1 and all the data will be
+ migrated to the new format the next time Cobalt is launched.
+ The schema is available at cobalt/storage/store/storage.proto.
+
## Version 16
+ - **Rebase libwebp to version 1.0.0**
+
+ Update the version of libwebp used by Cobalt from 0.3.1 to 1.0.0. The new
+ version brings with it performance improvements and bug fixes.
+
- **Move ``javascript_engine`` and ``cobalt_enable_jit`` build variables**
Move gyp variables ``javascript_engine`` and ``cobalt_enable_jit``, which
diff --git a/src/cobalt/accessibility/accessibility.gyp b/src/cobalt/accessibility/accessibility.gyp
index 08db41c..50de511 100644
--- a/src/cobalt/accessibility/accessibility.gyp
+++ b/src/cobalt/accessibility/accessibility.gyp
@@ -33,7 +33,6 @@
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
],
},
]
diff --git a/src/cobalt/audio/async_audio_decoder.cc b/src/cobalt/audio/async_audio_decoder.cc
index b0ad972..ed08da8 100644
--- a/src/cobalt/audio/async_audio_decoder.cc
+++ b/src/cobalt/audio/async_audio_decoder.cc
@@ -34,12 +34,9 @@
if (reader) {
decode_finish_callback.Run(reader->sample_rate(),
- reader->number_of_frames(),
- reader->number_of_channels(),
- reader->sample_data(), reader->sample_type());
+ reader->ResetAndReturnAudioBus().Pass());
} else {
- decode_finish_callback.Run(0.f, 0, 0, scoped_array<uint8>(),
- kSampleTypeFloat32);
+ decode_finish_callback.Run(0.f, scoped_ptr<ShellAudioBus>());
}
}
diff --git a/src/cobalt/audio/async_audio_decoder.h b/src/cobalt/audio/async_audio_decoder.h
index 1e8a025..f3536ee 100644
--- a/src/cobalt/audio/async_audio_decoder.h
+++ b/src/cobalt/audio/async_audio_decoder.h
@@ -15,21 +15,20 @@
#ifndef COBALT_AUDIO_ASYNC_AUDIO_DECODER_H_
#define COBALT_AUDIO_ASYNC_AUDIO_DECODER_H_
+#include "base/basictypes.h"
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/threading/thread.h"
-#include "cobalt/audio/audio_buffer.h"
#include "cobalt/audio/audio_helpers.h"
-#include "cobalt/dom/array_buffer.h"
namespace cobalt {
namespace audio {
class AsyncAudioDecoder {
public:
- typedef base::Callback<void(float sample_rate, int32 number_of_frames,
- int32 number_of_channels,
- scoped_array<uint8> channels_data,
- SampleType sample_type)> DecodeFinishCallback;
+ typedef base::Callback<void(float sample_rate,
+ scoped_ptr<ShellAudioBus> audio_bus)>
+ DecodeFinishCallback;
AsyncAudioDecoder();
diff --git a/src/cobalt/audio/audio.gyp b/src/cobalt/audio/audio.gyp
index 60bdcf6..d9eb61a 100644
--- a/src/cobalt/audio/audio.gyp
+++ b/src/cobalt/audio/audio.gyp
@@ -48,8 +48,6 @@
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
- '<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
],
'export_dependent_settings': [
# Additionally, ensure that the include directories for generated
@@ -58,35 +56,5 @@
'<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
]
},
-
- {
- 'target_name': 'audio_test',
- 'type': '<(gtest_target_type)',
- 'sources': [
- 'audio_node_input_output_test.cc',
- ],
- 'dependencies': [
- '<(DEPTH)/cobalt/audio/audio.gyp:audio',
- '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
-
- # TODO: Remove the dependency below, it works around the fact that
- # ScriptValueFactory has non-virtual method CreatePromise().
- '<(DEPTH)/cobalt/script/engine.gyp:engine',
- ],
- },
-
- {
- 'target_name': 'audio_test_deploy',
- 'type': 'none',
- 'dependencies': [
- 'audio_test',
- ],
- 'variables': {
- 'executable_name': 'audio_test',
- },
- 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
- },
],
}
diff --git a/src/cobalt/audio/audio_buffer.cc b/src/cobalt/audio/audio_buffer.cc
index 6e9e804..045c9b4 100644
--- a/src/cobalt/audio/audio_buffer.cc
+++ b/src/cobalt/audio/audio_buffer.cc
@@ -20,80 +20,11 @@
namespace cobalt {
namespace audio {
-AudioBuffer::AudioBuffer(script::EnvironmentSettings* settings,
- float sample_rate, int32 number_of_frames,
- int32 number_of_channels,
- scoped_array<uint8> channels_data,
- SampleType sample_type)
- : sample_rate_(sample_rate),
- length_(number_of_frames),
- sample_type_(sample_type) {
+AudioBuffer::AudioBuffer(float sample_rate, scoped_ptr<ShellAudioBus> audio_bus)
+ : sample_rate_(sample_rate), audio_bus_(audio_bus.Pass()) {
DCHECK_GT(sample_rate_, 0);
- DCHECK_GT(length_, 0);
- DCHECK_GT(number_of_channels, 0);
-
- // Create an ArrayBuffer stores sample data from all channels.
- const uint32 length =
- number_of_frames * number_of_channels *
- (sample_type == kSampleTypeFloat32 ? sizeof(float) : sizeof(int16));
- scoped_refptr<dom::ArrayBuffer> array_buffer(
- new dom::ArrayBuffer(settings, channels_data.Pass(), length));
-
- // Each channel should have |number_of_frames * size_of_sample_type| bytes.
- // We create |number_of_channels| of {Float32,Int16}Array as views into the
- // above ArrayBuffer. This does not need any extra allocation.
- if (sample_type == kSampleTypeFloat32) {
- channels_data_.resize(static_cast<size_t>(number_of_channels));
- uint32 start_offset_in_bytes = 0;
- for (int32 i = 0; i < number_of_channels; ++i) {
- channels_data_[static_cast<size_t>(i)] =
- new dom::Float32Array(settings, array_buffer, start_offset_in_bytes,
- static_cast<uint32>(number_of_frames), NULL);
- start_offset_in_bytes += number_of_frames * sizeof(float);
- }
- } else if (sample_type == kSampleTypeInt16) {
- channels_int16_data_.resize(static_cast<size_t>(number_of_channels));
- uint32 start_offset_in_bytes = 0;
- for (int32 i = 0; i < number_of_channels; ++i) {
- channels_int16_data_[static_cast<size_t>(i)] =
- new dom::Int16Array(settings, array_buffer, start_offset_in_bytes,
- static_cast<uint32>(number_of_frames), NULL);
- start_offset_in_bytes += number_of_frames * sizeof(int16);
- }
- } else {
- NOTREACHED();
- }
-}
-
-scoped_refptr<dom::Float32Array> AudioBuffer::GetChannelData(
- uint32 channel_index, script::ExceptionState* exception_state) const {
- DCHECK_EQ(sample_type_, kSampleTypeFloat32);
- // The index value MUST be less than number_of_channels() or an INDEX_SIZE_ERR
- // exception MUST be thrown.
- if (channel_index >= channels_data_.size()) {
- dom::DOMException::Raise(dom::DOMException::kIndexSizeErr, exception_state);
- return NULL;
- }
-
- return channels_data_[channel_index];
-}
-
-scoped_refptr<dom::Int16Array> AudioBuffer::GetChannelDataInt16(
- uint32 channel_index, script::ExceptionState* exception_state) const {
- DCHECK_EQ(sample_type_, kSampleTypeInt16);
- // The index value MUST be less than number_of_channels() or an INDEX_SIZE_ERR
- // exception MUST be thrown.
- if (channel_index >= channels_int16_data_.size()) {
- dom::DOMException::Raise(dom::DOMException::kIndexSizeErr, exception_state);
- return NULL;
- }
-
- return channels_int16_data_[channel_index];
-}
-
-void AudioBuffer::TraceMembers(script::Tracer* tracer) {
- tracer->TraceItems(channels_data_);
- tracer->TraceItems(channels_int16_data_);
+ DCHECK_GT(length(), 0);
+ DCHECK_GT(number_of_channels(), 0);
}
} // namespace audio
diff --git a/src/cobalt/audio/audio_buffer.h b/src/cobalt/audio/audio_buffer.h
index e9ebea9..f69d262 100644
--- a/src/cobalt/audio/audio_buffer.h
+++ b/src/cobalt/audio/audio_buffer.h
@@ -39,57 +39,31 @@
// https://www.w3.org/TR/webaudio/#AudioBuffer
class AudioBuffer : public script::Wrappable {
public:
- // The audio data passed in |channels_data| stores multi-channel audio in
- // planar. So for stereo audio, the first channel will be stored in the first
- // half of |channels_data| and the second channel will be stored in the second
- // half.
- AudioBuffer(script::EnvironmentSettings* settings, float sample_rate,
- int32 number_of_frames, int32 number_of_channels,
- scoped_array<uint8> channels_data, SampleType sample_type);
+ AudioBuffer(float sample_rate, scoped_ptr<ShellAudioBus> audio_bus);
// Web API: AudioBuffer
//
// The sample-rate for the PCM audio data in samples per second.
float sample_rate() const { return sample_rate_; }
-
// Length of the PCM audio data in sample-frames.
- int32 length() const { return length_; }
-
+ int32 length() const { return static_cast<int32>(audio_bus_->frames()); }
// Duration of the PCM audio data in seconds.
double duration() const { return length() / sample_rate(); }
-
// The number of discrete audio channels.
int32 number_of_channels() const {
- if (sample_type_ == kSampleTypeFloat32) {
- return static_cast<int32>(channels_data_.size());
- } else if (sample_type_ == kSampleTypeInt16) {
- return static_cast<int32>(channels_int16_data_.size());
- } else {
- NOTREACHED();
- return 0;
- }
+ return static_cast<int32>(audio_bus_->channels());
}
- // Represents the PCM audio data for the specific channel.
- scoped_refptr<dom::Float32Array> GetChannelData(
- uint32 channel_index, script::ExceptionState* exception_state) const;
-
- scoped_refptr<dom::Int16Array> GetChannelDataInt16(
- uint32 channel_index, script::ExceptionState* exception_state) const;
+ // Custom, not in any spec
+ //
+ ShellAudioBus* audio_bus() { return audio_bus_.get(); }
DEFINE_WRAPPABLE_TYPE(AudioBuffer);
- void TraceMembers(script::Tracer* tracer) override;
private:
- typedef std::vector<scoped_refptr<dom::Float32Array> > Float32ArrayVector;
- typedef std::vector<scoped_refptr<dom::Int16Array> > Int16ArrayVector;
+ const float sample_rate_;
- float sample_rate_;
- int32 length_;
- SampleType sample_type_;
-
- Float32ArrayVector channels_data_;
- Int16ArrayVector channels_int16_data_;
+ scoped_ptr<ShellAudioBus> audio_bus_;
DISALLOW_COPY_AND_ASSIGN(AudioBuffer);
};
diff --git a/src/cobalt/audio/audio_buffer.idl b/src/cobalt/audio/audio_buffer.idl
index 53f9e09..77a151c 100644
--- a/src/cobalt/audio/audio_buffer.idl
+++ b/src/cobalt/audio/audio_buffer.idl
@@ -21,6 +21,4 @@
// in seconds
readonly attribute double duration;
readonly attribute long numberOfChannels;
-
- [RaisesException] Float32Array getChannelData(unsigned long channel);
};
diff --git a/src/cobalt/audio/audio_buffer_source_node.cc b/src/cobalt/audio/audio_buffer_source_node.cc
index adbfc95..f6aa6e9 100644
--- a/src/cobalt/audio/audio_buffer_source_node.cc
+++ b/src/cobalt/audio/audio_buffer_source_node.cc
@@ -92,63 +92,54 @@
exception_state);
return;
}
- state_ = kStoped;
+ state_ = kStopped;
}
scoped_ptr<ShellAudioBus> AudioBufferSourceNode::PassAudioBusFromSource(
- int32 number_of_frames, SampleType sample_type) {
+ int32 number_of_frames, SampleType sample_type, bool* finished) {
+ DCHECK_GT(number_of_frames, 0);
+ DCHECK(finished);
+
// This is called by Audio thread.
audio_lock()->AssertLocked();
- if (state_ != kStarted || !buffer_ || buffer_->length() == read_index_) {
+ *finished = false;
+
+ if (state_ == kNone || !buffer_) {
return scoped_ptr<ShellAudioBus>();
}
- DCHECK_GT(number_of_frames, 0);
+ if (state_ == kStopped || buffer_->length() == read_index_) {
+ *finished = true;
+ return scoped_ptr<ShellAudioBus>();
+ }
+
+ DCHECK_EQ(state_, kStarted);
+
+ auto audio_bus = buffer_->audio_bus();
+ DCHECK_EQ(sample_type, audio_bus->sample_type());
+
int32 frames_to_end = buffer_->length() - read_index_;
number_of_frames = std::min(number_of_frames, frames_to_end);
- size_t channels = static_cast<size_t>(buffer_->number_of_channels());
+ scoped_ptr<ShellAudioBus> result;
- if (sample_type == kSampleTypeFloat32) {
- std::vector<scoped_refptr<dom::Float32Array>> audio_buffer_storages(
- channels);
- std::vector<float*> audio_buffers(channels, NULL);
- for (size_t i = 0; i < channels; ++i) {
- scoped_refptr<dom::Float32Array> buffer_data =
- buffer_->GetChannelData(static_cast<uint32>(i), NULL);
- audio_buffer_storages[i] = buffer_data->Subarray(
- NULL, read_index_, read_index_ + number_of_frames);
- audio_buffers[i] = audio_buffer_storages[i]->data();
- }
+ if (sample_type == kSampleTypeInt16) {
+ result.reset(new media::ShellAudioBus(
+ audio_bus->channels(), number_of_frames,
+ reinterpret_cast<int16*>(audio_bus->interleaved_data()) +
+ read_index_ * audio_bus->channels()));
+ } else {
+ DCHECK_EQ(sample_type, kSampleTypeFloat32);
- read_index_ += number_of_frames;
-
- scoped_ptr<ShellAudioBus> audio_bus(new ShellAudioBus(
- static_cast<size_t>(number_of_frames), audio_buffers));
-
- return audio_bus.Pass();
- } else if (sample_type == kSampleTypeInt16) {
- std::vector<scoped_refptr<dom::Int16Array>> audio_buffer_storages(channels);
- std::vector<int16*> audio_buffers(channels, NULL);
- for (size_t i = 0; i < channels; ++i) {
- scoped_refptr<dom::Int16Array> buffer_data =
- buffer_->GetChannelDataInt16(static_cast<uint32>(i), NULL);
- audio_buffer_storages[i] = buffer_data->Subarray(
- NULL, read_index_, read_index_ + number_of_frames);
- audio_buffers[i] = audio_buffer_storages[i]->data();
- }
-
- read_index_ += number_of_frames;
-
- scoped_ptr<ShellAudioBus> audio_bus(new ShellAudioBus(
- static_cast<size_t>(number_of_frames), audio_buffers));
-
- return audio_bus.Pass();
+ result.reset(new media::ShellAudioBus(
+ audio_bus->channels(), number_of_frames,
+ reinterpret_cast<float*>(audio_bus->interleaved_data()) +
+ read_index_ * audio_bus->channels()));
}
- NOTREACHED();
- return scoped_ptr<ShellAudioBus>();
+ read_index_ += number_of_frames;
+ return result.Pass();
}
void AudioBufferSourceNode::TraceMembers(script::Tracer* tracer) {
diff --git a/src/cobalt/audio/audio_buffer_source_node.h b/src/cobalt/audio/audio_buffer_source_node.h
index b017abb..c3bbe85 100644
--- a/src/cobalt/audio/audio_buffer_source_node.h
+++ b/src/cobalt/audio/audio_buffer_source_node.h
@@ -77,8 +77,9 @@
SetAttributeEventListener(base::Tokens::ended(), event_listener);
}
- scoped_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 number_of_frames, SampleType sample_type) override;
+ scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32 number_of_frames,
+ SampleType sample_type,
+ bool* finished) override;
DEFINE_WRAPPABLE_TYPE(AudioBufferSourceNode);
void TraceMembers(script::Tracer* tracer) override;
@@ -90,7 +91,7 @@
enum State {
kNone,
kStarted,
- kStoped,
+ kStopped,
};
scoped_refptr<AudioBuffer> buffer_;
diff --git a/src/cobalt/audio/audio_context.cc b/src/cobalt/audio/audio_context.cc
index 1399b4d..dccd821 100644
--- a/src/cobalt/audio/audio_context.cc
+++ b/src/cobalt/audio/audio_context.cc
@@ -102,16 +102,12 @@
// Success callback and error callback should be scheduled to run on the main
// thread's event loop.
void AudioContext::DecodeFinish(int callback_id, float sample_rate,
- int32 number_of_frames,
- int32 number_of_channels,
- scoped_array<uint8> channels_data,
- SampleType sample_type) {
+ scoped_ptr<ShellAudioBus> audio_bus) {
if (!main_message_loop_->BelongsToCurrentThread()) {
main_message_loop_->PostTask(
FROM_HERE,
base::Bind(&AudioContext::DecodeFinish, weak_this_, callback_id,
- sample_rate, number_of_frames, number_of_channels,
- base::Passed(&channels_data), sample_type));
+ sample_rate, base::Passed(&audio_bus)));
return;
}
@@ -122,10 +118,9 @@
scoped_ptr<DecodeCallbackInfo> info(info_iterator->second);
pending_decode_callbacks_.erase(info_iterator);
- if (channels_data) {
+ if (audio_bus) {
const scoped_refptr<AudioBuffer>& audio_buffer =
- new AudioBuffer(info->env_settings, sample_rate, number_of_frames,
- number_of_channels, channels_data.Pass(), sample_type);
+ new AudioBuffer(sample_rate, audio_bus.Pass());
info->success_callback.value().Run(audio_buffer);
} else if (info->error_callback) {
info->error_callback.value().value().Run();
diff --git a/src/cobalt/audio/audio_context.h b/src/cobalt/audio/audio_context.h
index ac07c20..ab34490 100644
--- a/src/cobalt/audio/audio_context.h
+++ b/src/cobalt/audio/audio_context.h
@@ -19,6 +19,7 @@
#include "base/callback.h"
#include "base/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/synchronization/lock.h"
@@ -26,6 +27,7 @@
#include "cobalt/audio/audio_buffer.h"
#include "cobalt/audio/audio_buffer_source_node.h"
#include "cobalt/audio/audio_destination_node.h"
+#include "cobalt/audio/audio_helpers.h"
#include "cobalt/dom/array_buffer.h"
#include "cobalt/dom/dom_exception.h"
#include "cobalt/dom/event_target.h"
@@ -164,9 +166,8 @@
std::string GetDebugName() override { return "AudioContext"; }
void DecodeAudioDataInternal(scoped_ptr<DecodeCallbackInfo> info);
- void DecodeFinish(int callback_id, float sample_rate, int32 number_of_frames,
- int32 number_of_channels, scoped_array<uint8> channels_data,
- SampleType sample_type);
+ void DecodeFinish(int callback_id, float sample_rate,
+ scoped_ptr<ShellAudioBus> audio_bus);
base::WeakPtrFactory<AudioContext> weak_ptr_factory_;
// We construct a WeakPtr upon AudioContext's construction in order to
diff --git a/src/cobalt/audio/audio_destination_node.cc b/src/cobalt/audio/audio_destination_node.cc
index 78d414f..a9308c4 100644
--- a/src/cobalt/audio/audio_destination_node.cc
+++ b/src/cobalt/audio/audio_destination_node.cc
@@ -33,7 +33,9 @@
// numberOfInputs : 1
// numberOfOutputs : 0
AudioDestinationNode::AudioDestinationNode(AudioContext* context)
- : AudioNode(context), max_channel_count_(kMaxChannelCount) {
+ : AudioNode(context),
+ message_loop_(MessageLoop::current()),
+ max_channel_count_(kMaxChannelCount) {
AudioLock::AutoLock lock(audio_lock());
AddInput(new AudioNodeInput(this));
@@ -57,16 +59,31 @@
audio_device_.reset(
new AudioDevice(static_cast<int>(channel_count(NULL)), this));
}
+ audio_device_to_delete_ = NULL;
}
-void AudioDestinationNode::FillAudioBus(ShellAudioBus* audio_bus,
+void AudioDestinationNode::FillAudioBus(bool all_consumed,
+ ShellAudioBus* audio_bus,
bool* silence) {
- // This is called by Audio thread.
+ // This is called on Audio thread.
AudioLock::AutoLock lock(audio_lock());
// Destination node only has one input.
DCHECK_EQ(number_of_inputs(), 1u);
- Input(0)->FillAudioBus(audio_bus, silence);
+ bool all_finished = true;
+ Input(0)->FillAudioBus(audio_bus, silence, &all_finished);
+ if (all_consumed && all_finished) {
+ audio_device_to_delete_ = audio_device_.get();
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&AudioDestinationNode::DestroyAudioDevice,
+ base::Unretained(this)));
+ }
+}
+
+void AudioDestinationNode::DestroyAudioDevice() {
+ if (audio_device_ == audio_device_to_delete_) {
+ audio_device_.reset();
+ }
}
} // namespace audio
diff --git a/src/cobalt/audio/audio_destination_node.h b/src/cobalt/audio/audio_destination_node.h
index eafebef..bc36afc 100644
--- a/src/cobalt/audio/audio_destination_node.h
+++ b/src/cobalt/audio/audio_destination_node.h
@@ -17,6 +17,7 @@
#include <vector>
+#include "base/message_loop.h"
#include "cobalt/audio/audio_device.h"
#include "cobalt/audio/audio_helpers.h"
#include "cobalt/audio/audio_node.h"
@@ -55,14 +56,16 @@
// From AudioNode.
void OnInputNodeConnected() override;
- scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32 /*number_of_frames*/,
- SampleType) override {
+ scoped_ptr<ShellAudioBus> PassAudioBusFromSource(
+ int32 /*number_of_frames*/, SampleType /*sample_type*/,
+ bool* /*finished*/) override {
NOTREACHED();
return scoped_ptr<ShellAudioBus>();
}
// From AudioDevice::RenderCallback.
- void FillAudioBus(ShellAudioBus* audio_bus, bool* silence) override;
+ void FillAudioBus(bool all_consumed, ShellAudioBus* audio_bus,
+ bool* silence) override;
DEFINE_WRAPPABLE_TYPE(AudioDestinationNode);
@@ -70,9 +73,13 @@
~AudioDestinationNode() override;
private:
+ void DestroyAudioDevice();
+
+ MessageLoop* message_loop_;
uint32 max_channel_count_;
scoped_ptr<AudioDevice> audio_device_;
+ AudioDevice* audio_device_to_delete_ = NULL;
DISALLOW_COPY_AND_ASSIGN(AudioDestinationNode);
};
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index 8404a2e..5be7e64 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -51,6 +51,14 @@
#if defined(SB_USE_SB_AUDIO_SINK)
+namespace {
+// Write |kTailSizeInFrames| frames of silence at the end of playback to ensure
+// the audible frames being played on platforms with strict underflow control.
+// Increasing this value will also increase the silence between two sounds. So
+// it shouldn't be set to a very too large.
+const int kTailSizeInFrames = kRenderBufferSizeFrames * 8;
+} // namespace
+
class AudioDevice::Impl {
public:
Impl(int number_of_channels, RenderCallback* callback);
@@ -87,12 +95,14 @@
scoped_array<uint8> output_frame_buffer_;
void* frame_buffers_[1];
- int64 frames_rendered_; // Frames retrieved from |render_callback_|.
- int64 frames_consumed_; // Accumulated frames consumed reported by the sink.
+ int64 frames_rendered_ = 0; // Frames retrieved from |render_callback_|.
+ int64 frames_consumed_ = 0; // Accumulated frames consumed by the sink.
+ int64 silence_written_ = 0; // Silence frames written after all nodes are
+ // finished.
- bool was_silence_last_update_;
+ bool was_silence_last_update_ = false;
- SbAudioSink audio_sink_;
+ SbAudioSink audio_sink_ = kSbAudioSinkInvalid;
DISALLOW_COPY_AND_ASSIGN(Impl);
};
@@ -107,11 +117,7 @@
GetPreferredOutputSampleType(), ShellAudioBus::kPlanar),
output_frame_buffer_(
new uint8[kFramesPerChannel * number_of_channels_ *
- GetStarboardSampleTypeSize(output_sample_type_)]),
- frames_rendered_(0),
- frames_consumed_(0),
- was_silence_last_update_(false),
- audio_sink_(kSbAudioSinkInvalid) {
+ GetStarboardSampleTypeSize(output_sample_type_)]) {
DCHECK(number_of_channels_ == 1 || number_of_channels_ == 2)
<< "Invalid number of channels: " << number_of_channels_;
DCHECK(render_callback_);
@@ -189,11 +195,21 @@
}
bool silence = true;
+ bool all_consumed =
+ silence_written_ != 0 && *frames_in_buffer <= silence_written_;
- // Fill our temporary buffer with planar PCM float samples.
- render_callback_->FillAudioBus(&input_audio_bus_, &silence);
+ render_callback_->FillAudioBus(all_consumed, &input_audio_bus_, &silence);
- if (!silence) {
+ bool fill_output = true;
+ if (silence) {
+ fill_output = kTailSizeInFrames > silence_written_;
+ silence_written_ += kRenderBufferSizeFrames;
+ } else {
+ // Reset |silence_written_| if a new sound is played after some silence
+ // frames were injected.
+ silence_written_ = 0;
+ }
+ if (fill_output) {
FillOutputAudioBus();
frames_rendered_ += kRenderBufferSizeFrames;
@@ -357,8 +373,9 @@
if ((kFramesPerChannel - *total_frames) >= kRenderBufferSizeFrames) {
// Fill our temporary buffer with PCM float samples.
+ bool all_consumed = false; // Keep the sink alive on legacy platforms.
bool silence = true;
- render_callback_->FillAudioBus(&audio_bus_, &silence);
+ render_callback_->FillAudioBus(all_consumed, &audio_bus_, &silence);
if (!silence) {
FillOutputAudioBus();
diff --git a/src/cobalt/audio/audio_device.h b/src/cobalt/audio/audio_device.h
index dd5f898..7e227ab 100644
--- a/src/cobalt/audio/audio_device.h
+++ b/src/cobalt/audio/audio_device.h
@@ -38,11 +38,16 @@
typedef ::media::ShellAudioBus ShellAudioBus;
#endif // defined(COBALT_MEDIA_SOURCE_2016)
+ // |all_consumed| will be set to true if all audio frames has been consumed.
+ // This gives the AudioDestinationNode a chance to decide if the AudioDevice
+ // should be killed.
+ // |audio_buffer| contains the audio frames to be mixed with input audio if
+ // there is any.
// |silence| will be set to true before calling if |audio_buffer| contains
- // only silence samples, it will be set to |false| otherwise. On return
- // FillAudioBus() will set |silence| to |false| if it has modified
- // |audio_buffer|.
- virtual void FillAudioBus(ShellAudioBus* audio_buffer, bool* silence) = 0;
+ // only silence samples, it will be set to |false| otherwise. It will be
+ // set to false on return if |audio_buffer| has been modified.
+ virtual void FillAudioBus(bool all_consumed, ShellAudioBus* audio_buffer,
+ bool* silence) = 0;
protected:
~RenderCallback() {}
diff --git a/src/cobalt/audio/audio_file_reader.h b/src/cobalt/audio/audio_file_reader.h
index b7c95ca..b7721da 100644
--- a/src/cobalt/audio/audio_file_reader.h
+++ b/src/cobalt/audio/audio_file_reader.h
@@ -15,8 +15,7 @@
#ifndef COBALT_AUDIO_AUDIO_FILE_READER_H_
#define COBALT_AUDIO_AUDIO_FILE_READER_H_
-#include "base/memory/scoped_ptr.h" // For scoped_array
-
+#include "base/memory/scoped_ptr.h"
#include "cobalt/audio/audio_helpers.h"
namespace cobalt {
@@ -29,14 +28,12 @@
static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size,
SampleType sample_type);
- // Returns the sample data stored as float sample in planar form. Note that
- // this function transfers the ownership of the data to the caller so it can
- // only be called once.
- virtual scoped_array<uint8> sample_data() = 0;
virtual float sample_rate() const = 0;
virtual int32 number_of_frames() const = 0;
virtual int32 number_of_channels() const = 0;
virtual SampleType sample_type() const = 0;
+
+ virtual scoped_ptr<ShellAudioBus> ResetAndReturnAudioBus() = 0;
};
} // namespace audio
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index 9be4c11..3cbd47c 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -21,6 +21,7 @@
#else // defined(COBALT_MEDIA_SOURCE_2016)
#include "media/base/endian_util.h"
#endif // defined(COBALT_MEDIA_SOURCE_2016)
+#include "starboard/memory.h"
namespace cobalt {
namespace audio {
@@ -105,7 +106,7 @@
void AudioFileReaderWAV::ParseChunks(const uint8* data, size_t size) {
uint32 offset = kWAVRIFFChunkHeaderSize;
- bool is_sample_in_float = false;
+ bool is_src_sample_in_float = false;
// If the WAV file is PCM format, it has two sub-chunks: first one is "fmt"
// and the second one is "data".
// TODO: support the cases that the WAV file is non-PCM format and the
@@ -124,12 +125,12 @@
}
if (chunk_id == kWAVChunkID_fmt && i == 0) {
- if (!ParseWAV_fmt(data, offset, chunk_size, &is_sample_in_float)) {
+ if (!ParseWAV_fmt(data, offset, chunk_size, &is_src_sample_in_float)) {
DLOG(WARNING) << "Parse fmt chunk failed.";
break;
}
} else if (chunk_id == kWAVChunkID_data && i == 1) {
- if (!ParseWAV_data(data, offset, chunk_size, is_sample_in_float)) {
+ if (!ParseWAV_data(data, offset, chunk_size, is_src_sample_in_float)) {
DLOG(WARNING) << "Parse data chunk failed.";
break;
}
@@ -143,7 +144,10 @@
}
bool AudioFileReaderWAV::ParseWAV_fmt(const uint8* data, size_t offset,
- size_t size, bool* is_sample_in_float) {
+ size_t size,
+ bool* is_src_sample_in_float) {
+ DCHECK(is_src_sample_in_float);
+
// Check size for complete header.
if (size < kWAVfmtChunkHeaderSize) {
return false;
@@ -157,7 +161,7 @@
return false;
}
- *is_sample_in_float = format_code == kWAVFormatCodeFloat;
+ *is_src_sample_in_float = format_code == kWAVFormatCodeFloat;
// Load channel count.
number_of_channels_ = load_uint16_little_endian(data + offset + 2);
@@ -176,8 +180,8 @@
// Check sample size, we only support 32 bit floats or 16 bit PCM.
uint16 bits_per_sample = load_uint16_little_endian(data + offset + 14);
- if ((*is_sample_in_float && bits_per_sample != 32) ||
- (!*is_sample_in_float && bits_per_sample != 16)) {
+ if ((*is_src_sample_in_float && bits_per_sample != 32) ||
+ (!*is_src_sample_in_float && bits_per_sample != 16)) {
DLOG(ERROR) << "Bad bits per sample on WAV. "
<< "Bits per sample: " << bits_per_sample;
return false;
@@ -187,57 +191,92 @@
}
bool AudioFileReaderWAV::ParseWAV_data(const uint8* data, size_t offset,
- size_t size, bool is_sample_in_float) {
- const uint8* data_samples = data + offset;
-
+ size_t size,
+ bool is_src_sample_in_float) {
// Set number of frames based on size of data chunk.
- const int32 bytes_per_src_sample =
- static_cast<int32>(is_sample_in_float ? sizeof(float) : sizeof(int16));
+ const int32 bytes_per_src_sample = static_cast<int32>(
+ is_src_sample_in_float ? sizeof(float) : sizeof(int16));
number_of_frames_ =
static_cast<int32>(size / (bytes_per_src_sample * number_of_channels_));
- const int32 bytes_per_dest_sample =
- static_cast<int32>(GetSampleTypeSize(sample_type_));
- const bool is_dest_float = sample_type_ == kSampleTypeFloat32;
// We store audio samples in the current platform's preferred format.
- sample_data_.reset(new uint8[static_cast<size_t>(
- number_of_frames_ * number_of_channels_ * bytes_per_dest_sample)]);
+ audio_bus_.reset(new ShellAudioBus(number_of_channels_, number_of_frames_,
+ sample_type_,
+ ShellAudioBus::kInterleaved));
- // Here we handle all 4 possible conversion cases. Also note that the
- // source data is stored interleaved, and that need to convert it to planar.
- uint8* dest_sample = sample_data_.get();
- for (int32 i = 0; i < number_of_channels_; ++i) {
- const uint8* src_samples = data_samples + i * bytes_per_src_sample;
+// Both the source data and the destination data are stored in interleaved.
+#if SB_IS(LITTLE_ENDIAN)
+ if ((!is_src_sample_in_float && sample_type_ == kSampleTypeInt16) ||
+ (is_src_sample_in_float && sample_type_ == kSampleTypeFloat32)) {
+ SbMemoryCopy(audio_bus_->interleaved_data(), data + offset, size);
+ } else if (!is_src_sample_in_float && sample_type_ == kSampleTypeFloat32) {
+ // Convert from int16 to float32
+ const int16* src_samples = reinterpret_cast<const int16*>(data + offset);
+ float* dest_samples =
+ reinterpret_cast<float*>(audio_bus_->interleaved_data());
- for (int32 j = 0; j < number_of_frames_; ++j) {
- if (is_dest_float) {
- float sample;
- if (is_sample_in_float) {
- uint32 sample_as_uint32 = load_uint32_little_endian(src_samples);
- sample = bit_cast<float>(sample_as_uint32);
- } else {
- uint16 sample_pcm_unsigned = load_uint16_little_endian(src_samples);
- int16 sample_pcm = bit_cast<int16>(sample_pcm_unsigned);
- sample = ConvertSample<int16, float>(sample_pcm);
- }
- reinterpret_cast<float*>(dest_sample)[i * number_of_frames_ + j] =
- sample;
- src_samples += bytes_per_src_sample * number_of_channels_;
- } else {
- int16 sample;
- if (is_sample_in_float) {
- uint32 sample_as_uint32 = load_uint32_little_endian(src_samples);
- float value = bit_cast<float>(sample_as_uint32);
- sample = ConvertSample<float, int16>(value);
- } else {
- sample = bit_cast<int16>(load_uint16_little_endian(src_samples));
- }
- reinterpret_cast<int16*>(dest_sample)[i * number_of_frames_ + j] =
- sample;
- src_samples += bytes_per_src_sample * number_of_channels_;
- }
+ for (int32 i = 0; i < number_of_frames_ * number_of_channels_; ++i) {
+ *dest_samples = ConvertSample<int16, float>(*src_samples);
+ ++src_samples;
+ ++dest_samples;
+ }
+ } else {
+ // Convert from float32 to int16
+ const float* src_samples = reinterpret_cast<const float*>(data + offset);
+ int16* dest_samples =
+ reinterpret_cast<int16*>(audio_bus_->interleaved_data());
+
+ for (int32 i = 0; i < number_of_frames_ * number_of_channels_; ++i) {
+ *dest_samples = ConvertSample<float, int16>(*src_samples);
+ ++src_samples;
+ ++dest_samples;
}
}
+#else // SB_IS(LITTLE_ENDIAN)
+ if (!is_src_sample_in_float && sample_type_ == kSampleTypeInt16) {
+ const uint8_t* src_samples = data + offset;
+ int16* dest_samples =
+ reinterpret_cast<int16*>(audio_bus_->interleaved_data());
+ for (int32 i = 0; i < number_of_frames_ * number_of_channels_; ++i) {
+ *dest_samples = load_uint16_little_endian(src_samples);
+ src_samples += bytes_per_src_sample;
+ ++dest_samples;
+ }
+ } else if (is_src_sample_in_float && sample_type_ == kSampleTypeFloat32) {
+ const uint8_t* src_samples = data + offset;
+ float* dest_samples =
+ reinterpret_cast<float*>(audio_bus_->interleaved_data());
+ for (int32 i = 0; i < number_of_frames_ * number_of_channels_; ++i) {
+ uint32 sample_as_uint32 = load_uint32_little_endian(src_samples);
+ *dest_samples = bit_cast<float>(sample_as_uint32);
+ src_samples += bytes_per_src_sample;
+ ++dest_samples;
+ }
+ } else if (!is_src_sample_in_float && sample_type_ == kSampleTypeFloat32) {
+ // Convert from int16 to float32
+ const uint8_t* src_samples = data + offset;
+ float* dest_samples =
+ reinterpret_cast<float*>(audio_bus_->interleaved_data());
+ for (int32 i = 0; i < number_of_frames_ * number_of_channels_; ++i) {
+ int16 sample_as_int16 = load_int16_little_endian(src_samples);
+ *dest_samples = ConvertSample<int16, float>(sample_as_int16);
+ src_samples += bytes_per_src_sample;
+ ++dest_samples;
+ }
+ } else {
+ // Convert from float32 to int16
+ const uint8_t* src_samples = data + offset;
+ int16* dest_samples =
+ reinterpret_cast<int16*>(audio_bus_->interleaved_data());
+ for (int32 i = 0; i < number_of_frames_ * number_of_channels_; ++i) {
+ uint32 sample_as_uint32 = load_uint32_little_endian(src_samples);
+ *dest_samples =
+ ConvertSample<float, int16>(bit_cast<float>(sample_as_uint32));
+ src_samples += bytes_per_src_sample;
+ ++dest_samples;
+ }
+ }
+#endif // SB_IS(LITTLE_ENDIAN)
return true;
}
diff --git a/src/cobalt/audio/audio_file_reader_wav.h b/src/cobalt/audio/audio_file_reader_wav.h
index 5ae0e51..cf3cdb4 100644
--- a/src/cobalt/audio/audio_file_reader_wav.h
+++ b/src/cobalt/audio/audio_file_reader_wav.h
@@ -15,6 +15,7 @@
#ifndef COBALT_AUDIO_AUDIO_FILE_READER_WAV_H_
#define COBALT_AUDIO_AUDIO_FILE_READER_WAV_H_
+#include "base/memory/scoped_ptr.h"
#include "cobalt/audio/audio_file_reader.h"
#include "cobalt/audio/audio_helpers.h"
@@ -28,25 +29,28 @@
static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size,
SampleType sample_type);
- scoped_array<uint8> sample_data() override { return sample_data_.Pass(); }
float sample_rate() const override { return sample_rate_; }
int32 number_of_frames() const override { return number_of_frames_; }
int32 number_of_channels() const override { return number_of_channels_; }
SampleType sample_type() const override { return sample_type_; }
+ scoped_ptr<ShellAudioBus> ResetAndReturnAudioBus() override {
+ return audio_bus_.Pass();
+ }
+
private:
AudioFileReaderWAV(const uint8* data, size_t size, SampleType sample_type);
bool ParseRIFFHeader(const uint8* data, size_t size);
void ParseChunks(const uint8* data, size_t size);
bool ParseWAV_fmt(const uint8* data, size_t offset, size_t size,
- bool* is_sample_in_float);
+ bool* is_src_sample_in_float);
bool ParseWAV_data(const uint8* data, size_t offset, size_t size,
- bool is_sample_in_float);
+ bool is_src_sample_in_float);
- bool is_valid() { return sample_data_ != NULL; }
+ bool is_valid() { return audio_bus_ != NULL; }
- scoped_array<uint8> sample_data_;
+ scoped_ptr<ShellAudioBus> audio_bus_;
float sample_rate_;
int32 number_of_frames_;
int32 number_of_channels_;
diff --git a/src/cobalt/audio/audio_helpers.h b/src/cobalt/audio/audio_helpers.h
index 2d36bec..24cea54 100644
--- a/src/cobalt/audio/audio_helpers.h
+++ b/src/cobalt/audio/audio_helpers.h
@@ -30,10 +30,12 @@
namespace audio {
#if defined(COBALT_MEDIA_SOURCE_2016)
+typedef media::ShellAudioBus ShellAudioBus;
typedef media::ShellAudioBus::SampleType SampleType;
const SampleType kSampleTypeInt16 = media::ShellAudioBus::kInt16;
const SampleType kSampleTypeFloat32 = media::ShellAudioBus::kFloat32;
#else // defined(COBALT_MEDIA_SOURCE_2016)
+typedef ::media::ShellAudioBus ShellAudioBus;
typedef ::media::ShellAudioBus::SampleType SampleType;
const SampleType kSampleTypeInt16 = ::media::ShellAudioBus::kInt16;
const SampleType kSampleTypeFloat32 = ::media::ShellAudioBus::kFloat32;
diff --git a/src/cobalt/audio/audio_node.h b/src/cobalt/audio/audio_node.h
index 630013e..a8c2dd8 100644
--- a/src/cobalt/audio/audio_node.h
+++ b/src/cobalt/audio/audio_node.h
@@ -112,9 +112,8 @@
// Called when a new input node has been connected.
virtual void OnInputNodeConnected() {}
- // TODO: Support wrapping ShellAudioBus into another ShellAudioBus.
virtual scoped_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 number_of_frames, SampleType sample_type) = 0;
+ int32 number_of_frames, SampleType sample_type, bool* finished) = 0;
AudioLock* audio_lock() const { return audio_lock_.get(); }
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index 529986d..cac480a 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -227,10 +227,14 @@
}
void AudioNodeInput::FillAudioBus(ShellAudioBus* output_audio_bus,
- bool* silence) {
+ bool* silence, bool* all_finished) {
+ DCHECK(silence);
+ DCHECK(all_finished);
+
// This is called by Audio thread.
owner_node_->audio_lock()->AssertLocked();
+ *all_finished = true;
// TODO: Consider computing computedNumberOfChannels and do up-mix or
// down-mix base on computedNumberOfChannels. The current implementation
// is based on the fact that the channelCountMode is max.
@@ -244,9 +248,11 @@
// from one or more AudioNode outputs. Fan-in is supported.
for (std::set<AudioNodeOutput*>::iterator iter = outputs_.begin();
iter != outputs_.end(); ++iter) {
+ bool finished = false;
scoped_ptr<ShellAudioBus> audio_bus = (*iter)->PassAudioBusFromSource(
static_cast<int32>(output_audio_bus->frames()),
- output_audio_bus->sample_type());
+ output_audio_bus->sample_type(), &finished);
+ *all_finished &= finished;
if (audio_bus) {
if (*silence && audio_bus->channels() == output_audio_bus->channels()) {
diff --git a/src/cobalt/audio/audio_node_input.h b/src/cobalt/audio/audio_node_input.h
index a186e60..6ed0af5 100644
--- a/src/cobalt/audio/audio_node_input.h
+++ b/src/cobalt/audio/audio_node_input.h
@@ -58,7 +58,8 @@
// For each input, an AudioNode performs a mixing of all connections to that
// input. FillAudioBus() performs that action. In the case of multiple
// connections, it sums the result into |audio_bus|.
- void FillAudioBus(ShellAudioBus* audio_bus, bool* silence);
+ void FillAudioBus(ShellAudioBus* audio_bus, bool* silence,
+ bool* all_finished);
private:
AudioNode* const owner_node_;
diff --git a/src/cobalt/audio/audio_node_input_output_test.cc b/src/cobalt/audio/audio_node_input_output_test.cc
index 612edaf..6d394eb 100644
--- a/src/cobalt/audio/audio_node_input_output_test.cc
+++ b/src/cobalt/audio/audio_node_input_output_test.cc
@@ -17,6 +17,8 @@
#include "cobalt/audio/audio_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
+// TODO: Consolidate ShellAudioBus creation code
+
namespace cobalt {
namespace audio {
@@ -46,32 +48,32 @@
// From AudioNode.
scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32, /*number_of_frames*/
- SampleType) override {
+ SampleType, bool*) override {
NOTREACHED();
return scoped_ptr<ShellAudioBus>();
}
// From AudioDevice::RenderCallback.
- void FillAudioBus(ShellAudioBus* audio_bus, bool* silence) override {
+ void FillAudioBus(bool all_consumed, ShellAudioBus* audio_bus,
+ bool* silence) override {
+ UNREFERENCED_PARAMETER(all_consumed);
+
AudioLock::AutoLock lock(audio_lock());
+ bool all_finished;
// Destination node only has one input.
- Input(0)->FillAudioBus(audio_bus, silence);
+ Input(0)->FillAudioBus(audio_bus, silence, &all_finished);
}
};
void FillAudioBusFromOneSource(
- size_t num_of_src_channel, size_t num_of_frames,
- scoped_array<uint8> src_data,
+ scoped_ptr<ShellAudioBus> src_data,
const AudioNodeChannelInterpretation& interpretation,
ShellAudioBus* audio_bus, bool* silence) {
scoped_refptr<AudioContext> audio_context(new AudioContext());
scoped_refptr<AudioBufferSourceNode> source(
audio_context->CreateBufferSource());
- scoped_refptr<AudioBuffer> buffer(
- new AudioBuffer(NULL, 44100, static_cast<int32>(num_of_frames),
- static_cast<int32>(num_of_src_channel), src_data.Pass(),
- kSampleTypeFloat32));
+ scoped_refptr<AudioBuffer> buffer(new AudioBuffer(44100, src_data.Pass()));
source->set_buffer(buffer);
scoped_refptr<AudioDestinationNodeMock> destination(
@@ -80,7 +82,7 @@
source->Connect(destination, 0, 0, NULL);
source->Start(0, 0, NULL);
- destination->FillAudioBus(audio_bus, silence);
+ destination->FillAudioBus(true, audio_bus, silence);
}
class AudioNodeInputOutputTest : public ::testing::Test {
@@ -89,35 +91,34 @@
};
TEST_F(AudioNodeInputOutputTest, StereoToStereoSpeakersLayoutTest) {
- size_t num_of_src_channel = 2;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 2;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[50];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 20.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 20.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[200]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 200 * sizeof(uint8));
-
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
if (c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 20);
} else {
@@ -131,35 +132,35 @@
}
TEST_F(AudioNodeInputOutputTest, StereoToStereoDiscreteLayoutTest) {
- size_t num_of_src_channel = 2;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 2;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[50];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 20.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 20.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[200]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 200 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
if (c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 20);
} else {
@@ -173,33 +174,32 @@
}
TEST_F(AudioNodeInputOutputTest, MonoToStereoSpeakersLayoutTest) {
- size_t num_of_src_channel = 1;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 1;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[25];
- for (size_t i = 0; i < num_of_frames; ++i) {
+ for (size_t i = 0; i < kNumOfFrames; ++i) {
src_data_in_float[i] = 50.0f;
}
- scoped_array<uint8> src_data(new uint8[100]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 100 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 50);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -209,33 +209,32 @@
}
TEST_F(AudioNodeInputOutputTest, MonoToStereoDiscreteLayoutTest) {
- size_t num_of_src_channel = 1;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 1;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[25];
- for (size_t i = 0; i < num_of_frames; ++i) {
+ for (size_t i = 0; i < kNumOfFrames; ++i) {
src_data_in_float[i] = 50.0f;
}
- scoped_array<uint8> src_data(new uint8[100]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 100 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames && c == 0) {
+ if (i < kNumOfFrames && c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 50);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -245,35 +244,35 @@
}
TEST_F(AudioNodeInputOutputTest, QuadToStereoSpeakersLayoutTest) {
- size_t num_of_src_channel = 4;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 4;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[100];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[400]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 400 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
if (c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 20);
} else {
@@ -287,35 +286,35 @@
}
TEST_F(AudioNodeInputOutputTest, QuadToStereoDiscreteLayoutTest) {
- size_t num_of_src_channel = 4;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 4;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[100];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[400]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 400 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
if (c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 10);
} else {
@@ -329,35 +328,35 @@
}
TEST_F(AudioNodeInputOutputTest, FivePointOneToStereoSpeakersLayoutTest) {
- size_t num_of_src_channel = 6;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 10;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 6;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 10;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[60];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[240]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 240 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
if (c == 0) {
EXPECT_FLOAT_EQ(audio_bus->GetFloat32Sample(c, i), 66.568f);
} else {
@@ -371,35 +370,35 @@
}
TEST_F(AudioNodeInputOutputTest, FivePointOneToStereoDiscreteLayoutTest) {
- size_t num_of_src_channel = 6;
- size_t num_of_dest_channel = 2;
- size_t num_of_frames = 10;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 6;
+ const size_t kNumOfDestChannels = 2;
+ const size_t kNumOfFrames = 10;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[60];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[240]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 240 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
if (c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 10);
} else {
@@ -413,35 +412,35 @@
}
TEST_F(AudioNodeInputOutputTest, StereoToMonoSpeakersLayoutTest) {
- size_t num_of_src_channel = 2;
- size_t num_of_dest_channel = 1;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 2;
+ const size_t kNumOfDestChannels = 1;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[50];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 20.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 20.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[200]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 200 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 30);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -451,35 +450,35 @@
}
TEST_F(AudioNodeInputOutputTest, StereoToMonoDiscreteLayoutTest) {
- size_t num_of_src_channel = 2;
- size_t num_of_dest_channel = 1;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 2;
+ const size_t kNumOfDestChannels = 1;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[50];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 20.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 20.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[200]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 200 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 20);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -489,35 +488,35 @@
}
TEST_F(AudioNodeInputOutputTest, QuadToMonoSpeakersLayoutTest) {
- size_t num_of_src_channel = 4;
- size_t num_of_dest_channel = 1;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 4;
+ const size_t kNumOfDestChannels = 1;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[100];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[400]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 400 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 25);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -527,35 +526,35 @@
}
TEST_F(AudioNodeInputOutputTest, QuadToMonoDiscreteLayoutTest) {
- size_t num_of_src_channel = 4;
- size_t num_of_dest_channel = 1;
- size_t num_of_frames = 25;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 4;
+ const size_t kNumOfDestChannels = 1;
+ const size_t kNumOfFrames = 25;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[100];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[400]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 400 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 10);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -565,35 +564,35 @@
}
TEST_F(AudioNodeInputOutputTest, FivePointOneToMonoSpeakersLayoutTest) {
- size_t num_of_src_channel = 6;
- size_t num_of_dest_channel = 1;
- size_t num_of_frames = 10;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 6;
+ const size_t kNumOfDestChannels = 1;
+ const size_t kNumOfFrames = 10;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
float src_data_in_float[60];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[240]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 240 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
EXPECT_FLOAT_EQ(audio_bus->GetFloat32Sample(c, i), 106.213f);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -603,35 +602,35 @@
}
TEST_F(AudioNodeInputOutputTest, FivePointOneToMonoDiscreteLayoutTest) {
- size_t num_of_src_channel = 6;
- size_t num_of_dest_channel = 1;
- size_t num_of_frames = 10;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 6;
+ const size_t kNumOfDestChannels = 1;
+ const size_t kNumOfFrames = 10;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationDiscrete;
float src_data_in_float[60];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames; ++i) {
- src_data_in_float[c * num_of_frames + i] = 10.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames; ++frame) {
+ src_data_in_float[frame * kNumOfSrcChannels + channel] =
+ 10.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data(new uint8[240]);
- uint8* src_buffer = src_data.get();
- memcpy(src_buffer, src_data_in_float, 240 * sizeof(uint8));
+ scoped_ptr<ShellAudioBus> src_data(
+ new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
- FillAudioBusFromOneSource(num_of_src_channel, num_of_frames, src_data.Pass(),
- interpretation, audio_bus.get(), &silence);
+ FillAudioBusFromOneSource(src_data.Pass(), kInterpretation, audio_bus.get(),
+ &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames) {
+ if (i < kNumOfFrames) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 10);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 0);
@@ -643,72 +642,70 @@
TEST_F(AudioNodeInputOutputTest, MultipleInputNodesLayoutTest) {
scoped_refptr<AudioContext> audio_context(new AudioContext());
- size_t num_of_src_channel = 2;
- size_t num_of_dest_channel = 2;
- AudioNodeChannelInterpretation interpretation =
+ const size_t kNumOfSrcChannels = 2;
+ const size_t kNumOfDestChannels = 2;
+ const AudioNodeChannelInterpretation kInterpretation =
kAudioNodeChannelInterpretationSpeakers;
- size_t num_of_frames_1 = 25;
+ const size_t kNumOfFrames_1 = 25;
float src_data_in_float_1[50];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames_1; ++i) {
- src_data_in_float_1[c * num_of_frames_1 + i] = 20.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames_1; ++frame) {
+ src_data_in_float_1[frame * kNumOfSrcChannels + channel] =
+ 20.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data_1(new uint8[200]);
- uint8* src_buffer_1 = src_data_1.get();
- memcpy(src_buffer_1, src_data_in_float_1, 200 * sizeof(uint8));
+
+ scoped_ptr<ShellAudioBus> src_data_1(new ShellAudioBus(
+ kNumOfSrcChannels, kNumOfFrames_1, src_data_in_float_1));
scoped_refptr<AudioBufferSourceNode> source_1(
audio_context->CreateBufferSource());
scoped_refptr<AudioBuffer> buffer_1(
- new AudioBuffer(NULL, 44100, static_cast<int32>(num_of_frames_1),
- static_cast<int32>(num_of_src_channel), src_data_1.Pass(),
- kSampleTypeFloat32));
+ new AudioBuffer(44100, src_data_1.Pass()));
source_1->set_buffer(buffer_1);
- size_t num_of_frames_2 = 50;
+ const size_t kNumOfFrames_2 = 50;
float src_data_in_float_2[100];
- for (size_t c = 0; c < num_of_src_channel; ++c) {
- for (size_t i = 0; i < num_of_frames_2; ++i) {
- src_data_in_float_2[c * num_of_frames_2 + i] = 40.0f * (c + 1);
+ for (size_t channel = 0; channel < kNumOfSrcChannels; ++channel) {
+ for (size_t frame = 0; frame < kNumOfFrames_2; ++frame) {
+ src_data_in_float_2[frame * kNumOfSrcChannels + channel] =
+ 40.0f * (channel + 1);
}
}
- scoped_array<uint8> src_data_2(new uint8[400]);
- uint8* src_buffer_2 = src_data_2.get();
- memcpy(src_buffer_2, src_data_in_float_2, 400 * sizeof(uint8));
+
+ scoped_ptr<ShellAudioBus> src_data_2(new ShellAudioBus(
+ kNumOfSrcChannels, kNumOfFrames_2, src_data_in_float_2));
scoped_refptr<AudioBufferSourceNode> source_2(
audio_context->CreateBufferSource());
scoped_refptr<AudioBuffer> buffer_2(
- new AudioBuffer(NULL, 44100, static_cast<int32>(num_of_frames_2),
- static_cast<int32>(num_of_src_channel), src_data_2.Pass(),
- kSampleTypeFloat32));
+ new AudioBuffer(44100, src_data_2.Pass()));
source_2->set_buffer(buffer_2);
scoped_refptr<AudioDestinationNodeMock> destination(
new AudioDestinationNodeMock(audio_context.get()));
- destination->set_channel_interpretation(interpretation);
+ destination->set_channel_interpretation(kInterpretation);
source_1->Connect(destination, 0, 0, NULL);
source_2->Connect(destination, 0, 0, NULL);
source_1->Start(0, 0, NULL);
source_2->Start(0, 0, NULL);
scoped_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(num_of_dest_channel, kRenderBufferSizeFrames,
+ new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
audio_bus->ZeroAllFrames();
bool silence = true;
- destination->FillAudioBus(audio_bus.get(), &silence);
+ destination->FillAudioBus(true, audio_bus.get(), &silence);
EXPECT_FALSE(silence);
- for (size_t c = 0; c < num_of_dest_channel; ++c) {
+ for (size_t c = 0; c < kNumOfDestChannels; ++c) {
for (size_t i = 0; i < kRenderBufferSizeFrames; ++i) {
- if (i < num_of_frames_1) {
+ if (i < kNumOfFrames_1) {
if (c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 60);
} else {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 120);
}
- } else if (i < num_of_frames_2) {
+ } else if (i < kNumOfFrames_2) {
if (c == 0) {
EXPECT_EQ(audio_bus->GetFloat32Sample(c, i), 40);
} else {
diff --git a/src/cobalt/audio/audio_node_output.cc b/src/cobalt/audio/audio_node_output.cc
index a761084..91dbfe7 100644
--- a/src/cobalt/audio/audio_node_output.cc
+++ b/src/cobalt/audio/audio_node_output.cc
@@ -60,12 +60,13 @@
}
scoped_ptr<ShellAudioBus> AudioNodeOutput::PassAudioBusFromSource(
- int32 number_of_frames, SampleType sample_type) {
+ int32 number_of_frames, SampleType sample_type, bool* finished) {
// This is called by Audio thread.
owner_node_->audio_lock()->AssertLocked();
// Pull audio buffer from its owner node.
- return owner_node_->PassAudioBusFromSource(number_of_frames, sample_type)
+ return owner_node_
+ ->PassAudioBusFromSource(number_of_frames, sample_type, finished)
.Pass();
}
diff --git a/src/cobalt/audio/audio_node_output.h b/src/cobalt/audio/audio_node_output.h
index 03d285b..728465d 100644
--- a/src/cobalt/audio/audio_node_output.h
+++ b/src/cobalt/audio/audio_node_output.h
@@ -52,7 +52,8 @@
void DisconnectAll();
scoped_ptr<ShellAudioBus> PassAudioBusFromSource(int32 number_of_frames,
- SampleType sample_type);
+ SampleType sample_type,
+ bool* finished);
private:
AudioNode* const owner_node_;
diff --git a/src/cobalt/audio/audio_test.gyp b/src/cobalt/audio/audio_test.gyp
new file mode 100644
index 0000000..8f15ec9
--- /dev/null
+++ b/src/cobalt/audio/audio_test.gyp
@@ -0,0 +1,67 @@
+# Copyright 2015 Google Inc. 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.
+
+{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
+ 'targets': [
+ # This target can choose the correct media dependency.
+ {
+ 'target_name': 'media',
+ 'type': 'static_library',
+ 'conditions': [
+ ['cobalt_media_source_2016==1', {
+ 'dependencies': [
+ '<(DEPTH)/cobalt/media/media2.gyp:media2',
+ ],
+ }, {
+ 'dependencies': [
+ '<(DEPTH)/cobalt/media/media.gyp:media',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'audio_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'audio_node_input_output_test.cc',
+ ],
+ 'dependencies': [
+ 'media',
+ '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+
+ # TODO: Remove the dependency below, it works around the fact that
+ # ScriptValueFactory has non-virtual method CreatePromise().
+ '<(DEPTH)/cobalt/script/engine.gyp:engine',
+ ],
+ },
+
+ {
+ 'target_name': 'audio_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'audio_test',
+ ],
+ 'variables': {
+ 'executable_name': 'audio_test',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 758a2d2..32f9c55 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -53,6 +53,7 @@
MacroOpWithNameOnly(childList) \
MacroOpWithNameOnly(click) \
MacroOpWithNameOnly(close) \
+ MacroOpWithNameOnly(dataavailable) \
MacroOpWithNameOnly(deviceorientation) \
MacroOpWithNameOnly(durationchange) \
MacroOpWithNameOnly(emptied) \
diff --git a/src/cobalt/bindings/bindings.gypi b/src/cobalt/bindings/bindings.gypi
index a54745c..0ee07e5 100644
--- a/src/cobalt/bindings/bindings.gypi
+++ b/src/cobalt/bindings/bindings.gypi
@@ -400,7 +400,8 @@
],
'sources': [
'<@(source_idl_files)',
- '<@(generated_header_idl_files)'
+ '<@(generated_header_idl_files)',
+ 'shared/idl_conditional_macros.h',
],
'actions': [{
'action_name': 'generate_type_conversion_header',
diff --git a/src/cobalt/bindings/code_generator_cobalt.py b/src/cobalt/bindings/code_generator_cobalt.py
index a16a417..61f0641 100644
--- a/src/cobalt/bindings/code_generator_cobalt.py
+++ b/src/cobalt/bindings/code_generator_cobalt.py
@@ -566,6 +566,7 @@
if interface.parent:
context['parent_interface'] = self.path_builder.BindingsClass(
interface.parent)
+ context['parent_interface_name'] = interface.parent
context['is_exception_interface'] = interface.is_exception
context['forward_declarations'] = sorted(
referenced_class_contexts, key=lambda x: x['fully_qualified_name'])
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
index 07111cf..6786eb6 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.cc
@@ -19,6 +19,11 @@
// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
// Auto-generated from template: bindings/mozjs45/templates/interface.cc.template
+
+// This must be included above the check for ENABLE_CONDITIONAL_INTERFACE, since
+// ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(ENABLE_CONDITIONAL_INTERFACE)
#include "cobalt/bindings/testing/mozjs_conditional_interface.h"
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.h
index d827418..9a95d0c 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_conditional_interface.h
@@ -20,6 +20,11 @@
#ifndef MozjsConditionalInterface_h
#define MozjsConditionalInterface_h
+
+// This must be included above the check for ENABLE_CONDITIONAL_INTERFACE, since
+// ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(ENABLE_CONDITIONAL_INTERFACE)
#include "base/hash_tables.h"
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
index f7fe523..c6b64dd 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_getter_setter_interface.cc
@@ -793,6 +793,11 @@
JS::RootedObject parent_prototype(
context, MozjsNamedIndexedGetterInterface::GetPrototype(context, global_object));
+ static_assert(
+ std::is_base_of<NamedIndexedGetterInterface, DerivedGetterSetterInterface>::value,
+ "Expected DerivedGetterSetterInterface to have C++ parent class "
+ "NamedIndexedGetterInterface, because that is its WebIDL parent.");
+
DCHECK(parent_prototype);
interface_data->prototype = JS_NewObjectWithGivenProto(
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
index 1705331..f90a539 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_derived_interface.cc
@@ -332,6 +332,11 @@
JS::RootedObject parent_prototype(
context, MozjsBaseInterface::GetPrototype(context, global_object));
+ static_assert(
+ std::is_base_of<BaseInterface, DerivedInterface>::value,
+ "Expected DerivedInterface to have C++ parent class "
+ "BaseInterface, because that is its WebIDL parent.");
+
DCHECK(parent_prototype);
interface_data->prototype = JS_NewObjectWithGivenProto(
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
index ec676ae..f3f9986 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.cc
@@ -19,6 +19,11 @@
// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
// Auto-generated from template: bindings/mozjs45/templates/interface.cc.template
+
+// This must be included above the check for NO_ENABLE_CONDITIONAL_INTERFACE, since
+// NO_ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(NO_ENABLE_CONDITIONAL_INTERFACE)
#include "cobalt/bindings/testing/mozjs_disabled_interface.h"
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.h b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.h
index b07b77d..c491a56 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.h
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_disabled_interface.h
@@ -20,6 +20,11 @@
#ifndef MozjsDisabledInterface_h
#define MozjsDisabledInterface_h
+
+// This must be included above the check for NO_ENABLE_CONDITIONAL_INTERFACE, since
+// NO_ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(NO_ENABLE_CONDITIONAL_INTERFACE)
#include "base/hash_tables.h"
diff --git a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
index bf69b82..4507ed8 100644
--- a/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
+++ b/src/cobalt/bindings/generated/mozjs45/testing/cobalt/bindings/testing/mozjs_window.cc
@@ -931,6 +931,11 @@
JS::RootedObject parent_prototype(
context, MozjsGlobalInterfaceParent::GetPrototype(context, global_object));
+ static_assert(
+ std::is_base_of<GlobalInterfaceParent, Window>::value,
+ "Expected Window to have C++ parent class "
+ "GlobalInterfaceParent, because that is its WebIDL parent.");
+
DCHECK(parent_prototype);
interface_data->prototype = JS_NewObjectWithGivenProto(
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
index bcdaf92..eea832e 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.cc
@@ -19,6 +19,11 @@
// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
// Auto-generated from template: bindings/v8c/templates/interface.cc.template
+
+// This must be included above the check for ENABLE_CONDITIONAL_INTERFACE, since
+// ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(ENABLE_CONDITIONAL_INTERFACE)
#include "cobalt/bindings/testing/v8c_conditional_interface.h"
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
index 212639b..3be275f 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_conditional_interface.h
@@ -21,6 +21,11 @@
#ifndef V8cConditionalInterface_h
#define V8cConditionalInterface_h
+
+// This must be included above the check for ENABLE_CONDITIONAL_INTERFACE, since
+// ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(ENABLE_CONDITIONAL_INTERFACE)
#include "base/hash_tables.h"
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
index 9b0252e..972fd8e 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_getter_setter_interface.cc
@@ -627,6 +627,10 @@
// inherited interface.
v8::Local<v8::FunctionTemplate> parent_template = V8cNamedIndexedGetterInterface::GetTemplate(isolate);
function_template->Inherit(parent_template);
+ static_assert(
+ std::is_base_of<NamedIndexedGetterInterface, DerivedGetterSetterInterface>::value,
+ "Expected DerivedGetterSetterInterface to have C++ parent class "
+ "NamedIndexedGetterInterface, because that is its WebIDL parent.");
}
// https://heycam.github.io/webidl/#es-constants
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
index e7a1eca..bc4610d 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_derived_interface.cc
@@ -239,6 +239,10 @@
// inherited interface.
v8::Local<v8::FunctionTemplate> parent_template = V8cBaseInterface::GetTemplate(isolate);
function_template->Inherit(parent_template);
+ static_assert(
+ std::is_base_of<BaseInterface, DerivedInterface>::value,
+ "Expected DerivedInterface to have C++ parent class "
+ "BaseInterface, because that is its WebIDL parent.");
}
// https://heycam.github.io/webidl/#es-constants
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
index 16f758d..763c975 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.cc
@@ -19,6 +19,11 @@
// This file has been auto-generated by bindings/code_generator_cobalt.py. DO NOT MODIFY!
// Auto-generated from template: bindings/v8c/templates/interface.cc.template
+
+// This must be included above the check for NO_ENABLE_CONDITIONAL_INTERFACE, since
+// NO_ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(NO_ENABLE_CONDITIONAL_INTERFACE)
#include "cobalt/bindings/testing/v8c_disabled_interface.h"
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
index e5734ce..99f6750 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_disabled_interface.h
@@ -21,6 +21,11 @@
#ifndef V8cDisabledInterface_h
#define V8cDisabledInterface_h
+
+// This must be included above the check for NO_ENABLE_CONDITIONAL_INTERFACE, since
+// NO_ENABLE_CONDITIONAL_INTERFACE may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined(NO_ENABLE_CONDITIONAL_INTERFACE)
#include "base/hash_tables.h"
diff --git a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc
index 07237ba..9865651 100644
--- a/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc
+++ b/src/cobalt/bindings/generated/v8c/testing/cobalt/bindings/testing/v8c_window.cc
@@ -710,6 +710,10 @@
// inherited interface.
v8::Local<v8::FunctionTemplate> parent_template = V8cGlobalInterfaceParent::GetTemplate(isolate);
function_template->Inherit(parent_template);
+ static_assert(
+ std::is_base_of<GlobalInterfaceParent, Window>::value,
+ "Expected Window to have C++ parent class "
+ "GlobalInterfaceParent, because that is its WebIDL parent.");
}
// https://heycam.github.io/webidl/#es-constants
diff --git a/src/cobalt/bindings/mozjs45/templates/interface.cc.template b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
index 6ec949a..1b2fd72 100644
--- a/src/cobalt/bindings/mozjs45/templates/interface.cc.template
+++ b/src/cobalt/bindings/mozjs45/templates/interface.cc.template
@@ -731,6 +731,11 @@
{% if parent_interface %}
JS::RootedObject parent_prototype(
context, {{parent_interface}}::GetPrototype(context, global_object));
+ static_assert(
+ std::is_base_of<{{parent_interface_name}}, {{interface_name}}>::value,
+ "Expected {{interface_name}} to have C++ parent class "
+ "{{parent_interface_name}}, because that is its WebIDL parent.");
+
{% elif is_exception_interface %}
// Get Error prototype.
JS::RootedObject parent_prototype(context);
diff --git a/src/cobalt/bindings/shared/idl_conditional_macros.h b/src/cobalt/bindings/shared/idl_conditional_macros.h
new file mode 100644
index 0000000..e1dddce
--- /dev/null
+++ b/src/cobalt/bindings/shared/idl_conditional_macros.h
@@ -0,0 +1,32 @@
+// Copyright 2018 Google Inc. 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 COBALT_BINDINGS_SHARED_IDL_CONDITIONAL_MACROS_H_
+#define COBALT_BINDINGS_SHARED_IDL_CONDITIONAL_MACROS_H_
+
+#include "starboard/configuration.h"
+
+// Define preprocessor macros that cannot be determined at GYP time for use in
+// IDL files with Conditionals (e.g. conditional interface definitions, or
+// conditional attributes). This is necessary to make macros for IDL
+// Conditionals that are dependent on Starboard feature macros that get defined
+// in header files.
+
+#if SB_HAS(ON_SCREEN_KEYBOARD)
+// This is used to conditionally define the On Screen Keyboard interface and
+// attribute.
+#define COBALT_ENABLE_ON_SCREEN_KEYBOARD
+#endif // SB_HAS(ON_SCREEN_KEYBOARD)
+
+#endif // COBALT_BINDINGS_SHARED_IDL_CONDITIONAL_MACROS_H_
diff --git a/src/cobalt/bindings/templates/interface-base.cc.template b/src/cobalt/bindings/templates/interface-base.cc.template
index a0e297e..f965c49 100644
--- a/src/cobalt/bindings/templates/interface-base.cc.template
+++ b/src/cobalt/bindings/templates/interface-base.cc.template
@@ -33,6 +33,11 @@
// Auto-generated from template: {{template_path}}
{% if conditional %}
+
+// This must be included above the check for {{conditional}}, since
+// {{conditional}} may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined({{conditional}})
{% endif %}
diff --git a/src/cobalt/bindings/templates/interface-base.h.template b/src/cobalt/bindings/templates/interface-base.h.template
index 5b8dd3a..192eb91 100644
--- a/src/cobalt/bindings/templates/interface-base.h.template
+++ b/src/cobalt/bindings/templates/interface-base.h.template
@@ -36,6 +36,11 @@
#define {{binding_class}}_h
{% if conditional %}
+
+// This must be included above the check for {{conditional}}, since
+// {{conditional}} may be defined within.
+#include "cobalt/bindings/shared/idl_conditional_macros.h"
+
#if defined({{conditional}})
{% endif %}
diff --git a/src/cobalt/bindings/testing/array_buffers_test.cc b/src/cobalt/bindings/testing/array_buffers_test.cc
index 1dd1e16..16c6999 100644
--- a/src/cobalt/bindings/testing/array_buffers_test.cc
+++ b/src/cobalt/bindings/testing/array_buffers_test.cc
@@ -15,6 +15,7 @@
#include "cobalt/bindings/testing/arbitrary_interface.h"
#include "cobalt/bindings/testing/bindings_test_base.h"
#include "cobalt/script/array_buffer.h"
+#include "cobalt/script/array_buffer_view.h"
#include "cobalt/script/data_view.h"
#include "cobalt/script/typed_arrays.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -84,6 +85,11 @@
// going wrong here.
}
+TEST_F(ArrayBufferTest, ArrayBufferViewTest) {
+ // TODO: Add a test for ArrayBufferView. Bindings support is required in
+ // order to write any meaningful test here.
+}
+
TEST_F(ArrayBufferTest, DataViewTest) {
auto array_buffer = script::ArrayBuffer::New(global_environment_, 1024);
auto data_view =
diff --git a/src/cobalt/bindings/testing/date_bindings_test.cc b/src/cobalt/bindings/testing/date_bindings_test.cc
index 56c3c37..536f85e 100644
--- a/src/cobalt/bindings/testing/date_bindings_test.cc
+++ b/src/cobalt/bindings/testing/date_bindings_test.cc
@@ -70,6 +70,15 @@
EXPECT_STREQ("Invalid Date", result.c_str());
}
+TEST_F(DateBindingsTest, PosixEpoch) {
+ std::string result;
+
+ EvaluateScript("Date.now();", &result);
+ auto js_now_ms = std::stoll(result);
+ auto posix_now_ms = SbTimeToPosix(SbTimeGetNow()) / kSbTimeMillisecond;
+ EXPECT_LT(std::abs(posix_now_ms - js_now_ms), 1000);
+}
+
} // namespace
} // namespace testing
} // namespace bindings
diff --git a/src/cobalt/bindings/v8c/templates/interface.cc.template b/src/cobalt/bindings/v8c/templates/interface.cc.template
index 5914f60..2d94f1d 100644
--- a/src/cobalt/bindings/v8c/templates/interface.cc.template
+++ b/src/cobalt/bindings/v8c/templates/interface.cc.template
@@ -556,6 +556,10 @@
// inherited interface.
v8::Local<v8::FunctionTemplate> parent_template = {{parent_interface}}::GetTemplate(isolate);
function_template->Inherit(parent_template);
+ static_assert(
+ std::is_base_of<{{parent_interface_name}}, {{interface_name}}>::value,
+ "Expected {{interface_name}} to have C++ parent class "
+ "{{parent_interface_name}}, because that is its WebIDL parent.");
}
{% elif is_exception_interface %}
{
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py
index 41d9f03..e15fce0 100644
--- a/src/cobalt/black_box_tests/black_box_tests.py
+++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -5,9 +5,6 @@
import argparse
import importlib
import logging
-import os
-import socket
-import subprocess
import sys
import unittest
@@ -20,6 +17,7 @@
# These tests can only be run on platforms whose app launcher can send suspend/
# resume signals.
_TESTS_NEEDING_SYSTEM_SIGNAL = [
+ 'cancel_sync_loads_when_suspended',
'preload_font',
'timer_hit_in_preload',
'timer_hit_after_preload',
@@ -33,8 +31,6 @@
'allow_eval',
'disable_eval_with_csp',
]
-# Port number of the HTTP server serving test data.
-_DEFAULT_TEST_DATA_SERVER_PORT = 8000
# Location of test files.
_TEST_DIR_PATH = 'cobalt.black_box_tests.tests.'
# Platform dependent device parameters.
@@ -49,17 +45,6 @@
sys.argv = sys.argv[:1]
-def GetDefaultBlackBoxTestDataAddress():
- """Gets the ip address with port for the server hosting test data."""
- # We are careful to choose this method that allows external device to connect
- # to the host running the web server hosting test data.
- address_pack_list = socket.getaddrinfo(socket.gethostname(),
- _DEFAULT_TEST_DATA_SERVER_PORT)
- first_address_pack = address_pack_list[0]
- ip_address, port = first_address_pack[4]
- return 'http://{}:{}/'.format(ip_address, port)
-
-
class BlackBoxTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs):
@@ -73,9 +58,6 @@
def tearDownClass(cls):
print('Done ' + cls.__name__)
- def GetURL(self, file_name):
- return GetDefaultBlackBoxTestDataAddress() + file_name
-
def CreateCobaltRunner(self, url, target_params=None):
new_runner = black_box_cobalt_runner.BlackBoxCobaltRunner(
device_params=_device_params, url=url, target_params=target_params)
@@ -113,9 +95,6 @@
self.test_name = test_name
def Run(self):
-
- if not self._StartTestdataServer():
- return 1
logging.basicConfig(level=logging.DEBUG)
GetDeviceParams()
if self.test_name:
@@ -125,36 +104,8 @@
suite = LoadTests(_device_params.platform, _device_params.config)
return_code = not unittest.TextTestRunner(
verbosity=0, stream=sys.stdout).run(suite).wasSuccessful()
- self._KillTestdataServer()
return return_code
- def _StartTestdataServer(self):
- """Start a local server to serve test data."""
- # Some tests like preload_font requires server feature support.
- # Using HTTP URL instead of file URL also saves the trouble to
- # deploy test data to device.
- self.default_test_data_server_process = subprocess.Popen(
- [
- 'python', '-m', 'SimpleHTTPServer',
- '{}'.format(_DEFAULT_TEST_DATA_SERVER_PORT)
- ],
- cwd=os.path.join(
- os.path.dirname(os.path.realpath(__file__)), 'testdata'),
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- if self.default_test_data_server_process.returncode is not None:
- # If the return code is not None now, server is not running normally.
- print('can not start default test data server.')
- return False
- else:
- print('Starting HTTP server on port: {}'.format(
- _DEFAULT_TEST_DATA_SERVER_PORT))
- return True
-
- def _KillTestdataServer(self):
- """Exit black_box_test_runner with test result."""
- self.default_test_data_server_process.kill()
-
def main():
parser = argparse.ArgumentParser()
diff --git a/src/cobalt/black_box_tests/testdata/cancel_sync_loads_when_suspended.html b/src/cobalt/black_box_tests/testdata/cancel_sync_loads_when_suspended.html
new file mode 100644
index 0000000..960bc50
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/cancel_sync_loads_when_suspended.html
@@ -0,0 +1,8 @@
+<HTML>
+ <HEAD></HEAD>
+ <BODY>
+ <script src='black_box_js_test_utils.js'></script>
+ <script src='cancel_sync_loads_when_suspended.js'></script>
+ <script>onEndTest()</script>
+ </BODY>
+</HTML>
diff --git a/src/cobalt/black_box_tests/testdata/retry_async_script_loads_after_suspend.html b/src/cobalt/black_box_tests/testdata/retry_async_script_loads_after_suspend.html
new file mode 100644
index 0000000..faf79c1
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/retry_async_script_loads_after_suspend.html
@@ -0,0 +1,20 @@
+<HTML>
+ <HEAD></HEAD>
+ <BODY>
+ <script src='black_box_js_test_utils.js'></script>
+ <script>
+ var s = document.createElement('script');
+ var onLoadCalled = false;
+ var scriptExecuted = false;
+ // notReached and onEndTest are defined in black_box_js_test_utils.js
+ s.onerror = notReached;
+ s.onload = () => {
+ assertTrue(scriptExecuted);
+ assertFalse(onLoadCalled);
+ onEndTest();
+ }
+ s.src='script_executed.js'
+ document.head.appendChild(s);
+ </script>
+ </BODY>
+</HTML>
diff --git a/src/cobalt/black_box_tests/testdata/script_executed.js b/src/cobalt/black_box_tests/testdata/script_executed.js
new file mode 100644
index 0000000..9c07464
--- /dev/null
+++ b/src/cobalt/black_box_tests/testdata/script_executed.js
@@ -0,0 +1,16 @@
+// Copyright 2018 Google Inc. 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.
+
+assertFalse(scriptExecuted);
+scriptExecuted = true;
diff --git a/src/cobalt/black_box_tests/tests/allow_eval.py b/src/cobalt/black_box_tests/tests/allow_eval.py
index ca6b47d..22fa60f 100644
--- a/src/cobalt/black_box_tests/tests/allow_eval.py
+++ b/src/cobalt/black_box_tests/tests/allow_eval.py
@@ -7,13 +7,15 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
class AllowEvalTest(black_box_tests.BlackBoxTestCase):
def test_simple(self):
- url = self.GetURL(file_name='allow_eval.html')
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/allow_eval.html')
- with self.CreateCobaltRunner(url=url) as runner:
- self.assertTrue(runner.JSTestsSucceeded())
+ with self.CreateCobaltRunner(url=url) as runner:
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/cancel_sync_loads_when_suspended.py b/src/cobalt/black_box_tests/tests/cancel_sync_loads_when_suspended.py
new file mode 100644
index 0000000..b8f0b7c
--- /dev/null
+++ b/src/cobalt/black_box_tests/tests/cancel_sync_loads_when_suspended.py
@@ -0,0 +1,133 @@
+# Copyright 2018 Google Inc. 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.
+"""Tests cancelation of synchronous loading of scripts on Suspend."""
+
+# This test script works by splitting the work over 3 threads, so that they
+# can each make progress even if they come across blocking operations.
+# The three threads are:
+# 1. Main thread, runs BlackBoxTestCase, sends suspend/resume signals, etc.
+# 2. HTTP Server, responsible for slowly responding to a fetch of a javascript
+# file.
+# 3. Webdriver thread, instructs Cobalt to navigate to a URL
+#
+# Steps in ~ chronological order:
+# 1. Create a TCP socket and listen on all interfaces.
+# 2. Start Cobalt, and point it to the socket created in Step 1.
+# 3. Wait HTTP request for html resource.
+# 4. Respond to a HTTP request.
+# 5. Wait for a request for javascript resource.
+# 6. Suspend Cobalt process.
+# 7. Cobalt disconnects from the socket.
+# 8. Resume Cobalt process, which enables the JS test to pass.
+# 9. Check to see if JSTestsSucceeded().
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import _env # pylint: disable=unused-import,g-bad-import-order
+
+import os
+import SimpleHTTPServer
+import threading
+import traceback
+import urlparse
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import MakeRequestHandlerClass
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+_CANCEL_SYNC_LOADS_WHEN_SUSPENDED_HTML = 'cancel_sync_loads_when_suspended.html'
+_CANCEL_SYNC_LOADS_WHEN_SUSPENDED_JS = 'cancel_sync_loads_when_suspended.js'
+_MAX_ALLOTTED_TIME_SECONDS = 60
+
+_received_script_resource_request = threading.Event()
+_test_finished = threading.Event()
+
+# The base path of the requested assets is the parent directory.
+_SERVER_ROOT_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
+
+
+class JavascriptRequestDetector(MakeRequestHandlerClass(_SERVER_ROOT_PATH)):
+ """Proxies everything to SimpleHTTPRequestHandler, except some paths."""
+
+ def do_GET(self): # pylint: disable=invalid-name
+ """Handles HTTP GET requests for resources."""
+
+ parsed_path = urlparse.urlparse(self.path)
+ if parsed_path.path == '/testdata/' + _CANCEL_SYNC_LOADS_WHEN_SUSPENDED_JS:
+ _received_script_resource_request.set()
+ # It is important not to send any response back, so we block.
+ print('Waiting on test to finish.')
+ _test_finished.wait()
+ print('Test is finished.')
+ return
+
+ return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+
+
+class CancelSyncLoadsWhenSuspended(black_box_tests.BlackBoxTestCase):
+ """Tests cancelation of synchronous loading of scripts on Suspend."""
+
+ def _LoadPage(self, webdriver, url):
+ """Instructs webdriver to navigate to url."""
+ try:
+ # Note: The following is a blocking request, and returns only when the
+ # page has fully loaded. In this test, the page will not fully load
+ # so, this does not return until Cobalt exits.
+ webdriver.get(url)
+ except: # pylint: disable=bare-except
+ traceback.print_exc()
+
+ def test_simple(self):
+
+ # Step 2. Start Cobalt, and point it to the socket created in Step 1.
+ try:
+ with ThreadedWebServer(
+ JavascriptRequestDetector) as server, self.CreateCobaltRunner(
+ url='about:blank') as runner:
+ target_url = server.GetURL(file_name='../testdata/' +
+ _CANCEL_SYNC_LOADS_WHEN_SUSPENDED_HTML)
+ cobalt_launcher_thread = threading.Thread(
+ target=CancelSyncLoadsWhenSuspended._LoadPage,
+ args=(self, runner.webdriver, target_url))
+ cobalt_launcher_thread.start()
+
+ # Step 3. Wait HTTP request for html resource.
+ print('Waiting for script resource request')
+ request_received = _received_script_resource_request.wait(
+ _MAX_ALLOTTED_TIME_SECONDS)
+ print('Request received: {}'.format(request_received))
+ # Step 5. Wait for a request for javascript resource.
+ self.assertTrue(request_received)
+
+ # Step 6. Suspend Cobalt process.
+ print('Suspending Cobalt.')
+ runner.SendSuspend()
+ # Step 7. Cobalt disconnects from the socket.
+ # Step 8. Resume Cobalt process, which enables the JS test to pass.
+ print('Resuming Cobalt.')
+ runner.SendResume()
+
+ # Step 9. Check to see if JSTestsSucceeded().
+ # Note that this call will check the DOM multiple times for a period of
+ # time (current default is 30 seconds).
+ self.assertTrue(runner.JSTestsSucceeded())
+ except: # pylint: disable=bare-except
+ traceback.print_exc()
+ # Consider an exception being thrown as a test failure.
+ self.assertTrue(False)
+ finally:
+ print('Cleaning up.')
+ _test_finished.set()
diff --git a/src/cobalt/black_box_tests/tests/disable_eval_with_csp.py b/src/cobalt/black_box_tests/tests/disable_eval_with_csp.py
index 936c57b..062dc11 100644
--- a/src/cobalt/black_box_tests/tests/disable_eval_with_csp.py
+++ b/src/cobalt/black_box_tests/tests/disable_eval_with_csp.py
@@ -7,13 +7,15 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
class DisableEvalWithCSPTest(black_box_tests.BlackBoxTestCase):
def test_simple(self):
- url = self.GetURL(file_name='disable_eval_with_csp.html')
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/disable_eval_with_csp.html')
- with self.CreateCobaltRunner(url=url) as runner:
- self.assertTrue(runner.JSTestsSucceeded())
+ with self.CreateCobaltRunner(url=url) as runner:
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/persistent_cookie.py b/src/cobalt/black_box_tests/tests/persistent_cookie.py
index 4bbefc5..a7dba95 100644
--- a/src/cobalt/black_box_tests/tests/persistent_cookie.py
+++ b/src/cobalt/black_box_tests/tests/persistent_cookie.py
@@ -7,6 +7,7 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
from cobalt.tools.automated_testing import webdriver_utils
keys = webdriver_utils.import_selenium_module('webdriver.common.keys')
@@ -20,27 +21,28 @@
def test_simple(self):
- url = self.GetURL(file_name='persistent_cookie.html')
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/persistent_cookie.html')
- # The webpage listens for NUMPAD1, NUMPAD2 and NUMPAD3 at opening.
- with self.CreateCobaltRunner(url=url) as runner:
- # Press NUMPAD1 to verify basic cookie functionality and set
- # a persistent cookie.
- runner.WaitForJSTestsSetup()
- runner.SendKeys(keys.Keys.NUMPAD1)
- self.assertTrue(runner.JSTestsSucceeded())
+ # The webpage listens for NUMPAD1, NUMPAD2 and NUMPAD3 at opening.
+ with self.CreateCobaltRunner(url=url) as runner:
+ # Press NUMPAD1 to verify basic cookie functionality and set
+ # a persistent cookie.
+ runner.WaitForJSTestsSetup()
+ runner.SendKeys(keys.Keys.NUMPAD1)
+ self.assertTrue(runner.JSTestsSucceeded())
- with self.CreateCobaltRunner(url=url) as runner:
- runner.WaitForJSTestsSetup()
- # Press NUMPAD2 to indicate this is the second time we opened
- # the webpage and verify a persistent cookie is on device. Then
- # clear this persistent cookie.
- runner.SendKeys(keys.Keys.NUMPAD2)
- self.assertTrue(runner.JSTestsSucceeded())
+ with self.CreateCobaltRunner(url=url) as runner:
+ runner.WaitForJSTestsSetup()
+ # Press NUMPAD2 to indicate this is the second time we opened
+ # the webpage and verify a persistent cookie is on device. Then
+ # clear this persistent cookie.
+ runner.SendKeys(keys.Keys.NUMPAD2)
+ self.assertTrue(runner.JSTestsSucceeded())
- with self.CreateCobaltRunner(url=url) as runner:
- runner.WaitForJSTestsSetup()
- # Press NUMPAD3 to verify the persistent cookie we cleared is
- # not on the device for this URL any more.
- runner.SendKeys(keys.Keys.NUMPAD3)
- self.assertTrue(runner.JSTestsSucceeded())
+ with self.CreateCobaltRunner(url=url) as runner:
+ runner.WaitForJSTestsSetup()
+ # Press NUMPAD3 to verify the persistent cookie we cleared is
+ # not on the device for this URL any more.
+ runner.SendKeys(keys.Keys.NUMPAD3)
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/preload_font.py b/src/cobalt/black_box_tests/tests/preload_font.py
index b50e779..e38ea25 100644
--- a/src/cobalt/black_box_tests/tests/preload_font.py
+++ b/src/cobalt/black_box_tests/tests/preload_font.py
@@ -9,6 +9,7 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
_MAX_RESUME_WAIT_SECONDS = 30
@@ -17,17 +18,18 @@
def test_simple(self):
- url = self.GetURL(file_name='preload_font.html')
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/preload_font.html')
- with self.CreateCobaltRunner(
- url=url, target_params=['--preload']) as runner:
- runner.WaitForJSTestsSetup()
- runner.SendResume()
- start_time = time.time()
- while runner.IsInPreload():
- if time.time() - start_time > _MAX_RESUME_WAIT_SECONDS:
- raise Exception('Cobalt can not exit preload mode after receiving'
- 'resume signal')
- time.sleep(.1)
- # At this point, Cobalt is in started mode.
- self.assertTrue(runner.JSTestsSucceeded())
+ with self.CreateCobaltRunner(
+ url=url, target_params=['--preload']) as runner:
+ runner.WaitForJSTestsSetup()
+ runner.SendResume()
+ start_time = time.time()
+ while runner.IsInPreload():
+ if time.time() - start_time > _MAX_RESUME_WAIT_SECONDS:
+ raise Exception('Cobalt can not exit preload mode after receiving'
+ 'resume signal')
+ time.sleep(.1)
+ # At this point, Cobalt is in started mode.
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/preload_visibility.py b/src/cobalt/black_box_tests/tests/preload_visibility.py
index cd23614..5a81881 100644
--- a/src/cobalt/black_box_tests/tests/preload_visibility.py
+++ b/src/cobalt/black_box_tests/tests/preload_visibility.py
@@ -7,17 +7,19 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
class PreloadVisibilityTest(black_box_tests.BlackBoxTestCase):
def test_simple(self):
- url = self.GetURL(file_name='preload_visibility.html')
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/preload_visibility.html')
- with self.CreateCobaltRunner(
- url=url, target_params=['--preload']) as runner:
- runner.WaitForJSTestsSetup()
- self.assertTrue(runner.IsInPreload())
- runner.SendResume()
- self.assertTrue(runner.JSTestsSucceeded())
+ with self.CreateCobaltRunner(
+ url=url, target_params=['--preload']) as runner:
+ runner.WaitForJSTestsSetup()
+ self.assertTrue(runner.IsInPreload())
+ runner.SendResume()
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/retry_async_script_loads_after_suspend.py b/src/cobalt/black_box_tests/tests/retry_async_script_loads_after_suspend.py
new file mode 100644
index 0000000..214d680
--- /dev/null
+++ b/src/cobalt/black_box_tests/tests/retry_async_script_loads_after_suspend.py
@@ -0,0 +1,145 @@
+# Copyright 2018 Google Inc. 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.
+"""Tests retry of asynchronous loading of scripts on Suspend/Resume."""
+
+# This test script works by splitting the work over 3 threads, so that they
+# can each make progress even if they come across blocking operations.
+# The three threads are:
+# 1. Main thread, runs BlackBoxTestCase, sends suspend/resume signals, etc.
+# 2. HTTP Server, responsible for slowly responding to a fetch of a javascript
+# file.
+# 3. Webdriver thread, instructs Cobalt to navigate to a URL
+#
+# Steps in ~ chronological order:
+# 1. Create a TCP socket and listen on all interfaces.
+# 2. Start Cobalt, and point it to the socket created in Step 1.
+# 3. Wait HTTP request for html resource.
+# 4. Respond to a HTTP request.
+# 5. Wait for a request for javascript resource.
+# 6. Suspend Cobalt process.
+# 7. Cobalt disconnects from the socket.
+# 8. Resume Cobalt process.
+# 9. Retry javascript resource, which allows the test to pass.
+# 9. Check to see if JSTestsSucceeded().
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import _env # pylint: disable=unused-import,g-bad-import-order
+
+import logging
+import os
+import SimpleHTTPServer
+import threading
+import traceback
+import urlparse
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import MakeRequestHandlerClass
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+_HTML_FILE_TO_REQUEST = 'retry_async_script_loads_after_suspend.html'
+_JS_FILE_TO_REQUEST = 'script_executed.js'
+_MAX_ALLOTTED_TIME_SECONDS = 60
+
+_received_script_resource_request = threading.Event()
+_test_finished = threading.Event()
+
+# The base path of the requested assets is the parent directory.
+_SERVER_ROOT_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
+
+
+class JavascriptRequestDetector(MakeRequestHandlerClass(_SERVER_ROOT_PATH)):
+ """Proxies everything to SimpleHTTPRequestHandler, except some paths."""
+ _counter_lock = threading.Lock()
+ _request_counter = 0
+
+ def do_GET(self): # pylint: disable=invalid-name
+ """Handles HTTP GET requests for resources."""
+
+ parsed_path = urlparse.urlparse(self.path)
+
+ if parsed_path.path == '/testdata/' + _JS_FILE_TO_REQUEST:
+ _received_script_resource_request.set()
+ current_count = None
+ with self._counter_lock:
+ JavascriptRequestDetector._request_counter += 1
+ current_count = JavascriptRequestDetector._request_counter
+
+ if current_count < 2:
+ # It is important not to send any response back, so we block.
+ logging.info('Waiting on test to finish.')
+ _test_finished.wait()
+ # Returns a 404, to make sure the page isn't loaded by the first
+ # request.
+ return
+
+ return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
+
+
+class RetryAsyncScriptLoadsAfterSuspend(black_box_tests.BlackBoxTestCase):
+ """Tests cancelation of synchronous loading of scripts on Suspend."""
+
+ def _LoadPage(self, webdriver, url):
+ """Instructs webdriver to navigate to url."""
+ try:
+ # Note: The following is a blocking request, and returns only when the
+ # page has fully loaded. In this test, the page will not fully load
+ # so, this does not return until Cobalt exits.
+ webdriver.get(url)
+ except: # pylint: disable=bare-except
+ traceback.print_exc()
+
+ def test_simple(self):
+
+ # Step 2. Start Cobalt, and point it to the socket created in Step 1.
+ try:
+ with ThreadedWebServer(
+ JavascriptRequestDetector) as server, self.CreateCobaltRunner(
+ url='about:blank') as runner:
+ target_url = server.GetURL(file_name='../testdata/' +
+ _HTML_FILE_TO_REQUEST)
+ cobalt_launcher_thread = threading.Thread(
+ target=RetryAsyncScriptLoadsAfterSuspend._LoadPage,
+ args=(self, runner.webdriver, target_url))
+ cobalt_launcher_thread.start()
+
+ # Step 3. Wait HTTP request for html resource.
+ logging.info('Waiting for script resource request')
+ request_received = _received_script_resource_request.wait(
+ _MAX_ALLOTTED_TIME_SECONDS)
+ logging.info('Request received: {}'.format(request_received))
+ # Step 5. Wait for a request for javascript resource.
+ self.assertTrue(request_received)
+
+ # Step 6. Suspend Cobalt process.
+ logging.info('Suspending Cobalt.')
+ runner.SendSuspend()
+ # Step 7. Cobalt disconnects from the socket.
+ # Step 8. Resume Cobalt process, which enables the JS test to pass.
+ logging.info('Resuming Cobalt.')
+ runner.SendResume()
+
+ # Step 9. Check to see if JSTestsSucceeded().
+ # Note that this call will check the DOM multiple times for a period of
+ # time (current default is 30 seconds).
+ self.assertTrue(runner.JSTestsSucceeded())
+ except: # pylint: disable=bare-except
+ traceback.print_exc()
+ # Consider an exception being thrown as a test failure.
+ self.assertTrue(False)
+ finally:
+ logging.info('Cleaning up.')
+ _test_finished.set()
diff --git a/src/cobalt/black_box_tests/tests/suspend_visibility.py b/src/cobalt/black_box_tests/tests/suspend_visibility.py
index 66b8c0c..e7dbe4d 100644
--- a/src/cobalt/black_box_tests/tests/suspend_visibility.py
+++ b/src/cobalt/black_box_tests/tests/suspend_visibility.py
@@ -7,16 +7,18 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
class SuspendVisibilityTest(black_box_tests.BlackBoxTestCase):
def test_simple(self):
- url = self.GetURL(file_name='suspend_visibility.html')
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/suspend_visibility.html')
- with self.CreateCobaltRunner(url=url) as runner:
- runner.WaitForJSTestsSetup()
- runner.SendSuspend()
- runner.SendResume()
- self.assertTrue(runner.JSTestsSucceeded())
+ with self.CreateCobaltRunner(url=url) as runner:
+ runner.WaitForJSTestsSetup()
+ runner.SendSuspend()
+ runner.SendResume()
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/timer_hit_after_preload.py b/src/cobalt/black_box_tests/tests/timer_hit_after_preload.py
index d0d803e..c889b48 100644
--- a/src/cobalt/black_box_tests/tests/timer_hit_after_preload.py
+++ b/src/cobalt/black_box_tests/tests/timer_hit_after_preload.py
@@ -7,18 +7,19 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
class TimerAfterPreloadTest(black_box_tests.BlackBoxTestCase):
def test_simple(self):
- url = self.GetURL(file_name='timer_hit_after_preload.html')
-
- with self.CreateCobaltRunner(
- url=url, target_params=['--preload']) as runner:
- self.assertTrue(runner.IsInPreload())
- # setInterval will hit once during the .5 seconds.
- runner.PollUntilFound('#script_executed')
- runner.SendResume()
- self.assertTrue(runner.JSTestsSucceeded())
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/timer_hit_after_preload.html')
+ with self.CreateCobaltRunner(
+ url=url, target_params=['--preload']) as runner:
+ self.assertTrue(runner.IsInPreload())
+ # setInterval will hit once during the .5 seconds.
+ runner.PollUntilFound('#script_executed')
+ runner.SendResume()
+ self.assertTrue(runner.JSTestsSucceeded())
diff --git a/src/cobalt/black_box_tests/tests/timer_hit_in_preload.py b/src/cobalt/black_box_tests/tests/timer_hit_in_preload.py
index 320f0a5..ea9c48a 100644
--- a/src/cobalt/black_box_tests/tests/timer_hit_in_preload.py
+++ b/src/cobalt/black_box_tests/tests/timer_hit_in_preload.py
@@ -7,15 +7,16 @@
import _env # pylint: disable=unused-import
from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
class TimerInPreloadTest(black_box_tests.BlackBoxTestCase):
def test_simple(self):
- url = self.GetURL(file_name='timer_hit_in_preload.html')
-
- with self.CreateCobaltRunner(
- url=url, target_params=['--preload']) as runner:
- self.assertTrue(runner.JSTestsSucceeded())
- self.assertTrue(runner.IsInPreload())
+ with ThreadedWebServer() as server:
+ url = server.GetURL(file_name='testdata/timer_hit_in_preload.html')
+ with self.CreateCobaltRunner(
+ url=url, target_params=['--preload']) as runner:
+ self.assertTrue(runner.JSTestsSucceeded())
+ self.assertTrue(runner.IsInPreload())
diff --git a/src/cobalt/black_box_tests/threaded_web_server.py b/src/cobalt/black_box_tests/threaded_web_server.py
new file mode 100644
index 0000000..72bf5cc
--- /dev/null
+++ b/src/cobalt/black_box_tests/threaded_web_server.py
@@ -0,0 +1,107 @@
+# Copyright 2018 Google Inc. 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.
+"""Contains a threaded web server used for serving testdata."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import SimpleHTTPServer
+import socket
+import SocketServer
+import threading
+
+
+class _ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+ pass
+
+
+def MakeRequestHandlerClass(base_path):
+ """RequestHandler that serves files that reside relative to base_path.
+
+ Args:
+ base_path: A path considered to be the root directory.
+
+ Returns:
+ A RequestHandler class.
+ """
+
+ class TestDataHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+ """Handles HTTP requests, but changes the base directory for assets."""
+
+ _current_working_directory = os.getcwd()
+ _base_path = base_path
+
+ def translate_path(self, path):
+ """Translate the request path to the file in the testdata directory."""
+
+ potential_path = SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(
+ self, path)
+ potential_path = potential_path.replace(self._current_working_directory,
+ self._base_path)
+ return potential_path
+
+ return TestDataHTTPRequestHandler
+
+
+class ThreadedWebServer(object):
+ """A HTTP WebServer that serves requests in a separate thread."""
+
+ def __init__(self,
+ handler=MakeRequestHandlerClass(os.path.dirname(__file__))):
+ _ThreadedTCPServer.allow_reuse_address = True
+ # Get the socket address for the ANY interface. Doing it this way
+ # has it so that it will work for IPv4, and IPv6 only networks.
+ # Note that putting '::' as the hostname does not work at this time
+ # (see https://bugs.python.org/issue20215). Instead, the following code
+ # was inspired by https://docs.python.org/2/library/socket.html.
+ for result in socket.getaddrinfo(None, 0, socket.AF_UNSPEC,
+ socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
+ # This is (0.0.0.0, 0) or equivalent in IPv6 (could be more than 2
+ # elements).
+ socket_address = result[4]
+ break
+
+ self._server = _ThreadedTCPServer(socket_address, handler)
+ self._server_thread = None
+
+ self._bound_port = self._server.server_address[1]
+ address_pack_list = socket.getaddrinfo(socket.gethostname(),
+ self._bound_port)
+ first_address_pack = address_pack_list[0]
+ self._bound_ip, _ = first_address_pack[4]
+ self._bound_host, _ = first_address_pack[4]
+
+ def GetURL(self, file_name):
+ """Given a |file_name|, return a HTTP URI that can be fetched.
+
+ Args:
+ file_name: a string containing a file_name.
+
+ Returns:
+ A string containing a HTTP URI.
+ """
+ return 'http://{}:{}/{}'.format(self._bound_host, self._bound_port,
+ file_name)
+
+ def __enter__(self):
+ self._server_thread = threading.Thread(target=self._server.serve_forever)
+ self._server_thread.start()
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self._server.shutdown()
+ self._server.server_close()
+ self._server_thread.join()
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index ccce8cb..f2352c2 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -370,8 +370,10 @@
&options->scratch_surface_cache_size_in_bytes);
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
auto command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(browser::switches::kDisableRasterizerCaching)) {
- options->disable_rasterizer_caching = true;
+ if (command_line->HasSwitch(browser::switches::kDisableRasterizerCaching) ||
+ command_line->HasSwitch(
+ browser::switches::kForceDeterministicRendering)) {
+ options->force_deterministic_rendering = true;
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
}
diff --git a/src/cobalt/browser/browser_bindings_gen.gyp b/src/cobalt/browser/browser_bindings_gen.gyp
index c4c63e2..75b1859 100644
--- a/src/cobalt/browser/browser_bindings_gen.gyp
+++ b/src/cobalt/browser/browser_bindings_gen.gyp
@@ -129,6 +129,7 @@
'../dom/navigator.idl',
'../dom/node.idl',
'../dom/node_list.idl',
+ '../dom/on_error_event_listener.idl',
'../dom/on_screen_keyboard.idl',
'../dom/performance.idl',
'../dom/performance_timing.idl',
@@ -185,8 +186,12 @@
'../media_capture/media_device_info.idl',
'../media_capture/media_devices.idl',
+ '../media_capture/media_recorder.idl',
'../media_session/media_metadata.idl',
'../media_session/media_session.idl',
+ '../media_session/media_session_action_details.idl',
+ '../media_stream/media_stream.idl',
+ '../media_stream/media_stream_track.idl',
'../speech/speech_recognition.idl',
'../speech/speech_recognition_alternative.idl',
@@ -255,10 +260,14 @@
'../dom/track_default_type.idl',
'../dom/wheel_event_init.idl',
'../media_capture/media_device_kind.idl',
+ '../media_capture/media_recorder_options.idl',
+ '../media_capture/recording_state.idl',
'../media_session/media_image.idl',
'../media_session/media_metadata_init.idl',
'../media_session/media_session_action.idl',
'../media_session/media_session_playback_state.idl',
+ '../media_stream/media_stream_constraints.idl',
+ '../media_stream/media_track_settings.idl',
'../page_visibility/visibility_state.idl',
'../speech/speech_recognition_error_code.idl',
'../speech/speech_synthesis_error_code.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 7911067..1eb7f99 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -608,33 +608,52 @@
const FilePath& path,
loader::image::EncodedStaticImage::ImageFormat image_format,
const base::Closure& done_callback) {
+ TRACE_EVENT0("cobalt::browser", "BrowserModule::RequestScreenshotToFile()");
DCHECK(screen_shot_writer_);
- DCHECK(main_web_module_layer_);
- base::optional<renderer::Submission> last_submission =
- main_web_module_layer_->GetCurrentSubmission();
- if (!last_submission) {
- LOG(WARNING) << "Unable to find last submission.";
+
+ scoped_refptr<render_tree::Node> render_tree = GetLastSubmissionAnimated();
+ if (!render_tree) {
+ LOG(WARNING) << "Unable to get animated render tree";
return;
}
- DCHECK(last_submission->render_tree);
+
screen_shot_writer_->RequestScreenshotToFile(
- image_format, path, last_submission->render_tree, done_callback);
+ image_format, path, render_tree, done_callback);
}
void BrowserModule::RequestScreenshotToBuffer(
loader::image::EncodedStaticImage::ImageFormat image_format,
const ScreenShotWriter::ImageEncodeCompleteCallback& screenshot_ready) {
+ TRACE_EVENT0("cobalt::browser", "BrowserModule::RequestScreenshotToBuffer()");
DCHECK(screen_shot_writer_);
+
+ scoped_refptr<render_tree::Node> render_tree = GetLastSubmissionAnimated();
+ if (!render_tree) {
+ LOG(WARNING) << "Unable to get animated render tree";
+ return;
+ }
+
+ screen_shot_writer_->RequestScreenshotToMemory(
+ image_format, render_tree, screenshot_ready);
+}
+
+scoped_refptr<render_tree::Node> BrowserModule::GetLastSubmissionAnimated() {
DCHECK(main_web_module_layer_);
base::optional<renderer::Submission> last_submission =
main_web_module_layer_->GetCurrentSubmission();
if (!last_submission) {
LOG(WARNING) << "Unable to find last submission.";
- return;
+ return nullptr;
}
DCHECK(last_submission->render_tree);
- screen_shot_writer_->RequestScreenshotToMemory(
- image_format, last_submission->render_tree, screenshot_ready);
+
+ render_tree::animations::AnimateNode* animate_node =
+ base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
+ last_submission->render_tree.get());
+ render_tree::animations::AnimateNode::AnimateResults results =
+ animate_node->Apply(last_submission->time_offset);
+
+ return results.animated->source();
}
void BrowserModule::ProcessRenderTreeSubmissionQueue() {
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 90bc287..22e3bed 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -392,6 +392,10 @@
// Get the SbWindow via |system_window_| or potentially NULL.
SbWindow GetSbWindow();
+ // This returns the render tree of the most recent submission, with animations
+ // applied according to the current time.
+ scoped_refptr<render_tree::Node> GetLastSubmissionAnimated();
+
// TODO:
// WeakPtr usage here can be avoided if BrowserModule has a thread to
// own where it can ensure that its tasks are all resolved when it is
diff --git a/src/cobalt/browser/screen_shot_writer.cc b/src/cobalt/browser/screen_shot_writer.cc
index fa01378..2ce4557 100644
--- a/src/cobalt/browser/screen_shot_writer.cc
+++ b/src/cobalt/browser/screen_shot_writer.cc
@@ -16,6 +16,7 @@
#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "cobalt/loader/image/image_encoder.h"
#include "cobalt/render_tree/resource_provider_stub.h"
@@ -73,6 +74,7 @@
void(const scoped_refptr<loader::image::EncodedStaticImage>&)>&
done_encoding_callback,
scoped_array<uint8> pixel_data, const math::Size& image_dimensions) {
+ TRACE_EVENT0("cobalt::browser", "ScreenshotWriter::EncodeData()");
scoped_refptr<loader::image::EncodedStaticImage> image_data =
loader::image::CompressRGBAImage(desired_format, pixel_data.get(),
image_dimensions);
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index f26b317..fe94555 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -35,10 +35,21 @@
const char kDisableImageAnimationsHelp[] =
"Enables/disables animations on animated images (e.g. animated WebP).";
+const char kForceDeterministicRendering[] = "force_deterministic_rendering";
+const char kForceDeterministicRenderingHelp[] =
+ "Forces the renderer to avoid doing anything that may result in "
+ "1-pixel-off non-deterministic rendering output. For example, a renderer "
+ "may implement an optimization where text glyphs are rendered once and "
+ "cached and re-used in situations where the cached glyph is approximately "
+ "very similar, even if it is not exactly the same. Setting this flag "
+ "avoids that kind of behavior, allowing strict screen-diff tests to pass.";
+
const char kDisableRasterizerCaching[] = "disable_rasterizer_caching";
const char kDisableRasterizerCachingHelp[] =
"Disables caching of rasterized render tree nodes; caching improves "
- "performance but may result in sub-pixel differences.";
+ "performance but may result in sub-pixel differences. Note that this "
+ "is deprecated, the '--force_deterministic_rendering' flag should be "
+ "used instead which does the same thing.";
const char kDisableSignIn[] = "disable_sign_in";
const char kDisableSignInHelp[] =
@@ -325,6 +336,7 @@
{kAudioDecoderStub, kAudioDecoderStubHelp},
{kDebugConsoleMode, kDebugConsoleModeHelp},
{kDisableImageAnimations, kDisableImageAnimationsHelp},
+ {kForceDeterministicRendering, kForceDeterministicRenderingHelp},
{kDisableRasterizerCaching, kDisableRasterizerCachingHelp},
{kDisableSignIn, kDisableSignInHelp},
{kDisableSplashScreenOnReloads, kDisableSplashScreenOnReloadsHelp},
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 56a7a00..a203feb 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -28,6 +28,7 @@
extern const char kDebugConsoleModeHelp[];
extern const char kDisableImageAnimations[];
extern const char kDisableImageAnimationsHelp[];
+extern const char kForceDeterministicRendering[];
extern const char kDisableRasterizerCaching[];
extern const char kDisableSignIn[];
extern const char kDisableSignInHelp[];
diff --git a/src/cobalt/browser/user_agent_string.cc b/src/cobalt/browser/user_agent_string.cc
index eaf091d..23720c3 100644
--- a/src/cobalt/browser/user_agent_string.cc
+++ b/src/cobalt/browser/user_agent_string.cc
@@ -21,6 +21,7 @@
#if defined(COBALT_ENABLE_LIB)
#include "cobalt/browser/lib/exported/user_agent.h"
#endif
+#include "cobalt/renderer/get_default_rasterizer_for_platform.h"
#include "cobalt/script/javascript_engine.h"
#include "cobalt/version.h"
#include "cobalt_build_id.h" // NOLINT(build/include)
@@ -126,16 +127,6 @@
namespace {
-#if SB_API_VERSION == SB_EXPERIMENTAL_API_VERSION
-const char kStarboardStabilitySuffix[] = "-Experimental";
-#elif defined(SB_RELEASE_CANDIDATE_API_VERSION) && \
- SB_API_VERSION >= SB_RELEASE_CANDIDATE_API_VERSION && \
- SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
-const char kStarboardStabilitySuffix[] = "-ReleaseCandidate";
-#else
-const char kStarboardStabilitySuffix[] = "";
-#endif
-
struct SanitizeReplacements {
const char* replace_chars;
const char* replace_with;
@@ -204,7 +195,7 @@
UserAgentPlatformInfo platform_info;
platform_info.starboard_version = base::StringPrintf(
- "Starboard/%d%s", SB_API_VERSION, kStarboardStabilitySuffix);
+ "Starboard/%d", SB_API_VERSION);
const size_t kSystemPropertyMaxLength = 1024;
char value[kSystemPropertyMaxLength];
@@ -228,6 +219,9 @@
platform_info.javascript_engine_version =
script::GetJavaScriptEngineNameAndVersion();
+ platform_info.rasterizer_type =
+ renderer::GetDefaultRasterizerForPlatform().rasterizer_name;
+
platform_info.cobalt_version = COBALT_VERSION;
platform_info.cobalt_build_version_number = COBALT_BUILD_VERSION_NUMBER;
@@ -338,19 +332,25 @@
platform_info.cobalt_build_version_number.c_str(),
platform_info.build_configuration.c_str());
- // JavaScript Engine Name/Version
+ // JavaScript Engine Name/Version
if (!platform_info.javascript_engine_version.empty()) {
base::StringAppendF(&user_agent, " %s",
platform_info.javascript_engine_version.c_str());
}
- // Starboard/APIVersion,
+ // Rasterizer Type
+ if (!platform_info.rasterizer_type.empty()) {
+ base::StringAppendF(&user_agent, " %s",
+ platform_info.rasterizer_type.c_str());
+ }
+
+ // Starboard/APIVersion,
if (!platform_info.starboard_version.empty()) {
base::StringAppendF(&user_agent, " %s",
platform_info.starboard_version.c_str());
}
- // Device/FirmwareVersion (Brand, Model, ConnectionType)
+ // Device/FirmwareVersion (Brand, Model, ConnectionType)
base::StringAppendF(
&user_agent, ", %s_%s_%s_%s/%s (%s, %s, %s)",
Sanitize(platform_info.network_operator.value_or("")).c_str(),
diff --git a/src/cobalt/browser/user_agent_string.h b/src/cobalt/browser/user_agent_string.h
index 900c8a9..27a720b 100644
--- a/src/cobalt/browser/user_agent_string.h
+++ b/src/cobalt/browser/user_agent_string.h
@@ -37,6 +37,7 @@
std::string aux_field;
base::optional<SbSystemConnectionType> connection_type;
std::string javascript_engine_version;
+ std::string rasterizer_type;
std::string cobalt_version;
std::string cobalt_build_version_number;
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index d004068..cd52dc8 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -43,6 +43,7 @@
#include "cobalt/dom/element.h"
#include "cobalt/dom/event.h"
#include "cobalt/dom/global_stats.h"
+#include "cobalt/dom/html_script_element.h"
#include "cobalt/dom/input_event.h"
#include "cobalt/dom/input_event_init.h"
#include "cobalt/dom/keyboard_event.h"
@@ -247,6 +248,8 @@
void LogScriptError(const base::SourceLocation& source_location,
const std::string& error_message);
+ void CancelSynchronousLoads();
+
private:
class DocumentLoadedObserver;
@@ -441,6 +444,13 @@
scoped_refptr<cobalt::dom::captions::SystemCaptionSettings>
system_caption_settings_;
+
+ // This event is used to interrupt the loader when JavaScript is loaded
+ // synchronously. It is manually reset so that events like Suspend can be
+ // correctly execute, even if there are multiple synchronous loads in queue
+ // before the suspend (or other) event handlers.
+ base::WaitableEvent synchronous_loader_interrupt_ = {
+ true /* manually reset */, false /* initially signaled */};
};
class WebModule::Impl::DocumentLoadedObserver : public dom::DocumentObserver {
@@ -578,8 +588,6 @@
global_environment_ = javascript_engine_->CreateGlobalEnvironment();
DCHECK(global_environment_);
- mutation_observer_task_manager_.RegisterAsTracingRoot(global_environment_);
-
execution_state_ =
script::ExecutionState::CreateExecutionState(global_environment_);
DCHECK(execution_state_);
@@ -598,11 +606,16 @@
dom::Window::CacheCallback splash_screen_cache_callback =
CacheUrlContentCallback(data.options.splash_screen_cache);
+ // These members will reference other |Traceable|s, however are not
+ // accessible from |Window|, so we must explicitly add them as roots.
+ global_environment_->AddRoot(&mutation_observer_task_manager_);
+ global_environment_->AddRoot(media_source_registry_.get());
+
window_ = new dom::Window(
data.window_dimensions.width(), data.window_dimensions.height(),
data.video_pixel_ratio, data.initial_application_state, css_parser_.get(),
- dom_parser_.get(), fetcher_factory_.get(), &resource_provider_,
- animated_image_tracker_.get(), image_cache_.get(),
+ dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(),
+ &resource_provider_, animated_image_tracker_.get(), image_cache_.get(),
reduced_image_cache_capacity_manager_.get(), remote_typeface_cache_.get(),
mesh_cache_.get(), local_storage_database_.get(),
data.can_play_type_handler, data.web_media_player_factory,
@@ -627,7 +640,7 @@
base::Bind(&WebModule::Impl::OnStartDispatchEvent,
base::Unretained(this)),
base::Bind(&WebModule::Impl::OnStopDispatchEvent, base::Unretained(this)),
- data.options.provide_screenshot_function,
+ data.options.provide_screenshot_function, &synchronous_loader_interrupt_,
data.options.csp_insecure_allowed_token, data.dom_max_element_depth,
data.options.video_playback_rate_multiplier,
#if defined(ENABLE_TEST_RUNNER)
@@ -750,6 +763,8 @@
void WebModule::Impl::InjectInputEvent(scoped_refptr<dom::Element> element,
const scoped_refptr<dom::Event>& event) {
+ TRACE_EVENT1("cobalt::browser", "WebModule::Impl::InjectInputEvent()",
+ "event", event->type().c_str());
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(is_running_);
DCHECK(window_);
@@ -757,7 +772,17 @@
if (element) {
element->DispatchEvent(event);
} else {
- window_->InjectEvent(event);
+ if (dom::PointerState::CanQueueEvent(event)) {
+ // As an optimization we batch together pointer/mouse events for as long
+ // as we can get away with it (e.g. until a non-pointer event is received
+ // or whenever the next layout occurs).
+ window_->document()->pointer_state()->QueuePointerEvent(event);
+ } else {
+ // In order to maintain the correct input event ordering, we first
+ // dispatch any queued pending pointer events.
+ HandlePointerEvents();
+ window_->InjectEvent(event);
+ }
}
}
@@ -912,6 +937,10 @@
}
}
+void WebModule::Impl::CancelSynchronousLoads() {
+ synchronous_loader_interrupt_.Signal();
+}
+
#if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
void WebModule::Impl::OnPartialLayoutConsoleCommandReceived(
const std::string& message) {
@@ -1074,6 +1103,7 @@
void WebModule::Impl::Unpause() {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Unpause()");
+ synchronous_loader_interrupt_.Reset();
SetApplicationState(base::kApplicationStateStarted);
}
@@ -1132,6 +1162,7 @@
void WebModule::Impl::Resume(render_tree::ResourceProvider* resource_provider) {
TRACE_EVENT0("cobalt::browser", "WebModule::Impl::Resume()");
+ synchronous_loader_interrupt_.Reset();
SetResourceProvider(resource_provider);
SetApplicationState(base::kApplicationStatePaused);
}
@@ -1142,6 +1173,7 @@
if (!is_running_) {
return;
}
+ synchronous_loader_interrupt_.Reset();
layout_manager_->Purge();
@@ -1599,6 +1631,8 @@
// Must only be called by a thread external from the WebModule thread.
DCHECK_NE(MessageLoop::current(), message_loop());
+ impl_->CancelSynchronousLoads();
+
// We must block here so that the call doesn't return until the web
// application has had a chance to process the whole event.
message_loop()->PostBlockingTask(
@@ -1619,6 +1653,8 @@
// Must only be called by a thread external from the WebModule thread.
DCHECK_NE(MessageLoop::current(), message_loop());
+ impl_->CancelSynchronousLoads();
+
// We must block here so that we don't queue the finish until after
// SuspendLoaders has run to completion, and therefore has already queued any
// precipitate tasks.
@@ -1647,6 +1683,8 @@
// Must only be called by a thread external from the WebModule thread.
DCHECK_NE(MessageLoop::current(), message_loop());
+ impl_->CancelSynchronousLoads();
+
// We block here so that we block the Low Memory event handler until we have
// reduced our memory consumption.
message_loop()->PostBlockingTask(FROM_HERE,
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 82230f8..a5fd30c 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -408,6 +408,8 @@
void ClearAllIntervalsAndTimeouts();
+ void CancelSynchronousLoads();
+
#if defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
void OnPartialLayoutConsoleCommandReceived(const std::string& message);
#endif // defined(ENABLE_PARTIAL_LAYOUT_CONTROL)
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index 3a22d93..cf79008 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -33,6 +33,7 @@
'<(DEPTH)/base/base.gyp:base_unittests',
'<(DEPTH)/cobalt/accessibility/accessibility_test.gyp:*',
'<(DEPTH)/cobalt/audio/audio.gyp:*',
+ '<(DEPTH)/cobalt/audio/audio_test.gyp:*',
'<(DEPTH)/cobalt/base/base.gyp:*',
'<(DEPTH)/cobalt/bindings/testing/testing.gyp:*',
'<(DEPTH)/cobalt/browser/browser.gyp:*',
@@ -44,7 +45,9 @@
'<(DEPTH)/cobalt/debug/debug.gyp:*',
'<(DEPTH)/cobalt/dom/dom.gyp:*',
'<(DEPTH)/cobalt/dom/dom_test.gyp:*',
+ '<(DEPTH)/cobalt/dom/testing/dom_testing.gyp:*',
'<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:*',
+ '<(DEPTH)/cobalt/dom_parser/dom_parser_test.gyp:*',
'<(DEPTH)/cobalt/h5vcc/h5vcc.gyp:*',
'<(DEPTH)/cobalt/input/input.gyp:*',
'<(DEPTH)/cobalt/layout/layout.gyp:*',
@@ -54,8 +57,11 @@
'<(DEPTH)/cobalt/math/math.gyp:*',
'<(DEPTH)/cobalt/media/sandbox/sandbox.gyp:*',
'<(DEPTH)/cobalt/media_capture/media_capture.gyp:*',
+ '<(DEPTH)/cobalt/media_capture/media_capture_test.gyp:*',
'<(DEPTH)/cobalt/media_session/media_session.gyp:*',
'<(DEPTH)/cobalt/media_session/media_session_test.gyp:*',
+ '<(DEPTH)/cobalt/media_stream/media_stream.gyp:*',
+ '<(DEPTH)/cobalt/media_stream/media_stream_test.gyp:*',
'<(DEPTH)/cobalt/network/network.gyp:*',
'<(DEPTH)/cobalt/overlay_info/overlay_info.gyp:*',
'<(DEPTH)/cobalt/page_visibility/page_visibility.gyp:*',
@@ -63,11 +69,14 @@
'<(DEPTH)/cobalt/renderer/renderer.gyp:*',
'<(DEPTH)/cobalt/renderer/sandbox/sandbox.gyp:*',
'<(DEPTH)/cobalt/samples/simple_example/simple_example.gyp:*',
+ '<(DEPTH)/cobalt/script/engine.gyp:engine_shell',
'<(DEPTH)/cobalt/script/script.gyp:*',
- '<(DEPTH)/cobalt/script/engine.gyp:all_engines',
'<(DEPTH)/cobalt/speech/sandbox/sandbox.gyp:*',
'<(DEPTH)/cobalt/speech/speech.gyp:*',
'<(DEPTH)/cobalt/storage/storage.gyp:*',
+ '<(DEPTH)/cobalt/storage/store/store.gyp:*',
+ '<(DEPTH)/cobalt/storage/store_upgrade/upgrade.gyp:*',
+ '<(DEPTH)/cobalt/storage/store_upgrade/upgrade_tool.gyp:*',
'<(DEPTH)/cobalt/trace_event/trace_event.gyp:*',
'<(DEPTH)/cobalt/web_animations/web_animations.gyp:*',
'<(DEPTH)/cobalt/webdriver/webdriver.gyp:*',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 5ea67dc..501b9b9 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-162639
\ No newline at end of file
+179149
\ No newline at end of file
diff --git a/src/cobalt/build/cobalt_configuration.gypi b/src/cobalt/build/cobalt_configuration.gypi
index f75cede..3934f82 100644
--- a/src/cobalt/build/cobalt_configuration.gypi
+++ b/src/cobalt/build/cobalt_configuration.gypi
@@ -366,7 +366,7 @@
'cobalt_gc_zeal%': 0,
# Use media source extension implementation that is conformed to the
- # Candidate Recommandation of July 5th 2016.
+ # Candidate Recommendation of July 5th 2016.
'cobalt_media_source_2016%': '<(cobalt_media_source_2016)',
# Note that the following media buffer related variables are only used when
diff --git a/src/cobalt/build/cobalt_configuration.py b/src/cobalt/build/cobalt_configuration.py
index c6458b5..90fb8d6 100644
--- a/src/cobalt/build/cobalt_configuration.py
+++ b/src/cobalt/build/cobalt_configuration.py
@@ -13,6 +13,7 @@
# limitations under the License.
"""Base cobalt configuration for GYP."""
+import logging
import os
import _env # pylint: disable=unused-import
@@ -21,7 +22,6 @@
import cobalt.tools.webdriver_benchmark_config as wb_config
from starboard.build import application_configuration
-
# The canonical Cobalt application name.
APPLICATION_NAME = 'cobalt'
@@ -52,6 +52,7 @@
# Whether to enable VR.
'enable_vr': int(os.environ.get('USE_VR', 0)),
}
+ logging.info('Build Number: {}'.format(variables['cobalt_version']))
return variables
def GetPostIncludes(self):
@@ -79,6 +80,8 @@
'loader_test',
'math_test',
'media_session_test',
+ 'media_stream_test',
+ 'memory_store_test',
'nb_test',
'net_unittests',
'network_test',
@@ -88,6 +91,7 @@
'renderer_test',
'sql_unittests',
'storage_test',
+ 'storage_upgrade_test',
'trace_event_test',
'web_animations_test',
'web_platform_tests',
diff --git a/src/cobalt/build/config/base.gni b/src/cobalt/build/config/base.gni
index cfcd816..fa84134 100644
--- a/src/cobalt/build/config/base.gni
+++ b/src/cobalt/build/config/base.gni
@@ -418,7 +418,7 @@
}
# Use media source extension implementation that is conformed to the
-# Candidate Recommandation of July 5th 2016.
+# Candidate Recommendation of July 5th 2016.
if (!defined(cobalt_use_media_source_2016)) {
cobalt_use_media_source_2016 = true
}
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index b04f13c..f60badb 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -71,7 +71,6 @@
with open(BUILD_ID_PATH, 'r') as build_id_file:
build_number = int(build_id_file.read().replace('\n', ''))
logging.info('Retrieving build number from %s', BUILD_ID_PATH)
- logging.info('Build Number: %d', build_number)
return build_number
revinfo = GetRevinfo()
@@ -99,7 +98,6 @@
data = data[len(_XSSI_PREFIX):]
results = json.loads(data)
build_number = results.get('build_number', 0)
- logging.info('Build Number: %d', build_number)
return build_number
diff --git a/src/cobalt/content/ssl/certs/7992b8bb.0 b/src/cobalt/content/ssl/certs/7992b8bb.0
deleted file mode 100644
index 870bc4a..0000000
--- a/src/cobalt/content/ssl/certs/7992b8bb.0
+++ /dev/null
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN
-BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
-bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1Qg
-RWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAw
-ODA3MDFaFw0yMzA0MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0w
-SwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnE
-n2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRp
-ZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEApCUZ4WWe60ghUEoI5RHwWrom/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537
-jVJp45wnEFPzpALFp/kRGml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1m
-ep5Fimh34khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z5UNP
-9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0hO8EuPbJbKoCPrZV
-4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QIDAQABo0IwQDAdBgNVHQ4EFgQUVpkH
-HtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
-hvcNAQELBQADggEBAJ5FdnsXSDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPo
-BP5yCccLqh0lVX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq
-URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nfpeYVhDfwwvJl
-lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8
-B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=
------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/9007ae68.0 b/src/cobalt/content/ssl/certs/9007ae68.0
deleted file mode 100644
index cdc2805..0000000
--- a/src/cobalt/content/ssl/certs/9007ae68.0
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw
-EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
-ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx
-EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
-c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy
-3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8
-u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2
-m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk
-CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa
-YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6
-vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL
-LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX
-ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is
-XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV
-HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ
-04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
-xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B
-LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM
-CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb
-VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85
-YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS
-ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix
-lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N
-UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ
-a7+h89n07eLw4+1knj0vllJPgFOL
------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/9d520b32.0 b/src/cobalt/content/ssl/certs/9d520b32.0
deleted file mode 100644
index 494787d..0000000
--- a/src/cobalt/content/ssl/certs/9d520b32.0
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
-U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh
-dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE
-BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl
-Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
-AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO
-/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX
-WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z
-ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4
-bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK
-9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
-SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm
-iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG
-Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW
-mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW
-T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/cb59f961.0 b/src/cobalt/content/ssl/certs/cb59f961.0
deleted file mode 100644
index b97542a..0000000
--- a/src/cobalt/content/ssl/certs/cb59f961.0
+++ /dev/null
@@ -1,24 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
-QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
-ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx
-NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt
-YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg
-MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw
-ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J
-1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O
-by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl
-6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c
-8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/
-BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j
-aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B
-Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj
-aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y
-ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
-bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA
-PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y
-gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ
-PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4
-IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
-t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/f90208f7.0 b/src/cobalt/content/ssl/certs/f90208f7.0
deleted file mode 100644
index 60d878e..0000000
--- a/src/cobalt/content/ssl/certs/f90208f7.0
+++ /dev/null
@@ -1,24 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
-QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
-ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx
-NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp
-cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn
-MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC
-AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU
-xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH
-NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW
-DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV
-d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud
-EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v
-cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P
-AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh
-bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD
-VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
-aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi
-fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD
-L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN
-UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n
-ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1
-erfutGWaIZDgqtCYvDi1czyL+Nw=
------END CERTIFICATE-----
diff --git a/src/cobalt/cssom/css_computed_style_data.h b/src/cobalt/cssom/css_computed_style_data.h
index 88d90a6..1d0dc90 100644
--- a/src/cobalt/cssom/css_computed_style_data.h
+++ b/src/cobalt/cssom/css_computed_style_data.h
@@ -33,7 +33,8 @@
// CSSComputedStyleData which has PropertyValue type properties only used
// internally and it is not exposed to JavaScript.
-class CSSComputedStyleData : public base::RefCounted<CSSComputedStyleData> {
+class CSSComputedStyleData
+ : public base::RefCountedThreadSafe<CSSComputedStyleData> {
public:
// This class provides the ability to determine whether the properties of two
// CSSComputedStyleData objects match for a given set of property keys.
diff --git a/src/cobalt/cssom/selector_tree.cc b/src/cobalt/cssom/selector_tree.cc
index 011032f..1a32266 100644
--- a/src/cobalt/cssom/selector_tree.cc
+++ b/src/cobalt/cssom/selector_tree.cc
@@ -183,7 +183,7 @@
SelectorTree::Node* SelectorTree::GetOrCreateNodeForComplexSelector(
ComplexSelector* complex_selector) {
CompoundSelector* selector = complex_selector->first_selector();
- Node* node = GetOrCreateNodeForCompoundSelector(selector, &root_,
+ Node* node = GetOrCreateNodeForCompoundSelector(selector, &root_node_,
kDescendantCombinator);
while (selector->right_combinator()) {
diff --git a/src/cobalt/cssom/selector_tree.h b/src/cobalt/cssom/selector_tree.h
index 10f1545..98f33f6 100644
--- a/src/cobalt/cssom/selector_tree.h
+++ b/src/cobalt/cssom/selector_tree.h
@@ -43,84 +43,12 @@
// https://docs.google.com/document/d/1LTbSenGsGR94JTGg6DfZDXYB3MBBCp4C8dRC4rckt_8/
class SelectorTree {
public:
- typedef std::vector<base::WeakPtr<CSSStyleRule> > Rules;
-
class Node;
- // This class can be used to store Nodes. It stores the Nodes in its
- // internal buffer whose size can be configured via template parameter.
- // After the internal buffer is used up the extra Nodes will be stored inside
- // the contained std::vector.
- // TODO: Move this off to its own file if this can also be used by
- // other code.
- template <size_t InternalCacheSize>
- class NodeSet {
- public:
- // Minimum interface for iterator.
- class const_iterator {
- public:
- const_iterator(const NodeSet* set, size_t index)
- : set_(set), index_(index) {}
- void operator++() { ++index_; }
- const Node* operator*() const { return set_->GetNode(index_); }
- bool operator!=(const const_iterator& that) const {
- return set_ != that.set_ || index_ != that.index_;
- }
+ typedef std::vector<base::WeakPtr<CSSStyleRule>> Rules;
- private:
- const NodeSet* set_;
- size_t index_;
- };
-
- NodeSet() : size_(0) {}
- void insert(const Node* node, bool check_for_duplicate = false) {
- // If |check_for_duplicate| is true, then check if the node is already
- // contained. In nearly all cases, this check is unnecessary because it is
- // already known that the node is not a duplicate. As a result, the caller
- // must explicitly request the check when needed.
- if (check_for_duplicate) {
- for (size_t i = 0; i < size_; ++i) {
- if (GetNode(i) == node) {
- return;
- }
- }
- }
-
- if (size_ < InternalCacheSize) {
- nodes_[size_] = node;
- } else {
- nodes_vector_.push_back(node);
- }
- ++size_;
- }
- template <class ConstIterator>
- void insert(ConstIterator begin, ConstIterator end,
- bool check_for_duplicate = false) {
- while (begin != end) {
- insert(*begin, check_for_duplicate);
- ++begin;
- }
- }
-
- const_iterator begin() const { return const_iterator(this, 0); }
- const_iterator end() const { return const_iterator(this, size_); }
- size_t size() const { return size_; }
- void clear() {
- size_ = 0;
- nodes_vector_.clear();
- }
- const Node* GetNode(size_t index) const {
- if (index < InternalCacheSize) {
- return nodes_[index];
- }
- return nodes_vector_[index - InternalCacheSize];
- }
-
- private:
- size_t size_;
- const Node* nodes_[InternalCacheSize];
- std::vector<const Node*> nodes_vector_;
- };
+ typedef std::vector<const Node*> Nodes;
+ typedef std::vector<std::pair<const Node*, const Node*>> NodePairs;
struct CompoundNodeLessThan {
bool operator()(const CompoundSelector* lhs,
@@ -255,10 +183,16 @@
Rules rules_;
};
- SelectorTree() : has_sibling_combinators_(false) { root_set_.insert(&root_); }
+ SelectorTree() : has_sibling_combinators_(false) {
+ root_nodes_.push_back(&root_node_);
+ }
- const Node* root() const { return &root_; }
- const NodeSet<1>& root_set() const { return root_set_; }
+ const Node* root_node() const { return &root_node_; }
+ const Nodes& root_nodes() const { return root_nodes_; }
+
+ Nodes* scratchpad_nodes_1() { return &scratchpad_nodes_1_; }
+ Nodes* scratchpad_nodes_2() { return &scratchpad_nodes_2_; }
+ NodePairs* scratchpad_node_pairs() { return &scratchpad_node_pairs_; }
bool has_sibling_combinators() const { return has_sibling_combinators_; }
@@ -284,8 +218,15 @@
Node* parent_node,
CombinatorType combinator);
- Node root_;
- NodeSet<1> root_set_;
+ Node root_node_;
+ Nodes root_nodes_;
+
+ // These member variables are available for temporary operations, so that
+ // Node vectors associated with the SelectorTree don't have to be repeatedly
+ // re-allocated. This significantly speeds up rule matching.
+ Nodes scratchpad_nodes_1_;
+ Nodes scratchpad_nodes_2_;
+ NodePairs scratchpad_node_pairs_;
// This variable maps from a parent Node and a combinator type to child Nodes
// under the particular parent Node corresponding to the particular combinator
diff --git a/src/cobalt/cssom/selector_tree_test.cc b/src/cobalt/cssom/selector_tree_test.cc
index 304f5c9..22560dd 100644
--- a/src/cobalt/cssom/selector_tree_test.cc
+++ b/src/cobalt/cssom/selector_tree_test.cc
@@ -26,15 +26,17 @@
TEST(SelectorTreeTest, RootShouldHaveNoChildrenAfterInitialization) {
SelectorTree selector_tree;
EXPECT_TRUE(
- selector_tree.children(selector_tree.root(), kChildCombinator).empty());
- EXPECT_TRUE(
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.empty());
EXPECT_TRUE(
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.empty());
EXPECT_TRUE(
- selector_tree.children(selector_tree.root(), kFollowingSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kNextSiblingCombinator)
+ .empty());
+ EXPECT_TRUE(
+ selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
.empty());
}
@@ -56,20 +58,22 @@
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
- ASSERT_EQ(
- 0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
- ASSERT_EQ(1,
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
- .size());
ASSERT_EQ(0,
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
- ASSERT_EQ(0, selector_tree.children(selector_tree.root(),
- kFollowingSiblingCombinator)
+ ASSERT_EQ(1, selector_tree
+ .children(selector_tree.root_node(), kDescendantCombinator)
.size());
+ ASSERT_EQ(0, selector_tree
+ .children(selector_tree.root_node(), kNextSiblingCombinator)
+ .size());
+ ASSERT_EQ(
+ 0, selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
+ .size());
const SelectorTree::Node* node_1 =
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
+ selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(1, node_1->rules().size());
@@ -102,17 +106,19 @@
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
- ASSERT_EQ(
- 0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
- ASSERT_EQ(1,
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
- .size());
ASSERT_EQ(0,
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
- ASSERT_EQ(0, selector_tree.children(selector_tree.root(),
- kFollowingSiblingCombinator)
+ ASSERT_EQ(1, selector_tree
+ .children(selector_tree.root_node(), kDescendantCombinator)
.size());
+ ASSERT_EQ(0, selector_tree
+ .children(selector_tree.root_node(), kNextSiblingCombinator)
+ .size());
+ ASSERT_EQ(
+ 0, selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
+ .size());
}
TEST(SelectorTreeTest, AppendRuleSimpleShouldTakeTwoIdenticalRules) {
@@ -138,19 +144,21 @@
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
- ASSERT_EQ(
- 0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
- ASSERT_EQ(1,
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
- .size());
ASSERT_EQ(0,
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
- ASSERT_EQ(0, selector_tree.children(selector_tree.root(),
- kFollowingSiblingCombinator)
+ ASSERT_EQ(1, selector_tree
+ .children(selector_tree.root_node(), kDescendantCombinator)
.size());
+ ASSERT_EQ(0, selector_tree
+ .children(selector_tree.root_node(), kNextSiblingCombinator)
+ .size());
+ ASSERT_EQ(
+ 0, selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
+ .size());
const SelectorTree::Node* node_1 =
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
+ selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(2, node_1->rules().size());
@@ -184,19 +192,21 @@
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
- ASSERT_EQ(
- 0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
- ASSERT_EQ(1,
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
- .size());
ASSERT_EQ(0,
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
- ASSERT_EQ(0, selector_tree.children(selector_tree.root(),
- kFollowingSiblingCombinator)
+ ASSERT_EQ(1, selector_tree
+ .children(selector_tree.root_node(), kDescendantCombinator)
.size());
+ ASSERT_EQ(0, selector_tree
+ .children(selector_tree.root_node(), kNextSiblingCombinator)
+ .size());
+ ASSERT_EQ(
+ 0, selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
+ .size());
const SelectorTree::Node* node_1 =
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
+ selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(1, node_1->rules().size());
@@ -246,19 +256,21 @@
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_FALSE(selector_tree.ValidateVersionCompatibility());
- ASSERT_EQ(
- 0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
- ASSERT_EQ(2,
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
- .size());
ASSERT_EQ(0,
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
- ASSERT_EQ(0, selector_tree
- .children(selector_tree.root(), kFollowingSiblingCombinator)
+ ASSERT_EQ(2, selector_tree
+ .children(selector_tree.root_node(), kDescendantCombinator)
.size());
+ ASSERT_EQ(0, selector_tree
+ .children(selector_tree.root_node(), kNextSiblingCombinator)
+ .size());
+ ASSERT_EQ(
+ 0, selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
+ .size());
auto node_iter =
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
+ selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin();
const SelectorTree::Node* node_1 = node_iter->second;
ASSERT_EQ(1, node_1->rules().size());
@@ -297,19 +309,21 @@
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
- ASSERT_EQ(
- 0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
- ASSERT_EQ(2,
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
- .size());
ASSERT_EQ(0,
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
- ASSERT_EQ(0, selector_tree
- .children(selector_tree.root(), kFollowingSiblingCombinator)
+ ASSERT_EQ(2, selector_tree
+ .children(selector_tree.root_node(), kDescendantCombinator)
.size());
+ ASSERT_EQ(0, selector_tree
+ .children(selector_tree.root_node(), kNextSiblingCombinator)
+ .size());
+ ASSERT_EQ(
+ 0, selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
+ .size());
auto node_iter =
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
+ selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin();
const SelectorTree::Node* node_1 = node_iter->second;
ASSERT_EQ(1, node_1->rules().size());
@@ -347,19 +361,21 @@
base::VersionCompatibility::GetInstance()->SetMinimumVersion(1);
EXPECT_TRUE(selector_tree.ValidateVersionCompatibility());
- ASSERT_EQ(
- 0, selector_tree.children(selector_tree.root(), kChildCombinator).size());
- ASSERT_EQ(1,
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
- .size());
ASSERT_EQ(0,
- selector_tree.children(selector_tree.root(), kNextSiblingCombinator)
+ selector_tree.children(selector_tree.root_node(), kChildCombinator)
.size());
- ASSERT_EQ(0, selector_tree
- .children(selector_tree.root(), kFollowingSiblingCombinator)
+ ASSERT_EQ(1, selector_tree
+ .children(selector_tree.root_node(), kDescendantCombinator)
.size());
+ ASSERT_EQ(0, selector_tree
+ .children(selector_tree.root_node(), kNextSiblingCombinator)
+ .size());
+ ASSERT_EQ(
+ 0, selector_tree
+ .children(selector_tree.root_node(), kFollowingSiblingCombinator)
+ .size());
const SelectorTree::Node* node_1 =
- selector_tree.children(selector_tree.root(), kDescendantCombinator)
+ selector_tree.children(selector_tree.root_node(), kDescendantCombinator)
.begin()
->second;
ASSERT_EQ(2, node_1->rules().size());
diff --git a/src/cobalt/debug/debug_web_server.cc b/src/cobalt/debug/debug_web_server.cc
index b87a5fe..6749690 100644
--- a/src/cobalt/debug/debug_web_server.cc
+++ b/src/cobalt/debug/debug_web_server.cc
@@ -169,6 +169,11 @@
// Construct the local disk path corresponding to the request path.
FilePath file_path(content_root_dir_);
+ if (!IsStringASCII(url_path)) {
+ LOG(WARNING) << "Got HTTP request with non-ASCII URL path.";
+ server_->Send404(connection_id);
+ return;
+ }
file_path = file_path.AppendASCII(url_path);
// If the disk path is a directory, look for an index file.
diff --git a/src/cobalt/doc/performance_tuning.md b/src/cobalt/doc/performance_tuning.md
index a142028..a3aa232 100644
--- a/src/cobalt/doc/performance_tuning.md
+++ b/src/cobalt/doc/performance_tuning.md
@@ -12,7 +12,9 @@
Many of the tweaks involve adding a new gyp variable to your platform's
`gyp_configuration.gypi` file. The default values for these variables are
-defined in [`base.gypi`](../build/config/base.gypi).
+defined in either
+[`base_configuration.gypi`](../../starboard/build/base_configuration.gypi) or
+[`cobalt_configuration.gypi`](../build/cobalt_configuration.gypi).
### Use a Release Build
@@ -182,7 +184,8 @@
### Try enabling rendering only to regions that change
-If you set the [`base.gypi`](../build/config/base.gypi) variable,
+If you set the
+[`cobalt_configuration.gypi`](../build/cobalt_configuration.gypi) variable,
`render_dirty_region_only` to `1`, then Cobalt will invoke logic to detect which
part of the frame has been affected by animations and can be configured to only
render to that region. However, this feature requires support from the driver
@@ -232,11 +235,17 @@
flags will carry over from external shell environment settings; they
must be set explicitly in `gyp_configuration.gypi`.
+**Tags:** *framerate, startup, browse-to-watch, input latency*
+
+
#### Link Time Optimization (LTO)
If your toolchain supports it, it is recommended that you enable the LTO
optimization, as it has been reported to yield significant performance
improvements in many high profile projects.
+**Tags:** *framerate, startup, browse-to-watch, input latency*
+
+
#### The GCC '-mplt' flag for MIPS architectures
The '-mplt' flag has been found to improve all around performance by
~20% on MIPS architecture platforms. If your platform has a MIPS
@@ -305,6 +314,19 @@
**Tags:** *configuration_public.h, cpu memory.*
+### Adjust media buffer size settings
+
+Many of the parameters around media buffer allocation can be adjusted in your
+gyp_configuration.gypi file. The variables in question are the family of
+`cobalt_media_*` variables, whose default values are specified in
+[`cobalt_configuration.gypi`](../build/cobalt_configuration.gypi). In
+particular, if your maximum video output resolution is less than 1080, then you
+may lower the budgets for many of the categories according to your maximum
+resolution.
+
+**Tags:** *cpu memory*
+
+
### Avoid using a the YouTube web app FPS counter (i.e. "?fps=1")
The YouTube web app is able to display a Frames Per Second (FPS) counter in the
diff --git a/src/cobalt/dom/base64.cc b/src/cobalt/dom/base64.cc
new file mode 100644
index 0000000..c47318c
--- /dev/null
+++ b/src/cobalt/dom/base64.cc
@@ -0,0 +1,150 @@
+// Copyright 2018 Google Inc. 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 "cobalt/dom/base64.h"
+
+#include "base/base64.h"
+
+namespace cobalt {
+namespace dom {
+
+namespace {
+// https://infra.spec.whatwg.org/#ascii-whitespace
+const char kAsciiWhitespace[] = {0x09, 0x0a, 0x0c, 0x0d, 0x20};
+
+bool IsAtobAllowedChar(char c) {
+ // atob() allows '+', '/', and ASCII alphanumeric characters.
+ // https://infra.spec.whatwg.org/#forgiving-base64-decode
+ if (c == '+') {
+ return true;
+ }
+ if (c < '/') {
+ return false;
+ }
+ if (c <= '9') {
+ return true;
+ }
+ if (c < 'A') {
+ return false;
+ }
+ if (c <= 'Z') {
+ return true;
+ }
+ if (c < 'a') {
+ return false;
+ }
+ if (c <= 'z') {
+ return true;
+ }
+ return false;
+}
+
+bool IsNotAsciiWhitespace(char c) {
+ for (char sp_char : kAsciiWhitespace) {
+ if (c == sp_char) {
+ return false;
+ }
+ }
+ return true;
+}
+
+base::optional<std::string> GetAtobAllowedStr(const std::string& input_str) {
+ // Our base64 decoding method does not check for forbidden characters. We use
+ // the following method to reject string containing disallowed character or
+ // format in atob().
+ // Step 1-4 of https://infra.spec.whatwg.org/#forgiving-base64-decode
+ std::string output_str;
+ // Step 1: Remove all ASCII whitespace from data.
+ std::copy_if(input_str.begin(), input_str.end(),
+ std::back_inserter(output_str), IsNotAsciiWhitespace);
+ // Step 2: If data's length divides by 4 leaving no remainder, then:
+ // 1. if data ends with one or two U+003D (=) code points, then
+ // remove them from data.
+ if (!output_str.empty() && output_str.length() % 4 == 0 &&
+ output_str.back() == '=') {
+ output_str.pop_back();
+ if (output_str.back() == '=') {
+ output_str.pop_back();
+ }
+ }
+ // Step 3: If data's length divides by 4 leaving a remainder of 1, return
+ // failure.
+ if (output_str.length() % 4 == 1) {
+ return base::nullopt;
+ }
+ // Step 4: If data contains a code point that is not allowed, return failure.
+ for (char current_char : output_str) {
+ if (!IsAtobAllowedChar(current_char)) {
+ return base::nullopt;
+ }
+ }
+ // Customized step: add padding to string less than 4 character.
+ for (size_t i = output_str.length() % 4; i > 1 && i < 4; i++) {
+ output_str.push_back('=');
+ }
+ return output_str;
+}
+
+base::optional<std::string> Utf8ToLatin1(const std::string& input) {
+ std::string output;
+ unsigned char current_char_remainder = 0x00;
+ for (unsigned char c : input) {
+ if (c <= 0x7f && !current_char_remainder) {
+ output.push_back(c);
+ } else if (c <= 0xbf && current_char_remainder) {
+ // This is the only byte or second byte of one character.
+ output.push_back(current_char_remainder | (c & 0x3f));
+ current_char_remainder = 0x00;
+ } else if (c == 0xc2 && !current_char_remainder) {
+ current_char_remainder = c & 0x80;
+ } else if (c == 0xc3 && !current_char_remainder) {
+ current_char_remainder = 0xc0;
+ } else {
+ return base::nullopt;
+ }
+ }
+ return output;
+}
+} // namespace
+
+base::optional<std::string> ForgivingBase64Encode(
+ const std::string& string_to_encode) {
+ // https://infra.spec.whatwg.org/#forgiving-base64-encode
+ auto maybe_string_to_encode_in_latin1 = Utf8ToLatin1(string_to_encode);
+ std::string output;
+ if (!maybe_string_to_encode_in_latin1 ||
+ !base::Base64Encode(*maybe_string_to_encode_in_latin1, &output)) {
+ return base::nullopt;
+ }
+ return output;
+}
+
+base::optional<std::vector<uint8_t>> ForgivingBase64Decode(
+ const std::string& encoded_string) {
+ // https://infra.spec.whatwg.org/#forgiving-base64-decode
+ // Step 1-4:
+ auto maybe_encoded_string_no_whitespace = GetAtobAllowedStr(encoded_string);
+ // Step 5-10:
+ std::vector<uint8_t> output;
+ // If input string format is not allowed or base64 encoding failed, return
+ // nullopt to signal failure.
+ if (!maybe_encoded_string_no_whitespace ||
+ !base::Base64Decode(*maybe_encoded_string_no_whitespace, &output)) {
+ return base::nullopt;
+ }
+ return output;
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/base64.h b/src/cobalt/dom/base64.h
new file mode 100644
index 0000000..7e46d34
--- /dev/null
+++ b/src/cobalt/dom/base64.h
@@ -0,0 +1,37 @@
+// Copyright 2018 Google Inc. 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 COBALT_DOM_BASE64_H_
+#define COBALT_DOM_BASE64_H_
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+
+namespace cobalt {
+namespace dom {
+
+// https://infra.spec.whatwg.org/#forgiving-base64-encode
+base::optional<std::string> ForgivingBase64Encode(
+ const std::string& string_to_encode);
+
+// https://infra.spec.whatwg.org/#forgiving-base64-decode
+base::optional<std::vector<uint8_t>> ForgivingBase64Decode(
+ const std::string& encoded_string);
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_BASE64_H_
diff --git a/src/cobalt/dom/blob.cc b/src/cobalt/dom/blob.cc
index cbcb039..e448cf8 100644
--- a/src/cobalt/dom/blob.cc
+++ b/src/cobalt/dom/blob.cc
@@ -72,19 +72,22 @@
// so the media type can be exposed and used, as described in:
// https://www.w3.org/TR/FileAPI/#constructorBlob
Blob::Blob(script::EnvironmentSettings* settings,
- script::Sequence<BlobPart> blobParts, const BlobPropertyBag& options)
+ const script::Sequence<BlobPart>& blob_parts,
+ const BlobPropertyBag& options)
: buffer_(new ArrayBuffer(settings, 0)), type_(options.type()) {
size_t byte_length = 0;
- for (script::Sequence<BlobPart>::size_type i = 0; i < blobParts.size(); i++) {
- byte_length += DataLength(blobParts.at(i));
+ for (script::Sequence<BlobPart>::size_type i = 0; i < blob_parts.size();
+ ++i) {
+ byte_length += DataLength(blob_parts.at(i));
}
buffer_ = new ArrayBuffer(settings, static_cast<uint32>(byte_length));
uint8* destination = buffer_->data();
size_t offset = 0;
- for (script::Sequence<BlobPart>::size_type i = 0; i < blobParts.size(); i++) {
- const uint8* source = DataStart(blobParts.at(i));
- uint64 count = DataLength(blobParts.at(i));
+ for (script::Sequence<BlobPart>::size_type i = 0; i < blob_parts.size();
+ ++i) {
+ const uint8* source = DataStart(blob_parts.at(i));
+ uint64 count = DataLength(blob_parts.at(i));
std::copy(source, source + count, destination + offset);
offset += count;
diff --git a/src/cobalt/dom/blob.h b/src/cobalt/dom/blob.h
index c38e0a3..577f898 100644
--- a/src/cobalt/dom/blob.h
+++ b/src/cobalt/dom/blob.h
@@ -52,7 +52,7 @@
const scoped_refptr<ArrayBuffer>& buffer = NULL);
Blob(script::EnvironmentSettings* settings,
- script::Sequence<BlobPart> blob_parts,
+ const script::Sequence<BlobPart>& blob_parts,
const BlobPropertyBag& options = EmptyBlobPropertyBag());
const uint8* data() { return buffer_->data(); }
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc
index cbdc79d..cfc1569 100644
--- a/src/cobalt/dom/custom_event_test.cc
+++ b/src/cobalt/dom/custom_event_test.cc
@@ -19,6 +19,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
+#include "base/threading/platform_thread.h"
#include "cobalt/css_parser/parser.h"
#include "cobalt/dom/custom_event_init.h"
#include "cobalt/dom/local_storage_database.h"
@@ -26,6 +27,7 @@
#include "cobalt/dom/window.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader_factory.h"
#include "cobalt/media_session/media_session.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
@@ -56,26 +58,28 @@
css_parser_(css_parser::Parser::Create()),
dom_parser_(new dom_parser::Parser(mock_error_callback_)),
fetcher_factory_(new loader::FetcherFactory(NULL)),
+ loader_factory_(new loader::LoaderFactory(
+ fetcher_factory_.get(), NULL, base::kThreadPriority_Default)),
local_storage_database_(NULL),
- url_("about:blank"),
- window_(new Window(
- 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
- dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, url_, "", "en-US", "en",
- base::Callback<void(const GURL&)>(),
- base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_)),
- NULL, network_bridge::PostSender(), csp::kCSPRequired,
- kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
- base::Closure() /* ran_animation_frame_callbacks */,
- dom::Window::CloseCallback() /* window_close */,
- base::Closure() /* window_minimize */, NULL, NULL, NULL,
- dom::Window::OnStartDispatchEventCallback(),
- dom::Window::OnStopDispatchEventCallback(),
- dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {
+ url_("about:blank") {
engine_ = script::JavaScriptEngine::CreateEngine();
global_environment_ = engine_->CreateGlobalEnvironment();
+ window_ = new Window(
+ 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
+ dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(), NULL,
+ NULL, NULL, NULL, NULL, NULL, &local_storage_database_, NULL, NULL,
+ NULL, NULL, global_environment_->script_value_factory(), NULL, NULL,
+ url_, "", "en-US", "en", base::Callback<void(const GURL&)>(),
+ base::Bind(&MockErrorCallback::Run,
+ base::Unretained(&mock_error_callback_)),
+ NULL, network_bridge::PostSender(), csp::kCSPRequired,
+ kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
+ base::Closure() /* ran_animation_frame_callbacks */,
+ dom::Window::CloseCallback() /* window_close */,
+ base::Closure() /* window_minimize */, NULL, NULL, NULL,
+ dom::Window::OnStartDispatchEventCallback(),
+ dom::Window::OnStopDispatchEventCallback(),
+ dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
global_environment_->CreateGlobalObject(window_,
environment_settings_.get());
}
@@ -92,9 +96,10 @@
scoped_ptr<css_parser::Parser> css_parser_;
scoped_ptr<dom_parser::Parser> dom_parser_;
scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+ scoped_ptr<loader::LoaderFactory> loader_factory_;
dom::LocalStorageDatabase local_storage_database_;
GURL url_;
- const scoped_refptr<Window> window_;
+ scoped_refptr<Window> window_;
};
bool CustomEventTest::EvaluateScript(const std::string& js_code,
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc
index 08c0146..805c8d8 100644
--- a/src/cobalt/dom/document.cc
+++ b/src/cobalt/dom/document.cc
@@ -92,6 +92,7 @@
user_agent_style_sheet_(options.user_agent_style_sheet),
initial_computed_style_declaration_(
new cssom::CSSComputedStyleDeclaration()),
+ ready_state_(kDocumentReadyStateComplete),
dom_max_element_depth_(options.dom_max_element_depth),
render_postponed_(false) {
DCHECK(html_element_context_);
@@ -499,12 +500,22 @@
void Document::SetIndicatedElement(HTMLElement* indicated_element) {
if (indicated_element != indicated_element_) {
- is_selector_tree_dirty_ = true;
if (indicated_element_) {
+ // Clear the rule matching state on this element and its ancestors, as
+ // their hover state may be changing. However, the tree's matching rules
+ // only need to be invalidated once, so only do it here if it won't occur
+ // below.
+ bool invalidate_tree_matching_rules = (indicated_element == NULL);
+ indicated_element_->ClearRuleMatchingStateOnElementAndAncestors(
+ invalidate_tree_matching_rules);
indicated_element_->OnCSSMutation();
}
if (indicated_element) {
indicated_element_ = base::AsWeakPtr(indicated_element);
+ // Clear the rule matching state on this element and its ancestors, as
+ // their hover state may be changing.
+ indicated_element_->ClearRuleMatchingStateOnElementAndAncestors(
+ true /*invalidate_tree_matching_rules*/);
indicated_element_->OnCSSMutation();
} else {
indicated_element_.reset();
@@ -610,7 +621,7 @@
void Document::OnDOMMutation() {
// Something in the document's DOM has been modified, but we don't know what,
- // so set the flag indicating that rule matching needs to be done.
+ // so set the flag indicating that computed styles need to be updated.
is_computed_style_dirty_ = true;
RecordMutation();
@@ -719,12 +730,15 @@
scoped_refptr<HTMLElement> root = html();
if (root) {
DCHECK_EQ(this, root->parent_node());
- // First update the computed style for root element.
+ // First, update the matching rules for all elements.
+ root->UpdateMatchingRulesRecursively();
+
+ // Then, update the computed style for the root element.
root->UpdateComputedStyle(
initial_computed_style_declaration_, initial_computed_style_data_,
style_change_event_time, HTMLElement::kAncestorsAreDisplayed);
- // Then update the computed styles for the other elements.
+ // Finally, update the computed styles for the other elements.
root->UpdateComputedStyleRecursively(
root->css_computed_style_declaration(), root->computed_style(),
style_change_event_time, true, 0 /* current_element_depth */);
@@ -780,9 +794,13 @@
for (std::vector<HTMLElement*>::reverse_iterator it = ancestors.rbegin();
it != ancestors.rend(); ++it) {
HTMLElement* current_element = *it;
- bool is_valid = ancestors_were_valid &&
- current_element->matching_rules_valid() &&
- current_element->computed_style_valid();
+
+ // Ensure that the matching rules are up to date prior to updating the
+ // computed style.
+ current_element->UpdateMatchingRules();
+
+ bool is_valid =
+ ancestors_were_valid && current_element->AreComputedStylesValid();
if (!is_valid) {
DCHECK(initial_computed_style_declaration_);
DCHECK(initial_computed_style_data_);
@@ -920,7 +938,7 @@
scoped_refptr<HTMLHtmlElement> current_html = html();
if (current_html) {
- current_html->InvalidateMatchingRulesRecursively();
+ current_html->ClearRuleMatchingStateOnElementAndDescendants();
}
is_selector_tree_dirty_ = false;
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h
index ffca957..9a66a2b 100644
--- a/src/cobalt/dom/document.h
+++ b/src/cobalt/dom/document.h
@@ -26,6 +26,7 @@
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/string_piece.h"
+#include "base/synchronization/waitable_event.h"
#include "cobalt/base/clock.h"
#include "cobalt/cssom/css_computed_style_declaration.h"
#include "cobalt/cssom/css_keyframes_rule.h"
@@ -261,6 +262,14 @@
cssom::SelectorTree* selector_tree() { return selector_tree_.get(); }
+ cssom::RulesWithCascadePrecedence* scratchpad_html_element_matching_rules() {
+ return &scratchpad_html_element_matching_rules_;
+ }
+ cssom::RulesWithCascadePrecedence* scratchpad_pseudo_element_matching_rules(
+ PseudoElementType element_type) {
+ return &(scratchpad_pseudo_element_matching_rules_[element_type]);
+ }
+
// Returns a mapping from keyframes name to CSSKeyframesRule. This can be
// used to quickly lookup the @keyframes rule given a string identifier.
const cssom::CSSKeyframesRule::NameMap& keyframes_map() const {
@@ -497,6 +506,11 @@
// recreate the selector tree than to attempt to manage updating all of its
// internal state.
bool should_recreate_selector_tree_;
+ // Matching rules that are available for temporary operations, so that the
+ // vectors don't have to be repeatedly re-allocated during rule matching.
+ cssom::RulesWithCascadePrecedence scratchpad_html_element_matching_rules_;
+ cssom::RulesWithCascadePrecedence
+ scratchpad_pseudo_element_matching_rules_[kMaxPseudoElementType];
// The document's latest sample from the global clock, used for updating
// animations.
const scoped_refptr<base::Clock> navigation_start_clock_;
diff --git a/src/cobalt/dom/document_test.cc b/src/cobalt/dom/document_test.cc
index e0b8462..5cfc674 100644
--- a/src/cobalt/dom/document_test.cc
+++ b/src/cobalt/dom/document_test.cc
@@ -68,10 +68,10 @@
DocumentTest::DocumentTest()
: css_parser_(css_parser::Parser::Create()),
dom_stat_tracker_(new DomStatTracker("DocumentTest")),
- html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
+ html_element_context_(NULL, NULL, css_parser_.get(), NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted) {
+ NULL, dom_stat_tracker_.get(), "",
+ base::kApplicationStateStarted, NULL) {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
}
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 793fa82..4f0154c 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -32,6 +32,8 @@
'attr.h',
'audio_track.h',
'audio_track_list.h',
+ 'base64.cc',
+ 'base64.h',
'benchmark_stat_names.cc',
'benchmark_stat_names.h',
'blob.cc',
@@ -100,7 +102,6 @@
'event.cc',
'event.h',
'event_init.h',
- 'event_listener.cc',
'event_listener.h',
'event_queue.cc',
'event_queue.h',
@@ -119,6 +120,8 @@
'font_face_updater.h',
'font_list.cc',
'font_list.h',
+ 'generic_event_handler_reference.cc',
+ 'generic_event_handler_reference.h',
'global_stats.cc',
'global_stats.h',
'history.cc',
@@ -215,6 +218,8 @@
'node_list.h',
'node_list_live.cc',
'node_list_live.h',
+ 'on_error_event_listener.cc',
+ 'on_error_event_listener.h',
'on_screen_keyboard.cc',
'on_screen_keyboard.h',
'on_screen_keyboard_bridge.h',
@@ -243,6 +248,8 @@
'screenshot.h',
'screenshot_manager.cc',
'screenshot_manager.h',
+ 'script_event_log.cc',
+ 'script_event_log.h',
'security_policy_violation_event.cc',
'security_policy_violation_event.h',
'serializer.cc',
@@ -302,6 +309,7 @@
'<(DEPTH)/cobalt/network_bridge/network_bridge.gyp:network_bridge',
'<(DEPTH)/cobalt/page_visibility/page_visibility.gyp:page_visibility',
'<(DEPTH)/cobalt/script/script.gyp:script',
+ '<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/cobalt/storage/storage.gyp:storage',
'<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
'<(DEPTH)/cobalt/web_animations/web_animations.gyp:web_animations',
@@ -373,25 +381,5 @@
'<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
],
},
-
- {
- 'target_name': 'dom_testing',
- 'type': 'static_library',
- 'sources': [
- 'testing/gtest_workarounds.h',
- 'testing/html_collection_testing.h',
- 'testing/mock_event_listener.h',
- 'testing/stub_css_parser.cc',
- 'testing/stub_css_parser.h',
- 'testing/stub_script_runner.cc',
- 'testing/stub_script_runner.h',
- 'testing/stub_window.h',
- ],
- 'dependencies': [
- # TODO: Remove the dependency below, it works around the fact that
- # ScriptValueFactory has non-virtual method CreatePromise().
- '<(DEPTH)/cobalt/script/engine.gyp:engine',
- ],
- },
],
}
diff --git a/src/cobalt/dom/dom_exception.cc b/src/cobalt/dom/dom_exception.cc
index a1ec7fb..5eb35b4 100644
--- a/src/cobalt/dom/dom_exception.cc
+++ b/src/cobalt/dom/dom_exception.cc
@@ -49,6 +49,8 @@
return "ReadOnlyError";
case DOMException::kInvalidPointerIdErr:
return "InvalidPointerId";
+ case DOMException::kNotAllowedErr:
+ return "NotAllowedError";
}
NOTREACHED();
return "";
diff --git a/src/cobalt/dom/dom_exception.h b/src/cobalt/dom/dom_exception.h
index 59f3919..faf85cb 100644
--- a/src/cobalt/dom/dom_exception.h
+++ b/src/cobalt/dom/dom_exception.h
@@ -46,7 +46,8 @@
kQuotaExceededErr = 22,
kHighestErrCodeValue = kQuotaExceededErr,
kReadOnlyErr,
- kInvalidPointerIdErr
+ kInvalidPointerIdErr,
+ kNotAllowedErr
};
explicit DOMException(ExceptionCode code);
diff --git a/src/cobalt/dom/dom_parser_test.cc b/src/cobalt/dom/dom_parser_test.cc
index 7e18c40..9601bd6 100644
--- a/src/cobalt/dom/dom_parser_test.cc
+++ b/src/cobalt/dom/dom_parser_test.cc
@@ -14,6 +14,7 @@
#include <string>
+#include "base/threading/platform_thread.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_parser.h"
#include "cobalt/dom/html_element_context.h"
@@ -21,6 +22,7 @@
#include "cobalt/dom/testing/stub_script_runner.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
@@ -32,6 +34,7 @@
~DOMParserTest() override {}
loader::FetcherFactory fetcher_factory_;
+ loader::LoaderFactory loader_factory_;
testing::StubCSSParser stub_css_parser_;
scoped_ptr<dom_parser::Parser> dom_parser_parser_;
testing::StubScriptRunner stub_script_runner_;
@@ -41,17 +44,21 @@
DOMParserTest::DOMParserTest()
: fetcher_factory_(NULL /* network_module */),
+ loader_factory_(&fetcher_factory_, NULL /* resource provider */,
+ base::kThreadPriority_Default),
dom_parser_parser_(new dom_parser::Parser()),
html_element_context_(
- &fetcher_factory_, &stub_css_parser_, dom_parser_parser_.get(),
- NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
- &stub_script_runner_, NULL /* script_value_factory */,
- NULL /* media_source_registry */, NULL /* resource_provider */,
- NULL /* animated_image_tracker */, NULL /* image_cache */,
+ &fetcher_factory_, &loader_factory_, &stub_css_parser_,
+ dom_parser_parser_.get(), NULL /* can_play_type_handler */,
+ NULL /* web_media_player_factory */, &stub_script_runner_,
+ NULL /* script_value_factory */, NULL /* media_source_registry */,
+ NULL /* resource_provider */, NULL /* animated_image_tracker */,
+ NULL /* image_cache */,
NULL /* reduced_image_cache_capacity_manager */,
NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
NULL /* dom_stat_tracker */, "" /* language */,
- base::kApplicationStateStarted),
+ base::kApplicationStateStarted,
+ NULL /* synchronous_loader_interrupt */),
dom_parser_(new DOMParser(&html_element_context_)) {}
TEST_F(DOMParserTest, ParsesXML) {
diff --git a/src/cobalt/dom/dom_test.gyp b/src/cobalt/dom/dom_test.gyp
index 3c0b534..5ab6948 100644
--- a/src/cobalt/dom/dom_test.gyp
+++ b/src/cobalt/dom/dom_test.gyp
@@ -75,7 +75,7 @@
'<(DEPTH)/cobalt/browser/browser.gyp:browser',
'<(DEPTH)/cobalt/css_parser/css_parser.gyp:css_parser',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
+ '<(DEPTH)/cobalt/dom/testing/dom_testing.gyp:dom_testing',
'<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
'<(DEPTH)/cobalt/loader/loader.gyp:loader',
'<(DEPTH)/cobalt/renderer/rasterizer/skia/skia/skia.gyp:skia',
diff --git a/src/cobalt/dom/element_test.cc b/src/cobalt/dom/element_test.cc
index 1932267..5c0be93 100644
--- a/src/cobalt/dom/element_test.cc
+++ b/src/cobalt/dom/element_test.cc
@@ -60,10 +60,10 @@
: css_parser_(css_parser::Parser::Create()),
dom_parser_(new dom_parser::Parser()),
dom_stat_tracker_(new DomStatTracker("ElementTest")),
- html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
+ html_element_context_(NULL, NULL, css_parser_.get(), dom_parser_.get(),
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted) {
+ NULL, NULL, NULL, dom_stat_tracker_.get(), "",
+ base::kApplicationStateStarted, NULL) {
EXPECT_TRUE(GlobalStats::GetInstance()->CheckNoLeaks());
document_ = new Document(&html_element_context_);
xml_document_ = new XMLDocument(&html_element_context_);
diff --git a/src/cobalt/dom/eme/eme_helpers.h b/src/cobalt/dom/eme/eme_helpers.h
new file mode 100644
index 0000000..383e043
--- /dev/null
+++ b/src/cobalt/dom/eme/eme_helpers.h
@@ -0,0 +1,62 @@
+// Copyright 2018 Google Inc. 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 COBALT_DOM_EME_EME_HELPERS_H_
+#define COBALT_DOM_EME_EME_HELPERS_H_
+
+#include <string>
+#include <vector>
+
+#include "cobalt/script/script_value_factory.h"
+#include "starboard/drm.h"
+
+namespace cobalt {
+namespace dom {
+namespace eme {
+
+template <typename PromiseValueReferenceType>
+void RejectPromise(PromiseValueReferenceType* promise_reference,
+ SbDrmStatus status, const std::string& error_message) {
+ switch (status) {
+ case kSbDrmStatusSuccess:
+ NOTREACHED() << "'kSbDrmStatusSuccess' is not an error.";
+ break;
+ case kSbDrmStatusTypeError:
+ // TODO: Pass |error_message| once we support message on simple errors.
+ promise_reference->value().Reject(script::kTypeError);
+ break;
+ case kSbDrmStatusNotSupportedError:
+ promise_reference->value().Reject(
+ new DOMException(DOMException::kNotSupportedErr, error_message));
+ break;
+ case kSbDrmStatusInvalidStateError:
+ promise_reference->value().Reject(
+ new DOMException(DOMException::kInvalidStateErr, error_message));
+ break;
+ case kSbDrmStatusQuotaExceededError:
+ promise_reference->value().Reject(
+ new DOMException(DOMException::kQuotaExceededErr, error_message));
+ break;
+ case kSbDrmStatusUnknownError:
+ promise_reference->value().Reject(
+ new DOMException(DOMException::kNone, error_message));
+ break;
+ }
+}
+
+} // namespace eme
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_EME_EME_HELPERS_H_
diff --git a/src/cobalt/dom/eme/media_key_message_type.idl b/src/cobalt/dom/eme/media_key_message_type.idl
index 1125463..0c1f0e0 100644
--- a/src/cobalt/dom/eme/media_key_message_type.idl
+++ b/src/cobalt/dom/eme/media_key_message_type.idl
@@ -16,8 +16,7 @@
enum MediaKeyMessageType {
"license-request",
- // TODO: Implement other message types.
- // "license-renewal",
- // "license-release",
- // "individualization-request"
+ "license-renewal",
+ "license-release",
+ "individualization-request"
};
diff --git a/src/cobalt/dom/eme/media_key_session.cc b/src/cobalt/dom/eme/media_key_session.cc
index 8db9c20..ad995c3 100644
--- a/src/cobalt/dom/eme/media_key_session.cc
+++ b/src/cobalt/dom/eme/media_key_session.cc
@@ -19,6 +19,7 @@
#include "cobalt/dom/array_buffer.h"
#include "cobalt/dom/array_buffer_view.h"
#include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/eme/eme_helpers.h"
#include "cobalt/dom/eme/media_key_message_event.h"
#include "cobalt/dom/eme/media_key_message_event_init.h"
#include "cobalt/dom/eme/media_keys.h"
@@ -232,7 +233,8 @@
// See
// https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-generaterequest.
void MediaKeySession::OnSessionUpdateRequestGenerated(
- VoidPromiseValue::Reference* promise_reference, scoped_array<uint8> message,
+ VoidPromiseValue::Reference* promise_reference,
+ SbDrmSessionRequestType type, scoped_array<uint8> message,
int message_size) {
MediaKeyMessageEventInit media_key_message_event_init;
// 10.9.4. If a license request for the requested license type can be
@@ -251,8 +253,24 @@
//
// TODO: Introduce message type parameter to |SbDrmSessionUpdateRequestFunc|
// and stop pretending that all messages are license requests.
- media_key_message_event_init.set_message_type(
- kMediaKeyMessageTypeLicenseRequest);
+ switch (type) {
+ case kSbDrmSessionRequestTypeLicenseRequest:
+ media_key_message_event_init.set_message_type(
+ kMediaKeyMessageTypeLicenseRequest);
+ break;
+ case kSbDrmSessionRequestTypeLicenseRenewal:
+ media_key_message_event_init.set_message_type(
+ kMediaKeyMessageTypeLicenseRenewal);
+ break;
+ case kSbDrmSessionRequestTypeLicenseRelease:
+ media_key_message_event_init.set_message_type(
+ kMediaKeyMessageTypeLicenseRelease);
+ break;
+ case kSbDrmSessionRequestTypeIndividualizationRequest:
+ media_key_message_event_init.set_message_type(
+ kMediaKeyMessageTypeIndividualizationRequest);
+ break;
+ }
// 10.3. Let this object's callable value be true.
callable_ = true;
@@ -279,12 +297,12 @@
// See
// https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-generaterequest.
void MediaKeySession::OnSessionUpdateRequestDidNotGenerate(
- VoidPromiseValue::Reference* promise_reference) {
+ VoidPromiseValue::Reference* promise_reference, SbDrmStatus status,
+ const std::string& error_message) {
// 10.10.1. If any of the preceding steps failed, reject promise with a new
// DOMException whose name is the appropriate error name.
//
- // TODO: Introduce Starboard API that allows CDM to propagate error codes.
- promise_reference->value().Reject(new DOMException(DOMException::kNone));
+ RejectPromise(promise_reference, status, error_message);
}
// See https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-update.
@@ -307,12 +325,12 @@
// See https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-update.
void MediaKeySession::OnSessionDidNotUpdate(
- VoidPromiseValue::Reference* promise_reference) {
+ VoidPromiseValue::Reference* promise_reference, SbDrmStatus status,
+ const std::string& error_message) {
// 8.1.3. If any of the preceding steps failed, reject promise with a new
// DOMException whose name is the appropriate error name.
//
- // TODO: Introduce Starboard API that allows CDM to propagate error codes.
- promise_reference->value().Reject(new DOMException(DOMException::kNone));
+ RejectPromise(promise_reference, status, error_message);
}
// See https://www.w3.org/TR/encrypted-media/#update-key-statuses.
diff --git a/src/cobalt/dom/eme/media_key_session.h b/src/cobalt/dom/eme/media_key_session.h
index cdd39ab..4a026a3 100644
--- a/src/cobalt/dom/eme/media_key_session.h
+++ b/src/cobalt/dom/eme/media_key_session.h
@@ -70,11 +70,15 @@
void OnSessionUpdateRequestGenerated(
VoidPromiseValue::Reference* promise_reference,
- scoped_array<uint8> message, int message_size);
+ SbDrmSessionRequestType type, scoped_array<uint8> message,
+ int message_size);
void OnSessionUpdateRequestDidNotGenerate(
- VoidPromiseValue::Reference* promise_reference);
+ VoidPromiseValue::Reference* promise_reference, SbDrmStatus status,
+ const std::string& error_message);
void OnSessionUpdated(VoidPromiseValue::Reference* promise_reference);
- void OnSessionDidNotUpdate(VoidPromiseValue::Reference* promise_reference);
+ void OnSessionDidNotUpdate(VoidPromiseValue::Reference* promise_reference,
+ SbDrmStatus status,
+ const std::string& error_message);
void OnSessionUpdateKeyStatuses(
const std::vector<std::string>& key_ids,
const std::vector<SbDrmKeyStatus>& key_statuses);
diff --git a/src/cobalt/dom/eme/media_keys.cc b/src/cobalt/dom/eme/media_keys.cc
index 351ebfa..c1e6e2e 100644
--- a/src/cobalt/dom/eme/media_keys.cc
+++ b/src/cobalt/dom/eme/media_keys.cc
@@ -16,6 +16,7 @@
#include "base/bind.h"
#include "cobalt/dom/dom_exception.h"
+#include "cobalt/dom/eme/eme_helpers.h"
#include "cobalt/dom/eme/media_key_session.h"
namespace cobalt {
@@ -48,6 +49,47 @@
return session;
}
+MediaKeys::BoolPromiseHandle MediaKeys::SetServerCertificate(
+ const BufferSource& server_certificate) {
+ BoolPromiseHandle promise = script_value_factory_->CreateBasicPromise<bool>();
+
+ // 1. If the Key System implementation represented by this object's cdm
+ // implementation value does not support server certificates, return a
+ // promise resolved with false.
+ if (!drm_system_->IsServerCertificateUpdatable()) {
+ promise->Resolve(false);
+ return promise;
+ }
+
+ // 2. If serverCertificate is an empty array, return a promise rejected with a
+ // newly created TypeError.
+ const uint8* server_certificate_buffer;
+ int server_certificate_buffer_size = 0;
+ GetBufferAndSize(server_certificate, &server_certificate_buffer,
+ &server_certificate_buffer_size);
+
+ if (server_certificate_buffer_size == 0) {
+ promise->Reject(script::kTypeError);
+ return promise;
+ }
+
+ // 3. Let certificate be a copy of the contents of the serverCertificate
+ // parameter.
+ // 4. Let promise be a new promise.
+ // 5. Run the following steps in parallel:
+ // 5.1 Let sanitized certificate be a validated and/or sanitized version of
+ // certificate.
+ // 5.2 Use this object's cdm instance to process sanitized certificate.
+ drm_system_->UpdateServerCertificate(
+ server_certificate_buffer, server_certificate_buffer_size,
+ base::Bind(&MediaKeys::OnServerCertificateUpdated, base::AsWeakPtr(this),
+ base::Owned(new BoolPromiseValue::Reference(this, promise))));
+
+ // 5.3 and 5.4 are pending processing in OnServerCertificateUpdated().
+ // 6. Return promise.
+ return promise;
+}
+
void MediaKeys::OnSessionClosed(MediaKeySession* session) {
// Erase-remove idiom, see
// https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom.
@@ -56,6 +98,19 @@
open_sessions_.end());
}
+void MediaKeys::OnServerCertificateUpdated(
+ BoolPromiseValue::Reference* promise_reference, SbDrmStatus status,
+ const std::string& error_message) {
+ // 5.3 If the preceding step failed, resolve promise with a new DOMException
+ // whose name is the appropriate error name.
+ if (status != kSbDrmStatusSuccess) {
+ RejectPromise(promise_reference, status, error_message);
+ return;
+ }
+ // 5.4 Resolve promise with true.
+ promise_reference->value().Resolve(true);
+}
+
void MediaKeys::TraceMembers(script::Tracer* tracer) {
tracer->TraceItems(open_sessions_);
}
diff --git a/src/cobalt/dom/eme/media_keys.h b/src/cobalt/dom/eme/media_keys.h
index 759a9e2..ff1330f 100644
--- a/src/cobalt/dom/eme/media_keys.h
+++ b/src/cobalt/dom/eme/media_keys.h
@@ -26,6 +26,7 @@
#include "cobalt/media/base/drm_system.h"
#include "cobalt/script/script_value_factory.h"
#include "cobalt/script/wrappable.h"
+#include "starboard/drm.h"
namespace cobalt {
namespace dom {
@@ -38,6 +39,8 @@
public base::SupportsWeakPtr<MediaKeys> {
public:
// Custom, not in any spec.
+ typedef script::Handle<script::Promise<bool>> BoolPromiseHandle;
+ typedef script::ScriptValue<script::Promise<bool>> BoolPromiseValue;
MediaKeys(const std::string& key_system,
script::ScriptValueFactory* script_value_factory);
@@ -49,6 +52,8 @@
scoped_refptr<MediaKeySession> CreateSession(
MediaKeySessionType session_type,
script::ExceptionState* exception_state);
+ BoolPromiseHandle SetServerCertificate(
+ const BufferSource& server_certificate);
DEFINE_WRAPPABLE_TYPE(MediaKeys);
void TraceMembers(script::Tracer* tracer) override;
@@ -57,6 +62,9 @@
~MediaKeys() override;
void OnSessionClosed(MediaKeySession* session);
+ void OnServerCertificateUpdated(
+ BoolPromiseValue::Reference* promise_reference, SbDrmStatus status,
+ const std::string& error_message);
script::ScriptValueFactory* script_value_factory_;
scoped_refptr<media::DrmSystem> drm_system_;
diff --git a/src/cobalt/dom/eme/media_keys.idl b/src/cobalt/dom/eme/media_keys.idl
index fc1a35f..250f576 100644
--- a/src/cobalt/dom/eme/media_keys.idl
+++ b/src/cobalt/dom/eme/media_keys.idl
@@ -17,6 +17,5 @@
interface MediaKeys {
[RaisesException] MediaKeySession createSession(
optional MediaKeySessionType sessionType = "temporary");
- // TODO: Trivially implement server certificates.
- // Promise<boolean> setServerCertificate(BufferSource serverCertificate);
+ Promise<boolean> setServerCertificate(BufferSource serverCertificate);
};
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc
index 82ad15f..85d4e7a 100644
--- a/src/cobalt/dom/error_event_test.cc
+++ b/src/cobalt/dom/error_event_test.cc
@@ -19,6 +19,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
+#include "base/threading/platform_thread.h"
#include "cobalt/css_parser/parser.h"
#include "cobalt/dom/error_event_init.h"
#include "cobalt/dom/local_storage_database.h"
@@ -26,6 +27,7 @@
#include "cobalt/dom/window.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader_factory.h"
#include "cobalt/media_session/media_session.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
@@ -56,26 +58,30 @@
css_parser_(css_parser::Parser::Create()),
dom_parser_(new dom_parser::Parser(mock_error_callback_)),
fetcher_factory_(new loader::FetcherFactory(NULL)),
+ loader_factory_(new loader::LoaderFactory(
+ fetcher_factory_.get(), NULL, base::kThreadPriority_Default)),
local_storage_database_(NULL),
- url_("about:blank"),
- window_(new Window(
- 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
- dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, url_, "", "en-US", "en",
- base::Callback<void(const GURL&)>(),
- base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_)),
- NULL, network_bridge::PostSender(), csp::kCSPRequired,
- kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
- base::Closure() /* ran_animation_frame_callbacks */,
- dom::Window::CloseCallback() /* window_close */,
- base::Closure() /* window_minimize */, NULL, NULL, NULL,
- dom::Window::OnStartDispatchEventCallback(),
- dom::Window::OnStopDispatchEventCallback(),
- dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {
+ url_("about:blank") {
engine_ = script::JavaScriptEngine::CreateEngine();
global_environment_ = engine_->CreateGlobalEnvironment();
+
+ window_ = new Window(
+ 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
+ dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(), NULL,
+ NULL, NULL, NULL, NULL, NULL, &local_storage_database_, NULL, NULL,
+ NULL, NULL, global_environment_->script_value_factory(), NULL, NULL,
+ url_, "", "en-US", "en", base::Callback<void(const GURL&)>(),
+ base::Bind(&MockErrorCallback::Run,
+ base::Unretained(&mock_error_callback_)),
+ NULL, network_bridge::PostSender(), csp::kCSPRequired,
+ kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
+ base::Closure() /* ran_animation_frame_callbacks */,
+ dom::Window::CloseCallback() /* window_close */,
+ base::Closure() /* window_minimize */, NULL, NULL, NULL,
+ dom::Window::OnStartDispatchEventCallback(),
+ dom::Window::OnStopDispatchEventCallback(),
+ dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
+
global_environment_->CreateGlobalObject(window_,
environment_settings_.get());
}
@@ -92,9 +98,10 @@
scoped_ptr<css_parser::Parser> css_parser_;
scoped_ptr<dom_parser::Parser> dom_parser_;
scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+ scoped_ptr<loader::LoaderFactory> loader_factory_;
dom::LocalStorageDatabase local_storage_database_;
GURL url_;
- const scoped_refptr<Window> window_;
+ scoped_refptr<Window> window_;
};
bool ErrorEventTest::EvaluateScript(const std::string& js_code,
diff --git a/src/cobalt/dom/event.cc b/src/cobalt/dom/event.cc
index da1a085..6f3d993 100644
--- a/src/cobalt/dom/event.cc
+++ b/src/cobalt/dom/event.cc
@@ -23,42 +23,48 @@
Event::Event(UninitializedFlag /* uninitialized_flag */)
: event_phase_(kNone),
- time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+ time_stamp_(GetEventTime(SbTimeGetMonotonicNow())) {
InitEventInternal(base::Token(), false, false);
}
Event::Event(const std::string& type)
: event_phase_(kNone),
- time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+ time_stamp_(GetEventTime(SbTimeGetMonotonicNow())) {
InitEventInternal(base::Token(type), false, false);
}
Event::Event(const std::string& type, const EventInit& init_dict)
: event_phase_(kNone),
- time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+ time_stamp_(GetEventTime(SbTimeGetMonotonicNow())) {
SB_DCHECK(init_dict.has_bubbles());
SB_DCHECK(init_dict.has_cancelable());
+ if (init_dict.time_stamp() != 0) {
+ time_stamp_ = init_dict.time_stamp();
+ }
InitEventInternal(base::Token(type), init_dict.bubbles(),
init_dict.cancelable());
}
Event::Event(base::Token type)
: event_phase_(kNone),
- time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+ time_stamp_(GetEventTime(SbTimeGetMonotonicNow())) {
InitEventInternal(type, false, false);
}
Event::Event(base::Token type, Bubbles bubbles, Cancelable cancelable)
: event_phase_(kNone),
- time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+ time_stamp_(GetEventTime(SbTimeGetMonotonicNow())) {
InitEventInternal(type, bubbles == kBubbles, cancelable == kCancelable);
}
Event::Event(base::Token type, const EventInit& init_dict)
: event_phase_(kNone),
- time_stamp_(static_cast<uint64>(base::Time::Now().ToJsTime())) {
+ time_stamp_(GetEventTime(SbTimeGetMonotonicNow())) {
SB_DCHECK(init_dict.has_bubbles());
SB_DCHECK(init_dict.has_cancelable());
+ if (init_dict.time_stamp() != 0) {
+ time_stamp_ = init_dict.time_stamp();
+ }
InitEventInternal(type, init_dict.bubbles(), init_dict.cancelable());
}
@@ -105,5 +111,14 @@
tracer->Trace(current_target_);
}
+uint64 Event::GetEventTime(SbTimeMonotonic monotonic_time) {
+ // For now, continue using the old specification which specifies real time
+ // since 1970.
+ // https://www.w3.org/TR/dom/#dom-event-timestamp
+ SbTimeMonotonic time_delta = SbTimeGetNow() - SbTimeGetMonotonicNow();
+ base::Time base_time = base::Time::FromSbTime(time_delta + monotonic_time);
+ return static_cast<uint64>(base_time.ToJsTime());
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/event.h b/src/cobalt/dom/event.h
index 4dba8b2..1359951 100644
--- a/src/cobalt/dom/event.h
+++ b/src/cobalt/dom/event.h
@@ -126,6 +126,13 @@
return immediate_propagation_stopped_;
}
+ // https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp
+ // An event's timeStamp should represent time since document creation, not
+ // real time. However, the old spec specifies time since 1970. To facilitate
+ // the transition to the new spec, use this function to calculate an event's
+ // time stamp.
+ static uint64 GetEventTime(SbTimeMonotonic monotonic_time);
+
DEFINE_WRAPPABLE_TYPE(Event);
void TraceMembers(script::Tracer* tracer) override;
diff --git a/src/cobalt/dom/event_init.idl b/src/cobalt/dom/event_init.idl
index f0974f5..28e9436 100644
--- a/src/cobalt/dom/event_init.idl
+++ b/src/cobalt/dom/event_init.idl
@@ -17,4 +17,7 @@
dictionary EventInit {
boolean bubbles = false;
boolean cancelable = false;
+
+ // Cobalt customization - timeStamp is not part of the spec.
+ DOMTimeStamp timeStamp = 0;
};
diff --git a/src/cobalt/dom/event_listener.cc b/src/cobalt/dom/event_listener.cc
deleted file mode 100644
index d53b8db..0000000
--- a/src/cobalt/dom/event_listener.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2016 Google Inc. 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 "cobalt/dom/event_listener.h"
-
-#include "base/debug/trace_event.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "cobalt/base/user_log.h"
-#include "cobalt/dom/event_target.h"
-
-namespace cobalt {
-namespace dom {
-namespace {
-
-// This class records the nested events and manages the user log information.
-class ScriptEventLog {
- public:
- ScriptEventLog() : current_stack_depth_(0) {
- base::UserLog::Register(base::UserLog::kEventStackLevelCountIndex,
- "EventLevelCnt", ¤t_stack_depth_,
- sizeof(current_stack_depth_));
-
- memset(event_log_stack_, 0, sizeof(event_log_stack_));
- const int kLogNameBufferSize = 16;
- char log_name_buffer[kLogNameBufferSize];
- for (int i = 0; i < base::UserLog::kEventStackMaxDepth; i++) {
- snprintf(log_name_buffer, kLogNameBufferSize, "EventLevel%d", i);
- base::UserLog::Register(static_cast<base::UserLog::Index>(
- base::UserLog::kEventStackMinLevelIndex + i),
- log_name_buffer, event_log_stack_[i],
- kLogEntryMaxLength);
- }
- }
-
- ~ScriptEventLog() {
- base::UserLog::Deregister(base::UserLog::kEventStackLevelCountIndex);
- for (int i = 0; i < base::UserLog::kEventStackMaxDepth; i++) {
- base::UserLog::Deregister(static_cast<base::UserLog::Index>(
- base::UserLog::kEventStackMinLevelIndex + i));
- }
- }
-
- void PushEvent(Event* event) {
- if (current_stack_depth_ < base::UserLog::kEventStackMaxDepth) {
- snprintf(event_log_stack_[current_stack_depth_], kLogEntryMaxLength,
- "%s@%s", event->type().c_str(),
- event->current_target()->GetDebugName().c_str());
- } else if (current_stack_depth_ == base::UserLog::kEventStackMaxDepth) {
- DLOG(WARNING) << "Reached maximum depth of " << kLogEntryMaxLength
- << ". Subsequent events will not be logged.";
- }
- current_stack_depth_++;
- }
-
- void PopEvent() {
- DCHECK(current_stack_depth_);
- current_stack_depth_--;
- if (current_stack_depth_ < base::UserLog::kEventStackMaxDepth) {
- memset(event_log_stack_[current_stack_depth_], 0, kLogEntryMaxLength);
- }
- }
-
- private:
- static const size_t kLogEntryMaxLength = 64;
- int current_stack_depth_;
- char event_log_stack_[base::UserLog::kEventStackMaxDepth][kLogEntryMaxLength];
-
- DISALLOW_COPY_AND_ASSIGN(ScriptEventLog);
-};
-
-base::LazyInstance<ScriptEventLog> script_event_log = LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-void EventListener::HandleEvent(const scoped_refptr<Event>& event,
- Type listener_type) const {
- TRACE_EVENT1("cobalt::dom", "EventListener::HandleEvent", "Event Name",
- TRACE_STR_COPY(event->type().c_str()));
- script_event_log.Get().PushEvent(event);
-
- bool had_exception;
- base::optional<bool> result =
- HandleEvent(event->current_target(), event, &had_exception);
-
- script_event_log.Get().PopEvent();
-
- if (had_exception) {
- return;
- }
- // EventHandlers (EventListeners set as attributes) may return false rather
- // than call event.preventDefault() in the handler function.
- if (listener_type == kAttribute && result && !result.value()) {
- event->PreventDefault();
- }
-}
-
-} // namespace dom
-} // namespace cobalt
diff --git a/src/cobalt/dom/event_listener.h b/src/cobalt/dom/event_listener.h
index 7beab2b..1b6ab70 100644
--- a/src/cobalt/dom/event_listener.h
+++ b/src/cobalt/dom/event_listener.h
@@ -28,19 +28,6 @@
// https://www.w3.org/TR/2014/WD-dom-20140710/#eventtarget
class EventListener {
public:
- // EventHandlers are implemented as EventListener?, so use this to
- // differentiate between the two.
- enum Type {
- kAttribute,
- kNotAttribute,
- };
-
- // Custom, not in any spec.
- //
- // Specify whether this is an attribute-type listener or not.
- void HandleEvent(const scoped_refptr<Event>& event, Type listener_type) const;
-
- protected:
// Web API: EventListener
//
// Cobalt's implementation of callback interfaces requires the 'callback this'
diff --git a/src/cobalt/dom/event_target.cc b/src/cobalt/dom/event_target.cc
index b76eeef..db84c25 100644
--- a/src/cobalt/dom/event_target.cc
+++ b/src/cobalt/dom/event_target.cc
@@ -33,10 +33,11 @@
return;
}
- EventListenerScriptValue::Reference listener_reference(this, listener);
+ scoped_ptr<GenericEventHandlerReference> listener_reference(
+ new GenericEventHandlerReference(this, listener));
- AddEventListenerInternal(base::Token(type), listener, use_capture,
- EventListener::kNotAttribute);
+ AddEventListenerInternal(base::Token(type), listener_reference.Pass(),
+ use_capture, kNotAttribute);
}
void EventTarget::RemoveEventListener(const std::string& type,
@@ -49,9 +50,8 @@
for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
iter != event_listener_infos_.end(); ++iter) {
- if ((*iter)->listener_type == EventListener::kNotAttribute &&
- (*iter)->type == type.c_str() &&
- (*iter)->listener.referenced_value().EqualTo(listener) &&
+ if ((*iter)->listener_type == kNotAttribute &&
+ (*iter)->type == type.c_str() && (*iter)->listener->EqualTo(listener) &&
(*iter)->use_capture == use_capture) {
event_listener_infos_.erase(iter);
return;
@@ -124,44 +124,75 @@
void EventTarget::SetAttributeEventListener(
base::Token type, const EventListenerScriptValue& listener) {
- // Remove existing attribute listener of the same type.
- for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
- iter != event_listener_infos_.end(); ++iter) {
- if ((*iter)->listener_type == EventListener::kAttribute &&
- (*iter)->type == type) {
- event_listener_infos_.erase(iter);
- break;
- }
- }
+ DCHECK(!unpack_onerror_events_ || type != base::Tokens::error());
- if (listener.IsNull()) {
- return;
- }
- AddEventListenerInternal(type, listener, false, EventListener::kAttribute);
+ scoped_ptr<GenericEventHandlerReference> listener_reference(
+ new GenericEventHandlerReference(this, listener));
+ SetAttributeEventListenerInternal(type, listener_reference.Pass());
}
const EventTarget::EventListenerScriptValue*
EventTarget::GetAttributeEventListener(base::Token type) const {
- for (EventListenerInfos::const_iterator iter = event_listener_infos_.begin();
- iter != event_listener_infos_.end(); ++iter) {
- if ((*iter)->listener_type == EventListener::kAttribute &&
- (*iter)->type == type) {
- return &(*iter)->listener.referenced_value();
- }
- }
- return NULL;
+ DCHECK(!unpack_onerror_events_ || type != base::Tokens::error());
+
+ GenericEventHandlerReference* handler =
+ GetAttributeEventListenerInternal(type);
+ return handler ? handler->event_listener_value() : NULL;
+}
+
+void EventTarget::SetAttributeOnErrorEventListener(
+ base::Token type, const OnErrorEventListenerScriptValue& listener) {
+ DCHECK_EQ(base::Tokens::error(), type);
+
+ scoped_ptr<GenericEventHandlerReference> listener_reference(
+ new GenericEventHandlerReference(this, listener));
+ SetAttributeEventListenerInternal(type, listener_reference.Pass());
+}
+
+const EventTarget::OnErrorEventListenerScriptValue*
+EventTarget::GetAttributeOnErrorEventListener(base::Token type) const {
+ DCHECK_EQ(base::Tokens::error(), type);
+
+ GenericEventHandlerReference* handler =
+ GetAttributeEventListenerInternal(type);
+ return handler ? handler->on_error_event_listener_value() : NULL;
}
bool EventTarget::HasOneOrMoreAttributeEventListener() const {
for (EventListenerInfos::const_iterator iter = event_listener_infos_.begin();
iter != event_listener_infos_.end(); ++iter) {
- if ((*iter)->listener_type == EventListener::kAttribute) {
+ if ((*iter)->listener_type == kAttribute) {
return true;
}
}
return false;
}
+void EventTarget::SetAttributeEventListenerInternal(
+ base::Token type, scoped_ptr<GenericEventHandlerReference> event_handler) {
+ // Remove existing attribute listener of the same type.
+ for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
+ iter != event_listener_infos_.end(); ++iter) {
+ if ((*iter)->listener_type == kAttribute && (*iter)->type == type) {
+ event_listener_infos_.erase(iter);
+ break;
+ }
+ }
+
+ AddEventListenerInternal(type, event_handler.Pass(), false, kAttribute);
+}
+
+GenericEventHandlerReference* EventTarget::GetAttributeEventListenerInternal(
+ base::Token type) const {
+ for (EventListenerInfos::const_iterator iter = event_listener_infos_.begin();
+ iter != event_listener_infos_.end(); ++iter) {
+ if ((*iter)->listener_type == kAttribute && (*iter)->type == type) {
+ return (*iter)->listener.get();
+ }
+ }
+ return NULL;
+}
+
void EventTarget::FireEventOnListeners(const scoped_refptr<Event>& event) {
DCHECK(event->IsBeingDispatched());
DCHECK(event->target());
@@ -174,7 +205,9 @@
iter != event_listener_infos_.end(); ++iter) {
if ((*iter)->type == event->type()) {
event_listener_infos.push_back(new EventListenerInfo(
- (*iter)->type, this, (*iter)->listener.referenced_value(),
+ (*iter)->type,
+ make_scoped_ptr(
+ new GenericEventHandlerReference(this, *(*iter)->listener)),
(*iter)->use_capture, (*iter)->listener_type));
}
}
@@ -193,7 +226,9 @@
if (event->event_phase() == Event::kBubblingPhase && (*iter)->use_capture) {
continue;
}
- (*iter)->listener.value().HandleEvent(event, (*iter)->listener_type);
+
+ (*iter)->listener->HandleEvent(event, (*iter)->listener_type == kAttribute,
+ unpack_onerror_events_);
}
event->set_current_target(NULL);
@@ -206,26 +241,27 @@
}
void EventTarget::AddEventListenerInternal(
- base::Token type, const EventListenerScriptValue& listener,
- bool use_capture, EventListener::Type listener_type) {
+ base::Token type, scoped_ptr<GenericEventHandlerReference> listener,
+ bool use_capture, Type listener_type) {
TRACK_MEMORY_SCOPE("DOM");
- DCHECK(!listener.IsNull());
+ if (listener->IsNull()) {
+ return;
+ }
for (EventListenerInfos::iterator iter = event_listener_infos_.begin();
iter != event_listener_infos_.end(); ++iter) {
- if ((*iter)->type == type &&
- (*iter)->listener.referenced_value().EqualTo(listener) &&
+ if ((*iter)->type == type && (*iter)->listener->EqualTo(*listener) &&
(*iter)->use_capture == use_capture &&
(*iter)->listener_type == listener_type) {
// Attribute listeners should have already been removed.
- DCHECK_EQ(listener_type, EventListener::kNotAttribute);
+ DCHECK_EQ(listener_type, kNotAttribute);
return;
}
}
event_listener_infos_.push_back(
- new EventListenerInfo(type, this, listener, use_capture, listener_type));
+ new EventListenerInfo(type, listener.Pass(), use_capture, listener_type));
}
bool EventTarget::HasEventListener(base::Token type) {
@@ -241,11 +277,10 @@
}
EventTarget::EventListenerInfo::EventListenerInfo(
- base::Token type, EventTarget* const event_target,
- const EventListenerScriptValue& listener, bool use_capture,
- EventListener::Type listener_type)
+ base::Token type, scoped_ptr<GenericEventHandlerReference> listener,
+ bool use_capture, Type listener_type)
: type(type),
- listener(event_target, listener),
+ listener(listener.Pass()),
use_capture(use_capture),
listener_type(listener_type) {
GlobalStats::GetInstance()->AddEventListener();
diff --git a/src/cobalt/dom/event_target.h b/src/cobalt/dom/event_target.h
index a89dbe5..b9c5d9c 100644
--- a/src/cobalt/dom/event_target.h
+++ b/src/cobalt/dom/event_target.h
@@ -23,10 +23,13 @@
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/tracked_objects.h"
+#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/base/token.h"
#include "cobalt/base/tokens.h"
#include "cobalt/dom/event.h"
#include "cobalt/dom/event_listener.h"
+#include "cobalt/dom/generic_event_handler_reference.h"
+#include "cobalt/dom/on_error_event_listener.h"
#include "cobalt/script/exception_state.h"
#include "cobalt/script/script_value.h"
#include "cobalt/script/wrappable.h"
@@ -42,7 +45,37 @@
class EventTarget : public script::Wrappable,
public base::SupportsWeakPtr<EventTarget> {
public:
+ // EventHandlers are implemented as EventListener?, so use this to
+ // differentiate between the two.
+ enum Type {
+ kAttribute,
+ kNotAttribute,
+ };
+
+ // Helper enum to decide whether or not onerror event parameters should be
+ // unpacked or not (e.g. in the special case of the |window| object).
+ // This special handling is described in:
+ // https://html.spec.whatwg.org/#event-handler-attributes
+ // (search for "onerror")
+ enum UnpackOnErrorEventsBool {
+ kUnpackOnErrorEvents,
+ kDoNotUnpackOnErrorEvents,
+ };
+
+ // The parameter |unpack_onerror_events| can be set to true (e.g. for the
+ // |window| object) in order to indicate that the ErrorEvent should have
+ // its members unpacked before calling its event handler. This is to
+ // accommodate for a special case in the window.onerror handling. This
+ // special handling
+ explicit EventTarget(
+ UnpackOnErrorEventsBool onerror_event_parameter_handling =
+ kDoNotUnpackOnErrorEvents)
+ : unpack_onerror_events_(onerror_event_parameter_handling ==
+ kUnpackOnErrorEvents) {}
+
typedef script::ScriptValue<EventListener> EventListenerScriptValue;
+ typedef script::ScriptValue<OnErrorEventListener>
+ OnErrorEventListenerScriptValue;
// Web API: EventTarget
//
@@ -100,11 +133,11 @@
SetAttributeEventListener(base::Tokens::click(), event_listener);
}
- const EventListenerScriptValue* onerror() {
- return GetAttributeEventListener(base::Tokens::error());
+ const OnErrorEventListenerScriptValue* onerror() {
+ return GetAttributeOnErrorEventListener(base::Tokens::error());
}
- void set_onerror(const EventListenerScriptValue& event_listener) {
- SetAttributeEventListener(base::Tokens::error(), event_listener);
+ void set_onerror(const OnErrorEventListenerScriptValue& event_listener) {
+ SetAttributeOnErrorEventListener(base::Tokens::error(), event_listener);
}
const EventListenerScriptValue* onfocus() {
@@ -393,6 +426,16 @@
const EventListenerScriptValue* GetAttributeEventListener(
base::Token type) const;
+ // Similar to SetAttributeEventListener(), but this function should only
+ // be called with type == base::Tokens::error().
+ void SetAttributeOnErrorEventListener(
+ base::Token type, const OnErrorEventListenerScriptValue& listener);
+
+ // Similar to GetAttributeEventListener(), but this function should only
+ // be called with type == base::Tokens::error().
+ const OnErrorEventListenerScriptValue* GetAttributeOnErrorEventListener(
+ base::Token type) const;
+
// Return true if one or more event listeners are registered
bool HasOneOrMoreAttributeEventListener() const;
@@ -409,24 +452,33 @@
private:
struct EventListenerInfo {
- EventListenerInfo(base::Token type, EventTarget* const event_target,
- const EventListenerScriptValue& listener,
- bool use_capture, EventListener::Type listener_type);
+ EventListenerInfo(base::Token type,
+ scoped_ptr<GenericEventHandlerReference> listener,
+ bool use_capture, Type listener_type);
~EventListenerInfo();
base::Token type;
- script::ScriptValue<EventListener>::Reference listener;
+ scoped_ptr<GenericEventHandlerReference> listener;
bool use_capture;
- EventListener::Type listener_type;
+ Type listener_type;
};
typedef ScopedVector<EventListenerInfo> EventListenerInfos;
- void AddEventListenerInternal(base::Token type,
- const EventListenerScriptValue& listener,
- bool use_capture,
- EventListener::Type listener_type);
+ void SetAttributeEventListenerInternal(
+ base::Token type, scoped_ptr<GenericEventHandlerReference> event_handler);
+ GenericEventHandlerReference* GetAttributeEventListenerInternal(
+ base::Token type) const;
+
+ void AddEventListenerInternal(
+ base::Token type, scoped_ptr<GenericEventHandlerReference> listener,
+ bool use_capture, Type listener_type);
EventListenerInfos event_listener_infos_;
+
+ // Tracks whether this current event listener should unpack the onerror
+ // event object when calling its callback. This is needed to implement
+ // the special case of window.onerror handling.
+ bool unpack_onerror_events_;
};
} // namespace dom
diff --git a/src/cobalt/dom/generic_event_handler_reference.cc b/src/cobalt/dom/generic_event_handler_reference.cc
new file mode 100644
index 0000000..ceeafec
--- /dev/null
+++ b/src/cobalt/dom/generic_event_handler_reference.cc
@@ -0,0 +1,125 @@
+// Copyright 2018 Google Inc. 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 "cobalt/dom/generic_event_handler_reference.h"
+
+#include "base/debug/trace_event.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/dom/script_event_log.h"
+
+namespace cobalt {
+namespace dom {
+
+GenericEventHandlerReference::GenericEventHandlerReference(
+ script::Wrappable* wrappable,
+ const EventListenerScriptValue& script_value) {
+ if (!script_value.IsNull()) {
+ event_listener_reference_.reset(
+ new EventListenerScriptValue::Reference(wrappable, script_value));
+ }
+}
+
+GenericEventHandlerReference::GenericEventHandlerReference(
+ script::Wrappable* wrappable,
+ const OnErrorEventListenerScriptValue& script_value) {
+ if (!script_value.IsNull()) {
+ on_error_event_listener_reference_.reset(
+ new OnErrorEventListenerScriptValue::Reference(wrappable,
+ script_value));
+ }
+}
+
+GenericEventHandlerReference::GenericEventHandlerReference(
+ script::Wrappable* wrappable, const GenericEventHandlerReference& other) {
+ if (other.event_listener_reference_) {
+ event_listener_reference_.reset(new EventListenerScriptValue::Reference(
+ wrappable, other.event_listener_reference_->referenced_value()));
+ } else if (other.on_error_event_listener_reference_) {
+ on_error_event_listener_reference_.reset(
+ new OnErrorEventListenerScriptValue::Reference(
+ wrappable,
+ other.on_error_event_listener_reference_->referenced_value()));
+ }
+}
+
+void GenericEventHandlerReference::HandleEvent(
+ const scoped_refptr<Event>& event, bool is_attribute,
+ bool unpack_error_event) {
+ TRACE_EVENT1("cobalt::dom", "GenericEventHandlerReference::HandleEvent",
+ "Event Name", TRACE_STR_COPY(event->type().c_str()));
+ script_event_log.Get().PushEvent(event);
+
+ bool had_exception;
+ base::optional<bool> result;
+
+ // Forward the HandleEvent() call to the appropriate internal object.
+ if (event_listener_reference_) {
+ // Non-onerror event handlers cannot have their parameters unpacked.
+ result = event_listener_reference_->value().HandleEvent(
+ event->current_target(), event, &had_exception);
+ } else if (on_error_event_listener_reference_) {
+ result = on_error_event_listener_reference_->value().HandleEvent(
+ event->current_target(), event, &had_exception, unpack_error_event);
+ } else {
+ NOTREACHED();
+ had_exception = true;
+ }
+
+ script_event_log.Get().PopEvent();
+
+ if (had_exception) {
+ return;
+ }
+ // EventHandlers (EventListeners set as attributes) may return false rather
+ // than call event.preventDefault() in the handler function.
+ if (is_attribute && result && !result.value()) {
+ event->PreventDefault();
+ }
+}
+
+bool GenericEventHandlerReference::EqualTo(
+ const EventListenerScriptValue& other) {
+ return (IsNull() && other.IsNull()) ||
+ (event_listener_reference_ &&
+ event_listener_reference_->referenced_value().EqualTo(other));
+}
+
+bool GenericEventHandlerReference::EqualTo(
+ const GenericEventHandlerReference& other) {
+ if (IsNull() && other.IsNull()) {
+ return true;
+ }
+
+ if (event_listener_reference_ && other.event_listener_reference_) {
+ return event_listener_reference_->referenced_value().EqualTo(
+ other.event_listener_reference_->referenced_value());
+ }
+ if (on_error_event_listener_reference_ &&
+ other.on_error_event_listener_reference_) {
+ return on_error_event_listener_reference_->referenced_value().EqualTo(
+ other.on_error_event_listener_reference_->referenced_value());
+ }
+ return false;
+}
+
+bool GenericEventHandlerReference::IsNull() const {
+ return (!event_listener_reference_ ||
+ event_listener_reference_->referenced_value().IsNull()) &&
+ (!on_error_event_listener_reference_ ||
+ on_error_event_listener_reference_->referenced_value().IsNull());
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/generic_event_handler_reference.h b/src/cobalt/dom/generic_event_handler_reference.h
new file mode 100644
index 0000000..b5abfb7
--- /dev/null
+++ b/src/cobalt/dom/generic_event_handler_reference.h
@@ -0,0 +1,100 @@
+// Copyright 2018 Google Inc. 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 COBALT_DOM_GENERIC_EVENT_HANDLER_REFERENCE_H_
+#define COBALT_DOM_GENERIC_EVENT_HANDLER_REFERENCE_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/event_listener.h"
+#include "cobalt/dom/on_error_event_listener.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// Essentially acts as an abstract interface of the union for types
+// [script::ScriptValue<EventListener>,
+// script::ScriptValue<OnErrorEventListener>]. In particular it primarily
+// allows code in event_target.cc to not need to concern itself with which
+// exact EventListener script value type it is dealing with. The need for
+// this abstraction arises from the fact that the |window.onerror| event
+// handler requires special case handling:
+// https://html.spec.whatwg.org/#onerroreventhandler )
+//
+// NOTE that this is *not* an ideal solution to the problem of generalizing
+// over multiple ScriptValue types. The problem is that the ScriptValue
+// base class is both templated and abstract, making it difficult to cast its
+// internal type to get a new ScriptValue. This problem could be solved by
+// refactoring ScriptValue such that there is a non-templated abstract type
+// (say, RawScriptValue) with a function like "void* GetValue()", but usually
+// not referenced directly by client code. Instead there would be a separate
+// templated concrete wrapper type (say, ScriptValue) that wraps RawScriptValue
+// and manages the casting and type checking. This would allow us to convert
+// between OnErrorEventListener and EventListener, if OnErrorEventListener was
+// derived from EventListener.
+class GenericEventHandlerReference {
+ public:
+ typedef script::ScriptValue<EventListener> EventListenerScriptValue;
+ typedef script::ScriptValue<OnErrorEventListener>
+ OnErrorEventListenerScriptValue;
+
+ GenericEventHandlerReference(script::Wrappable* wrappable,
+ const EventListenerScriptValue& script_value);
+ GenericEventHandlerReference(
+ script::Wrappable* wrappable,
+ const OnErrorEventListenerScriptValue& script_value);
+ GenericEventHandlerReference(script::Wrappable* wrappable,
+ const GenericEventHandlerReference& other);
+
+ // Forwards on to the internal event handler's HandleEvent() call, passing
+ // in the value of |unpack_error_event| if the internal type is a
+ // OnErrorEventListenerScriptValue type.
+ void HandleEvent(const scoped_refptr<Event>& event, bool is_attribute,
+ bool unpack_error_event);
+
+ bool EqualTo(const EventListenerScriptValue& other);
+ bool EqualTo(const GenericEventHandlerReference& other);
+ bool IsNull() const;
+
+ // If the internal type is a EventListenerScriptValue, then its value will
+ // be returned, otherwise null is returned;
+ const EventListenerScriptValue* event_listener_value() {
+ return event_listener_reference_
+ ? &event_listener_reference_->referenced_value()
+ : nullptr;
+ }
+
+ // If the internal type is a OnErrorEventListenerScriptValue, then its value
+ // will be returned, otherwise null is returned;
+ const OnErrorEventListenerScriptValue* on_error_event_listener_value() {
+ return on_error_event_listener_reference_
+ ? &on_error_event_listener_reference_->referenced_value()
+ : nullptr;
+ }
+
+ private:
+ // At most only one of the below two fields may be non-null... They are
+ // serving as a poor man's std::variant.
+ scoped_ptr<EventListenerScriptValue::Reference> event_listener_reference_;
+ scoped_ptr<OnErrorEventListenerScriptValue::Reference>
+ on_error_event_listener_reference_;
+
+ DISALLOW_COPY_AND_ASSIGN(GenericEventHandlerReference);
+};
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_GENERIC_EVENT_HANDLER_REFERENCE_H_
diff --git a/src/cobalt/dom/global_event_handlers.idl b/src/cobalt/dom/global_event_handlers.idl
index df852fc..733dcc8 100644
--- a/src/cobalt/dom/global_event_handlers.idl
+++ b/src/cobalt/dom/global_event_handlers.idl
@@ -18,7 +18,7 @@
interface GlobalEventHandlers {
attribute EventHandler onblur;
attribute EventHandler onclick;
- attribute EventHandler onerror;
+ attribute OnErrorEventListener onerror;
attribute EventHandler onfocus;
attribute EventHandler onkeydown;
@@ -72,3 +72,4 @@
// function. Define it as a nullable EventListener for now until we can do
// some refactoring to accept both in the EventTarget implementation.
typedef EventListener? EventHandler;
+typedef OnErrorEventListener? OnErrorEventHandler;
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 6ddf6cf..74471b0 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -142,6 +142,27 @@
} // namespace
+void HTMLElement::RuleMatchingState::Clear() {
+ if (is_set) {
+ is_set = false;
+
+ parent_matching_nodes.clear();
+ parent_descendant_nodes.clear();
+
+ previous_sibling_matching_nodes.clear();
+ previous_sibling_following_sibling_nodes.clear();
+
+ matching_nodes_parent_nodes.clear();
+ matching_nodes.clear();
+
+ are_descendant_nodes_dirty = true;
+ descendant_nodes.clear();
+
+ are_following_sibling_nodes_dirty = true;
+ following_sibling_nodes.clear();
+ }
+}
+
std::string HTMLElement::dir() const {
// The dir attribute is limited to only known values. On getting, dir must
// return the conforming value associated with the state the attribute is in,
@@ -593,30 +614,50 @@
return NULL;
}
+void HTMLElement::ClearRuleMatchingState() {
+ ClearRuleMatchingStateInternal(true /*invalidate_descendants*/);
+}
+
+void HTMLElement::ClearRuleMatchingStateInternal(bool invalidate_descendants) {
+ rule_matching_state_.Clear();
+ matching_rules_valid_ = false;
+
+ if (invalidate_descendants) {
+ InvalidateMatchingRulesRecursivelyInternal(true /*is_initial_element*/);
+ }
+}
+
+void HTMLElement::ClearRuleMatchingStateOnElementAndAncestors(
+ bool invalidate_matching_rules) {
+ Element* parent_element = this->parent_element();
+ HTMLElement* parent_html_element =
+ parent_element ? parent_element->AsHTMLElement() : NULL;
+ if (parent_html_element) {
+ parent_html_element->ClearRuleMatchingStateOnElementAndAncestors(
+ invalidate_matching_rules);
+ }
+ ClearRuleMatchingStateInternal(invalidate_matching_rules &&
+ parent_html_element == NULL);
+}
+
+void HTMLElement::ClearRuleMatchingStateOnElementAndDescendants() {
+ ClearRuleMatchingStateInternal(false /* invalidate_descendants*/);
+ for (Element* element = first_element_child(); element;
+ element = element->next_element_sibling()) {
+ HTMLElement* html_element = element->AsHTMLElement();
+ if (html_element) {
+ html_element->ClearRuleMatchingStateOnElementAndDescendants();
+ }
+ }
+}
+
void HTMLElement::InvalidateMatchingRulesRecursively() {
- InvalidateMatchingRulesRecursivelyInternal(true /*is_initial_invalidation*/);
+ InvalidateMatchingRulesRecursivelyInternal(true /*is_initial_element*/);
}
void HTMLElement::InvalidateMatchingRulesRecursivelyInternal(
- bool is_initial_invalidation) {
- if (matching_rules_valid_) {
- // Move |matching_rules_| into |old_matching_rules_|. This is used for
- // determining whether or not the matching rules actually changed when they
- // are updated.
- old_matching_rules_.swap(matching_rules_);
-
- matching_rules_.clear();
- rule_matching_state_.matching_nodes.clear();
- rule_matching_state_.descendant_potential_nodes.clear();
- rule_matching_state_.following_sibling_potential_nodes.clear();
- for (int pseudo_element_type = 0;
- pseudo_element_type < kMaxPseudoElementType; ++pseudo_element_type) {
- if (pseudo_elements_[pseudo_element_type]) {
- pseudo_elements_[pseudo_element_type]->ClearMatchingRules();
- }
- }
- matching_rules_valid_ = false;
- }
+ bool is_initial_element) {
+ matching_rules_valid_ = false;
// Invalidate matching rules on all children.
for (Element* element = first_element_child(); element;
@@ -624,26 +665,48 @@
HTMLElement* html_element = element->AsHTMLElement();
if (html_element) {
html_element->InvalidateMatchingRulesRecursivelyInternal(
- false /*is_initial_invalidation*/);
+ false /*is_initial_element*/);
}
}
// Invalidate matching rules on all following siblings if this is the initial
- // invalidation and sibling combinators are used; if this is not the initial
- // invalidation, then these will already be handled by a previous call.
- if (is_initial_invalidation &&
+ // element and sibling combinators are used; if this is not the initial
+ // element, then these will already be handled by a previous call.
+ if (is_initial_element &&
node_document()->selector_tree()->has_sibling_combinators()) {
for (Element* element = next_element_sibling(); element;
element = element->next_element_sibling()) {
HTMLElement* html_element = element->AsHTMLElement();
if (html_element) {
html_element->InvalidateMatchingRulesRecursivelyInternal(
- false /*is_initial_invalidation*/);
+ false /*is_initial_element*/);
}
}
}
}
+void HTMLElement::UpdateMatchingRules() { UpdateElementMatchingRules(this); }
+
+void HTMLElement::UpdateMatchingRulesRecursively() {
+ UpdateMatchingRules();
+ for (Element* element = first_element_child(); element;
+ element = element->next_element_sibling()) {
+ HTMLElement* html_element = element->AsHTMLElement();
+ if (html_element) {
+ html_element->UpdateMatchingRulesRecursively();
+ }
+ }
+}
+
+void HTMLElement::OnMatchingRulesModified() {
+ dom_stat_tracker_->OnUpdateMatchingRules();
+ computed_style_valid_ = false;
+}
+
+void HTMLElement::OnPseudoElementMatchingRulesModified() {
+ pseudo_elements_computed_styles_valid_ = false;
+}
+
void HTMLElement::UpdateComputedStyleRecursively(
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
parent_computed_style_declaration,
@@ -655,9 +718,8 @@
return;
}
- // Update computed style for this element.
- bool is_valid =
- ancestors_were_valid && matching_rules_valid_ && computed_style_valid_;
+ // Update computed styles for this element if any aren't valid.
+ bool is_valid = ancestors_were_valid && AreComputedStylesValid();
if (!is_valid) {
UpdateComputedStyle(parent_computed_style_declaration, root_computed_style,
style_change_event_time, kAncestorsAreDisplayed);
@@ -805,6 +867,7 @@
style_(new cssom::CSSDeclaredStyleDeclaration(
document->html_element_context()->css_parser())),
computed_style_valid_(false),
+ pseudo_elements_computed_styles_valid_(false),
descendant_computed_styles_valid_(false),
ancestors_are_displayed_(kAncestorsAreDisplayed),
css_computed_style_declaration_(new cssom::CSSComputedStyleDeclaration()),
@@ -858,25 +921,34 @@
RunUnFocusingSteps();
document->OnFocusChange();
}
+
+ // Only clear the rule matching on this element. Descendants will be handled
+ // by OnRemovedFromDocument() being called on them from
+ // Node::OnRemovedFromDocument().
+ ClearRuleMatchingStateInternal(false /*invalidate_descendants*/);
}
void HTMLElement::OnMutation() { InvalidateMatchingRulesRecursively(); }
void HTMLElement::OnSetAttribute(const std::string& name,
const std::string& value) {
- if (name == "class" || name == "id") {
- InvalidateMatchingRulesRecursively();
- } else if (name == "dir") {
+ if (name == "dir") {
SetDirectionality(value);
}
+
+ // Always clear the matching state when an attribute changes. Any attribute
+ // changing can potentially impact the matching rules.
+ ClearRuleMatchingState();
}
void HTMLElement::OnRemoveAttribute(const std::string& name) {
- if (name == "class" || name == "id") {
- InvalidateMatchingRulesRecursively();
- } else if (name == "dir") {
+ if (name == "dir") {
SetDirectionality("");
}
+
+ // Always clear the matching state when an attribute changes. Any attribute
+ // changing can potentially impact the matching rules.
+ ClearRuleMatchingState();
}
// Algorithm for IsFocusable:
@@ -958,8 +1030,8 @@
Event::kNotCancelable, document->window(),
this));
- // Custom, not in any sepc.
- InvalidateMatchingRulesRecursively();
+ // Custom, not in any spec.
+ ClearRuleMatchingState();
}
// Algorithm for RunUnFocusingSteps:
@@ -993,8 +1065,8 @@
Event::kNotCancelable, document->window(),
this));
- // Custom, not in any sepc.
- InvalidateMatchingRulesRecursively();
+ // Custom, not in any spec.
+ ClearRuleMatchingState();
}
void HTMLElement::SetDirectionality(const std::string& value) {
@@ -1249,6 +1321,9 @@
DCHECK(document) << "Element should be attached to document in order to "
"participate in layout.";
+ // Verify that the matching rules for this element are valid. They should have
+ // been updated prior to UpdateComputedStyle() being called.
+ DCHECK(matching_rules_valid_);
// If there is no previous computed style, there should also be no layout
// boxes.
DCHECK(computed_style() || NULL == layout_boxes());
@@ -1259,18 +1334,6 @@
// invalid or no computed style has been created yet.
bool generate_computed_style = !computed_style_valid_ || !computed_style();
- // Update matching rules if necessary.
- if (!matching_rules_valid_) {
- dom_stat_tracker_->OnUpdateMatchingRules();
- UpdateMatchingRules(this);
-
- // Check for whether the matching rules have changed. If they have, then a
- // new computed style must be generated from them.
- if (!generate_computed_style && old_matching_rules_ != matching_rules_) {
- generate_computed_style = true;
- }
- }
-
// If any declared properties inherited from the parent are no longer valid,
// then a new computed style must be generated with the updated inherited
// values.
@@ -1325,29 +1388,35 @@
// Update the displayed status of our ancestors.
ancestors_are_displayed_ = ancestors_are_displayed;
- // NOTE: Currently, pseudo element's computed styles are always generated. If
- // this becomes a performance bottleneck, change the logic so that it only
- // occurs when needed.
-
- // Promote the matching rules for all known pseudo elements.
- for (int pseudo_element_type = 0; pseudo_element_type < kMaxPseudoElementType;
- ++pseudo_element_type) {
- if (pseudo_elements_[pseudo_element_type]) {
- dom_stat_tracker_->OnGeneratePseudoElementComputedStyle();
- DoComputedStyleUpdate(
- pseudo_elements_[pseudo_element_type]->matching_rules(),
- &property_key_to_base_url_map, NULL, css_computed_style_declaration(),
- root_computed_style, document->viewport_size(),
- pseudo_elements_[pseudo_element_type]->computed_style(),
- style_change_event_time,
- pseudo_elements_[pseudo_element_type]->css_transitions(),
- pseudo_elements_[pseudo_element_type]->css_animations(),
- document->keyframes_map(),
- old_is_displayed ? kAncestorsAreDisplayed : kAncestorsAreNotDisplayed,
- IsDisplayed() ? kAncestorsAreDisplayed : kAncestorsAreNotDisplayed,
- kIsPseudoElement, &invalidation_flags,
- pseudo_elements_[pseudo_element_type]
- ->css_computed_style_declaration());
+ // Process pseudo elements. They must have their computed style generated if
+ // either their owning HTML element's style was just generated or their
+ // computed style is invalid (this occurs when their matching rules change).
+ for (int type = 0; type < kMaxPseudoElementType; ++type) {
+ PseudoElement* type_pseudo_element =
+ pseudo_element(PseudoElementType(type));
+ if (type_pseudo_element) {
+ if (generate_computed_style ||
+ type_pseudo_element->computed_style_invalid()) {
+ dom_stat_tracker_->OnGeneratePseudoElementComputedStyle();
+ DoComputedStyleUpdate(
+ type_pseudo_element->matching_rules(),
+ &property_key_to_base_url_map, NULL,
+ css_computed_style_declaration(), root_computed_style,
+ document->viewport_size(), type_pseudo_element->computed_style(),
+ style_change_event_time, type_pseudo_element->css_transitions(),
+ type_pseudo_element->css_animations(), document->keyframes_map(),
+ old_is_displayed ? kAncestorsAreDisplayed
+ : kAncestorsAreNotDisplayed,
+ IsDisplayed() ? kAncestorsAreDisplayed : kAncestorsAreNotDisplayed,
+ kIsPseudoElement, &invalidation_flags,
+ type_pseudo_element->css_computed_style_declaration());
+ type_pseudo_element->clear_computed_style_invalid();
+ } else {
+ // Update the inherited data if a new style was not generated. The
+ // ancestor data with inherited properties may have changed.
+ type_pseudo_element->css_computed_style_declaration()
+ ->UpdateInheritedData();
+ }
}
}
@@ -1374,6 +1443,19 @@
}
computed_style_valid_ = true;
+ pseudo_elements_computed_styles_valid_ = true;
+}
+
+void HTMLElement::SetPseudoElement(PseudoElementType type,
+ scoped_ptr<PseudoElement> pseudo_element) {
+ DCHECK_EQ(this, pseudo_element->parent_element());
+ DCHECK(type < kMaxPseudoElementType);
+ pseudo_elements_[type] = pseudo_element.Pass();
+ pseudo_elements_computed_styles_valid_ = false;
+}
+
+bool HTMLElement::AreComputedStylesValid() const {
+ return computed_style_valid_ && pseudo_elements_computed_styles_valid_;
}
bool HTMLElement::IsDesignated() const {
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 23e23df..b3481ea 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -90,14 +90,50 @@
// https://www.w3.org/TR/html5/dom.html#htmlelement
class HTMLElement : public Element, public cssom::MutationObserver {
public:
- typedef cssom::SelectorTree::NodeSet<12> MatchingNodes;
- typedef cssom::SelectorTree::NodeSet<40> DescendantPotentialNodes;
- typedef cssom::SelectorTree::NodeSet<8> FollowingSiblingPotentialNodes;
+ typedef cssom::SelectorTree::Nodes SelectorTreeNodes;
+ // Cached state used with rule matching to minimize the amount of work that
+ // must occur during UpdateMatchingRules() when little has changed.
struct RuleMatchingState {
- MatchingNodes matching_nodes;
- DescendantPotentialNodes descendant_potential_nodes;
- FollowingSiblingPotentialNodes following_sibling_potential_nodes;
+ RuleMatchingState()
+ : is_set(false),
+ are_descendant_nodes_dirty(true),
+ are_following_sibling_nodes_dirty(true) {}
+
+ // Fully clears the state but does not deallocate the vectors. This allows
+ // the vectors to be reused without additional allocations the next time
+ // they are needed and speeds up rule matching.
+ void Clear();
+
+ // Whether or not the rule matching state is set. When it is not set, the
+ // next call to UpdateMatchingRules() always generates new matching rules
+ // from the rule matching state.
+ bool is_set;
+
+ // The cached node state of the parent and previous sibling. Caching these
+ // allows the element to know the exact nodes that have changed and
+ // resultantly need to be checked during calls to UpdateMatchingRules().
+ SelectorTreeNodes parent_matching_nodes;
+ SelectorTreeNodes parent_descendant_nodes;
+
+ SelectorTreeNodes previous_sibling_matching_nodes;
+ SelectorTreeNodes previous_sibling_following_sibling_nodes;
+
+ // The element's current matching nodes, along with their parent nodes.
+ // These are kept in sync. This allows matching nodes to be removed when
+ // their parent nodes are no longer available to the element.
+ SelectorTreeNodes matching_nodes_parent_nodes;
+ SelectorTreeNodes matching_nodes;
+
+ // The nodes that are to be used by the element's descendants and following
+ // siblings during their own rule matching. These are generated by combining
+ // the nodes from the element's parent and previous sibling with the
+ // element's own matching nodes that contain the required combinator.
+ bool are_descendant_nodes_dirty;
+ SelectorTreeNodes descendant_nodes;
+
+ bool are_following_sibling_nodes_dirty;
+ SelectorTreeNodes following_sibling_nodes;
};
enum AncestorsAreDisplayed {
@@ -184,15 +220,25 @@
// Rule matching related methods.
//
+ // Returns the rule matching state of this element.
+ RuleMatchingState* rule_matching_state() { return &rule_matching_state_; }
+
+ void ClearRuleMatchingState();
+ void ClearRuleMatchingStateOnElementAndAncestors(
+ bool invalidate_tree_matching_rules);
+ void ClearRuleMatchingStateOnElementAndDescendants();
+
// Returns the cached matching rules of this element.
cssom::RulesWithCascadePrecedence* matching_rules() {
return &matching_rules_;
}
- // Returns the rule matching state of this element.
- RuleMatchingState* rule_matching_state() { return &rule_matching_state_; }
- // Invalidates the matching rules and rule matching state in this element,
- // its descendants and its siblings.
+
void InvalidateMatchingRulesRecursively();
+ void UpdateMatchingRules();
+ void UpdateMatchingRulesRecursively();
+
+ void OnMatchingRulesModified();
+ void OnPseudoElementMatchingRulesModified();
// Computed style related methods.
//
@@ -208,15 +254,6 @@
return css_computed_style_declaration_->data();
}
- void MarkDisplayNoneOnNodeAndDescendants() override;
- void PurgeCachedBackgroundImagesOfNodeAndDescendants() override;
- void InvalidateComputedStylesOfNodeAndDescendants() override;
- void InvalidateLayoutBoxesOfNodeAndAncestors() override;
- void InvalidateLayoutBoxesOfNodeAndDescendants() override;
- void InvalidateLayoutBoxSizes() override;
- void InvalidateLayoutBoxCrossReferences() override;
- void InvalidateLayoutBoxRenderTreeNodes() override;
-
// Updates the cached computed style of this element and its descendants.
void UpdateComputedStyleRecursively(
const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
@@ -235,6 +272,15 @@
const base::TimeDelta& style_change_event_time,
AncestorsAreDisplayed ancestor_is_displayed);
+ void MarkDisplayNoneOnNodeAndDescendants() override;
+ void PurgeCachedBackgroundImagesOfNodeAndDescendants() override;
+ void InvalidateComputedStylesOfNodeAndDescendants() override;
+ void InvalidateLayoutBoxesOfNodeAndAncestors() override;
+ void InvalidateLayoutBoxesOfNodeAndDescendants() override;
+ void InvalidateLayoutBoxSizes() override;
+ void InvalidateLayoutBoxCrossReferences() override;
+ void InvalidateLayoutBoxRenderTreeNodes() override;
+
// Layout box related methods.
//
// The LayoutContainerBox gives the HTML Element an interface to the container
@@ -251,17 +297,16 @@
return pseudo_elements_[type].get();
}
- void set_pseudo_element(PseudoElementType type,
- scoped_ptr<PseudoElement> pseudo_element) {
- DCHECK_EQ(this, pseudo_element->parent_element());
- DCHECK(type < kMaxPseudoElementType);
- pseudo_elements_[type] = pseudo_element.Pass();
- }
+ void SetPseudoElement(PseudoElementType type,
+ scoped_ptr<PseudoElement> pseudo_element);
- bool computed_style_valid() const { return computed_style_valid_; }
+ // Returns true if the element's computed style and all of its pseudo
+ // element's computed styles are valid.
+ bool AreComputedStylesValid() const;
bool descendant_computed_styles_valid() const {
return descendant_computed_styles_valid_;
}
+
bool matching_rules_valid() const { return matching_rules_valid_; }
void set_matching_rules_valid() { matching_rules_valid_ = true; }
@@ -318,10 +363,13 @@
// directionality does not invalidate the computed style.
void SetDirectionality(const std::string& value);
- // Invalidates the matching rules and rule matching state in this element and
+ // Invalidate the matching rules and rule matching state in this element and
// its descendants. In the case where this is the the initial invalidation,
// it will also invalidate the rule matching state of its siblings.
- void InvalidateMatchingRulesRecursivelyInternal(bool is_initial_invalidation);
+ void InvalidateMatchingRulesRecursivelyInternal(bool is_initial_element);
+ // Fully clear the rule matching state of this element and optionally
+ // invalidate all of its descendants matching rules.
+ void ClearRuleMatchingStateInternal(bool invalidate_descendants);
// Clear the list of active background images, and notify the animated image
// tracker to stop the animations.
@@ -364,6 +412,9 @@
// Keeps track of whether the HTML element's current computed style is out
// of date or not.
bool computed_style_valid_;
+ // Keeps track of whether the HTML element's pseudo element's computed styles
+ // are out of date or not.
+ bool pseudo_elements_computed_styles_valid_;
// Keeps track of whether the HTML element's descendants' computed styles are
// out of date or not.
bool descendant_computed_styles_valid_;
@@ -382,10 +433,9 @@
cssom::AnimationSet css_animations_;
// The following fields are used in rule matching.
- cssom::RulesWithCascadePrecedence old_matching_rules_;
- cssom::RulesWithCascadePrecedence matching_rules_;
RuleMatchingState rule_matching_state_;
bool matching_rules_valid_;
+ cssom::RulesWithCascadePrecedence matching_rules_;
// This contains information about the boxes generated from the element.
scoped_ptr<LayoutBoxes> layout_boxes_;
diff --git a/src/cobalt/dom/html_element_context.cc b/src/cobalt/dom/html_element_context.cc
index 60d0e15..089d5b1 100644
--- a/src/cobalt/dom/html_element_context.cc
+++ b/src/cobalt/dom/html_element_context.cc
@@ -21,6 +21,7 @@
HTMLElementContext::HTMLElementContext()
: fetcher_factory_(NULL),
+ loader_factory_(NULL),
css_parser_(NULL),
dom_parser_(NULL),
can_play_type_handler_(NULL),
@@ -42,7 +43,8 @@
}
HTMLElementContext::HTMLElementContext(
- loader::FetcherFactory* fetcher_factory, cssom::CSSParser* css_parser,
+ loader::FetcherFactory* fetcher_factory,
+ loader::LoaderFactory* loader_factory, cssom::CSSParser* css_parser,
Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
media::WebMediaPlayerFactory* web_media_player_factory,
script::ScriptRunner* script_runner,
@@ -57,8 +59,10 @@
loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
const std::string& font_language_script,
base::ApplicationState initial_application_state,
+ base::WaitableEvent* synchronous_loader_interrupt,
float video_playback_rate_multiplier)
: fetcher_factory_(fetcher_factory),
+ loader_factory_(loader_factory),
css_parser_(css_parser),
dom_parser_(dom_parser),
can_play_type_handler_(can_play_type_handler),
@@ -77,6 +81,7 @@
font_language_script_(font_language_script),
page_visibility_state_(initial_application_state),
video_playback_rate_multiplier_(video_playback_rate_multiplier),
+ synchronous_loader_interrupt_(synchronous_loader_interrupt),
sync_load_thread_("Synchronous Load"),
html_element_factory_(new HTMLElementFactory()) {
sync_load_thread_.Start();
diff --git a/src/cobalt/dom/html_element_context.h b/src/cobalt/dom/html_element_context.h
index cab1c71..bb3d58f 100644
--- a/src/cobalt/dom/html_element_context.h
+++ b/src/cobalt/dom/html_element_context.h
@@ -18,6 +18,7 @@
#include <string>
#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "cobalt/base/application_state.h"
#include "cobalt/cssom/css_parser.h"
@@ -50,7 +51,8 @@
HTMLElementContext();
HTMLElementContext(
- loader::FetcherFactory* fetcher_factory, cssom::CSSParser* css_parser,
+ loader::FetcherFactory* fetcher_factory,
+ loader::LoaderFactory* loader_factory, cssom::CSSParser* css_parser,
Parser* dom_parser, media::CanPlayTypeHandler* can_play_type_handler,
media::WebMediaPlayerFactory* web_media_player_factory,
script::ScriptRunner* script_runner,
@@ -65,11 +67,14 @@
loader::mesh::MeshCache* mesh_cache, DomStatTracker* dom_stat_tracker,
const std::string& font_language_script,
base::ApplicationState initial_application_state,
+ base::WaitableEvent* synchronous_loader_interrupt,
float video_playback_rate_multiplier = 1.0);
~HTMLElementContext();
loader::FetcherFactory* fetcher_factory() { return fetcher_factory_; }
+ loader::LoaderFactory* loader_factory() { return loader_factory_; }
+
cssom::CSSParser* css_parser() { return css_parser_; }
Parser* dom_parser() { return dom_parser_; }
@@ -99,6 +104,10 @@
return resource_provider_;
}
+ base::WaitableEvent* synchronous_loader_interrupt() {
+ return synchronous_loader_interrupt_;
+ }
+
loader::image::AnimatedImageTracker* animated_image_tracker() const {
return animated_image_tracker_;
}
@@ -138,6 +147,7 @@
private:
loader::FetcherFactory* const fetcher_factory_;
+ loader::LoaderFactory* const loader_factory_;
cssom::CSSParser* const css_parser_;
Parser* const dom_parser_;
media::CanPlayTypeHandler* can_play_type_handler_;
@@ -156,6 +166,7 @@
const std::string font_language_script_;
page_visibility::PageVisibilityState page_visibility_state_;
const float video_playback_rate_multiplier_;
+ base::WaitableEvent* synchronous_loader_interrupt_ = nullptr;
base::Thread sync_load_thread_;
scoped_ptr<HTMLElementFactory> html_element_factory_;
diff --git a/src/cobalt/dom/html_element_factory_test.cc b/src/cobalt/dom/html_element_factory_test.cc
index 78c9687..377382a 100644
--- a/src/cobalt/dom/html_element_factory_test.cc
+++ b/src/cobalt/dom/html_element_factory_test.cc
@@ -15,6 +15,7 @@
#include "cobalt/dom/html_element_factory.h"
#include "base/message_loop.h"
+#include "base/threading/platform_thread.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_stat_tracker.h"
#include "cobalt/dom/html_anchor_element.h"
@@ -40,6 +41,7 @@
#include "cobalt/dom/testing/stub_script_runner.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
@@ -49,11 +51,13 @@
protected:
HTMLElementFactoryTest()
: fetcher_factory_(NULL /* network_module */),
+ loader_factory_(&fetcher_factory_, NULL /* resource loader */,
+ base::kThreadPriority_Default),
dom_parser_(new dom_parser::Parser()),
dom_stat_tracker_(new DomStatTracker("HTMLElementFactoryTest")),
html_element_context_(
- &fetcher_factory_, &stub_css_parser_, dom_parser_.get(),
- NULL /* can_play_type_handler */,
+ &fetcher_factory_, &loader_factory_, &stub_css_parser_,
+ dom_parser_.get(), NULL /* can_play_type_handler */,
NULL /* web_media_player_factory */, &stub_script_runner_,
NULL /* script_value_factory */, NULL /* media_source_registry */,
NULL /* resource_provider */, NULL /* animated_image_tracker */,
@@ -61,11 +65,13 @@
NULL /* reduced_image_cache_capacity_manager */,
NULL /* remote_typeface_cache */, NULL /* mesh_cache */,
dom_stat_tracker_.get(), "" /* language */,
- base::kApplicationStateStarted),
+ base::kApplicationStateStarted,
+ NULL /* synchronous_loader_interrupt */),
document_(new Document(&html_element_context_)) {}
~HTMLElementFactoryTest() override {}
loader::FetcherFactory fetcher_factory_;
+ loader::LoaderFactory loader_factory_;
scoped_ptr<Parser> dom_parser_;
testing::StubCSSParser stub_css_parser_;
testing::StubScriptRunner stub_script_runner_;
diff --git a/src/cobalt/dom/html_element_test.cc b/src/cobalt/dom/html_element_test.cc
index cc2362b..5f10c02 100644
--- a/src/cobalt/dom/html_element_test.cc
+++ b/src/cobalt/dom/html_element_test.cc
@@ -112,10 +112,10 @@
protected:
HTMLElementTest()
: dom_stat_tracker_(new DomStatTracker("HTMLElementTest")),
- html_element_context_(NULL, &css_parser_, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ html_element_context_(NULL, NULL, &css_parser_, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted),
+ base::kApplicationStateStarted, NULL),
document_(new Document(&html_element_context_)) {}
~HTMLElementTest() override {}
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc
index d8dbd04..d139c38 100644
--- a/src/cobalt/dom/html_link_element.cc
+++ b/src/cobalt/dom/html_link_element.cc
@@ -173,16 +173,20 @@
}
request_mode_ = GetRequestMode(GetAttribute("crossOrigin"));
- loader_ = make_scoped_ptr(new loader::Loader(
- base::Bind(
- &loader::FetcherFactory::CreateSecureFetcher,
- base::Unretained(document->html_element_context()->fetcher_factory()),
- absolute_url_, csp_callback, request_mode_,
- document->location() ? document->location()->GetOriginAsObject()
- : loader::Origin()),
- scoped_ptr<loader::Decoder>(new loader::TextDecoder(
- base::Bind(&HTMLLinkElement::OnLoadingDone, base::Unretained(this)))),
- base::Bind(&HTMLLinkElement::OnLoadingError, base::Unretained(this))));
+
+ DCHECK(!loader_);
+ loader::Origin origin = document->location()
+ ? document->location()->GetOriginAsObject()
+ : loader::Origin();
+
+ loader_ = html_element_context()->loader_factory()
+ ->CreateLinkLoader(
+ absolute_url_, origin, csp_callback, request_mode_,
+ base::Bind(&HTMLLinkElement::OnLoadingDone,
+ base::Unretained(this)),
+ base::Bind(&HTMLLinkElement::OnLoadingError,
+ base::Unretained(this)))
+ .Pass();
}
void HTMLLinkElement::OnLoadingDone(const loader::Origin& last_url_origin,
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 5aac100..b7b404f 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -1006,21 +1006,21 @@
media_source_ =
html_element_context()->media_source_registry()->Retrieve(url.spec());
- // If media_source_ is NULL, the player will try to load it as a normal
- // media resource url and throw a DOM exception when it fails.
- if (media_source_) {
-#if defined(COBALT_MEDIA_SOURCE_2016)
- media_source_->AttachToElement(this);
-#else // defined(COBALT_MEDIA_SOURCE_2016)
- media_source_->SetPlayer(player_.get());
-#endif // defined(COBALT_MEDIA_SOURCE_2016)
- media_source_url_ = url;
- } else {
+ if (!media_source_) {
NoneSupported("Media source is NULL.");
return;
}
+#if defined(COBALT_MEDIA_SOURCE_2016)
+ if (!media_source_->AttachToElement(this)) {
+ media_source_ = nullptr;
+ NoneSupported("Unable to attach media source.");
+ return;
+ }
+#else // defined(COBALT_MEDIA_SOURCE_2016)
+ media_source_->SetPlayer(player_.get());
+#endif // defined(COBALT_MEDIA_SOURCE_2016)
+ media_source_url_ = url;
}
-
// The resource fetch algorithm
network_state_ = kNetworkLoading;
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index 50688c4..5336ed2 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -71,7 +71,9 @@
inline_script_location_(GetSourceLocationName(), 1, 1),
is_sync_load_successful_(false),
prevent_garbage_collection_count_(0),
- should_execute_(true) {
+ should_execute_(true),
+ synchronous_loader_interrupt_(
+ document->html_element_context()->synchronous_loader_interrupt()) {
DCHECK(document->html_element_context()->script_runner());
}
@@ -290,10 +292,16 @@
// The element is the pending parsing-blocking script of the Document of
// the parser that created the element. (There can only be one such script
// per Document at a time.)
+
+ // This variable will be set to true in the completion callback for the
+ // loader below. If that completion callback never fires, the variable
+ // will stay false. This can happen if the loader was interrupted, or
+ // failed for another reason.
is_sync_load_successful_ = false;
loader::LoadSynchronously(
html_element_context()->sync_load_thread()->message_loop(),
+ synchronous_loader_interrupt_,
base::Bind(
&loader::FetcherFactory::CreateSecureFetcher,
base::Unretained(html_element_context()->fetcher_factory()), url_,
@@ -338,17 +346,19 @@
// once the resource has been fetched (defined above) has been run.
document_->IncreaseLoadingCounter();
- loader_.reset(new loader::Loader(
- base::Bind(
- &loader::FetcherFactory::CreateSecureFetcher,
- base::Unretained(html_element_context()->fetcher_factory()), url_,
- csp_callback, request_mode_,
- document_->location() ? document_->location()->GetOriginAsObject()
- : loader::Origin()),
- scoped_ptr<loader::Decoder>(new loader::TextDecoder(base::Bind(
- &HTMLScriptElement::OnLoadingDone, base::Unretained(this)))),
- base::Bind(&HTMLScriptElement::OnLoadingError,
- base::Unretained(this))));
+ loader::Origin origin = document_->location()
+ ? document_->location()->GetOriginAsObject()
+ : loader::Origin();
+
+ loader_ = html_element_context()
+ ->loader_factory()
+ ->CreateScriptLoader(
+ url_, origin, csp_callback,
+ base::Bind(&HTMLScriptElement::OnLoadingDone,
+ base::Unretained(this)),
+ base::Bind(&HTMLScriptElement::OnLoadingError,
+ base::Unretained(this)))
+ .Pass();
} break;
case 5: {
// This is an asynchronous script. Prevent garbage collection until
@@ -365,17 +375,21 @@
// The element must be added to the set of scripts that will execute as
// soon as possible of the Document of the script element at the time the
// prepare a script algorithm started.
- loader_.reset(new loader::Loader(
- base::Bind(
- &loader::FetcherFactory::CreateSecureFetcher,
- base::Unretained(html_element_context()->fetcher_factory()), url_,
- csp_callback, request_mode_,
- document_->location() ? document_->location()->GetOriginAsObject()
- : loader::Origin()),
- scoped_ptr<loader::Decoder>(new loader::TextDecoder(base::Bind(
- &HTMLScriptElement::OnLoadingDone, base::Unretained(this)))),
- base::Bind(&HTMLScriptElement::OnLoadingError,
- base::Unretained(this))));
+ DCHECK(!loader_);
+
+ loader::Origin origin = document_->location()
+ ? document_->location()->GetOriginAsObject()
+ : loader::Origin();
+
+ loader_ = html_element_context()
+ ->loader_factory()
+ ->CreateScriptLoader(
+ url_, origin, csp_callback,
+ base::Bind(&HTMLScriptElement::OnLoadingDone,
+ base::Unretained(this)),
+ base::Bind(&HTMLScriptElement::OnLoadingError,
+ base::Unretained(this)))
+ .Pass();
} break;
case 6: {
// Otherwise.
diff --git a/src/cobalt/dom/html_script_element.h b/src/cobalt/dom/html_script_element.h
index d6c42ff..d507712 100644
--- a/src/cobalt/dom/html_script_element.h
+++ b/src/cobalt/dom/html_script_element.h
@@ -19,6 +19,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_checker.h"
#include "cobalt/base/source_location.h"
#include "cobalt/dom/html_element.h"
@@ -149,6 +150,8 @@
// javascript parser takes in to record if the error reqort should be muted
// due to cross-origin fetched script.
loader::Origin fetched_last_url_origin_;
+
+ base::WaitableEvent* synchronous_loader_interrupt_;
};
} // namespace dom
diff --git a/src/cobalt/dom/local_storage_database.cc b/src/cobalt/dom/local_storage_database.cc
index 02fc976..e573e81 100644
--- a/src/cobalt/dom/local_storage_database.cc
+++ b/src/cobalt/dom/local_storage_database.cc
@@ -18,120 +18,47 @@
#include "cobalt/dom/storage_area.h"
#include "cobalt/storage/storage_manager.h"
#include "nb/memory_scope.h"
-#include "sql/statement.h"
namespace cobalt {
namespace dom {
namespace {
-const int kOriginalLocalStorageSchemaVersion = 1;
-const int kLatestLocalStorageSchemaVersion = 1;
-
-void SqlInit(storage::SqlContext* sql_context) {
- TRACK_MEMORY_SCOPE("Storage");
- TRACE_EVENT0("cobalt::storage", "LocalStorage::SqlInit()");
- sql::Connection* conn = sql_context->sql_connection();
- int schema_version;
- bool table_exists =
- sql_context->GetSchemaVersion("LocalStorageTable", &schema_version);
-
- if (table_exists) {
- if (schema_version == storage::StorageManager::kSchemaTableIsNew) {
- // This savegame predates the existence of the schema table.
- // Since the local-storage table did not change between the initial
- // release of the app and the introduction of the schema table, assume
- // that this existing local-storage table is schema version 1. This
- // avoids a loss of data on upgrade.
-
- DLOG(INFO) << "Updating LocalStorageTable schema version to "
- << kOriginalLocalStorageSchemaVersion;
- sql_context->UpdateSchemaVersion("LocalStorageTable",
- kOriginalLocalStorageSchemaVersion);
- } else if (schema_version == storage::StorageManager::kSchemaVersionLost) {
- // Since there has only been one schema so far, treat this the same as
- // kSchemaTableIsNew. When there are multiple schemas in the wild,
- // we may want to drop the table instead.
- sql_context->UpdateSchemaVersion("LocalStorageTable",
- kOriginalLocalStorageSchemaVersion);
- }
- } else {
- // The table does not exist, so create it in its latest form.
- sql::Statement create_table(conn->GetUniqueStatement(
- "CREATE TABLE LocalStorageTable ("
- " site_identifier TEXT, "
- " key TEXT, "
- " value TEXT NOT NULL ON CONFLICT FAIL, "
- " UNIQUE(site_identifier, key) ON CONFLICT REPLACE"
- ")"));
- bool ok = create_table.Run();
- DCHECK(ok);
- sql_context->UpdateSchemaVersion("LocalStorageTable",
- kLatestLocalStorageSchemaVersion);
- }
+void LocalStorageInit(const storage::MemoryStore& memory_store) {
+ LOG(INFO) << "local_storage Init";
+ UNREFERENCED_PARAMETER(memory_store);
}
-void SqlReadValues(const std::string& id,
- const LocalStorageDatabase::ReadCompletionCallback& callback,
- storage::SqlContext* sql_context) {
+void LocalStorageReadValues(
+ const std::string& id,
+ const LocalStorageDatabase::ReadCompletionCallback& callback,
+ const storage::MemoryStore& memory_store) {
+ LOG(INFO) << "LocalStorageReadValues";
TRACK_MEMORY_SCOPE("Storage");
- scoped_ptr<StorageArea::StorageMap> values(new StorageArea::StorageMap);
- sql::Connection* conn = sql_context->sql_connection();
- sql::Statement get_values(conn->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT key, value FROM LocalStorageTable WHERE site_identifier = ?"));
- get_values.BindString(0, id);
- while (get_values.Step()) {
- // TODO: In Steel, these were string16.
- std::string key(get_values.ColumnString(0));
- std::string value(get_values.ColumnString(1));
- values->insert(std::make_pair(key, value));
- }
+
+ scoped_ptr<storage::MemoryStore::LocalStorageMap> values(
+ new storage::MemoryStore::LocalStorageMap);
+ memory_store.ReadAllLocalStorage(id, values.get());
callback.Run(values.Pass());
}
-void SqlWrite(const std::string& id, const std::string& key,
- const std::string& value, storage::SqlContext* sql_context) {
+void LocalStorageWrite(const std::string& id, const std::string& key,
+ const std::string& value,
+ storage::MemoryStore* memory_store) {
TRACK_MEMORY_SCOPE("Storage");
- sql::Connection* conn = sql_context->sql_connection();
- sql::Statement write_statement(conn->GetCachedStatement(
- SQL_FROM_HERE,
- "INSERT INTO LocalStorageTable (site_identifier, key, value) "
- "VALUES (?, ?, ?)"));
- write_statement.BindString(0, id);
- write_statement.BindString(1, key);
- write_statement.BindString(2, value);
- bool ok = write_statement.Run();
- DCHECK(ok);
- sql_context->FlushOnChange();
+ memory_store->WriteToLocalStorage(id, key, value);
}
-void SqlDelete(const std::string& id, const std::string& key,
- storage::SqlContext* sql_context) {
+void LocalStorageDelete(const std::string& id, const std::string& key,
+ storage::MemoryStore* memory_store) {
TRACK_MEMORY_SCOPE("Storage");
- sql::Connection* conn = sql_context->sql_connection();
- sql::Statement delete_statement(
- conn->GetCachedStatement(SQL_FROM_HERE,
- "DELETE FROM LocalStorageTable "
- "WHERE site_identifier = ? AND key = ?"));
- delete_statement.BindString(0, id);
- delete_statement.BindString(1, key);
- bool ok = delete_statement.Run();
- DCHECK(ok);
- sql_context->FlushOnChange();
+ memory_store->DeleteFromLocalStorage(id, key);
}
-void SqlClear(const std::string& id, storage::SqlContext* sql_context) {
+void LocalStorageClear(const std::string& id,
+ storage::MemoryStore* memory_store) {
TRACK_MEMORY_SCOPE("Storage");
- sql::Connection* conn = sql_context->sql_connection();
- sql::Statement clear_statement(
- conn->GetCachedStatement(SQL_FROM_HERE,
- "DELETE FROM LocalStorageTable "
- "WHERE site_identifier = ?"));
- clear_statement.BindString(0, id);
- bool ok = clear_statement.Run();
- DCHECK(ok);
- sql_context->FlushOnChange();
+ memory_store->ClearLocalStorage(id);
}
} // namespace
@@ -142,7 +69,7 @@
// a potential wait while the storage manager loads from disk.
void LocalStorageDatabase::Init() {
if (!initialized_) {
- storage_->GetSqlContext(base::Bind(&SqlInit));
+ storage_->WithReadOnlyMemoryStore(base::Bind(&LocalStorageInit));
initialized_ = true;
}
}
@@ -151,28 +78,32 @@
const ReadCompletionCallback& callback) {
TRACK_MEMORY_SCOPE("Storage");
Init();
- storage_->GetSqlContext(base::Bind(&SqlReadValues, id, callback));
+ storage_->WithReadOnlyMemoryStore(
+ base::Bind(&LocalStorageReadValues, id, callback));
}
void LocalStorageDatabase::Write(const std::string& id, const std::string& key,
const std::string& value) {
TRACK_MEMORY_SCOPE("Storage");
Init();
- storage_->GetSqlContext(base::Bind(&SqlWrite, id, key, value));
+ storage_->WithMemoryStore(base::Bind(&LocalStorageWrite, id, key, value));
}
void LocalStorageDatabase::Delete(const std::string& id,
const std::string& key) {
+ TRACK_MEMORY_SCOPE("Storage");
Init();
- storage_->GetSqlContext(base::Bind(&SqlDelete, id, key));
+ storage_->WithMemoryStore(base::Bind(&LocalStorageDelete, id, key));
}
void LocalStorageDatabase::Clear(const std::string& id) {
+ TRACK_MEMORY_SCOPE("Storage");
Init();
- storage_->GetSqlContext(base::Bind(&SqlClear, id));
+ storage_->WithMemoryStore(base::Bind(&LocalStorageClear, id));
}
void LocalStorageDatabase::Flush(const base::Closure& callback) {
+ TRACK_MEMORY_SCOPE("Storage");
storage_->FlushNow(callback);
}
diff --git a/src/cobalt/dom/mutation_observer.cc b/src/cobalt/dom/mutation_observer.cc
index 228b9d4..8cc0ba2 100644
--- a/src/cobalt/dom/mutation_observer.cc
+++ b/src/cobalt/dom/mutation_observer.cc
@@ -157,7 +157,7 @@
void MutationObserver::TraceMembers(script::Tracer* tracer) {
tracer->TraceItems(observed_nodes_);
- tracer->TraceSequence(record_queue_);
+ tracer->TraceItems(record_queue_);
}
void MutationObserver::TrackObservedNode(const scoped_refptr<dom::Node>& node) {
diff --git a/src/cobalt/dom/mutation_observer_task_manager.h b/src/cobalt/dom/mutation_observer_task_manager.h
index dbfeacc..8f815d1 100644
--- a/src/cobalt/dom/mutation_observer_task_manager.h
+++ b/src/cobalt/dom/mutation_observer_task_manager.h
@@ -19,7 +19,6 @@
#include "base/hash_tables.h"
#include "base/threading/thread_checker.h"
-#include "cobalt/script/global_environment.h"
#include "cobalt/script/tracer.h"
namespace cobalt {
@@ -38,12 +37,6 @@
public:
MutationObserverTaskManager() : task_posted_(false) {}
- void RegisterAsTracingRoot(script::GlobalEnvironment* global_environment) {
- // Note that we only add ourselves, and never remove ourselves, as we will
- // actually outlive the web module.
- global_environment->AddRoot(this);
- }
-
// These should be called in the constructor/destructor of the
// MutationObserver.
void OnMutationObserverCreated(MutationObserver* observer);
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index f195bdb..4bd92c7 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -80,6 +80,10 @@
DEFINE_WRAPPABLE_TYPE(Navigator);
void TraceMembers(script::Tracer* tracer) override;
+ void SetEnvironmentSettings(script::EnvironmentSettings* settings) {
+ media_devices_->SetEnvironmentSettings(settings);
+ }
+
private:
~Navigator() override {}
diff --git a/src/cobalt/dom/node_list_live_test.cc b/src/cobalt/dom/node_list_live_test.cc
index 3c11bec..5e18279 100644
--- a/src/cobalt/dom/node_list_live_test.cc
+++ b/src/cobalt/dom/node_list_live_test.cc
@@ -28,9 +28,9 @@
NodeListLiveTest()
: dom_stat_tracker_("NodeListLiveTest"),
html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&dom_stat_tracker_, "",
- base::kApplicationStateStarted),
+ base::kApplicationStateStarted, NULL),
document_(new Document(&html_element_context_)) {}
~NodeListLiveTest() override {}
diff --git a/src/cobalt/dom/node_list_test.cc b/src/cobalt/dom/node_list_test.cc
index 48826be..fbdd198 100644
--- a/src/cobalt/dom/node_list_test.cc
+++ b/src/cobalt/dom/node_list_test.cc
@@ -28,9 +28,9 @@
NodeListTest()
: dom_stat_tracker_(new DomStatTracker("NodeListTest")),
html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted),
+ base::kApplicationStateStarted, NULL),
document_(new Document(&html_element_context_)) {}
~NodeListTest() override {}
diff --git a/src/cobalt/dom/on_error_event_listener.cc b/src/cobalt/dom/on_error_event_listener.cc
new file mode 100644
index 0000000..6af5697
--- /dev/null
+++ b/src/cobalt/dom/on_error_event_listener.cc
@@ -0,0 +1,50 @@
+// Copyright 2018 Google Inc. 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 "cobalt/dom/on_error_event_listener.h"
+
+#include <string>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/error_event.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/dom/script_event_log.h"
+
+namespace cobalt {
+namespace dom {
+
+base::optional<bool> OnErrorEventListener::HandleEvent(
+ const scoped_refptr<script::Wrappable>& callback_this,
+ const scoped_refptr<Event>& event, bool* had_exception,
+ bool unpack_error_events) const {
+ if (unpack_error_events) {
+ // Only ErrorEvents should be dispatched to OnErrorEventListeners.
+ ErrorEvent* error_event =
+ base::polymorphic_downcast<ErrorEvent*>(event.get());
+
+ // Unpack the error event into its components and pass those down.
+ return HandleEvent(callback_this, EventOrMessage(error_event->message()),
+ error_event->filename(), error_event->lineno(),
+ error_event->colno(), error_event->error(),
+ had_exception);
+ } else {
+ return HandleEvent(callback_this, EventOrMessage(event), std::string(), 0,
+ 0, NULL, had_exception);
+ }
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/on_error_event_listener.h b/src/cobalt/dom/on_error_event_listener.h
new file mode 100644
index 0000000..411bdb7
--- /dev/null
+++ b/src/cobalt/dom/on_error_event_listener.h
@@ -0,0 +1,56 @@
+// Copyright 2018 Google Inc. 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 COBALT_DOM_ON_ERROR_EVENT_LISTENER_H_
+#define COBALT_DOM_ON_ERROR_EVENT_LISTENER_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "cobalt/dom/event.h"
+#include "cobalt/dom/event_listener.h"
+#include "cobalt/script/union_type.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// This interface exists to support the special case OnErrorEventHandler:
+// https://html.spec.whatwg.org/#onerroreventhandler
+class OnErrorEventListener {
+ public:
+ using EventOrMessage = script::UnionType2<scoped_refptr<Event>, std::string>;
+
+ // The public interface simply accepts the error event, which will be
+ // translated to the more complicated IDL handleEvent interface which is
+ // marked protected. During that translation, the event parameters may
+ // be unpacked if the |unpack_error_events| flag is true.
+ base::optional<bool> HandleEvent(
+ const scoped_refptr<script::Wrappable>& callback_this,
+ const scoped_refptr<Event>& event, bool* had_exception,
+ bool unpack_error_events) const;
+
+ protected:
+ virtual base::optional<bool> HandleEvent(
+ const scoped_refptr<script::Wrappable>& callback_this,
+ EventOrMessage message, const std::string& filename, uint32 lineno,
+ uint32 colno, const script::ValueHandleHolder* error,
+ bool* had_exception) const = 0;
+};
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_ON_ERROR_EVENT_LISTENER_H_
diff --git a/src/cobalt/dom/on_error_event_listener.idl b/src/cobalt/dom/on_error_event_listener.idl
new file mode 100644
index 0000000..d393f68
--- /dev/null
+++ b/src/cobalt/dom/on_error_event_listener.idl
@@ -0,0 +1,27 @@
+// Copyright 2018 Google Inc. 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.
+
+// This interface exists to support the special case OnErrorEventHandler:
+// https://html.spec.whatwg.org/#onerroreventhandler
+
+callback interface OnErrorEventListener {
+ // TODO: Because the EventHandler type is typedef'd as a nullable
+ // EventListener, we need to allow the return of a boolean value. The value
+ // of this will only be checked if the EventListener was added as an
+ // attribute (not through addEventListener).
+ boolean? handleEvent(
+ (Event or DOMString) event, optional DOMString filename,
+ optional unsigned long lineno, optional unsigned long colno,
+ optional any error);
+};
diff --git a/src/cobalt/dom/on_screen_keyboard.idl b/src/cobalt/dom/on_screen_keyboard.idl
index 036b012..ed3761b 100644
--- a/src/cobalt/dom/on_screen_keyboard.idl
+++ b/src/cobalt/dom/on_screen_keyboard.idl
@@ -15,7 +15,9 @@
// Custom API for hiding, showing, focusing, blurring, and receiving input
// from an on screen keyboard.
-interface OnScreenKeyboard : EventTarget {
+[
+ Conditional=COBALT_ENABLE_ON_SCREEN_KEYBOARD
+] interface OnScreenKeyboard : EventTarget {
Promise<void> show();
Promise<void> hide();
Promise<void> focus();
diff --git a/src/cobalt/dom/on_screen_keyboard_test.cc b/src/cobalt/dom/on_screen_keyboard_test.cc
index cbece44..565086f 100644
--- a/src/cobalt/dom/on_screen_keyboard_test.cc
+++ b/src/cobalt/dom/on_screen_keyboard_test.cc
@@ -17,6 +17,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
+#include "base/threading/platform_thread.h"
#include "cobalt/bindings/testing/utils.h"
#include "cobalt/css_parser/parser.h"
#include "cobalt/dom/local_storage_database.h"
@@ -24,6 +25,7 @@
#include "cobalt/dom/window.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader_factory.h"
#include "cobalt/media_session/media_session.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
@@ -177,6 +179,8 @@
css_parser_(css_parser::Parser::Create()),
dom_parser_(new dom_parser::Parser(mock_error_callback_)),
fetcher_factory_(new loader::FetcherFactory(NULL)),
+ loader_factory_(new loader::LoaderFactory(
+ fetcher_factory_.get(), NULL, base::kThreadPriority_Default)),
local_storage_database_(NULL),
url_("about:blank"),
engine_(script::JavaScriptEngine::CreateEngine()),
@@ -184,8 +188,9 @@
on_screen_keyboard_bridge_(new OnScreenKeyboardMockBridge()),
window_(new Window(
1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
- dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL,
+ dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(),
+ NULL, NULL, NULL, NULL, NULL, NULL, &local_storage_database_, NULL,
+ NULL, NULL, NULL,
global_environment_
->script_value_factory() /* script_value_factory */,
NULL, NULL, url_, "", "en-US", "en",
@@ -200,7 +205,8 @@
on_screen_keyboard_bridge_.get(), NULL, NULL,
dom::Window::OnStartDispatchEventCallback(),
dom::Window::OnStopDispatchEventCallback(),
- dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {
+ dom::ScreenshotManager::ProvideScreenshotFunctionCallback(),
+ NULL)) {
global_environment_->CreateGlobalObject(window_,
environment_settings_.get());
on_screen_keyboard_bridge_->window_ = window_;
@@ -241,6 +247,7 @@
scoped_ptr<css_parser::Parser> css_parser_;
scoped_ptr<dom_parser::Parser> dom_parser_;
scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+ scoped_ptr<loader::LoaderFactory> loader_factory_;
dom::LocalStorageDatabase local_storage_database_;
GURL url_;
@@ -266,39 +273,40 @@
} // namespace
+#if SB_HAS(ON_SCREEN_KEYBOARD)
TEST_F(OnScreenKeyboardTest, ObjectExists) {
std::string result;
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard;", &result));
EXPECT_TRUE(bindings::testing::IsAcceptablePrototypeString("OnScreenKeyboard",
result));
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.show", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.show;", &result));
EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function show()",
result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.hide", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.hide;", &result));
EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function hide()",
result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.focus", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.focus;", &result));
EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function focus()",
result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.blur", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.blur;", &result));
EXPECT_PRED_FORMAT2(::testing::IsSubstring, "function blur()",
result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.keepFocus", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.keepFocus;", &result));
EXPECT_STREQ("false", result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.data", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.data;", &result));
EXPECT_STREQ("", result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onshow", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onshow;", &result));
EXPECT_STREQ("null", result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onhide", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onhide;", &result));
EXPECT_STREQ("null", result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onblur", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onblur;", &result));
EXPECT_STREQ("null", result.c_str());
- EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onfocus", &result));
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard.onfocus;", &result));
EXPECT_STREQ("null", result.c_str());
}
@@ -438,7 +446,7 @@
logString = 'show';
};
promise = window.onScreenKeyboard.show();
- logString
+ logString;
)";
for (int i = 0; i < 3; ++i) {
std::string result;
@@ -463,7 +471,7 @@
logString2 = 'show2';
});
let promise = window.onScreenKeyboard.show();
- logString1
+ logString1;
)";
EXPECT_TRUE(EvaluateScript(script, &result));
EXPECT_EQ("show1", result);
@@ -484,7 +492,7 @@
logString = 'hide';
};
promise = window.onScreenKeyboard.hide();
- logString
+ logString;
)";
for (int i = 0; i < 3; ++i) {
std::string result;
@@ -508,7 +516,7 @@
logString2 = 'hide2';
});
let promise = window.onScreenKeyboard.hide();
- logString1
+ logString1;
)";
EXPECT_TRUE(EvaluateScript(script, &result));
EXPECT_EQ("hide1", result);
@@ -529,7 +537,7 @@
logString = 'focus';
};
promise = window.onScreenKeyboard.focus();
- logString
+ logString;
)";
for (int i = 0; i < 3; ++i) {
std::string result;
@@ -553,7 +561,7 @@
logString2 = 'focus2';
});
let promise = window.onScreenKeyboard.focus();
- logString1
+ logString1;
)";
EXPECT_TRUE(EvaluateScript(script, &result));
EXPECT_EQ("focus1", result);
@@ -574,7 +582,7 @@
logString = 'blur';
};
promise = window.onScreenKeyboard.blur();
- logString
+ logString;
)";
for (int i = 0; i < 3; ++i) {
std::string result;
@@ -598,7 +606,7 @@
logString2 = 'blur2';
});
let promise = window.onScreenKeyboard.blur();
- logString1
+ logString1;
)";
EXPECT_TRUE(EvaluateScript(script, &result));
EXPECT_EQ("blur1", result);
@@ -634,6 +642,36 @@
)";
EXPECT_TRUE(EvaluateScript(script, NULL));
}
+#else // SB_HAS(ON_SCREEN_KEYBOARD)
+TEST_F(OnScreenKeyboardTest, ObjectDoesntExist) {
+ std::string result;
+
+ EXPECT_TRUE(
+ EvaluateScript("window.hasOwnProperty('onScreenKeyboard');", &result));
+ EXPECT_EQ("false", result);
+ EXPECT_TRUE(EvaluateScript("window.onScreenKeyboard;", &result));
+ EXPECT_FALSE(bindings::testing::IsAcceptablePrototypeString(
+ "OnScreenKeyboard", result));
+ EXPECT_TRUE(EvaluateScript("typeof window.onScreenKeyboard === 'undefined';",
+ &result));
+ EXPECT_EQ("true", result);
+
+ // We should be able to assign anything we like to window.onScreenKeyboard.
+ const char number_script[] = R"(
+ window.onScreenKeyboard = 42;
+ window.onScreenKeyboard;
+ )";
+ EXPECT_TRUE(EvaluateScript(number_script, &result));
+ EXPECT_EQ("42", result);
+
+ const char object_script[] = R"(
+ window.onScreenKeyboard = {foo: 'bar'};
+ window.onScreenKeyboard.hasOwnProperty('foo');
+ )";
+ EXPECT_TRUE(EvaluateScript(object_script, &result));
+ EXPECT_EQ("true", result);
+}
+#endif // SB_HAS(ON_SCREEN_KEYBOARD)
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/pointer_state.cc b/src/cobalt/dom/pointer_state.cc
index 6f41a71..83ba908 100644
--- a/src/cobalt/dom/pointer_state.cc
+++ b/src/cobalt/dom/pointer_state.cc
@@ -16,6 +16,7 @@
#include <algorithm>
+#include "base/debug/trace_event.h"
#include "cobalt/dom/mouse_event.h"
#include "cobalt/dom/pointer_event.h"
#include "cobalt/dom/wheel_event.h"
@@ -25,10 +26,11 @@
namespace dom {
void PointerState::QueuePointerEvent(const scoped_refptr<Event>& event) {
+ TRACE_EVENT1("cobalt::dom", "PointerState::QueuePointerEvent()", "event",
+ event->type().c_str());
+
// Only accept this for event types that are MouseEvents or known derivatives.
- SB_DCHECK(event->GetWrappableType() == base::GetTypeId<PointerEvent>() ||
- event->GetWrappableType() == base::GetTypeId<MouseEvent>() ||
- event->GetWrappableType() == base::GetTypeId<WheelEvent>());
+ SB_DCHECK(CanQueueEvent(event));
// Queue the event to be handled on the next layout.
pointer_events_.push(event);
@@ -259,5 +261,12 @@
pointers_with_active_buttons_.clear();
}
+// static
+bool PointerState::CanQueueEvent(const scoped_refptr<Event>& event) {
+ return event->GetWrappableType() == base::GetTypeId<PointerEvent>() ||
+ event->GetWrappableType() == base::GetTypeId<MouseEvent>() ||
+ event->GetWrappableType() == base::GetTypeId<WheelEvent>();
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/pointer_state.h b/src/cobalt/dom/pointer_state.h
index 0f73a29..d1c7d9c 100644
--- a/src/cobalt/dom/pointer_state.h
+++ b/src/cobalt/dom/pointer_state.h
@@ -77,6 +77,8 @@
// shutdown.
void ClearForShutdown();
+ static bool CanQueueEvent(const scoped_refptr<Event>& event);
+
private:
// Stores pointer events until they are handled after a layout.
std::queue<scoped_refptr<Event> > pointer_events_;
diff --git a/src/cobalt/dom/pseudo_element.cc b/src/cobalt/dom/pseudo_element.cc
index cb54916..ffbbe58 100644
--- a/src/cobalt/dom/pseudo_element.cc
+++ b/src/cobalt/dom/pseudo_element.cc
@@ -22,8 +22,8 @@
PseudoElement::PseudoElement(HTMLElement* parent_element)
: parent_element_(parent_element),
animations_(new web_animations::AnimationSet()),
- css_computed_style_declaration_(
- new cssom::CSSComputedStyleDeclaration()) {
+ css_computed_style_declaration_(new cssom::CSSComputedStyleDeclaration()),
+ computed_style_invalid_(true) {
DCHECK(parent_element_);
css_computed_style_declaration_->set_animations(animations_);
diff --git a/src/cobalt/dom/pseudo_element.h b/src/cobalt/dom/pseudo_element.h
index 9123f75..cebe07e 100644
--- a/src/cobalt/dom/pseudo_element.h
+++ b/src/cobalt/dom/pseudo_element.h
@@ -60,6 +60,11 @@
cssom::RulesWithCascadePrecedence* matching_rules() {
return &matching_rules_;
}
+ void ClearMatchingRules() { matching_rules_.clear(); }
+
+ bool computed_style_invalid() const { return computed_style_invalid_; }
+ void set_computed_style_invalid() { computed_style_invalid_ = true; }
+ void clear_computed_style_invalid() { computed_style_invalid_ = false; }
cssom::TransitionSet* css_transitions() { return &css_transitions_.value(); }
cssom::AnimationSet* css_animations() { return &css_animations_.value(); }
@@ -69,7 +74,6 @@
}
HTMLElement* parent_element() { return parent_element_; }
- void ClearMatchingRules() { matching_rules_.clear(); }
void set_layout_boxes(scoped_ptr<LayoutBoxes> layout_boxes) {
layout_boxes_ = layout_boxes.Pass();
@@ -91,6 +95,7 @@
base::optional<cssom::AnimationSet> css_animations_;
cssom::RulesWithCascadePrecedence matching_rules_;
+ bool computed_style_invalid_;
// This contains information about the boxes generated from the element.
scoped_ptr<LayoutBoxes> layout_boxes_;
diff --git a/src/cobalt/dom/rule_matching.cc b/src/cobalt/dom/rule_matching.cc
index f764718..41ca71e 100644
--- a/src/cobalt/dom/rule_matching.cc
+++ b/src/cobalt/dom/rule_matching.cc
@@ -64,7 +64,15 @@
namespace {
using cssom::SelectorTree;
-const size_t kRuleMatchingNodeSetSize = 60;
+
+struct NodeCombinatorType {
+ NodeCombinatorType(const SelectorTree::Node* node,
+ cssom::CombinatorType combinator_type)
+ : node(node), combinator_type(combinator_type) {}
+
+ const SelectorTree::Node* node;
+ cssom::CombinatorType combinator_type;
+};
//////////////////////////////////////////////////////////////////////////
// Helper functions
@@ -345,226 +353,6 @@
return selector_matcher.element();
}
-void AddRulesOnNodeToElement(HTMLElement* element,
- const SelectorTree::Node* node) {
- cssom::PseudoElement* pseudo_element =
- node->compound_selector()->pseudo_element();
-
- // Where to add matching rules.
- cssom::RulesWithCascadePrecedence* target_matching_rules;
-
- if (!pseudo_element) {
- target_matching_rules = element->matching_rules();
- } else {
- PseudoElementType pseudo_element_type = kNotPseudoElementType;
-
- if (pseudo_element->AsAfterPseudoElement()) {
- pseudo_element_type = kAfterPseudoElementType;
- } else if (pseudo_element->AsBeforePseudoElement()) {
- pseudo_element_type = kBeforePseudoElementType;
- } else {
- NOTREACHED();
- }
-
- // Make sure the pseudo element exists under element.
- if (!element->pseudo_element(pseudo_element_type)) {
- element->set_pseudo_element(
- pseudo_element_type,
- make_scoped_ptr(new dom::PseudoElement(element)));
- }
-
- target_matching_rules =
- element->pseudo_element(pseudo_element_type)->matching_rules();
- }
-
- element->rule_matching_state()->matching_nodes.insert(node);
-
- for (SelectorTree::Rules::const_iterator rule_iterator =
- node->rules().begin();
- rule_iterator != node->rules().end(); ++rule_iterator) {
- cssom::CSSStyleRule* rule = *rule_iterator;
- if (!rule) {
- continue;
- }
- DCHECK(rule->parent_style_sheet());
- cssom::CascadePrecedence precedence(
- pseudo_element ? cssom::kNormalOverride
- : rule->parent_style_sheet()->origin(),
- node->cumulative_specificity(),
- cssom::Appearance(rule->parent_style_sheet()->index(), rule->index()));
- target_matching_rules->push_back(std::make_pair(rule, precedence));
- }
-}
-
-void GatherCandidateNodesFromMap(
- cssom::SimpleSelectorType simple_selector_type,
- cssom::CombinatorType combinator_type,
- const SelectorTree::SelectorTextToNodesMap* map, base::Token key,
- SelectorTree::NodeSet<kRuleMatchingNodeSetSize>* candidate_nodes) {
- SelectorTree::SelectorTextToNodesMap::const_iterator it = map->find(key);
- if (it != map->end()) {
- const SelectorTree::SimpleSelectorNodes& nodes = it->second;
- for (SelectorTree::SimpleSelectorNodes::const_iterator nodes_iterator =
- nodes.begin();
- nodes_iterator != nodes.end(); ++nodes_iterator) {
- if (nodes_iterator->simple_selector_type == simple_selector_type &&
- nodes_iterator->combinator_type == combinator_type) {
- candidate_nodes->insert(nodes_iterator->node);
- }
- }
- }
-}
-
-void GatherCandidateNodesFromSet(
- cssom::PseudoClassType pseudo_class_type,
- cssom::CombinatorType combinator_type,
- const SelectorTree::PseudoClassNodes& pseudo_class_nodes,
- SelectorTree::NodeSet<kRuleMatchingNodeSetSize>* candidate_nodes) {
- for (SelectorTree::PseudoClassNodes::const_iterator nodes_iterator =
- pseudo_class_nodes.begin();
- nodes_iterator != pseudo_class_nodes.end(); ++nodes_iterator) {
- if (nodes_iterator->pseudo_class_type == pseudo_class_type &&
- nodes_iterator->combinator_type == combinator_type) {
- candidate_nodes->insert(nodes_iterator->node);
- }
- }
-}
-
-template <class NodeSet>
-void ForEachChildOnNodes(
- const NodeSet& node_set, cssom::CombinatorType combinator_type,
- HTMLElement* element,
- const base::Callback<void(HTMLElement* element, const SelectorTree::Node*)>&
- callback) {
- // Gathering Phase: Generate candidate nodes from the node set.
- SelectorTree::NodeSet<kRuleMatchingNodeSetSize> candidate_nodes;
-
- const std::vector<base::Token>& element_class_list =
- element->class_list()->GetTokens();
-
- // Don't retrieve the element's attributes until they are needed. Retrieving
- // them typically requires the creation of a NamedNodeMap.
- scoped_refptr<NamedNodeMap> element_attributes;
-
- // Iterate through all nodes in node_set.
- for (typename NodeSet::const_iterator node_iterator = node_set.begin();
- node_iterator != node_set.end(); ++node_iterator) {
- const SelectorTree::Node* node = *node_iterator;
-
- if (!node->HasCombinator(combinator_type)) {
- continue;
- }
-
- // Gather candidate sets in node's children under the given combinator.
-
- const SelectorTree::SelectorTextToNodesMap* selector_nodes_map =
- node->selector_nodes_map();
- if (selector_nodes_map) {
- // Universal selector.
- if (node->HasSimpleSelector(cssom::kUniversalSelector, combinator_type)) {
- GatherCandidateNodesFromMap(cssom::kUniversalSelector, combinator_type,
- selector_nodes_map, base::Token(),
- &candidate_nodes);
- }
-
- // Type selector.
- if (node->HasSimpleSelector(cssom::kTypeSelector, combinator_type)) {
- GatherCandidateNodesFromMap(cssom::kTypeSelector, combinator_type,
- selector_nodes_map, element->local_name(),
- &candidate_nodes);
- }
-
- // Attribute selector.
- if (node->HasSimpleSelector(cssom::kAttributeSelector, combinator_type)) {
- // If the element's attributes have not been retrieved yet, then
- // retrieve them now.
- if (!element_attributes) {
- element_attributes = element->attributes();
- }
-
- for (unsigned int index = 0; index < element_attributes->length();
- ++index) {
- GatherCandidateNodesFromMap(
- cssom::kAttributeSelector, combinator_type, selector_nodes_map,
- base::Token(element_attributes->Item(index)->name()),
- &candidate_nodes);
- }
- }
-
- // Class selector.
- if (node->HasSimpleSelector(cssom::kClassSelector, combinator_type)) {
- for (size_t index = 0; index < element_class_list.size(); ++index) {
- GatherCandidateNodesFromMap(
- cssom::kClassSelector, combinator_type, selector_nodes_map,
- element_class_list[index], &candidate_nodes);
- }
- }
-
- // Id selector.
- if (node->HasSimpleSelector(cssom::kIdSelector, combinator_type)) {
- GatherCandidateNodesFromMap(cssom::kIdSelector, combinator_type,
- selector_nodes_map, element->id(),
- &candidate_nodes);
- }
- }
-
- if (node->HasAnyPseudoClass()) {
- // Empty pseudo class.
- if (node->HasPseudoClass(cssom::kEmptyPseudoClass, combinator_type) &&
- element->IsEmpty()) {
- GatherCandidateNodesFromSet(cssom::kEmptyPseudoClass, combinator_type,
- node->pseudo_class_nodes(),
- &candidate_nodes);
- }
-
- // Focus pseudo class.
- if (node->HasPseudoClass(cssom::kFocusPseudoClass, combinator_type) &&
- element->HasFocus()) {
- GatherCandidateNodesFromSet(cssom::kFocusPseudoClass, combinator_type,
- node->pseudo_class_nodes(),
- &candidate_nodes);
- }
-
- // Hover pseudo class.
- if (node->HasPseudoClass(cssom::kHoverPseudoClass, combinator_type) &&
- element->IsDesignated()) {
- GatherCandidateNodesFromSet(cssom::kHoverPseudoClass, combinator_type,
- node->pseudo_class_nodes(),
- &candidate_nodes);
- }
-
- // Not pseudo class.
- if (node->HasPseudoClass(cssom::kNotPseudoClass, combinator_type)) {
- GatherCandidateNodesFromSet(cssom::kNotPseudoClass, combinator_type,
- node->pseudo_class_nodes(),
- &candidate_nodes);
- }
- }
- }
-
- // Verifying Phase: Check all candidate nodes and run callback for matching
- // nodes.
- for (SelectorTree::NodeSet<kRuleMatchingNodeSetSize>::const_iterator
- candidate_node_iterator = candidate_nodes.begin();
- candidate_node_iterator != candidate_nodes.end();
- ++candidate_node_iterator) {
- const SelectorTree::Node* candidate_node = *candidate_node_iterator;
-
- // Verify that this node is a match:
- // 1. If the candidate node's compound selector doesn't require a visit to
- // verify the match, then the act of gathering the node as a candidate
- // proved the match.
- // 2. Otherwise, the node requires additional verification checks, so call
- // MatchSelectorAndElement().
- if (!candidate_node->compound_selector()
- ->requires_rule_matching_verification_visit() ||
- MatchSelectorAndElement(candidate_node->compound_selector(), element,
- false)) {
- callback.Run(element, candidate_node);
- }
- }
-}
-
bool MatchRuleAndElement(cssom::CSSStyleRule* rule, Element* element) {
for (cssom::Selectors::const_iterator selector_iterator =
rule->selectors().begin();
@@ -577,114 +365,562 @@
return false;
}
+void GatherCandidateNodesFromSelectorNodesMap(
+ cssom::SimpleSelectorType simple_selector_type,
+ cssom::CombinatorType combinator_type,
+ const SelectorTree::Node* parent_node,
+ const SelectorTree::SelectorTextToNodesMap* map, base::Token key,
+ SelectorTree::NodePairs* candidate_nodes) {
+ SelectorTree::SelectorTextToNodesMap::const_iterator it = map->find(key);
+ if (it != map->end()) {
+ const SelectorTree::SimpleSelectorNodes& nodes = it->second;
+ for (const auto& nodes_iterator : nodes) {
+ if (nodes_iterator.simple_selector_type == simple_selector_type &&
+ nodes_iterator.combinator_type == combinator_type) {
+ candidate_nodes->emplace_back(parent_node, nodes_iterator.node);
+ }
+ }
+ }
+}
+
+void GatherCandidateNodesFromPseudoClassNodes(
+ cssom::PseudoClassType pseudo_class_type,
+ cssom::CombinatorType combinator_type,
+ const SelectorTree::Node* parent_node,
+ const SelectorTree::PseudoClassNodes& pseudo_class_nodes,
+ SelectorTree::NodePairs* candidate_nodes) {
+ for (const auto& nodes_iterator : pseudo_class_nodes) {
+ if (nodes_iterator.pseudo_class_type == pseudo_class_type &&
+ nodes_iterator.combinator_type == combinator_type) {
+ candidate_nodes->emplace_back(parent_node, nodes_iterator.node);
+ }
+ }
+}
+
+bool GatherNodeModificationsAndUpdateTargetNodes(
+ const SelectorTree::Nodes& source_nodes, SelectorTree::Nodes* target_nodes,
+ SelectorTree::Nodes* added_nodes, SelectorTree::Nodes* removed_nodes) {
+ if (source_nodes == *target_nodes) {
+ return false;
+ }
+
+ added_nodes->clear();
+ removed_nodes->clear();
+
+ if (target_nodes->empty()) {
+ // If the previous state of the nodes (|target_nodes|) is empty, then all
+ // nodes in the new state (|source_nodes|) are being added.
+ *added_nodes = source_nodes;
+ } else if (source_nodes.empty()) {
+ // If the new state (|source_nodes|) is empty, then all nodes in the
+ // previous state (|target_nodes|) are being removed.
+ *removed_nodes = *target_nodes;
+ } else {
+ // If neither the previous state nor the new state or empty, then the lists
+ // must be manually searched to determine what is being added and removed.
+ // Remove all nodes in |source_nodes| from |target_nodes|. Any of the nodes
+ // that are not found in |target_nodes| are newly added nodes.
+ size_t start_index = 0;
+ for (const auto& check_node : source_nodes) {
+ bool node_found = false;
+ for (size_t index = start_index; index < target_nodes->size(); ++index) {
+ if ((*target_nodes)[index] == check_node) {
+ if (index == start_index) {
+ ++start_index;
+ }
+ node_found = true;
+ (*target_nodes)[index] = NULL;
+ break;
+ }
+ }
+ if (!node_found) {
+ added_nodes->push_back(check_node);
+ }
+ }
+ // Find nodes that remain in |target_nodes|. These did not exist in
+ // |source_nodes| and are newly removed nodes.
+ for (const auto& check_node : *target_nodes) {
+ if (check_node != NULL) {
+ removed_nodes->push_back(check_node);
+ }
+ }
+ }
+
+ *target_nodes = source_nodes;
+
+ // Return whether or not any modifications were found.
+ return !added_nodes->empty() || !removed_nodes->empty();
+}
+
+// Returns true if a matching node was added.
+bool GatherMatchingNodes(const SelectorTree::Nodes& nodes,
+ cssom::CombinatorType combinator_type,
+ HTMLElement* element,
+ SelectorTree::NodePairs* scratchpad_node_pairs) {
+ bool matching_node_added = false;
+
+ // Gathering Phase: Generate candidate nodes from the nodes.
+ SelectorTree::NodePairs* candidate_node_pairs = scratchpad_node_pairs;
+ candidate_node_pairs->clear();
+
+ const std::vector<base::Token>& element_class_list =
+ element->class_list()->GetTokens();
+
+ // Don't retrieve the element's attributes until they are needed. Retrieving
+ // them typically requires the creation of a NamedNodeMap.
+ scoped_refptr<NamedNodeMap> element_attributes;
+
+ // Iterate through all nodes.
+ for (const auto& node : nodes) {
+ if (!node->HasCombinator(combinator_type)) {
+ continue;
+ }
+
+ // Gather candidate nodes in node's children under the given combinator.
+ const SelectorTree::SelectorTextToNodesMap* selector_nodes_map =
+ node->selector_nodes_map();
+ if (selector_nodes_map) {
+ // Universal selector.
+ if (node->HasSimpleSelector(cssom::kUniversalSelector, combinator_type)) {
+ GatherCandidateNodesFromSelectorNodesMap(
+ cssom::kUniversalSelector, combinator_type, node,
+ selector_nodes_map, base::Token(), candidate_node_pairs);
+ }
+
+ // Type selector.
+ if (node->HasSimpleSelector(cssom::kTypeSelector, combinator_type)) {
+ GatherCandidateNodesFromSelectorNodesMap(
+ cssom::kTypeSelector, combinator_type, node, selector_nodes_map,
+ element->local_name(), candidate_node_pairs);
+ }
+
+ // Attribute selector.
+ if (node->HasSimpleSelector(cssom::kAttributeSelector, combinator_type)) {
+ // If the element's attributes have not been retrieved yet, then
+ // retrieve them now.
+ if (!element_attributes) {
+ element_attributes = element->attributes();
+ }
+
+ for (unsigned int index = 0; index < element_attributes->length();
+ ++index) {
+ GatherCandidateNodesFromSelectorNodesMap(
+ cssom::kAttributeSelector, combinator_type, node,
+ selector_nodes_map,
+ base::Token(element_attributes->Item(index)->name()),
+ candidate_node_pairs);
+ }
+ }
+
+ // Class selector.
+ if (node->HasSimpleSelector(cssom::kClassSelector, combinator_type)) {
+ for (const auto& element_class : element_class_list) {
+ GatherCandidateNodesFromSelectorNodesMap(
+ cssom::kClassSelector, combinator_type, node, selector_nodes_map,
+ element_class, candidate_node_pairs);
+ }
+ }
+
+ // Id selector.
+ if (node->HasSimpleSelector(cssom::kIdSelector, combinator_type)) {
+ GatherCandidateNodesFromSelectorNodesMap(
+ cssom::kIdSelector, combinator_type, node, selector_nodes_map,
+ element->id(), candidate_node_pairs);
+ }
+ }
+
+ // Only check for specific pseudo classes when the node has as least one.
+ if (node->HasAnyPseudoClass()) {
+ // Empty pseudo class.
+ if (node->HasPseudoClass(cssom::kEmptyPseudoClass, combinator_type) &&
+ element->IsEmpty()) {
+ GatherCandidateNodesFromPseudoClassNodes(
+ cssom::kEmptyPseudoClass, combinator_type, node,
+ node->pseudo_class_nodes(), candidate_node_pairs);
+ }
+
+ // Focus pseudo class.
+ if (node->HasPseudoClass(cssom::kFocusPseudoClass, combinator_type) &&
+ element->HasFocus()) {
+ GatherCandidateNodesFromPseudoClassNodes(
+ cssom::kFocusPseudoClass, combinator_type, node,
+ node->pseudo_class_nodes(), candidate_node_pairs);
+ }
+
+ // Hover pseudo class.
+ if (node->HasPseudoClass(cssom::kHoverPseudoClass, combinator_type) &&
+ element->IsDesignated()) {
+ GatherCandidateNodesFromPseudoClassNodes(
+ cssom::kHoverPseudoClass, combinator_type, node,
+ node->pseudo_class_nodes(), candidate_node_pairs);
+ }
+
+ // Not pseudo class.
+ if (node->HasPseudoClass(cssom::kNotPseudoClass, combinator_type)) {
+ GatherCandidateNodesFromPseudoClassNodes(
+ cssom::kNotPseudoClass, combinator_type, node,
+ node->pseudo_class_nodes(), candidate_node_pairs);
+ }
+ }
+ }
+
+ // Verifying Phase: Check all candidate nodes and run callback for matching
+ // nodes.
+ for (const auto& candidate_node_pair : *candidate_node_pairs) {
+ const SelectorTree::Node* candidate_node = candidate_node_pair.second;
+
+ // Verify that this node is a match:
+ // 1. If the candidate node's compound selector doesn't require a visit to
+ // verify the match, then the act of gathering the node as a candidate
+ // proved the match.
+ // 2. Otherwise, the node requires additional verification checks, so call
+ // MatchSelectorAndElement().
+ if (!candidate_node->compound_selector()
+ ->requires_rule_matching_verification_visit() ||
+ MatchSelectorAndElement(candidate_node->compound_selector(), element,
+ false)) {
+ matching_node_added = true;
+ element->rule_matching_state()->matching_nodes_parent_nodes.push_back(
+ candidate_node_pair.first);
+ element->rule_matching_state()->matching_nodes.push_back(candidate_node);
+ }
+ }
+
+ return matching_node_added;
+}
+
+// Returns true if a matching node was removed.
+bool RemoveNodesFromMatchingNodes(
+ const SelectorTree::Nodes& parent_nodes_to_remove,
+ SelectorTree::Nodes* parent_nodes, SelectorTree::Nodes* matching_nodes) {
+ DCHECK_EQ(parent_nodes->size(), matching_nodes->size());
+ bool node_removed = false;
+
+ // Find any |parent_nodes| that are being removed. Remove these from both
+ // |parent_nodes| and |matching_nodes|, as these two containers are kept in
+ // sync.
+ for (const auto& parent_node_to_remove : parent_nodes_to_remove) {
+ for (size_t index = 0; index < parent_nodes->size(); ++index) {
+ if (parent_node_to_remove == (*parent_nodes)[index]) {
+ node_removed = true;
+ parent_nodes->erase(parent_nodes->begin() + index);
+ matching_nodes->erase(matching_nodes->begin() + index);
+ break;
+ }
+ }
+ }
+
+ return node_removed;
+}
+
+// Returns true if the matching nodes were modified.
+bool UpdateMatchingNodesFromNodeModifications(
+ const SelectorTree::Nodes& added_nodes,
+ const SelectorTree::Nodes& removed_nodes,
+ cssom::CombinatorType combinator_type, HTMLElement* element,
+ SelectorTree::NodePairs* scratchpad_node_pairs) {
+ // First gather the matching nodes found in |added_nodes|.
+ bool matching_nodes_modified = GatherMatchingNodes(
+ added_nodes, combinator_type, element, scratchpad_node_pairs);
+
+ // Now remove any matching nodes found in |removed_nodes|.
+ HTMLElement::RuleMatchingState* element_matching_state =
+ element->rule_matching_state();
+ matching_nodes_modified |= RemoveNodesFromMatchingNodes(
+ removed_nodes, &element_matching_state->matching_nodes_parent_nodes,
+ &element_matching_state->matching_nodes);
+
+ return matching_nodes_modified;
+}
+
+void UpdateRuleMatchingStateCombinatorNodes(
+ HTMLElement::RuleMatchingState* matching_state,
+ cssom::CombinatorType combinator_type) {
+ // Only descendant and following sibling combinators are supported.
+ DCHECK(combinator_type == cssom::kDescendantCombinator ||
+ combinator_type == cssom::kFollowingSiblingCombinator);
+ if (!matching_state) {
+ return;
+ }
+
+ DCHECK(matching_state->is_set);
+ bool& is_dirty = combinator_type == cssom::kDescendantCombinator
+ ? matching_state->are_descendant_nodes_dirty
+ : matching_state->are_following_sibling_nodes_dirty;
+ if (!is_dirty) {
+ return;
+ }
+
+ const SelectorTree::Nodes& base_combinator_nodes =
+ combinator_type == cssom::kDescendantCombinator
+ ? matching_state->parent_descendant_nodes
+ : matching_state->previous_sibling_following_sibling_nodes;
+ SelectorTree::Nodes& combinator_nodes =
+ combinator_type == cssom::kDescendantCombinator
+ ? matching_state->descendant_nodes
+ : matching_state->following_sibling_nodes;
+
+ // First copy the base combinator nodes and then add any matching nodes that
+ // have the required combinator and are not already included within the list.
+ combinator_nodes = base_combinator_nodes;
+ for (const auto& node : matching_state->matching_nodes) {
+ if (node->HasCombinator(combinator_type) &&
+ std::find(base_combinator_nodes.begin(), base_combinator_nodes.end(),
+ node) == base_combinator_nodes.end()) {
+ combinator_nodes.push_back(node);
+ }
+ }
+
+ is_dirty = false;
+}
+
+// Returns true if the matching state was modified.
+bool UpdateElementRuleMatchingState(HTMLElement* element) {
+ SelectorTree* selector_tree = element->node_document()->selector_tree();
+ bool sibling_matching_active = selector_tree->has_sibling_combinators();
+
+ // Retrieve the parent and previous sibling of the current element.
+ HTMLElement* parent = element->parent_element()
+ ? element->parent_element()->AsHTMLElement()
+ : NULL;
+ HTMLElement* previous_sibling =
+ sibling_matching_active && element->previous_element_sibling()
+ ? element->previous_element_sibling()->AsHTMLElement()
+ : NULL;
+
+ // Retrieve the rule matching state of this element, its parent, and its
+ // previous sibling.
+ HTMLElement::RuleMatchingState* element_matching_state =
+ element->rule_matching_state();
+ HTMLElement::RuleMatchingState* parent_matching_state =
+ parent ? parent->rule_matching_state() : NULL;
+ HTMLElement::RuleMatchingState* previous_sibling_matching_state =
+ previous_sibling ? previous_sibling->rule_matching_state() : NULL;
+
+ // A parent must always have valid matching rules when its child is being
+ // updated.
+ DCHECK(!parent || parent->matching_rules_valid());
+
+ // Ensure that the previous sibling has updated matching rules. This is
+ // necessary because Document::UpdateComputedStyleOnElementAndAncestor() does
+ // not update previous siblings so it is possible for an element to have its
+ // computed style updated while previous siblings have invalid matching rules.
+ if (previous_sibling) {
+ UpdateElementMatchingRules(previous_sibling);
+ DCHECK(previous_sibling->matching_rules_valid());
+ }
+
+ // Ensure that the required combinator nodes of the parent and previous
+ // sibling are up to date. These are lazily updated, so they won't be updated
+ // until the first child/next sibling needs them.
+ UpdateRuleMatchingStateCombinatorNodes(parent_matching_state,
+ cssom::kDescendantCombinator);
+ UpdateRuleMatchingStateCombinatorNodes(previous_sibling_matching_state,
+ cssom::kFollowingSiblingCombinator);
+
+ bool matching_nodes_modified = !element_matching_state->is_set;
+
+ // Gather modifications between the element's previous state and current state
+ // for each combinator type, and use them to update the element's matching
+ // nodes.
+ SelectorTree::Nodes* added_nodes = selector_tree->scratchpad_nodes_1();
+ SelectorTree::Nodes* removed_nodes = selector_tree->scratchpad_nodes_2();
+
+ // Child combinator
+ if (parent_matching_state &&
+ GatherNodeModificationsAndUpdateTargetNodes(
+ parent_matching_state->matching_nodes,
+ &element_matching_state->parent_matching_nodes, added_nodes,
+ removed_nodes)) {
+ matching_nodes_modified |= UpdateMatchingNodesFromNodeModifications(
+ *added_nodes, *removed_nodes, cssom::kChildCombinator, element,
+ selector_tree->scratchpad_node_pairs());
+ }
+
+ // Descendant combinator
+ const SelectorTree::Nodes& parent_descendant_nodes =
+ parent_matching_state ? parent_matching_state->descendant_nodes
+ : selector_tree->root_nodes();
+ if (GatherNodeModificationsAndUpdateTargetNodes(
+ parent_descendant_nodes,
+ &element_matching_state->parent_descendant_nodes, added_nodes,
+ removed_nodes)) {
+ element_matching_state->are_descendant_nodes_dirty = true;
+ matching_nodes_modified |= UpdateMatchingNodesFromNodeModifications(
+ *added_nodes, *removed_nodes, cssom::kDescendantCombinator, element,
+ selector_tree->scratchpad_node_pairs());
+ }
+
+ // Siblings only need to be checked if they're active.
+ if (sibling_matching_active) {
+ const SelectorTree::Nodes empty_nodes;
+
+ // Next sibling combinator
+ const SelectorTree::Nodes& previous_sibling_matching_nodes =
+ previous_sibling_matching_state
+ ? previous_sibling_matching_state->matching_nodes
+ : empty_nodes;
+ if (GatherNodeModificationsAndUpdateTargetNodes(
+ previous_sibling_matching_nodes,
+ &element_matching_state->previous_sibling_matching_nodes,
+ added_nodes, removed_nodes)) {
+ matching_nodes_modified |= UpdateMatchingNodesFromNodeModifications(
+ *added_nodes, *removed_nodes, cssom::kNextSiblingCombinator, element,
+ selector_tree->scratchpad_node_pairs());
+ }
+
+ // Following sibling combinator
+ const SelectorTree::Nodes& previous_sibling_following_sibling_nodes =
+ previous_sibling_matching_state
+ ? previous_sibling_matching_state->following_sibling_nodes
+ : empty_nodes;
+ if (GatherNodeModificationsAndUpdateTargetNodes(
+ previous_sibling_following_sibling_nodes,
+ &element_matching_state->previous_sibling_following_sibling_nodes,
+ added_nodes, removed_nodes)) {
+ element_matching_state->are_following_sibling_nodes_dirty = true;
+ matching_nodes_modified |= UpdateMatchingNodesFromNodeModifications(
+ *added_nodes, *removed_nodes, cssom::kFollowingSiblingCombinator,
+ element, selector_tree->scratchpad_node_pairs());
+ }
+ }
+
+ element_matching_state->is_set = true;
+
+ // Matching node modifications may impact combinator nodes, so they must be
+ // dirtied. However, they are lazily updated the first time that they are
+ // needed by a child/following sibling, rather than immediately updated. This
+ // prevents unnecessary updates of leaf elements and last sibling elements.
+ if (matching_nodes_modified) {
+ element_matching_state->are_descendant_nodes_dirty = true;
+ element_matching_state->are_following_sibling_nodes_dirty = true;
+ }
+
+ return matching_nodes_modified;
+}
+
+void UpdateElementMatchingRulesFromRuleMatchingState(HTMLElement* element) {
+ Document* element_document = element->node_document();
+
+ // Store the element's and its pseudo element's old matching rules so that
+ // they can be compared to the new matching rules after they are generated.
+ cssom::RulesWithCascadePrecedence* element_old_matching_rules =
+ element_document->scratchpad_html_element_matching_rules();
+ *element_old_matching_rules = *element->matching_rules();
+ element->matching_rules()->clear();
+
+ for (int type = 0; type < kMaxPseudoElementType; ++type) {
+ PseudoElement* pseudo_element =
+ element->pseudo_element(PseudoElementType(type));
+ if (pseudo_element) {
+ cssom::RulesWithCascadePrecedence* old_pseudo_element_matching_rules =
+ element_document->scratchpad_pseudo_element_matching_rules(
+ PseudoElementType(type));
+ *old_pseudo_element_matching_rules = *pseudo_element->matching_rules();
+ pseudo_element->ClearMatchingRules();
+ }
+ }
+
+ // Walk all matching nodes, generating matching rules for the element and
+ // its pseudo elements from them.
+ for (const auto& node : element->rule_matching_state()->matching_nodes) {
+ // Determine the matching rules that this specific node targets. If the
+ // node does not have a pseudo element, then this will be the element's
+ // matching rules; otherwise, it'll be the matching rules associated with
+ // the pseudo element's type.
+ cssom::RulesWithCascadePrecedence* target_matching_rules;
+
+ cssom::PseudoElement* pseudo_element =
+ node->compound_selector()->pseudo_element();
+ if (!pseudo_element) {
+ target_matching_rules = element->matching_rules();
+ } else {
+ PseudoElementType pseudo_element_type = kNotPseudoElementType;
+
+ if (pseudo_element->AsAfterPseudoElement()) {
+ pseudo_element_type = kAfterPseudoElementType;
+ } else if (pseudo_element->AsBeforePseudoElement()) {
+ pseudo_element_type = kBeforePseudoElementType;
+ } else {
+ NOTREACHED();
+ }
+
+ // Make sure the pseudo element exists under element. If not, create it
+ // and clear out the old matching rules since the pseudo element had no
+ // pre-existing matching rules.
+ if (!element->pseudo_element(pseudo_element_type)) {
+ element->SetPseudoElement(
+ pseudo_element_type,
+ make_scoped_ptr(new dom::PseudoElement(element)));
+
+ cssom::RulesWithCascadePrecedence* old_pseudo_element_matching_rules =
+ element_document->scratchpad_pseudo_element_matching_rules(
+ PseudoElementType(pseudo_element_type));
+ old_pseudo_element_matching_rules->clear();
+ }
+
+ target_matching_rules =
+ element->pseudo_element(pseudo_element_type)->matching_rules();
+ }
+
+ // Add all of the node's rules to the target matching rules.
+ for (const auto& weak_rule : node->rules()) {
+ const auto rule = weak_rule.get();
+ if (!rule) {
+ continue;
+ }
+ DCHECK(rule->parent_style_sheet());
+ cssom::CascadePrecedence precedence(
+ pseudo_element ? cssom::kNormalOverride
+ : rule->parent_style_sheet()->origin(),
+ node->cumulative_specificity(),
+ cssom::Appearance(rule->parent_style_sheet()->index(),
+ rule->index()));
+ target_matching_rules->push_back(std::make_pair(rule, precedence));
+ }
+ }
+
+ // Check for if the newly generated matching rules are different for the
+ // element or its pseudo elements.
+ if (*element_old_matching_rules != *element->matching_rules()) {
+ element->OnMatchingRulesModified();
+ }
+
+ for (int type = 0; type < kMaxPseudoElementType; ++type) {
+ PseudoElement* pseudo_element =
+ element->pseudo_element(PseudoElementType(type));
+ if (pseudo_element) {
+ cssom::RulesWithCascadePrecedence* old_pseudo_element_matching_rules =
+ element_document->scratchpad_pseudo_element_matching_rules(
+ PseudoElementType(type));
+ if (*old_pseudo_element_matching_rules !=
+ *pseudo_element->matching_rules()) {
+ pseudo_element->set_computed_style_invalid();
+ element->OnPseudoElementMatchingRulesModified();
+ }
+ }
+ }
+}
+
} // namespace
//////////////////////////////////////////////////////////////////////////
// Public APIs
//////////////////////////////////////////////////////////////////////////
-void UpdateMatchingRules(HTMLElement* current_element) {
- DCHECK(current_element);
- DCHECK(current_element->node_document());
- SelectorTree* selector_tree =
- current_element->node_document()->selector_tree();
-
- // Get parent and previous sibling of the current element.
- HTMLElement* parent = current_element->parent_element()
- ? current_element->parent_element()->AsHTMLElement()
- : NULL;
- HTMLElement* previous_sibling =
- current_element->previous_element_sibling()
- ? current_element->previous_element_sibling()->AsHTMLElement()
- : NULL;
-
- // Calculate current element's matching nodes.
-
- if (parent) {
- // Child combinator.
- ForEachChildOnNodes(parent->rule_matching_state()->matching_nodes,
- cssom::kChildCombinator, current_element,
- base::Bind(&AddRulesOnNodeToElement));
-
- // Descendant combinator.
- ForEachChildOnNodes(
- parent->rule_matching_state()->descendant_potential_nodes,
- cssom::kDescendantCombinator, current_element,
- base::Bind(&AddRulesOnNodeToElement));
- } else {
- // Descendant combinator from root.
- ForEachChildOnNodes(selector_tree->root_set(), cssom::kDescendantCombinator,
- current_element, base::Bind(&AddRulesOnNodeToElement));
+void UpdateElementMatchingRules(HTMLElement* element) {
+ DCHECK(element);
+ DCHECK(element->node_document());
+ if (element->matching_rules_valid()) {
+ return;
}
- if (selector_tree->has_sibling_combinators()) {
- if (previous_sibling) {
- // Next sibling combinator.
- ForEachChildOnNodes(
- previous_sibling->rule_matching_state()->matching_nodes,
- cssom::kNextSiblingCombinator, current_element,
- base::Bind(&AddRulesOnNodeToElement));
-
- // Following sibling combinator.
- ForEachChildOnNodes(previous_sibling->rule_matching_state()
- ->following_sibling_potential_nodes,
- cssom::kFollowingSiblingCombinator, current_element,
- base::Bind(&AddRulesOnNodeToElement));
- }
+ // Only update the matching rules if the rule matching state is modified.
+ if (UpdateElementRuleMatchingState(element)) {
+ UpdateElementMatchingRulesFromRuleMatchingState(element);
}
- // Calculate current element's descendant potential nodes.
- if (parent) {
- current_element->rule_matching_state()->descendant_potential_nodes.insert(
- parent->rule_matching_state()->descendant_potential_nodes.begin(),
- parent->rule_matching_state()->descendant_potential_nodes.end());
- } else {
- current_element->rule_matching_state()->descendant_potential_nodes.insert(
- selector_tree->root());
- }
- // Walk all of the matching nodes, adding any that have a descendant
- // combinator as a descendant potential node. Any missing that combinator can
- // never match descendants.
- for (dom::HTMLElement::MatchingNodes::const_iterator iter =
- current_element->rule_matching_state()->matching_nodes.begin();
- iter != current_element->rule_matching_state()->matching_nodes.end();
- ++iter) {
- if ((*iter)->HasCombinator(cssom::kDescendantCombinator)) {
- // It is possible for the two lists to contain duplicate nodes, so only
- // add the node if it isn't a duplicate.
- current_element->rule_matching_state()->descendant_potential_nodes.insert(
- *iter, true /*check_for_duplicate*/);
- }
- }
-
- // Calculate current element's following sibling potential nodes.
- if (selector_tree->has_sibling_combinators()) {
- if (previous_sibling) {
- current_element->rule_matching_state()
- ->following_sibling_potential_nodes.insert(
- previous_sibling->rule_matching_state()
- ->following_sibling_potential_nodes.begin(),
- previous_sibling->rule_matching_state()
- ->following_sibling_potential_nodes.end());
- }
- // Walk all of the matching nodes, adding any that have a following sibling
- // combinator as a following sibling potential node. Any missing that
- // combinator can never match following siblings..
- for (dom::HTMLElement::MatchingNodes::const_iterator iter =
- current_element->rule_matching_state()->matching_nodes.begin();
- iter != current_element->rule_matching_state()->matching_nodes.end();
- ++iter) {
- if ((*iter)->HasCombinator(cssom::kFollowingSiblingCombinator)) {
- // It is possible for the two lists to contain duplicate nodes, so only
- // add the node if it isn't a duplicate.
- current_element->rule_matching_state()
- ->following_sibling_potential_nodes.insert(
- *iter, true /*check_for_duplicate*/);
- }
- }
- }
-
- current_element->set_matching_rules_valid();
+ element->set_matching_rules_valid();
}
scoped_refptr<Element> QuerySelector(Node* node, const std::string& selectors,
diff --git a/src/cobalt/dom/rule_matching.h b/src/cobalt/dom/rule_matching.h
index 31d2353..6f5fc31 100644
--- a/src/cobalt/dom/rule_matching.h
+++ b/src/cobalt/dom/rule_matching.h
@@ -37,7 +37,7 @@
// Updates the matching rules on an element. The parent and previous sibling
// need to have their matching rules updated before, since the current element's
// rule matching will depend on their rule matching states.
-void UpdateMatchingRules(HTMLElement* current_element);
+void UpdateElementMatchingRules(HTMLElement* current_element);
// Returns the first element in the subtree that matches the given selector.
scoped_refptr<Element> QuerySelector(Node* node, const std::string& selectors,
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc
index e586475..07136d7 100644
--- a/src/cobalt/dom/rule_matching_test.cc
+++ b/src/cobalt/dom/rule_matching_test.cc
@@ -43,10 +43,10 @@
: css_parser_(css_parser::Parser::Create()),
dom_parser_(new dom_parser::Parser()),
dom_stat_tracker_(new DomStatTracker("RuleMatchingTest")),
- html_element_context_(NULL, css_parser_.get(), dom_parser_.get(), NULL,
+ html_element_context_(NULL, NULL, css_parser_.get(), dom_parser_.get(),
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted),
+ NULL, NULL, NULL, dom_stat_tracker_.get(), "",
+ base::kApplicationStateStarted, NULL),
document_(new Document(&html_element_context_)),
root_(document_->CreateElement("html")->AsHTMLElement()),
head_(document_->CreateElement("head")->AsHTMLElement()),
@@ -83,7 +83,7 @@
while (child) {
if (child->AsElement()) {
DCHECK(child->AsElement()->AsHTMLElement());
- UpdateMatchingRules(child->AsElement()->AsHTMLElement());
+ UpdateElementMatchingRules(child->AsElement()->AsHTMLElement());
}
child = iterator.Next();
}
@@ -591,7 +591,7 @@
(*matching_rules)[0].first);
}
-// "span + span" shouldm't match first span in <span/><span/>.
+// "span + span" shouldn't match first span in <span/><span/>.
TEST_F(RuleMatchingTest, ComplexSelectorNextSiblingCombinatorNoMatch) {
head_->set_inner_html("<style>span + span {}</style>");
body_->set_inner_html("<span/><span/>");
@@ -868,5 +868,385 @@
(*matching_rules)[0].first);
}
+// div:hover should match after hover added to element.
+TEST_F(RuleMatchingTest, HoverPseudoClassSelectorAddHoverToElement) {
+ head_->set_inner_html("<style>div:hover {}</style>");
+ body_->set_inner_html("<div/>");
+
+ document_->SetIndicatedElement(body_->first_element_child()->AsHTMLElement());
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()->AsHTMLElement()->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ document_->SetIndicatedElement(NULL);
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(0, matching_rules->size());
+}
+
+// div:hover should not match after hover removed from element.
+TEST_F(RuleMatchingTest, HoverPseudoClassSelectorRemoveHoverFromElement) {
+ head_->set_inner_html("<style>div:hover {}</style>");
+ body_->set_inner_html("<div/>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()->AsHTMLElement()->matching_rules();
+ ASSERT_EQ(0, matching_rules->size());
+
+ document_->SetIndicatedElement(body_->first_element_child()->AsHTMLElement());
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// div:hover should match after hover added to element's descendant.
+TEST_F(RuleMatchingTest, HoverPseudoClassSelectorAddHoverToDescendant) {
+ head_->set_inner_html("<style>div:hover {}</style>");
+ body_->set_inner_html("<div><span/></div>");
+
+ document_->SetIndicatedElement(
+ body_->first_element_child()->first_element_child()->AsHTMLElement());
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()->AsHTMLElement()->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ document_->SetIndicatedElement(NULL);
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(0, matching_rules->size());
+}
+
+// div:hover should not match after hover removed from element's descendant.
+TEST_F(RuleMatchingTest, HoverPseudoClassSelectorRemoveHoverFromDescendant) {
+ head_->set_inner_html("<style>div:hover {}</style>");
+ body_->set_inner_html("<div><span/></div>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()->AsHTMLElement()->matching_rules();
+ ASSERT_EQ(0, matching_rules->size());
+
+ document_->SetIndicatedElement(
+ body_->first_element_child()->first_element_child()->AsHTMLElement());
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// .my-class should match after class "my-class" set on element.
+TEST_F(RuleMatchingTest, ClassSelectorSetClassOnElement) {
+ head_->set_inner_html("<style>.my-class {}</style>");
+ body_->set_inner_html("<div class=\"other-class\"/>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()->AsHTMLElement()->matching_rules();
+ ASSERT_EQ(0, matching_rules->size());
+
+ body_->first_element_child()->AsHTMLElement()->set_class_name("my-class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// .my-class should not match after class removed from element.
+TEST_F(RuleMatchingTest, ClassSelectorRemoveClassOnElement) {
+ head_->set_inner_html("<style>.my-class {}</style>");
+ body_->set_inner_html("<div class=\"my-class\"/>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()->AsHTMLElement()->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ body_->first_element_child()->AsHTMLElement()->RemoveAttribute("class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(0, matching_rules->size());
+}
+
+// Child should add matching rule from newly added matching node.
+TEST_F(RuleMatchingTest, ChildMatchingNodeAdded) {
+ head_->set_inner_html("<style>div.my-class > div {}</style>");
+ body_->set_inner_html("<div class=\"other-class\"><div/></div>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->first_element_child()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(0, matching_rules->size());
+
+ body_->first_element_child()->AsHTMLElement()->set_class_name("my-class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// Child should remove matching rule from newly removed matching node.
+TEST_F(RuleMatchingTest, ChildMatchingNodeRemoved) {
+ head_->set_inner_html("<style>div.my-class > div {}</style>");
+ body_->set_inner_html("<div class=\"my-class\"><div/></div>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->first_element_child()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ body_->first_element_child()->AsHTMLElement()->RemoveAttribute("class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(0, matching_rules->size());
+}
+
+// Descendant should add matching rule from newly added matching node.
+TEST_F(RuleMatchingTest, DescendantMatchingNodeAdded) {
+ head_->set_inner_html("<style>div.my-class div {}</style>");
+ body_->set_inner_html("<div class=\"other-class\"><div/></div>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->first_element_child()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(0, matching_rules->size());
+
+ body_->first_element_child()->AsHTMLElement()->set_class_name("my-class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// Descendant should remove matching rule from newly removed matching node.
+TEST_F(RuleMatchingTest, DescendantMatchingNodeRemoved) {
+ head_->set_inner_html("<style>div.my-class div {}</style>");
+ body_->set_inner_html("<div class=\"my-class\"><div/></div>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->first_element_child()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ body_->first_element_child()->AsHTMLElement()->RemoveAttribute("class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(0, matching_rules->size());
+}
+
+// Next sibling should add matching rule from newly added matching node.
+TEST_F(RuleMatchingTest, NextSiblingMatchingNodeAdded) {
+ head_->set_inner_html("<style>div.my-class + div {}</style>");
+ body_->set_inner_html("<div class=\"other-class\"/><div/>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->next_element_sibling()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(0, matching_rules->size());
+
+ body_->first_element_child()->AsHTMLElement()->set_class_name("my-class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// Next sibling should remove matching rule from newly removed matching node.
+TEST_F(RuleMatchingTest, NextSiblingMatchingNodeRemoved) {
+ head_->set_inner_html("<style>div.my-class + div {}</style>");
+ body_->set_inner_html("<div class=\"my-class\"/><div/>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->next_element_sibling()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ body_->first_element_child()->AsHTMLElement()->RemoveAttribute("class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(0, matching_rules->size());
+}
+
+// Following sibling should add matching rule from newly added matching node.
+TEST_F(RuleMatchingTest, FollowingSiblingMatchingNodeAdded) {
+ head_->set_inner_html("<style>div.my-class ~ div {}</style>");
+ body_->set_inner_html("<div class=\"other-class\"/><div/>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->next_element_sibling()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(0, matching_rules->size());
+
+ body_->first_element_child()->AsHTMLElement()->set_class_name("my-class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// Following sibling should remove matching rule from newly removed matching
+// node.
+TEST_F(RuleMatchingTest, FollowingSiblingMatchingNodeRemoved) {
+ head_->set_inner_html("<style>div.my-class ~ div {}</style>");
+ body_->set_inner_html("<div class=\"my-class\"/><div/>");
+
+ UpdateAllMatchingRules();
+
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ body_->first_element_child()
+ ->next_element_sibling()
+ ->AsHTMLElement()
+ ->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ body_->first_element_child()->AsHTMLElement()->RemoveAttribute("class");
+
+ UpdateAllMatchingRules();
+
+ ASSERT_EQ(0, matching_rules->size());
+}
+
+// After pseudo element should add matching rule from newly added matching node.
+TEST_F(RuleMatchingTest, AfterPseudoElementMatchingNodeRemoved) {
+ head_->set_inner_html(
+ "<style>div.my-class:after {}</style>"
+ "<style>div:after {}</style>");
+ body_->set_inner_html("<div class=\"my-class\"/><div/>");
+
+ UpdateAllMatchingRules();
+
+ HTMLElement* html_element = body_->first_element_child()->AsHTMLElement();
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ html_element->matching_rules();
+ EXPECT_EQ(0, matching_rules->size());
+ ASSERT_TRUE(html_element->pseudo_element(kAfterPseudoElementType));
+ matching_rules =
+ html_element->pseudo_element(kAfterPseudoElementType)->matching_rules();
+ ASSERT_EQ(2, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ body_->first_element_child()->AsHTMLElement()->RemoveAttribute("class");
+
+ UpdateAllMatchingRules();
+
+ matching_rules = html_element->matching_rules();
+ EXPECT_EQ(0, matching_rules->size());
+ ASSERT_TRUE(html_element->pseudo_element(kAfterPseudoElementType));
+ matching_rules =
+ html_element->pseudo_element(kAfterPseudoElementType)->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(1)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
+// After pseudo element should remove matching rule from newly removed matching
+// node.
+TEST_F(RuleMatchingTest, AfterPseudoElementMatchingNodeAdded) {
+ head_->set_inner_html(
+ "<style>div.my-class:after {}</style>"
+ "<style>div:after {}</style>");
+ body_->set_inner_html("<div class=\"other-class\"/><div/>");
+
+ UpdateAllMatchingRules();
+
+ HTMLElement* html_element = body_->first_element_child()->AsHTMLElement();
+ cssom::RulesWithCascadePrecedence* matching_rules =
+ html_element->matching_rules();
+ EXPECT_EQ(0, matching_rules->size());
+ ASSERT_TRUE(html_element->pseudo_element(kAfterPseudoElementType));
+ matching_rules =
+ html_element->pseudo_element(kAfterPseudoElementType)->matching_rules();
+ ASSERT_EQ(1, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(1)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+
+ body_->first_element_child()->AsHTMLElement()->set_class_name("my-class");
+
+ UpdateAllMatchingRules();
+
+ matching_rules = html_element->matching_rules();
+ EXPECT_EQ(0, matching_rules->size());
+ ASSERT_TRUE(html_element->pseudo_element(kAfterPseudoElementType));
+ matching_rules =
+ html_element->pseudo_element(kAfterPseudoElementType)->matching_rules();
+ ASSERT_EQ(2, matching_rules->size());
+ EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0),
+ (*matching_rules)[0].first);
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/script_event_log.cc b/src/cobalt/dom/script_event_log.cc
new file mode 100644
index 0000000..1ebdf60
--- /dev/null
+++ b/src/cobalt/dom/script_event_log.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 Google Inc. 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 "cobalt/dom/script_event_log.h"
+
+#include "cobalt/dom/event_target.h"
+
+namespace cobalt {
+namespace dom {
+
+ScriptEventLog::ScriptEventLog() : current_stack_depth_(0) {
+ base::UserLog::Register(base::UserLog::kEventStackLevelCountIndex,
+ "EventLevelCnt", ¤t_stack_depth_,
+ sizeof(current_stack_depth_));
+
+ memset(event_log_stack_, 0, sizeof(event_log_stack_));
+ const int kLogNameBufferSize = 16;
+ char log_name_buffer[kLogNameBufferSize];
+ for (int i = 0; i < base::UserLog::kEventStackMaxDepth; i++) {
+ snprintf(log_name_buffer, kLogNameBufferSize, "EventLevel%d", i);
+ base::UserLog::Register(static_cast<base::UserLog::Index>(
+ base::UserLog::kEventStackMinLevelIndex + i),
+ log_name_buffer, event_log_stack_[i],
+ kLogEntryMaxLength);
+ }
+}
+
+ScriptEventLog::~ScriptEventLog() {
+ base::UserLog::Deregister(base::UserLog::kEventStackLevelCountIndex);
+ for (int i = 0; i < base::UserLog::kEventStackMaxDepth; i++) {
+ base::UserLog::Deregister(static_cast<base::UserLog::Index>(
+ base::UserLog::kEventStackMinLevelIndex + i));
+ }
+}
+
+void ScriptEventLog::PushEvent(Event* event) {
+ if (current_stack_depth_ < base::UserLog::kEventStackMaxDepth) {
+ snprintf(event_log_stack_[current_stack_depth_], kLogEntryMaxLength,
+ "%s@%s", event->type().c_str(),
+ event->current_target()->GetDebugName().c_str());
+ } else if (current_stack_depth_ == base::UserLog::kEventStackMaxDepth) {
+ DLOG(WARNING) << "Reached maximum depth of " << kLogEntryMaxLength
+ << ". Subsequent events will not be logged.";
+ }
+ current_stack_depth_++;
+}
+
+void ScriptEventLog::PopEvent() {
+ DCHECK(current_stack_depth_);
+ current_stack_depth_--;
+ if (current_stack_depth_ < base::UserLog::kEventStackMaxDepth) {
+ memset(event_log_stack_[current_stack_depth_], 0, kLogEntryMaxLength);
+ }
+}
+
+base::LazyInstance<ScriptEventLog> script_event_log = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/script_event_log.h b/src/cobalt/dom/script_event_log.h
new file mode 100644
index 0000000..a419e8d
--- /dev/null
+++ b/src/cobalt/dom/script_event_log.h
@@ -0,0 +1,46 @@
+// Copyright 2018 Google Inc. 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 COBALT_DOM_SCRIPT_EVENT_LOG_H_
+#define COBALT_DOM_SCRIPT_EVENT_LOG_H_
+
+#include "base/lazy_instance.h"
+#include "cobalt/base/user_log.h"
+#include "cobalt/dom/event.h"
+
+namespace cobalt {
+namespace dom {
+
+// This class records the nested events and manages the user log information.
+class ScriptEventLog {
+ public:
+ ScriptEventLog();
+ ~ScriptEventLog();
+ void PushEvent(Event* event);
+ void PopEvent();
+
+ private:
+ static const size_t kLogEntryMaxLength = 64;
+ int current_stack_depth_;
+ char event_log_stack_[base::UserLog::kEventStackMaxDepth][kLogEntryMaxLength];
+
+ DISALLOW_COPY_AND_ASSIGN(ScriptEventLog);
+};
+
+extern base::LazyInstance<ScriptEventLog> script_event_log;
+
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_SCRIPT_EVENT_LOG_H_
diff --git a/src/cobalt/dom/serializer_test.cc b/src/cobalt/dom/serializer_test.cc
index bfe31c2..eb0683d 100644
--- a/src/cobalt/dom/serializer_test.cc
+++ b/src/cobalt/dom/serializer_test.cc
@@ -45,9 +45,9 @@
: dom_parser_(new dom_parser::Parser()),
dom_stat_tracker_(new DomStatTracker("SerializerTest")),
html_element_context_(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted),
+ base::kApplicationStateStarted, NULL),
document_(new Document(&html_element_context_)),
root_(new Element(document_, base::Token("root"))),
source_location_(base::SourceLocation("[object SerializerTest]", 1, 1)) {}
diff --git a/src/cobalt/dom/testing/dom_testing.gyp b/src/cobalt/dom/testing/dom_testing.gyp
new file mode 100644
index 0000000..3c72685
--- /dev/null
+++ b/src/cobalt/dom/testing/dom_testing.gyp
@@ -0,0 +1,42 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'dom_testing',
+ 'type': 'static_library',
+ 'sources': [
+ 'gtest_workarounds.h',
+ 'html_collection_testing.h',
+ 'mock_event_listener.h',
+ 'stub_css_parser.cc',
+ 'stub_css_parser.h',
+ 'stub_script_runner.cc',
+ 'stub_script_runner.h',
+ 'stub_window.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/browser/browser.gyp:browser',
+ '<(DEPTH)/cobalt/browser/browser_bindings.gyp:bindings',
+ '<(DEPTH)/cobalt/cssom/cssom.gyp:cssom',
+ '<(DEPTH)/cobalt/css_parser/css_parser.gyp:css_parser',
+ '<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
+ ],
+ },
+ ],
+}
diff --git a/src/cobalt/dom/testing/stub_window.h b/src/cobalt/dom/testing/stub_window.h
index 298e799..6eb3913 100644
--- a/src/cobalt/dom/testing/stub_window.h
+++ b/src/cobalt/dom/testing/stub_window.h
@@ -20,11 +20,13 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/message_loop.h"
+#include "base/threading/platform_thread.h"
#include "cobalt/css_parser/parser.h"
#include "cobalt/dom/local_storage_database.h"
#include "cobalt/dom/window.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader_factory.h"
#include "cobalt/media_session/media_session.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/javascript_engine.h"
@@ -38,20 +40,27 @@
// stubbed out.
class StubWindow {
public:
- StubWindow()
+ explicit StubWindow(
+ scoped_ptr<script::EnvironmentSettings> environment_settings =
+ scoped_ptr<script::EnvironmentSettings>(
+ new script::EnvironmentSettings))
: message_loop_(MessageLoop::TYPE_DEFAULT),
css_parser_(css_parser::Parser::Create()),
dom_parser_(new dom_parser::Parser(base::Bind(&StubErrorCallback))),
fetcher_factory_(new loader::FetcherFactory(NULL)),
+ loader_factory_(new loader::LoaderFactory(
+ fetcher_factory_.get(), NULL, base::kThreadPriority_Default)),
local_storage_database_(NULL),
url_("about:blank"),
- dom_stat_tracker_(new dom::DomStatTracker("StubWindow")) {
+ dom_stat_tracker_(new dom::DomStatTracker("StubWindow")),
+ environment_settings_(environment_settings.Pass()) {
engine_ = script::JavaScriptEngine::CreateEngine();
global_environment_ = engine_->CreateGlobalEnvironment();
window_ = new dom::Window(
1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
- dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
- NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL, NULL,
+ dom_parser_.get(), fetcher_factory_.get(), loader_factory_.get(), NULL,
+ NULL, NULL, NULL, NULL, NULL, &local_storage_database_, NULL, NULL,
+ NULL, NULL, global_environment_->script_value_factory(), NULL,
dom_stat_tracker_.get(), url_, "", "en-US", "en",
base::Callback<void(const GURL&)>(), base::Bind(&StubErrorCallback),
NULL, network_bridge::PostSender(), csp::kCSPRequired,
@@ -61,8 +70,9 @@
base::Closure() /* window_minimize */, NULL, NULL, NULL,
dom::Window::OnStartDispatchEventCallback(),
dom::Window::OnStopDispatchEventCallback(),
- dom::ScreenshotManager::ProvideScreenshotFunctionCallback());
- global_environment_->CreateGlobalObject(window_, &environment_settings_);
+ dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
+ global_environment_->CreateGlobalObject(window_,
+ environment_settings_.get());
}
scoped_refptr<dom::Window> window() { return window_; }
@@ -70,6 +80,11 @@
return global_environment_;
}
css_parser::Parser* css_parser() { return css_parser_.get(); }
+ script::EnvironmentSettings* environment_settings() {
+ return environment_settings_.get();
+ }
+
+ ~StubWindow() { window_->DestroyTimers(); }
private:
static void StubErrorCallback(const std::string& /*error*/) {}
@@ -78,10 +93,11 @@
scoped_ptr<css_parser::Parser> css_parser_;
scoped_ptr<dom_parser::Parser> dom_parser_;
scoped_ptr<loader::FetcherFactory> fetcher_factory_;
+ scoped_ptr<loader::LoaderFactory> loader_factory_;
dom::LocalStorageDatabase local_storage_database_;
GURL url_;
scoped_ptr<dom::DomStatTracker> dom_stat_tracker_;
- script::EnvironmentSettings environment_settings_;
+ scoped_ptr<script::EnvironmentSettings> environment_settings_;
scoped_ptr<script::JavaScriptEngine> engine_;
scoped_refptr<script::GlobalEnvironment> global_environment_;
scoped_refptr<dom::Window> window_;
diff --git a/src/cobalt/dom/url_registry.h b/src/cobalt/dom/url_registry.h
index 4b6954a..aea50c9 100644
--- a/src/cobalt/dom/url_registry.h
+++ b/src/cobalt/dom/url_registry.h
@@ -19,6 +19,7 @@
#include "base/hash_tables.h"
#include "base/memory/ref_counted.h"
+#include "cobalt/script/tracer.h"
namespace cobalt {
namespace dom {
@@ -33,13 +34,17 @@
// Note: It is unsafe to directly encode the pointer to the object in the
// url as the url is assigned from JavaScript.
template <typename ObjectType>
-class UrlRegistry {
+class UrlRegistry : public script::Traceable {
public:
void Register(const std::string& blob_url,
const scoped_refptr<ObjectType>& object);
scoped_refptr<ObjectType> Retrieve(const std::string& blob_url);
bool Unregister(const std::string& blob_url);
+ void TraceMembers(script::Tracer* tracer) override {
+ tracer->TraceValues(object_registry_);
+ }
+
private:
typedef base::hash_map<std::string, scoped_refptr<ObjectType> > UrlMap;
UrlMap object_registry_;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 0023eb0..0bfd760 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -15,13 +15,14 @@
#include <algorithm>
-#include "base/base64.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/debug/trace_event.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/base/tokens.h"
#include "cobalt/cssom/css_computed_style_declaration.h"
#include "cobalt/cssom/user_agent_style_sheet.h"
+#include "cobalt/dom/base64.h"
#include "cobalt/dom/camera_3d.h"
#include "cobalt/dom/console.h"
#include "cobalt/dom/device_orientation_event.h"
@@ -91,6 +92,7 @@
base::ApplicationState initial_application_state,
cssom::CSSParser* css_parser, Parser* dom_parser,
loader::FetcherFactory* fetcher_factory,
+ loader::LoaderFactory* loader_factory,
render_tree::ResourceProvider** resource_provider,
loader::image::AnimatedImageTracker* animated_image_tracker,
loader::image::ImageCache* image_cache,
@@ -124,11 +126,15 @@
const OnStopDispatchEventCallback& on_stop_dispatch_event_callback,
const ScreenshotManager::ProvideScreenshotFunctionCallback&
screenshot_function_callback,
+ base::WaitableEvent* synchronous_loader_interrupt,
int csp_insecure_allowed_token, int dom_max_element_depth,
float video_playback_rate_multiplier, ClockType clock_type,
const CacheCallback& splash_screen_cache_callback,
const scoped_refptr<captions::SystemCaptionSettings>& captions)
- : width_(width),
+ // 'window' object EventTargets require special handling for onerror events,
+ // see EventTarget constructor for more details.
+ : EventTarget(kUnpackOnErrorEvents),
+ width_(width),
height_(height),
device_pixel_ratio_(device_pixel_ratio),
is_resize_event_pending_(false),
@@ -137,12 +143,13 @@
test_runner_(new TestRunner()),
#endif // ENABLE_TEST_RUNNER
html_element_context_(new HTMLElementContext(
- fetcher_factory, css_parser, dom_parser, can_play_type_handler,
- web_media_player_factory, script_runner, script_value_factory,
- media_source_registry, resource_provider, animated_image_tracker,
- image_cache, reduced_image_cache_capacity_manager,
- remote_typeface_cache, mesh_cache, dom_stat_tracker,
- font_language_script, initial_application_state,
+ fetcher_factory, loader_factory, css_parser, dom_parser,
+ can_play_type_handler, web_media_player_factory, script_runner,
+ script_value_factory, media_source_registry, resource_provider,
+ animated_image_tracker, image_cache,
+ reduced_image_cache_capacity_manager, remote_typeface_cache,
+ mesh_cache, dom_stat_tracker, font_language_script,
+ initial_application_state, synchronous_loader_interrupt,
video_playback_rate_multiplier)),
performance_(new Performance(
#if defined(ENABLE_TEST_RUNNER)
@@ -345,22 +352,24 @@
std::string Window::Btoa(const std::string& string_to_encode,
script::ExceptionState* exception_state) {
- std::string output;
- if (!base::Base64Encode(string_to_encode, &output)) {
+ TRACE_EVENT0("cobalt::dom", "Window::Btoa()");
+ auto output = ForgivingBase64Encode(string_to_encode);
+ if (!output) {
DOMException::Raise(DOMException::kInvalidCharacterErr, exception_state);
return std::string();
}
- return output;
+ return *output;
}
std::vector<uint8_t> Window::Atob(const std::string& encoded_string,
script::ExceptionState* exception_state) {
- std::string output;
- if (!base::Base64Decode(encoded_string, &output)) {
+ TRACE_EVENT0("cobalt::dom", "Window::Atob()");
+ auto output = ForgivingBase64Decode(encoded_string);
+ if (!output) {
DOMException::Raise(DOMException::kInvalidCharacterErr, exception_state);
return {};
}
- return {output.begin(), output.end()};
+ return *output;
}
int Window::SetTimeout(const WindowTimers::TimerCallbackArg& handler,
@@ -483,6 +492,9 @@
}
void Window::InjectEvent(const scoped_refptr<Event>& event) {
+ TRACE_EVENT1("cobalt::dom", "Window::InjectEvent()", "event",
+ event->type().c_str());
+
// Forward the event on to the correct object in DOM.
if (event->GetWrappableType() == base::GetTypeId<KeyboardEvent>()) {
// Event.target:focused element processing the key event or if no element
@@ -500,10 +512,6 @@
if (on_screen_keyboard_) {
on_screen_keyboard_->DispatchEvent(event);
}
- } else if (event->GetWrappableType() == base::GetTypeId<PointerEvent>() ||
- event->GetWrappableType() == base::GetTypeId<MouseEvent>() ||
- event->GetWrappableType() == base::GetTypeId<WheelEvent>()) {
- document_->pointer_state()->QueuePointerEvent(event);
} else {
SB_NOTREACHED();
}
@@ -672,6 +680,7 @@
void Window::SetEnvironmentSettings(script::EnvironmentSettings* settings) {
screenshot_manager_.SetEnvironmentSettings(settings);
+ navigator_->SetEnvironmentSettings(settings);
}
void Window::CacheSplashScreen(const std::string& content) {
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 80ef19b..4815246 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -23,6 +23,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "base/synchronization/waitable_event.h"
#include "base/timer.h"
#include "cobalt/base/application_state.h"
#include "cobalt/cssom/css_parser.h"
@@ -54,6 +55,7 @@
#include "cobalt/loader/image/image.h"
#include "cobalt/loader/image/image_cache.h"
#include "cobalt/loader/loader.h"
+#include "cobalt/loader/loader_factory.h"
#include "cobalt/loader/mesh/mesh_cache.h"
#include "cobalt/media/can_play_type_handler.h"
#include "cobalt/media/web_media_player_factory.h"
@@ -127,6 +129,7 @@
base::ApplicationState initial_application_state,
cssom::CSSParser* css_parser, Parser* dom_parser,
loader::FetcherFactory* fetcher_factory,
+ loader::LoaderFactory* loader_factory,
render_tree::ResourceProvider** resource_provider,
loader::image::AnimatedImageTracker* animated_image_tracker,
loader::image::ImageCache* image_cache,
@@ -162,6 +165,7 @@
const OnStopDispatchEventCallback& stop_tracking_dispatch_event_callback,
const ScreenshotManager::ProvideScreenshotFunctionCallback&
screenshot_function_callback,
+ base::WaitableEvent* synchronous_loader_interrupt,
int csp_insecure_allowed_token = 0, int dom_max_element_depth = 0,
float video_playback_rate_multiplier = 1.f,
ClockType clock_type = kClockTypeSystemTime,
diff --git a/src/cobalt/dom/window_on_screen_keyboard.idl b/src/cobalt/dom/window_on_screen_keyboard.idl
index 3fdb0a1..b0cbbd7 100644
--- a/src/cobalt/dom/window_on_screen_keyboard.idl
+++ b/src/cobalt/dom/window_on_screen_keyboard.idl
@@ -16,5 +16,5 @@
// screen keyboard.
partial interface Window {
- readonly attribute OnScreenKeyboard onScreenKeyboard;
+[ Conditional=COBALT_ENABLE_ON_SCREEN_KEYBOARD ] readonly attribute OnScreenKeyboard onScreenKeyboard;
};
diff --git a/src/cobalt/dom/window_test.cc b/src/cobalt/dom/window_test.cc
index ea3f2fd..6401e7f 100644
--- a/src/cobalt/dom/window_test.cc
+++ b/src/cobalt/dom/window_test.cc
@@ -26,6 +26,8 @@
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/media_session/media_session.h"
#include "cobalt/network_bridge/net_poster.h"
+#include "cobalt/script/global_environment.h"
+#include "cobalt/script/javascript_engine.h"
#include "googleurl/src/gurl.h"
#include "starboard/window.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -46,23 +48,26 @@
dom_parser_(new dom_parser::Parser(mock_error_callback_)),
fetcher_factory_(new loader::FetcherFactory(NULL)),
local_storage_database_(NULL),
- url_("about:blank"),
- window_(new Window(
- 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
- dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, url_, "", "en-US", "en",
- base::Callback<void(const GURL &)>(),
- base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_)),
- NULL, network_bridge::PostSender(), csp::kCSPRequired,
- kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
- base::Closure() /* ran_animation_frame_callbacks */,
- dom::Window::CloseCallback() /* window_close */,
- base::Closure() /* window_minimize */, NULL, NULL, NULL,
- dom::Window::OnStartDispatchEventCallback(),
- dom::Window::OnStopDispatchEventCallback(),
- dom::ScreenshotManager::ProvideScreenshotFunctionCallback())) {}
+ url_("about:blank") {
+ engine_ = script::JavaScriptEngine::CreateEngine();
+ global_environment_ = engine_->CreateGlobalEnvironment();
+ window_ = new Window(
+ 1920, 1080, 1.f, base::kApplicationStateStarted, css_parser_.get(),
+ dom_parser_.get(), fetcher_factory_.get(), NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &local_storage_database_, NULL, NULL, NULL, NULL,
+ global_environment_->script_value_factory(), NULL, NULL, url_, "",
+ "en-US", "en", base::Callback<void(const GURL &)>(),
+ base::Bind(&MockErrorCallback::Run,
+ base::Unretained(&mock_error_callback_)),
+ NULL, network_bridge::PostSender(), csp::kCSPRequired,
+ kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
+ base::Closure() /* ran_animation_frame_callbacks */,
+ dom::Window::CloseCallback() /* window_close */,
+ base::Closure() /* window_minimize */, NULL, NULL, NULL,
+ dom::Window::OnStartDispatchEventCallback(),
+ dom::Window::OnStopDispatchEventCallback(),
+ dom::ScreenshotManager::ProvideScreenshotFunctionCallback(), NULL);
+ }
~WindowTest() override {}
@@ -72,6 +77,8 @@
scoped_ptr<dom_parser::Parser> dom_parser_;
scoped_ptr<loader::FetcherFactory> fetcher_factory_;
dom::LocalStorageDatabase local_storage_database_;
+ scoped_ptr<script::JavaScriptEngine> engine_;
+ scoped_refptr<script::GlobalEnvironment> global_environment_;
GURL url_;
scoped_refptr<Window> window_;
};
diff --git a/src/cobalt/dom_parser/dom_parser.gyp b/src/cobalt/dom_parser/dom_parser.gyp
index eaf84bf..9934f06 100644
--- a/src/cobalt/dom_parser/dom_parser.gyp
+++ b/src/cobalt/dom_parser/dom_parser.gyp
@@ -38,39 +38,8 @@
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
'<(DEPTH)/cobalt/loader/loader.gyp:loader',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/third_party/libxml/libxml.gyp:libxml',
],
},
-
- {
- 'target_name': 'dom_parser_test',
- 'type': '<(gtest_target_type)',
- 'sources': [
- 'html_decoder_test.cc',
- 'xml_decoder_test.cc',
- ],
- 'dependencies': [
- '<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
- '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- 'dom_parser',
- ],
- },
-
- {
- 'target_name': 'dom_parser_test_deploy',
- 'type': 'none',
- 'dependencies': [
- 'dom_parser_test',
- ],
- 'variables': {
- 'executable_name': 'dom_parser_test',
- },
- 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
- },
],
}
diff --git a/src/cobalt/dom_parser/dom_parser_test.gyp b/src/cobalt/dom_parser/dom_parser_test.gyp
new file mode 100644
index 0000000..6ed8f59
--- /dev/null
+++ b/src/cobalt/dom_parser/dom_parser_test.gyp
@@ -0,0 +1,49 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'dom_parser_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'html_decoder_test.cc',
+ 'xml_decoder_test.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+ '<(DEPTH)/cobalt/dom/testing/dom_testing.gyp:dom_testing',
+ '<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ },
+
+ {
+ 'target_name': 'dom_parser_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'dom_parser_test',
+ ],
+ 'variables': {
+ 'executable_name': 'dom_parser_test',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index dcfeba4..ef966fe 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -16,6 +16,7 @@
#include "base/callback.h"
#include "base/message_loop.h"
+#include "base/threading/platform_thread.h"
#include "cobalt/dom/attr.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/dom_stat_tracker.h"
@@ -28,6 +29,7 @@
#include "cobalt/dom/text.h"
#include "cobalt/dom_parser/parser.h"
#include "cobalt/loader/fetcher_factory.h"
+#include "cobalt/loader/loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -47,6 +49,7 @@
~HTMLDecoderTest() override {}
loader::FetcherFactory fetcher_factory_;
+ loader::LoaderFactory loader_factory_;
scoped_ptr<Parser> dom_parser_;
dom::testing::StubCSSParser stub_css_parser_;
dom::testing::StubScriptRunner stub_script_runner_;
@@ -62,14 +65,17 @@
HTMLDecoderTest::HTMLDecoderTest()
: fetcher_factory_(NULL /* network_module */),
+ loader_factory_(&fetcher_factory_, NULL /* ResourceProvider */,
+ base::kThreadPriority_Default),
dom_parser_(new Parser()),
dom_stat_tracker_(new dom::DomStatTracker("HTMLDecoderTest")),
html_element_context_(
- &fetcher_factory_, &stub_css_parser_, dom_parser_.get(),
- NULL /* can_play_type_handler */, NULL /* web_media_player_factory */,
- &stub_script_runner_, NULL /* script_value_factory */, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted),
+ &fetcher_factory_, &loader_factory_, &stub_css_parser_,
+ dom_parser_.get(), NULL /* can_play_type_handler */,
+ NULL /* web_media_player_factory */, &stub_script_runner_,
+ NULL /* script_value_factory */, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, dom_stat_tracker_.get(), "", base::kApplicationStateStarted,
+ NULL),
document_(new dom::Document(&html_element_context_)),
root_(new dom::Element(document_, base::Token("element"))),
source_location_(base::SourceLocation("[object HTMLDecoderTest]", 1, 1)) {
diff --git a/src/cobalt/input/input_device_manager_desktop.cc b/src/cobalt/input/input_device_manager_desktop.cc
index ddba793..2386d10 100644
--- a/src/cobalt/input/input_device_manager_desktop.cc
+++ b/src/cobalt/input/input_device_manager_desktop.cc
@@ -17,8 +17,10 @@
#include <cmath>
#include <string>
+#include "base/time.h"
#include "cobalt/base/token.h"
#include "cobalt/base/tokens.h"
+#include "cobalt/dom/event.h"
#include "cobalt/dom/input_event.h"
#include "cobalt/dom/input_event_init.h"
#include "cobalt/dom/keyboard_event.h"
@@ -29,11 +31,23 @@
#include "cobalt/dom/wheel_event_init.h"
#include "cobalt/input/create_default_camera_3d.h"
#include "cobalt/input/input_poller_impl.h"
+#include "cobalt/overlay_info/overlay_info_registry.h"
#include "cobalt/system_window/input_event.h"
namespace cobalt {
namespace input {
+namespace {
+void UpdateEventInit(const system_window::InputEvent* input_event,
+ EventInit* event) {
+ if (input_event->timestamp() != 0) {
+ // Convert SbTimeMonotonic to DOMTimeStamp.
+ event->set_time_stamp(cobalt::dom::Event::GetEventTime(
+ input_event->timestamp()));
+ }
+}
+}
+
InputDeviceManagerDesktop::InputDeviceManagerDesktop(
const KeyboardEventCallback& keyboard_event_callback,
const PointerEventCallback& pointer_event_callback,
@@ -213,17 +227,23 @@
dom::KeyboardEvent::KeyLocationCode location =
dom::KeyboardEvent::KeyCodeToKeyLocation(key_code);
dom::KeyboardEventInit keyboard_event;
+ UpdateEventInit(input_event, &keyboard_event);
UpdateEventModifierInit(input_event, &keyboard_event);
keyboard_event.set_location(location);
keyboard_event.set_repeat(input_event->is_repeat());
keyboard_event.set_char_code(key_code);
keyboard_event.set_key_code(key_code);
keypress_generator_filter_.HandleKeyboardEvent(type, keyboard_event);
+
+ int32_t key_code_in_int32 = static_cast<int32_t>(key_code);
+ overlay_info::OverlayInfoRegistry::Register(
+ "input_manager:keydown", &key_code_in_int32, sizeof(key_code_in_int32));
}
void InputDeviceManagerDesktop::HandlePointerEvent(
base::Token type, const system_window::InputEvent* input_event) {
dom::PointerEventInit pointer_event;
+ UpdateEventInit(input_event, &pointer_event);
UpdateMouseEventInit(input_event, &pointer_event);
switch (input_event->type()) {
@@ -262,6 +282,7 @@
const system_window::InputEvent* input_event) {
base::Token type = base::Tokens::wheel();
dom::WheelEventInit wheel_event;
+ UpdateEventInit(input_event, &wheel_event);
UpdateMouseEventInit(input_event, &wheel_event);
wheel_event.set_delta_x(input_event->delta().x());
@@ -279,6 +300,7 @@
base::Token type = base::Tokens::input();
dom::InputEventInit input_event;
+ UpdateEventInit(event, &input_event);
input_event.set_data(event->input_text());
// We do not handle composition sessions currently, so isComposing should
// always be false.
diff --git a/src/cobalt/layout/layout.gyp b/src/cobalt/layout/layout.gyp
index 1873f57..3ff7781 100644
--- a/src/cobalt/layout/layout.gyp
+++ b/src/cobalt/layout/layout.gyp
@@ -94,7 +94,6 @@
'<(DEPTH)/cobalt/loader/loader.gyp:loader',
'<(DEPTH)/cobalt/render_tree/render_tree.gyp:animations',
'<(DEPTH)/cobalt/render_tree/render_tree.gyp:render_tree',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/third_party/icu/icu.gyp:icuuc',
],
# Exporting dom so that layout_test gets the transitive include paths to
diff --git a/src/cobalt/layout/topmost_event_target.cc b/src/cobalt/layout/topmost_event_target.cc
index 502701e..fa1832c 100644
--- a/src/cobalt/layout/topmost_event_target.cc
+++ b/src/cobalt/layout/topmost_event_target.cc
@@ -14,6 +14,7 @@
#include "cobalt/layout/topmost_event_target.h"
+#include "base/debug/trace_event.h"
#include "base/optional.h"
#include "cobalt/base/token.h"
#include "cobalt/base/tokens.h"
@@ -29,8 +30,6 @@
#include "cobalt/dom/pointer_state.h"
#include "cobalt/dom/ui_event.h"
#include "cobalt/dom/wheel_event.h"
-#include "cobalt/layout/container_box.h"
-#include "cobalt/layout/used_style.h"
#include "cobalt/math/vector2d.h"
#include "cobalt/math/vector2d_f.h"
@@ -40,9 +39,15 @@
scoped_refptr<dom::HTMLElement> TopmostEventTarget::FindTopmostEventTarget(
const scoped_refptr<dom::Document>& document,
const math::Vector2dF& coordinate) {
+ TRACE_EVENT0("cobalt::layout",
+ "TopmostEventTarget::FindTopmostEventTarget()");
DCHECK(document);
DCHECK(!box_);
DCHECK(render_sequence_.empty());
+
+ // Make sure the document's layout box tree is up-to-date.
+ document->DoSynchronousLayout();
+
html_element_ = document->html();
ConsiderElement(html_element_, coordinate);
box_ = NULL;
@@ -291,6 +296,9 @@
void TopmostEventTarget::MaybeSendPointerEvents(
const scoped_refptr<dom::Event>& event) {
+ TRACE_EVENT0("cobalt::layout",
+ "TopmostEventTarget::MaybeSendPointerEvents()");
+
const dom::MouseEvent* const mouse_event =
base::polymorphic_downcast<const dom::MouseEvent* const>(event.get());
DCHECK(mouse_event);
diff --git a/src/cobalt/layout_tests/testdata/cobalt/image-onerror-expected.png b/src/cobalt/layout_tests/testdata/cobalt/image-onerror-expected.png
new file mode 100644
index 0000000..dec2811
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/image-onerror-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/cobalt/image-onerror.html b/src/cobalt/layout_tests/testdata/cobalt/image-onerror.html
new file mode 100644
index 0000000..f7f10d7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/cobalt/image-onerror.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>image-onerror</title>
+ <style>
+ #result {
+ width: 100px;
+ height:100px;
+ background-color:#0047AB;
+ }
+ </style>
+</head>
+<body>
+
+<div id="result"></div>
+
+<script>
+
+function expect(condition, message) {
+ if (!condition) {
+ document.querySelector('#result').style.display = 'none';
+ const fullMessage = `failed expectation at:
+${new Error().stack}
+${typeof message === 'undefined' ? '' : 'with message: ' + message}`;
+ console.error(fullMessage);
+ }
+}
+
+if (window.testRunner) {
+ window.testRunner.waitUntilDone();
+}
+
+const image = new Image();
+
+let imageOnErrorWasCalled = false;
+let imageErrorEventHandlerCalled = false;
+
+function endTestIfAllErrorHandlersCalled() {
+ if (imageOnErrorWasCalled && imageErrorEventHandlerCalled) {
+ // Success!
+ if (window.testRunner) {
+ window.testRunner.notifyDone();
+ }
+ }
+}
+
+image.onerror = (errorEvent) => {
+ expect(errorEvent.type === 'error', errorEvent.type);
+ imageOnErrorWasCalled = true;
+ endTestIfAllErrorHandlersCalled();
+};
+
+image.addEventListener('error', (errorEvent) => {
+ expect(errorEvent.type === 'error', errorEvent.type);
+ imageErrorEventHandlerCalled = true;
+ endTestIfAllErrorHandlersCalled();
+});
+
+image.src = 'bogusscheme://invalidurl';
+
+setTimeout(() => {
+ // If we hit this timeout we have failed because we expect the previous event
+ // handlers to have both fired before we get here.
+ expect(false);
+
+ // However, if we do get here, the subsequent expect statements can help
+ // identify which of the error handlers failed to be called.
+ expect(imageOnErrorWasCalled);
+ expect(imageErrorEventHandlerCalled);
+
+ if (window.testRunner) {
+ window.testRunner.notifyDone();
+ }
+}, 1000);
+
+</script>
+
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
index 5fa976c..d208efa 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/cobalt/layout_tests.txt
@@ -7,6 +7,7 @@
fixed-width-divs-with-background-color
font-weight
image-from-blob
+image-onerror
inline-box-with-overflow-words
inline-style-allowed-while-cloning-objects
interface-object-types-are-correct
diff --git a/src/cobalt/layout_tests/testdata/cobalt/window-onerror.html b/src/cobalt/layout_tests/testdata/cobalt/window-onerror.html
index 6baa77d..20bf0b7 100644
--- a/src/cobalt/layout_tests/testdata/cobalt/window-onerror.html
+++ b/src/cobalt/layout_tests/testdata/cobalt/window-onerror.html
@@ -28,26 +28,13 @@
let windowOnErrorWasCalled = false;
window.onerror = (message, filename, lineno, colno, error) => {
- if (typeof message === 'object') {
- // The code below is to work around Cobalt's window.onerror signature not being correct
- // (although weird, the spec says you're supposed to expand the event into its attributes before
- // dispatching it). It is still important to test the rest of window.onerror support, despite
- // this known issue.
- const errorEvent = message;
- message = errorEvent.message;
- filename = errorEvent.filename;
- lineno = errorEvent.lineno;
- colno = errorEvent.colno;
- error = errorEvent.error;
- }
-
// We allow anything that ends with "myCoolMessage" to pass, Chrome/Firefox behavior differs here
// (Chrome: "Uncaught Error: myCoolMessage", Firefox: "Error: myCoolMessage").
expect(/.*myCoolMessage/.test(message), message);
// Allow any filename that ends with "window-onerror.html", in order to not couple too heavily to
// the implementation of layout_tests.
expect(/.*window-onerror.html/.test(filename), filename);
- expect(lineno === 72, lineno);
+ expect(lineno === 83, lineno);
// The value of the column number is not standard across major browsers.
// Chrome: 1 without devtools open, 7 with devtools open.
// Edge: Always 1.
@@ -60,11 +47,35 @@
windowOnErrorWasCalled = true;
};
+// Make sure that when an event handler is added for the 'error' event via
+// addEventListener, it assumes the usual function signature of just the event
+// object.
+let windowErrorEventHandlerCalled = false;
+window.addEventListener('error', (errorEvent) => {
+ expect(errorEvent.type == 'error', errorEvent.type);
+
+ // Check the same parameters as above in the onerror attribute event handler,
+ // just make sure they are passed through as an event object instead of
+ // expanded.
+ expect(/.*myCoolMessage/.test(errorEvent.message), errorEvent.message);
+ expect(/.*window-onerror.html/.test(errorEvent.filename),
+ errorEvent.filename);
+ expect(errorEvent.lineno === 83, errorEvent.lineno);
+ expect(errorEvent.colno === 1 || errorEvent.colno === 7 ||
+ errorEvent.colno === 18, errorEvent.colno);
+ expect(typeof errorEvent.error === 'object', typeof errorEvent.error);
+ expect(String(errorEvent.error) === 'Error: myCoolMessage',
+ String(errorEvent.error));
+
+ windowErrorEventHandlerCalled = true;
+});
+
if (window.testRunner) {
window.testRunner.waitUntilDone();
}
setTimeout(() => {
expect(windowOnErrorWasCalled);
+ expect(windowErrorEventHandlerCalled);
if (window.testRunner) {
window.testRunner.notifyDone();
}
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/html/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/html/web_platform_tests.txt
new file mode 100644
index 0000000..e7ee0c4
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/html/web_platform_tests.txt
@@ -0,0 +1,3 @@
+# HTML
+
+webappapis/atob/base64.html,PASS
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc
index 281803e..78cf442 100644
--- a/src/cobalt/layout_tests/web_platform_tests.cc
+++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -197,12 +197,14 @@
std::vector<TestResult> test_results;
scoped_ptr<base::Value> root;
- base::JSONReader reader;
+ base::JSONReader reader(
+ base::JSONParserOptions::JSON_REPLACE_INVALID_CHARACTERS);
root.reset(reader.ReadToValue(json_results));
// Expect that parsing test result succeeded.
EXPECT_EQ(base::JSONReader::JSON_NO_ERROR, reader.error_code());
if (!root) {
// Unparseable JSON, or empty string.
+ LOG(ERROR) << "Web Platform Tests returned unparseable JSON test result!";
return test_results;
}
@@ -317,6 +319,10 @@
::testing::ValuesIn(EnumerateWebPlatformTests("XMLHttpRequest")));
INSTANTIATE_TEST_CASE_P(
+ cobalt_special, WebPlatformTest,
+ ::testing::ValuesIn(EnumerateWebPlatformTests("cobalt_special")));
+
+INSTANTIATE_TEST_CASE_P(
csp, WebPlatformTest,
::testing::ValuesIn(EnumerateWebPlatformTests("content-security-policy")));
@@ -336,6 +342,9 @@
::testing::ValuesIn(EnumerateWebPlatformTests("fetch", "'fetch' in this")));
#endif
+INSTANTIATE_TEST_CASE_P(html, WebPlatformTest,
+ ::testing::ValuesIn(EnumerateWebPlatformTests("html")));
+
INSTANTIATE_TEST_CASE_P(
mediasession, WebPlatformTest,
::testing::ValuesIn(EnumerateWebPlatformTests("mediasession")));
@@ -347,10 +356,6 @@
"streams", "'ReadableStream' in this")));
#endif
-INSTANTIATE_TEST_CASE_P(
- cobalt_special, WebPlatformTest,
- ::testing::ValuesIn(EnumerateWebPlatformTests("cobalt_special")));
-
#endif // !defined(COBALT_WIN)
} // namespace layout_tests
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index de9ca49..4f856e6 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -30,7 +30,7 @@
#include "cobalt/loader/image/image.h"
#include "cobalt/render_tree/color_rgba.h"
#include "cobalt/render_tree/resource_provider.h"
-#include "third_party/libwebp/webp/demux.h"
+#include "third_party/libwebp/src/webp/demux.h"
namespace cobalt {
namespace loader {
diff --git a/src/cobalt/loader/image/image_encoder.cc b/src/cobalt/loader/image/image_encoder.cc
index da3f832..08ffc75 100644
--- a/src/cobalt/loader/image/image_encoder.cc
+++ b/src/cobalt/loader/image/image_encoder.cc
@@ -14,8 +14,9 @@
#include "cobalt/loader/image/image_encoder.h"
+#include "base/debug/trace_event.h"
#include "cobalt/renderer/test/png_utils/png_encode.h"
-#include "third_party/libwebp/webp/encode.h"
+#include "third_party/libwebp/src/webp/encode.h"
namespace cobalt {
namespace loader {
@@ -26,6 +27,7 @@
scoped_array<uint8> WriteRGBAPixelsToPNG(const uint8* const pixel_data,
const math::Size& dimensions,
size_t* out_num_bytes) {
+ TRACE_EVENT0("cobalt::loader", "ImageEncoder::WriteRGBAPixelsToPNG()");
const int kRGBABytesPerPixel = 4;
const int kPitchSizeInBytes = dimensions.width() * kRGBABytesPerPixel;
return EncodeRGBAToBuffer(pixel_data, dimensions.width(), dimensions.height(),
@@ -35,6 +37,7 @@
scoped_refptr<loader::image::EncodedStaticImage> CompressRGBAImage(
loader::image::EncodedStaticImage::ImageFormat desired_format,
const uint8* const image_data, const math::Size& dimensions) {
+ TRACE_EVENT0("cobalt::loader", "ImageEncoder::CompressRGBAImage()");
using ImageFormat = loader::image::EncodedStaticImage::ImageFormat;
switch (desired_format) {
case ImageFormat::kPNG: {
diff --git a/src/cobalt/loader/image/webp_image_decoder.cc b/src/cobalt/loader/image/webp_image_decoder.cc
index 5602ef6..c372790 100644
--- a/src/cobalt/loader/image/webp_image_decoder.cc
+++ b/src/cobalt/loader/image/webp_image_decoder.cc
@@ -39,8 +39,6 @@
config_.options.no_fancy_upsampling = 1;
// Don't use multi-threaded decoding.
config_.options.use_threads = 0;
- // Discard enhancement layer.
- config_.options.no_enhancement = 1;
}
WEBPImageDecoder::~WEBPImageDecoder() {
diff --git a/src/cobalt/loader/image/webp_image_decoder.h b/src/cobalt/loader/image/webp_image_decoder.h
index aa93e42..45d773f 100644
--- a/src/cobalt/loader/image/webp_image_decoder.h
+++ b/src/cobalt/loader/image/webp_image_decoder.h
@@ -20,7 +20,7 @@
#include "cobalt/loader/image/animated_webp_image.h"
#include "cobalt/loader/image/image_data_decoder.h"
-#include "third_party/libwebp/webp/decode.h"
+#include "third_party/libwebp/src/webp/decode.h"
namespace cobalt {
namespace loader {
diff --git a/src/cobalt/loader/loader_factory.cc b/src/cobalt/loader/loader_factory.cc
index 5ef2229..0c7d695 100644
--- a/src/cobalt/loader/loader_factory.cc
+++ b/src/cobalt/loader/loader_factory.cc
@@ -97,6 +97,51 @@
return loader.Pass();
}
+scoped_ptr<Loader> LoaderFactory::CreateLinkLoader(
+ const GURL& url, const Origin& origin,
+ const csp::SecurityCallback& url_security_callback,
+ const loader::RequestMode cors_mode,
+ const TextDecoder::SuccessCallback& success_callback,
+ const Loader::OnErrorFunction& loader_error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ scoped_ptr<loader::Decoder> decoder(
+ new loader::TextDecoder(success_callback));
+
+ Loader::FetcherCreator fetcher_creator =
+ MakeFetcherCreator(url, url_security_callback, cors_mode, origin);
+ scoped_ptr<Loader> loader(new Loader(
+ fetcher_creator, decoder.Pass(), loader_error_callback,
+
+ base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+ is_suspended_));
+
+ OnLoaderCreated(loader.get());
+ return loader.Pass();
+}
+
+scoped_ptr<Loader> LoaderFactory::CreateScriptLoader(
+ const GURL& url, const Origin& origin,
+ const csp::SecurityCallback& url_security_callback,
+ const TextDecoder::SuccessCallback& success_callback,
+ const Loader::OnErrorFunction& loader_error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ scoped_ptr<loader::Decoder> decoder(
+ new loader::TextDecoder(success_callback));
+
+ Loader::FetcherCreator fetcher_creator =
+ MakeFetcherCreator(url, url_security_callback, kNoCORSMode, origin);
+ scoped_ptr<Loader> loader(new Loader(
+ fetcher_creator, decoder.Pass(), loader_error_callback,
+
+ base::Bind(&LoaderFactory::OnLoaderDestroyed, base::Unretained(this)),
+ is_suspended_));
+
+ OnLoaderCreated(loader.get());
+ return loader.Pass();
+}
+
Loader::FetcherCreator LoaderFactory::MakeFetcherCreator(
const GURL& url, const csp::SecurityCallback& url_security_callback,
RequestMode request_mode, const Origin& origin) {
diff --git a/src/cobalt/loader/loader_factory.h b/src/cobalt/loader/loader_factory.h
index cfef2fc..cde4c56 100644
--- a/src/cobalt/loader/loader_factory.h
+++ b/src/cobalt/loader/loader_factory.h
@@ -19,11 +19,13 @@
#include "base/threading/thread.h"
#include "cobalt/csp/content_security_policy.h"
+#include "cobalt/loader/fetcher.h"
#include "cobalt/loader/fetcher_factory.h"
#include "cobalt/loader/font/typeface_decoder.h"
#include "cobalt/loader/image/image_decoder.h"
#include "cobalt/loader/loader.h"
#include "cobalt/loader/mesh/mesh_decoder.h"
+#include "cobalt/loader/text_decoder.h"
#include "cobalt/render_tree/resource_provider.h"
#include "googleurl/src/gurl.h"
@@ -60,6 +62,21 @@
const mesh::MeshDecoder::SuccessCallback& success_callback,
const mesh::MeshDecoder::ErrorCallback& error_callback);
+ // Creates a loader that fetches and decodes a Javascript resource.
+ scoped_ptr<Loader> CreateScriptLoader(
+ const GURL& url, const Origin& origin,
+ const csp::SecurityCallback& url_security_callback,
+ const TextDecoder::SuccessCallback& success_callback,
+ const Loader::OnErrorFunction& loader_error_callback);
+
+ // Creates a loader that fetches and decodes a link resources.
+ scoped_ptr<Loader> CreateLinkLoader(
+ const GURL& url, const Origin& origin,
+ const csp::SecurityCallback& url_security_callback,
+ const loader::RequestMode cors_mode,
+ const TextDecoder::SuccessCallback& success_callback,
+ const Loader::OnErrorFunction& loader_error_callback);
+
// Clears out the loader factory's resource provider, aborting any in-progress
// loads.
void Suspend();
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h
index 6185752..e25c19f 100644
--- a/src/cobalt/loader/resource_cache.h
+++ b/src/cobalt/loader/resource_cache.h
@@ -573,6 +573,10 @@
ResourceSet non_callback_blocking_loading_resource_set_;
// Resources that have completed loading and have callbacks pending.
ResourceCallbackMap pending_callback_map_;
+ // Timer used to ensure that pending callbacks are handled in a timely manner
+ // when callbacks are being blocked by additional loading resources.
+ base::OneShotTimer<ResourceCache<CacheType>> process_pending_callback_timer_;
+
// Whether or not ProcessPendingCallbacks() is running.
bool is_processing_pending_callbacks_;
// Whether or not callbacks are currently disabled.
@@ -709,6 +713,7 @@
template <typename CacheType>
void ResourceCache<CacheType>::ProcessPendingCallbacks() {
DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+ process_pending_callback_timer_.Stop();
// If callbacks are disabled, simply return.
if (are_callbacks_disabled_) {
@@ -921,6 +926,10 @@
template <typename CacheType>
void ResourceCache<CacheType>::ProcessPendingCallbacksIfUnblocked() {
+ // If there are no callback blocking resources, then simply process any
+ // pending callbacks now; otherwise, start |process_pending_callback_timer_|,
+ // which ensures that the callbacks are handled in a timely manner while still
+ // allowing them to be batched.
if (callback_blocking_loading_resource_set_.empty()) {
ProcessPendingCallbacks();
@@ -931,6 +940,19 @@
callback_blocking_loading_resource_set_.swap(
non_callback_blocking_loading_resource_set_);
}
+ } else if (!pending_callback_map_.empty() &&
+ !process_pending_callback_timer_.IsRunning()) {
+ // The maximum delay for a pending callback is set to 500ms. After that, the
+ // callback will be processed regardless of how many callback blocking
+ // loading resources remain. This specific value maximizes callback batching
+ // on fast networks while also keeping the callback delay on slow networks
+ // to a minimum and is based on significant testing.
+ const int64 kMaxPendingCallbackDelayInMilliseconds = 500;
+ process_pending_callback_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(
+ kMaxPendingCallbackDelayInMilliseconds),
+ this, &ResourceCache::ProcessPendingCallbacks);
}
}
diff --git a/src/cobalt/loader/sync_loader.cc b/src/cobalt/loader/sync_loader.cc
index eb75db5..76b69da 100644
--- a/src/cobalt/loader/sync_loader.cc
+++ b/src/cobalt/loader/sync_loader.cc
@@ -33,7 +33,9 @@
// decoder creators.
class LoaderOnThread {
public:
- LoaderOnThread() : waitable_event_(false, false) {}
+ LoaderOnThread()
+ : start_waitable_event_(false, false),
+ end_waitable_event_(false, false) {}
// Start() and End() should be called on the same thread, the sychronous load
// thread, so the member objects are created, execute, and are destroyed,
@@ -44,15 +46,28 @@
base::Callback<void(const std::string&)> error_callback);
void End();
- void Signal() { waitable_event_.Signal(); }
- void Wait() { waitable_event_.Wait(); }
+ void SignalStartDone() { start_waitable_event_.Signal(); }
+ void SignalEndDone() { end_waitable_event_.Signal(); }
+
+ void WaitForStart(base::WaitableEvent* interrupt_event) {
+ base::WaitableEvent* event_array[] = {&start_waitable_event_,
+ interrupt_event};
+ size_t effective_size = arraysize(event_array);
+ if (!interrupt_event) {
+ --effective_size;
+ }
+ base::WaitableEvent::WaitMany(event_array, effective_size);
+ }
+
+ void WaitForEnd() { end_waitable_event_.Wait(); }
private:
scoped_ptr<Decoder> decoder_;
scoped_ptr<FetcherToDecoderAdapter> fetcher_to_decoder_adaptor_;
scoped_ptr<Fetcher> fetcher_;
- base::WaitableEvent waitable_event_;
+ base::WaitableEvent start_waitable_event_;
+ base::WaitableEvent end_waitable_event_;
};
// This class is responsible for passing chunks of data from fetcher to decoder
@@ -75,12 +90,12 @@
DCHECK(fetcher);
decoder_->SetLastURLOrigin(fetcher->last_url_origin());
decoder_->Finish();
- loader_on_thread_->Signal();
+ loader_on_thread_->SignalStartDone();
}
void OnError(Fetcher* fetcher, const std::string& error) override {
UNREFERENCED_PARAMETER(fetcher);
error_callback_.Run(error);
- loader_on_thread_->Signal();
+ loader_on_thread_->SignalStartDone();
}
private:
@@ -103,7 +118,7 @@
fetcher_.reset();
fetcher_to_decoder_adaptor_.reset();
decoder_.reset();
- Signal();
+ SignalEndDone();
}
} // namespace
@@ -113,7 +128,7 @@
//////////////////////////////////////////////////////////////////
void LoadSynchronously(
- MessageLoop* message_loop,
+ MessageLoop* message_loop, base::WaitableEvent* interrupt_trigger,
base::Callback<scoped_ptr<Fetcher>(Fetcher::Handler*)> fetcher_creator,
base::Callback<scoped_ptr<Decoder>()> decoder_creator,
base::Callback<void(const std::string&)> error_callback) {
@@ -127,12 +142,16 @@
FROM_HERE,
base::Bind(&LoaderOnThread::Start, base::Unretained(&loader_on_thread),
fetcher_creator, decoder_creator, error_callback));
- loader_on_thread.Wait();
+ loader_on_thread.WaitForStart(interrupt_trigger);
message_loop->PostTask(
FROM_HERE,
base::Bind(&LoaderOnThread::End, base::Unretained(&loader_on_thread)));
- loader_on_thread.Wait();
+
+ // Wait for a different event here, since it is possible that the first
+ // wait was interrupted, and the fetcher completion can still |Signal()|
+ // the start event.
+ loader_on_thread.WaitForEnd();
}
} // namespace loader
diff --git a/src/cobalt/loader/sync_loader.h b/src/cobalt/loader/sync_loader.h
index dadc2b8..1280203 100644
--- a/src/cobalt/loader/sync_loader.h
+++ b/src/cobalt/loader/sync_loader.h
@@ -20,6 +20,7 @@
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "cobalt/loader/decoder.h"
#include "cobalt/loader/fetcher.h"
@@ -31,7 +32,7 @@
// and use the given message loop to load the resource. The fetcher and decoder
// are responsible for setting up timeout for themselves.
void LoadSynchronously(
- MessageLoop* message_loop,
+ MessageLoop* message_loop, base::WaitableEvent* interrupt_trigger,
base::Callback<scoped_ptr<Fetcher>(Fetcher::Handler*)> fetcher_creator,
base::Callback<scoped_ptr<Decoder>()> decoder_creator,
base::Callback<void(const std::string&)> error_callback);
diff --git a/src/cobalt/loader/text_decoder.h b/src/cobalt/loader/text_decoder.h
index bbb24a2..69b58fd 100644
--- a/src/cobalt/loader/text_decoder.h
+++ b/src/cobalt/loader/text_decoder.h
@@ -34,9 +34,10 @@
// results.
class TextDecoder : public Decoder {
public:
- explicit TextDecoder(
- base::Callback<void(const loader::Origin&, scoped_ptr<std::string>)>
- done_callback)
+ typedef base::Callback<void(const loader::Origin&, scoped_ptr<std::string>)>
+ SuccessCallback;
+
+ explicit TextDecoder(const SuccessCallback& done_callback)
: done_callback_(done_callback), suspended_(false) {}
~TextDecoder() override {}
@@ -97,8 +98,7 @@
private:
base::ThreadChecker thread_checker_;
- base::Callback<void(const loader::Origin&, scoped_ptr<std::string>)>
- done_callback_;
+ SuccessCallback done_callback_;
loader::Origin last_url_origin_;
scoped_ptr<std::string> text_;
bool suspended_;
diff --git a/src/cobalt/media/base/BUILD.gn b/src/cobalt/media/base/BUILD.gn
index 0c3ad22..8c223cf 100644
--- a/src/cobalt/media/base/BUILD.gn
+++ b/src/cobalt/media/base/BUILD.gn
@@ -147,8 +147,6 @@
"media_client.h",
"media_content_type.cc",
"media_content_type.h",
- "media_keys.cc",
- "media_keys.h",
"media_log.cc",
"media_log.h",
"media_log_event.h",
diff --git a/src/cobalt/media/base/drm_system.cc b/src/cobalt/media/base/drm_system.cc
index 8ecc2a3..f03ba30 100644
--- a/src/cobalt/media/base/drm_system.cc
+++ b/src/cobalt/media/base/drm_system.cc
@@ -93,11 +93,15 @@
#if SB_HAS(DRM_KEY_STATUSES)
,
OnSessionKeyStatusesChangedFunc
+#endif // SB_HAS(DRM_KEY_STATUSES)
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+ ,
+ OnServerCertificateUpdatedFunc
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
#if SB_HAS(DRM_SESSION_CLOSED)
,
OnSessionClosedFunc
#endif // SB_HAS(DRM_SESSION_CLOSED)
-#endif // SB_HAS(DRM_KEY_STATUSES)
)), // NOLINT(whitespace/parens)
message_loop_(MessageLoop::current()),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
@@ -130,6 +134,31 @@
)); // NOLINT(whitespace/parens)
}
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+bool DrmSystem::IsServerCertificateUpdatable() {
+ return SbDrmIsServerCertificateUpdatable(wrapped_drm_system_);
+}
+
+void DrmSystem::UpdateServerCertificate(
+ const uint8_t* certificate, int certificate_size,
+ ServerCertificateUpdatedCallback server_certificate_updated_callback) {
+ DCHECK(IsServerCertificateUpdatable());
+ int ticket = next_session_update_request_ticket_++;
+ ticket_to_server_certificate_updated_map_.insert(
+ std::make_pair(ticket, server_certificate_updated_callback));
+ SbDrmUpdateServerCertificate(wrapped_drm_system_, ticket, certificate,
+ certificate_size);
+}
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+bool DrmSystem::IsServerCertificateUpdatable() { return false; }
+
+void DrmSystem::UpdateServerCertificate(
+ const uint8_t* certificate, int certificate_size,
+ ServerCertificateUpdatedCallback server_certificate_updated_callback) {
+ NOTREACHED();
+}
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
void DrmSystem::GenerateSessionUpdateRequest(
Session* session, const std::string& type, const uint8_t* init_data,
int init_data_length, const SessionUpdateRequestGeneratedCallback&
@@ -172,8 +201,11 @@
}
void DrmSystem::OnSessionUpdateRequestGenerated(
- int ticket, const base::optional<std::string>& session_id,
+ SessionTicketAndOptionalId ticket_and_optional_id, SbDrmStatus status,
+ SbDrmSessionRequestType type, const std::string& error_message,
scoped_array<uint8> message, int message_size) {
+ int ticket = ticket_and_optional_id.ticket;
+ const base::optional<std::string>& session_id = ticket_and_optional_id.id;
if (SbDrmTicketIsValid(ticket)) {
// Called back as a result of |SbDrmGenerateSessionUpdateRequest|.
@@ -197,11 +229,12 @@
id_to_session_map_.insert(
std::make_pair(*session_id, session_update_request.session));
- session_update_request.generated_callback.Run(message.Pass(),
+ session_update_request.generated_callback.Run(type, message.Pass(),
message_size);
} else {
// Failure during request generation.
- session_update_request.did_not_generate_callback.Run();
+ session_update_request.did_not_generate_callback.Run(status,
+ error_message);
}
// Sweep the context of |GenerateSessionUpdateRequest|.
@@ -226,12 +259,13 @@
}
Session* session = session_iterator->second;
- session->update_request_generated_callback().Run(message.Pass(),
+ session->update_request_generated_callback().Run(type, message.Pass(),
message_size);
}
}
-void DrmSystem::OnSessionUpdated(int ticket, bool succeeded) {
+void DrmSystem::OnSessionUpdated(int ticket, SbDrmStatus status,
+ const std::string& error_message) {
// Restore the context of |UpdateSession|.
TicketToSessionUpdateMap::iterator session_update_iterator =
ticket_to_session_update_map_.find(ticket);
@@ -242,10 +276,10 @@
const SessionUpdate& session_update = session_update_iterator->second;
// Interpret the result.
- if (succeeded) {
+ if (status == kSbDrmStatusSuccess) {
session_update.updated_callback.Run();
} else {
- session_update.did_not_update_callback.Run();
+ session_update.did_not_update_callback.Run(status, error_message);
}
// Sweep the context of |UpdateSession|.
@@ -285,11 +319,36 @@
}
#endif // SB_HAS(DRM_SESSION_CLOSED)
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+void DrmSystem::OnServerCertificateUpdated(int ticket, SbDrmStatus status,
+ const std::string& error_message) {
+ auto iter = ticket_to_server_certificate_updated_map_.find(ticket);
+ if (iter == ticket_to_server_certificate_updated_map_.end()) {
+ LOG(ERROR) << "Unknown ticket: " << ticket << ".";
+ return;
+ }
+ iter->second.Run(status, error_message);
+ ticket_to_server_certificate_updated_map_.erase(iter);
+}
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
// static
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
+ SbDrmSystem wrapped_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) {
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
void DrmSystem::OnSessionUpdateRequestGeneratedFunc(
SbDrmSystem wrapped_drm_system, void* context, int ticket,
const void* session_id, int session_id_size, const void* content,
int content_size, const char* url) {
+ SbDrmStatus status =
+ session_id ? kSbDrmStatusSuccess : kSbDrmStatusUnknownError;
+ SbDrmSessionRequestType type = kSbDrmSessionRequestTypeLicenseRequest;
+ const char* error_message = NULL;
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
DCHECK(context);
DrmSystem* drm_system = static_cast<DrmSystem*>(context);
DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
@@ -306,23 +365,39 @@
}
drm_system->message_loop_->PostTask(
- FROM_HERE, base::Bind(&DrmSystem::OnSessionUpdateRequestGenerated,
- drm_system->weak_this_, ticket, session_id_copy,
- base::Passed(&content_copy), content_size));
+ FROM_HERE,
+ base::Bind(&DrmSystem::OnSessionUpdateRequestGenerated,
+ drm_system->weak_this_,
+ SessionTicketAndOptionalId{ticket, session_id_copy}, status,
+ type, error_message ? std::string(error_message) : "",
+ base::Passed(&content_copy), content_size));
}
// static
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
+ void* context, int ticket,
+ SbDrmStatus status,
+ const char* error_message,
+ const void* /*session_id*/,
+ int /*session_id_size*/) {
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
void DrmSystem::OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
void* context, int ticket,
const void* /*session_id*/,
int /*session_id_size*/, bool succeeded) {
+ SbDrmStatus status =
+ succeeded ? kSbDrmStatusSuccess : kSbDrmStatusUnknownError;
+ const char* error_message = NULL;
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
DCHECK(context);
DrmSystem* drm_system = static_cast<DrmSystem*>(context);
DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
drm_system->message_loop_->PostTask(
- FROM_HERE, base::Bind(&DrmSystem::OnSessionUpdated,
- drm_system->weak_this_, ticket, succeeded));
+ FROM_HERE,
+ base::Bind(&DrmSystem::OnSessionUpdated, drm_system->weak_this_, ticket,
+ status, error_message ? std::string(error_message) : ""));
}
#if SB_HAS(DRM_KEY_STATUSES)
@@ -359,6 +434,23 @@
}
#endif // SB_HAS(DRM_KEY_STATUSES)
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+// static
+void DrmSystem::OnServerCertificateUpdatedFunc(SbDrmSystem wrapped_drm_system,
+ void* context, int ticket,
+ SbDrmStatus status,
+ const char* error_message) {
+ DCHECK(context);
+ DrmSystem* drm_system = static_cast<DrmSystem*>(context);
+ DCHECK_EQ(wrapped_drm_system, drm_system->wrapped_drm_system_);
+
+ drm_system->message_loop_->PostTask(
+ FROM_HERE, base::Bind(&DrmSystem::OnServerCertificateUpdated,
+ drm_system->weak_this_, ticket, status,
+ error_message ? std::string(error_message) : ""));
+}
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
#if SB_HAS(DRM_SESSION_CLOSED)
// static
void DrmSystem::OnSessionClosedFunc(SbDrmSystem wrapped_drm_system,
diff --git a/src/cobalt/media/base/drm_system.h b/src/cobalt/media/base/drm_system.h
index b1c85af..9a06ffc 100644
--- a/src/cobalt/media/base/drm_system.h
+++ b/src/cobalt/media/base/drm_system.h
@@ -35,11 +35,16 @@
// from the same thread where |DrmSystem| was instantiated.
class DrmSystem : public base::RefCounted<DrmSystem> {
public:
- typedef base::Callback<void(scoped_array<uint8> message, int message_size)>
+ typedef base::Callback<void(SbDrmSessionRequestType type,
+ scoped_array<uint8> message, int message_size)>
SessionUpdateRequestGeneratedCallback;
- typedef base::Callback<void()> SessionUpdateRequestDidNotGenerateCallback;
+ typedef base::Callback<void(SbDrmStatus status,
+ const std::string& error_message)>
+ SessionUpdateRequestDidNotGenerateCallback;
typedef base::Callback<void()> SessionUpdatedCallback;
- typedef base::Callback<void()> SessionDidNotUpdateCallback;
+ typedef base::Callback<void(SbDrmStatus status,
+ const std::string& error_message)>
+ SessionDidNotUpdateCallback;
#if SB_HAS(DRM_KEY_STATUSES)
typedef base::Callback<void(const std::vector<std::string>& key_ids,
const std::vector<SbDrmKeyStatus>& key_statuses)>
@@ -48,6 +53,9 @@
#if SB_HAS(DRM_SESSION_CLOSED)
typedef base::Callback<void()> SessionClosedCallback;
#endif // SB_HAS(DRM_SESSION_CLOSED)
+ typedef base::Callback<void(SbDrmStatus status,
+ const std::string& error_message)>
+ ServerCertificateUpdatedCallback;
// Flyweight that provides RAII semantics for sessions.
// Most of logic is implemented by |DrmSystem| and thus sessions must be
@@ -150,6 +158,11 @@
#endif // SB_HAS(DRM_SESSION_CLOSED)
); // NOLINT(whitespace/parens)
+ bool IsServerCertificateUpdatable();
+ void UpdateServerCertificate(
+ const uint8_t* certificate, int certificate_size,
+ ServerCertificateUpdatedCallback server_certificate_updated_callback);
+
private:
// Stores context of |GenerateSessionUpdateRequest|.
struct SessionUpdateRequest {
@@ -162,6 +175,9 @@
typedef base::hash_map<std::string, Session*> IdToSessionMap;
+ typedef base::hash_map<int, ServerCertificateUpdatedCallback>
+ TicketToServerCertificateUpdatedMap;
+
// Stores context of |Session::Update|.
struct SessionUpdate {
SessionUpdatedCallback updated_callback;
@@ -169,6 +185,13 @@
};
typedef base::hash_map<int, SessionUpdate> TicketToSessionUpdateMap;
+ // Defined to work around the limitation on number of parameters of
+ // base::Bind().
+ struct SessionTicketAndOptionalId {
+ int ticket;
+ base::optional<std::string> id;
+ };
+
// Private API for |Session|.
void GenerateSessionUpdateRequest(
Session* session, const std::string& type, const uint8_t* init_data,
@@ -185,18 +208,39 @@
// Called on the constructor thread, parameters are copied and owned by these
// methods.
void OnSessionUpdateRequestGenerated(
- int ticket, const base::optional<std::string>& session_id,
+ SessionTicketAndOptionalId ticket_and_optional_id, SbDrmStatus status,
+ SbDrmSessionRequestType type, const std::string& error_message,
scoped_array<uint8> message, int message_size);
- void OnSessionUpdated(int ticket, bool succeeded);
+ void OnSessionUpdated(int ticket, SbDrmStatus status,
+ const std::string& error_message);
+
#if SB_HAS(DRM_KEY_STATUSES)
void OnSessionKeyStatusChanged(
const std::string& session_id, const std::vector<std::string>& key_ids,
const std::vector<SbDrmKeyStatus>& key_statuses);
#endif // SB_HAS(DRM_KEY_STATUSES)
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+ void OnServerCertificateUpdated(int ticket, SbDrmStatus status,
+ const std::string& error_message);
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
#if SB_HAS(DRM_SESSION_CLOSED)
void OnSessionClosed(const std::string& session_id);
#endif // SB_HAS(DRM_SESSION_CLOSED)
// Called on any thread, parameters need to be copied immediately.
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+ static void OnSessionUpdateRequestGeneratedFunc(
+ SbDrmSystem wrapped_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);
+ static void OnSessionUpdatedFunc(SbDrmSystem wrapped_drm_system,
+ void* context, int ticket,
+ SbDrmStatus status,
+ const char* error_message,
+ const void* session_id,
+ int session_id_length);
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
static void OnSessionUpdateRequestGeneratedFunc(
SbDrmSystem wrapped_drm_system, void* context, int ticket,
const void* session_id, int session_id_size, const void* content,
@@ -205,6 +249,8 @@
void* context, int ticket,
const void* session_id,
int session_id_length, bool succeeded);
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
#if SB_HAS(DRM_KEY_STATUSES)
static void OnSessionKeyStatusesChangedFunc(
SbDrmSystem wrapped_drm_system, void* context, const void* session_id,
@@ -218,6 +264,14 @@
const void* session_id,
int session_id_size);
#endif // SB_HAS(DRM_SESSION_CLOSED)
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+ static void OnServerCertificateUpdatedFunc(SbDrmSystem wrapped_drm_system,
+ void* context, int ticket,
+ SbDrmStatus status,
+ const char* error_message);
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
const SbDrmSystem wrapped_drm_system_;
MessageLoop* const message_loop_;
@@ -234,6 +288,8 @@
// Supports spontaneous invocations of |SbDrmSessionUpdateRequestFunc|.
IdToSessionMap id_to_session_map_;
+ TicketToServerCertificateUpdatedMap ticket_to_server_certificate_updated_map_;
+
// Supports concurrent calls to |Session::Update|.
int next_session_update_ticket_;
TicketToSessionUpdateMap ticket_to_session_update_map_;
diff --git a/src/cobalt/media/base/media_keys.cc b/src/cobalt/media/base/media_keys.cc
deleted file mode 100644
index ab878cb..0000000
--- a/src/cobalt/media/base/media_keys.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/media/base/media_keys.h"
-
-namespace cobalt {
-namespace media {
-
-MediaKeys::MediaKeys() {}
-
-MediaKeys::~MediaKeys() {}
-
-CdmContext* MediaKeys::GetCdmContext() { return NULL; }
-
-void MediaKeys::DeleteOnCorrectThread() const { delete this; }
-
-// static
-void MediaKeysTraits::Destruct(const MediaKeys* media_keys) {
- media_keys->DeleteOnCorrectThread();
-}
-
-} // namespace media
-} // namespace cobalt
diff --git a/src/cobalt/media/base/media_keys.h b/src/cobalt/media/base/media_keys.h
deleted file mode 100644
index cf66444..0000000
--- a/src/cobalt/media/base/media_keys.h
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COBALT_MEDIA_BASE_MEDIA_KEYS_H_
-#define COBALT_MEDIA_BASE_MEDIA_KEYS_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_vector.h"
-#include "cobalt/media/base/eme_constants.h"
-#include "cobalt/media/base/media_export.h"
-#include "googleurl/src/gurl.h"
-#include "starboard/types.h"
-
-namespace base {
-class Time;
-}
-
-namespace cobalt {
-namespace media {
-
-class CdmContext;
-struct CdmKeyInformation;
-struct MediaKeysTraits;
-
-template <typename... T>
-class CdmPromiseTemplate;
-
-typedef CdmPromiseTemplate<std::string> NewSessionCdmPromise;
-typedef CdmPromiseTemplate<> SimpleCdmPromise;
-typedef ScopedVector<CdmKeyInformation> CdmKeysInfo;
-
-// An interface that represents the Content Decryption Module (CDM) in the
-// Encrypted Media Extensions (EME) spec in Chromium.
-// See http://w3c.github.io/encrypted-media/#cdm
-//
-// * Ownership
-//
-// This class is ref-counted. However, a ref-count should only be held by:
-// - The owner of the CDM. This is usually some class in the EME stack, e.g.
-// CdmSessionAdapter in the render process, or MojoCdmService in a non-render
-// process.
-// - The media player that uses the CDM, to prevent the CDM from being
-// destructed while still being used by the media player.
-//
-// When binding class methods into callbacks, prefer WeakPtr to using |this|
-// directly to avoid having a ref-count held by the callback.
-//
-// * Thread Safety
-//
-// Most CDM operations happen on one thread. However, it is not uncommon that
-// the media player lives on a different thread and may call into the CDM from
-// that thread. For example, if the CDM supports a Decryptor interface, the
-// Decryptor methods could be called on a different thread. The CDM
-// implementation should make sure it's thread safe for these situations.
-//
-// TODO(xhwang): Rename MediaKeys to ContentDecryptionModule. See
-// http://crbug.com/309237
-
-class MEDIA_EXPORT MediaKeys
- : public base::RefCountedThreadSafe<MediaKeys, MediaKeysTraits> {
- public:
- // TODO(xhwang): Remove after prefixed EME support is removed. See
- // http://crbug.com/249976
- // Must be a superset of cdm::MediaKeyException.
- enum Exception {
- NOT_SUPPORTED_ERROR,
- INVALID_STATE_ERROR,
- INVALID_ACCESS_ERROR,
- QUOTA_EXCEEDED_ERROR,
- UNKNOWN_ERROR,
- CLIENT_ERROR,
- OUTPUT_ERROR,
- EXCEPTION_MAX = OUTPUT_ERROR
- };
-
- // Type of license required when creating/loading a session.
- // Must be consistent with the values specified in the spec:
- // https://w3c.github.io/encrypted-media/#idl-def-MediaKeySessionType
- enum SessionType {
- TEMPORARY_SESSION,
- PERSISTENT_LICENSE_SESSION,
- PERSISTENT_RELEASE_MESSAGE_SESSION,
- SESSION_TYPE_MAX = PERSISTENT_RELEASE_MESSAGE_SESSION
- };
-
- // Type of message being sent to the application.
- // Must be consistent with the values specified in the spec:
- // https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType
- enum MessageType {
- LICENSE_REQUEST,
- LICENSE_RENEWAL,
- LICENSE_RELEASE,
- MESSAGE_TYPE_MAX = LICENSE_RELEASE
- };
-
- // Provides a server certificate to be used to encrypt messages to the
- // license server.
- virtual void SetServerCertificate(
- const std::vector<uint8_t>& certificate,
- std::unique_ptr<SimpleCdmPromise> promise) = 0;
-
- // Creates a session with |session_type|. Then generates a request with the
- // |init_data_type| and |init_data|.
- // Note:
- // 1. The session ID will be provided when the |promise| is resolved.
- // 2. The generated request should be returned through a SessionMessageCB,
- // which must be AFTER the |promise| is resolved. Otherwise, the session ID
- // in the callback will not be recognized.
- // 3. UpdateSession(), CloseSession() and RemoveSession() should only be
- // called after the |promise| is resolved.
- virtual void CreateSessionAndGenerateRequest(
- SessionType session_type, EmeInitDataType init_data_type,
- const std::vector<uint8_t>& init_data,
- std::unique_ptr<NewSessionCdmPromise> promise) = 0;
-
- // Loads a session with the |session_id| provided.
- // Note: UpdateSession(), CloseSession() and RemoveSession() should only be
- // called after the |promise| is resolved.
- virtual void LoadSession(SessionType session_type,
- const std::string& session_id,
- std::unique_ptr<NewSessionCdmPromise> promise) = 0;
-
- // Updates a session specified by |session_id| with |response|.
- virtual void UpdateSession(const std::string& session_id,
- const std::vector<uint8_t>& response,
- std::unique_ptr<SimpleCdmPromise> promise) = 0;
-
- // Closes the session specified by |session_id|. The CDM should resolve or
- // reject the |promise| when the call has been processed. This may be before
- // the session is closed. Once the session is closed, a SessionClosedCB must
- // also be called.
- virtual void CloseSession(const std::string& session_id,
- std::unique_ptr<SimpleCdmPromise> promise) = 0;
-
- // Removes stored session data associated with the session specified by
- // |session_id|.
- virtual void RemoveSession(const std::string& session_id,
- std::unique_ptr<SimpleCdmPromise> promise) = 0;
-
- // Returns the CdmContext associated with |this|. The returned CdmContext is
- // owned by |this| and the caller needs to make sure it is not used after
- // |this| is destructed.
- // Returns null if CdmContext is not supported. Instead the media player may
- // use the CDM via some platform specific method.
- // By default this method returns null.
- // TODO(xhwang): Convert all SetCdm() implementations to use CdmContext so
- // that this function should never return NULL.
- virtual CdmContext* GetCdmContext();
-
- // Deletes |this| on the correct thread. By default |this| is deleted
- // immediately. Override this method if |this| needs to be deleted on a
- // specific thread.
- virtual void DeleteOnCorrectThread() const;
-
- protected:
- friend class base::RefCountedThreadSafe<MediaKeys, MediaKeysTraits>;
-
- MediaKeys();
- virtual ~MediaKeys();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MediaKeys);
-};
-
-struct MEDIA_EXPORT MediaKeysTraits {
- // Destroys |media_keys| on the correct thread.
- static void Destruct(const MediaKeys* media_keys);
-};
-
-// CDM session event callbacks.
-
-// Called when the CDM needs to queue a message event to the session object.
-// See http://w3c.github.io/encrypted-media/#dom-evt-message
-typedef base::Callback<void(
- const std::string& session_id, MediaKeys::MessageType message_type,
- const std::vector<uint8_t>& message)> SessionMessageCB;
-
-// Called when the session specified by |session_id| is closed. Note that the
-// CDM may close a session at any point, such as in response to a CloseSession()
-// call, when the session is no longer needed, or when system resources are
-// lost. See http://w3c.github.io/encrypted-media/#session-close
-typedef base::Callback<void(const std::string& session_id)> SessionClosedCB;
-
-// Called when there has been a change in the keys in the session or their
-// status. See http://w3c.github.io/encrypted-media/#dom-evt-keystatuseschange
-typedef base::Callback<void(const std::string& session_id,
- bool has_additional_usable_key,
- CdmKeysInfo keys_info)> SessionKeysChangeCB;
-
-// Called when the CDM changes the expiration time of a session.
-// See http://w3c.github.io/encrypted-media/#update-expiration
-typedef base::Callback<void(const std::string& session_id,
- const base::Time& new_expiry_time)>
- SessionExpirationUpdateCB;
-
-} // namespace media
-} // namespace cobalt
-
-#endif // COBALT_MEDIA_BASE_MEDIA_KEYS_H_
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 028145b..7548977 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -805,7 +805,7 @@
TRACE_EVENT0("cobalt::media", "SbPlayerPipeline::CreatePlayer");
DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(video_stream_);
+ DCHECK(audio_stream_ || video_stream_);
if (stopped_) {
return;
@@ -826,8 +826,10 @@
const AudioDecoderConfig& audio_config =
audio_stream_ ? audio_stream_->audio_decoder_config()
: invalid_audio_config;
+ VideoDecoderConfig invalid_video_config;
const VideoDecoderConfig& video_config =
- video_stream_->video_decoder_config();
+ video_stream_ ? video_stream_->video_decoder_config()
+ : invalid_video_config;
{
base::AutoLock auto_lock(lock_);
@@ -851,7 +853,9 @@
if (audio_stream_) {
UpdateDecoderConfig(audio_stream_);
}
- UpdateDecoderConfig(video_stream_);
+ if (video_stream_) {
+ UpdateDecoderConfig(video_stream_);
+ }
return;
}
@@ -905,12 +909,22 @@
}
#endif // !SB_HAS(AUDIOLESS_VIDEO)
+#if SB_API_VERSION < SB_AUDIO_ONLY_VIDEO_API_VERSION
if (video_stream == NULL) {
LOG(INFO) << "The video has to contain a video track.";
ResetAndRunIfNotNull(&error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
"The video has to contain a video track.");
return;
}
+#endif // SB_API_VERSION < SB_AUDIO_ONLY_VIDEO_API_VERSION
+
+ if (audio_stream == NULL && video_stream == NULL) {
+ LOG(INFO) << "The video has to contain an audio track or a video track.";
+ ResetAndRunIfNotNull(
+ &error_cb_, DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
+ "The video has to contain an audio track or a video track.");
+ return;
+ }
{
base::AutoLock auto_lock(lock_);
@@ -919,13 +933,17 @@
bool is_encrypted =
audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted();
- is_encrypted |= video_stream_->video_decoder_config().is_encrypted();
- bool natural_size_changed =
- (video_stream_->video_decoder_config().natural_size().width() !=
- natural_size_.width() ||
- video_stream_->video_decoder_config().natural_size().height() !=
- natural_size_.height());
- natural_size_ = video_stream_->video_decoder_config().natural_size();
+ is_encrypted |=
+ video_stream_ && video_stream_->video_decoder_config().is_encrypted();
+ bool natural_size_changed = false;
+ if (video_stream_) {
+ natural_size_changed =
+ (video_stream_->video_decoder_config().natural_size().width() !=
+ natural_size_.width() ||
+ video_stream_->video_decoder_config().natural_size().height() !=
+ natural_size_.height());
+ natural_size_ = video_stream_->video_decoder_config().natural_size();
+ }
if (natural_size_changed) {
content_size_change_cb_.Run();
}
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index 35ea7fd..9b12235 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -135,7 +135,7 @@
set_bounds_helper_(set_bounds_helper),
allow_resume_after_suspend_(allow_resume_after_suspend),
video_frame_provider_(video_frame_provider) {
- DCHECK(video_config.IsValidConfig());
+ DCHECK(audio_config.IsValidConfig() || video_config.IsValidConfig());
DCHECK(host_);
DCHECK(set_bounds_helper_);
DCHECK(video_frame_provider_);
@@ -429,13 +429,19 @@
#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
SbPlayerInfo info;
SbPlayerGetInfo(player_, &info);
- DCHECK_NE(info.duration_pts, SB_PLAYER_NO_DURATION);
+ if (info.duration_pts == SB_PLAYER_NO_DURATION) {
+ // URL-based player may not have loaded asset yet, so map no duration to 0.
+ return base::TimeDelta();
+ }
return base::TimeDelta::FromMicroseconds(
SB_MEDIA_TIME_TO_SB_TIME(info.duration_pts));
#else // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
SbPlayerInfo2 info;
SbPlayerGetInfo2(player_, &info);
- DCHECK_NE(info.duration, SB_PLAYER_NO_DURATION);
+ if (info.duration == SB_PLAYER_NO_DURATION) {
+ // URL-based player may not have loaded asset yet, so map no duration to 0.
+ return base::TimeDelta();
+ }
return base::TimeDelta::FromMicroseconds(info.duration);
#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
}
@@ -606,8 +612,10 @@
audio_codec = MediaAudioCodecToSbMediaAudioCodec(audio_config_.codec());
}
- SbMediaVideoCodec video_codec =
- MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
+ SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone;
+ if (video_config_.IsValidConfig()) {
+ video_codec = MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
+ }
DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
diff --git a/src/cobalt/media/decoder_buffer_allocator.cc b/src/cobalt/media/decoder_buffer_allocator.cc
index 5421c15..1edc5f0 100644
--- a/src/cobalt/media/decoder_buffer_allocator.cc
+++ b/src/cobalt/media/decoder_buffer_allocator.cc
@@ -178,6 +178,7 @@
void DecoderBufferAllocator::UpdateVideoConfig(
const VideoDecoderConfig& config) {
+#if COBALT_MEDIA_BUFFER_USING_MEMORY_POOL
if (!reuse_allocator_) {
return;
}
@@ -185,8 +186,10 @@
if (reuse_allocator_->max_capacity() && resolution > kVideoResolution1080p) {
reuse_allocator_->set_max_capacity(COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K);
}
+#endif // COBALT_MEDIA_BUFFER_USING_MEMORY_POOL
}
+#if COBALT_MEDIA_BUFFER_USING_MEMORY_POOL
DecoderBufferAllocator::ReuseAllocator::ReuseAllocator(
Allocator* fallback_allocator, std::size_t initial_capacity,
std::size_t allocation_increment, std::size_t max_capacity)
@@ -276,6 +279,6 @@
// COBALT_MEDIA_BUFFER_MAX_CAPACITY_4K > 0
return true;
}
-
+#endif // COBALT_MEDIA_BUFFER_USING_MEMORY_POOL
} // namespace media
} // namespace cobalt
diff --git a/src/cobalt/media_capture/get_user_media_test.cc b/src/cobalt/media_capture/get_user_media_test.cc
new file mode 100644
index 0000000..07ceac5
--- /dev/null
+++ b/src/cobalt/media_capture/get_user_media_test.cc
@@ -0,0 +1,86 @@
+// Copyright 2018 Google Inc. 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 "cobalt/media_capture/media_devices.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/testing/stub_window.h"
+#include "cobalt/script/global_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const int kMaxDomElementDepth = 8;
+
+scoped_ptr<cobalt::script::EnvironmentSettings> CreateDOMSettings() {
+ cobalt::dom::DOMSettings::Options options;
+#if defined(ENABLE_FAKE_MICROPHONE)
+ options.microphone_options.enable_fake_microphone = true;
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+
+ return make_scoped_ptr<cobalt::script::EnvironmentSettings>(
+ new cobalt::dom::DOMSettings(kMaxDomElementDepth, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, options));
+}
+
+} // namespace.
+
+namespace cobalt {
+namespace media_capture {
+
+class GetUserMediaTest : public ::testing::Test {
+ protected:
+ GetUserMediaTest()
+ : window_(CreateDOMSettings()),
+ media_devices_(new MediaDevices(
+ window_.global_environment()->script_value_factory())) {
+ media_devices_->SetEnvironmentSettings(window_.environment_settings());
+ }
+
+ dom::testing::StubWindow window_;
+ scoped_refptr<MediaDevices> media_devices_;
+};
+
+TEST_F(GetUserMediaTest, TestEmptyParameters) {
+ script::Handle<MediaDevices::MediaStreamPromise> media_stream_promise =
+ media_devices_->GetUserMedia();
+ ASSERT_FALSE(media_stream_promise.IsEmpty());
+ EXPECT_EQ(cobalt::script::PromiseState::kRejected,
+ media_stream_promise->State());
+}
+
+TEST_F(GetUserMediaTest, NoMediaSources) {
+ media_stream::MediaStreamConstraints constraints;
+ constraints.set_audio(false);
+ script::Handle<MediaDevices::MediaStreamPromise> media_stream_promise =
+ media_devices_->GetUserMedia(constraints);
+ ASSERT_FALSE(media_stream_promise.IsEmpty());
+ EXPECT_EQ(cobalt::script::PromiseState::kRejected,
+ media_stream_promise->State());
+}
+
+TEST_F(GetUserMediaTest, AudioMediaSources) {
+ media_stream::MediaStreamConstraints constraints;
+ constraints.set_audio(true);
+ script::Handle<MediaDevices::MediaStreamPromise> media_stream_promise =
+ media_devices_->GetUserMedia(constraints);
+ ASSERT_FALSE(media_stream_promise.IsEmpty());
+ EXPECT_EQ(cobalt::script::PromiseState::kFulfilled,
+ media_stream_promise->State());
+}
+
+} // namespace media_capture
+} // namespace cobalt
diff --git a/src/cobalt/media_capture/media_capture.gyp b/src/cobalt/media_capture/media_capture.gyp
index 364c272..12f1fb1 100644
--- a/src/cobalt/media_capture/media_capture.gyp
+++ b/src/cobalt/media_capture/media_capture.gyp
@@ -25,9 +25,14 @@
'media_devices.h',
'media_device_info.cc',
'media_device_info.h',
+ 'media_recorder.cc',
+ 'media_recorder.h',
],
'dependencies': [
'<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ '<(DEPTH)/cobalt/media_stream/media_stream.gyp:media_stream',
+ '<(DEPTH)/cobalt/script/engine.gyp:engine',
+ '<(DEPTH)/cobalt/speech/speech.gyp:speech',
],
'export_dependent_settings': [
# Additionally, ensure that the include directories for generated
diff --git a/src/cobalt/media_capture/media_capture_test.gyp b/src/cobalt/media_capture/media_capture_test.gyp
new file mode 100644
index 0000000..c5903c7
--- /dev/null
+++ b/src/cobalt/media_capture/media_capture_test.gyp
@@ -0,0 +1,55 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'media_capture_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'get_user_media_test.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+ '<(DEPTH)/cobalt/dom/testing/dom_testing.gyp:dom_testing',
+ '<(DEPTH)/cobalt/media_capture/media_capture.gyp:media_capture',
+
+ # TODO: Remove the dependency below, it works around the fact that
+ # ScriptValueFactory has non-virtual method CreatePromise().
+ '<(DEPTH)/cobalt/script/engine.gyp:engine',
+ # For Fake Microphone.
+ '<(DEPTH)/cobalt/speech/speech.gyp:speech',
+
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ },
+
+ {
+ 'target_name': 'media_capture_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'media_capture_test',
+ ],
+ 'variables': {
+ 'executable_name': 'media_capture_test',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/cobalt/media_capture/media_devices.cc b/src/cobalt/media_capture/media_devices.cc
index b9a194b..e90199a 100644
--- a/src/cobalt/media_capture/media_devices.cc
+++ b/src/cobalt/media_capture/media_devices.cc
@@ -16,8 +16,11 @@
#include <string>
+#include "cobalt/dom/dom_exception.h"
#include "cobalt/media_capture/media_device_info.h"
+#include "cobalt/media_stream/media_stream.h"
#include "cobalt/speech/microphone.h"
+#include "cobalt/speech/microphone_fake.h"
#include "cobalt/speech/microphone_starboard.h"
#include "starboard/string.h"
@@ -30,13 +33,28 @@
namespace {
-scoped_ptr<speech::Microphone> CreateMicrophone() {
- scoped_ptr<speech::Microphone> mic;
-#ifdef ENABLE_MICROPHONE_IDL
+using speech::Microphone;
+
+scoped_ptr<Microphone> CreateMicrophone(const Microphone::Options& options) {
+#if defined(ENABLE_FAKE_MICROPHONE)
+ if (options.enable_fake_microphone) {
+ return make_scoped_ptr<speech::Microphone>(
+ new speech::MicrophoneFake(options));
+ }
+#else
+ UNREFERENCED_PARAMETER(options);
+#endif // defined(ENABLE_FAKE_MICROPHONE)
+
+ scoped_ptr<Microphone> mic;
+
+#if defined(ENABLE_MICROPHONE_IDL)
mic.reset(new speech::MicrophoneStarboard(
speech::MicrophoneStarboard::kDefaultSampleRate,
- speech::MicrophoneStarboard::kDefaultSampleRate));
-#endif
+ /* Buffer for one second. */
+ speech::MicrophoneStarboard::kDefaultSampleRate *
+ speech::MicrophoneStarboard::kSbMicrophoneSampleSizeInBytes));
+#endif // defined(ENABLE_MICROPHONE_IDL)
+
return mic.Pass();
}
@@ -48,20 +66,104 @@
script::Handle<MediaDevices::MediaInfoSequencePromise>
MediaDevices::EnumerateDevices() {
+ DCHECK(settings_);
+ DCHECK(script_value_factory_);
script::Handle<MediaInfoSequencePromise> promise =
script_value_factory_->CreateBasicPromise<MediaInfoSequence>();
script::Sequence<scoped_refptr<Wrappable>> output;
- scoped_ptr<speech::Microphone> microphone = CreateMicrophone();
+
+ scoped_ptr<speech::Microphone> microphone =
+ CreateMicrophone(settings_->microphone_options());
if (microphone) {
scoped_refptr<Wrappable> media_device(
new MediaDeviceInfo(script_value_factory_, kMediaDeviceKindAudioinput,
microphone->Label()));
output.push_back(media_device);
}
-
promise->Resolve(output);
return promise;
}
+script::Handle<MediaDevices::MediaStreamPromise> MediaDevices::GetUserMedia() {
+ DCHECK(script_value_factory_);
+ script::Handle<MediaDevices::MediaStreamPromise> promise =
+ script_value_factory_->CreateInterfacePromise<
+ script::ScriptValueFactory::WrappablePromise>();
+ // Per specification at
+ // https://w3c.github.io/mediacapture-main/#dom-mediadevices-getusermedia
+ //
+ // Step 3: If requestedMediaTypes is the empty set, return a promise rejected
+ // with a TypeError. The word "optional" occurs in the WebIDL due to WebIDL
+ // rules, but the argument must be supplied in order for the call to succeed.
+
+ promise->Reject(script::kTypeError);
+ return promise;
+}
+
+script::Handle<MediaDevices::MediaStreamPromise> MediaDevices::GetUserMedia(
+ const media_stream::MediaStreamConstraints& constraints) {
+ script::Handle<MediaDevices::MediaStreamPromise> promise =
+ script_value_factory_->CreateInterfacePromise<
+ script::ScriptValueFactory::WrappablePromise>();
+ if (!constraints.audio()) {
+ // Step 3: If requestedMediaTypes is the empty set, return a promise
+ // rejected with a TypeError. The word "optional" occurs in the WebIDL due
+ // to WebIDL rules, but the argument must be supplied in order for the call
+ // to succeed.
+ DLOG(INFO) << "Audio constraint must be true.";
+ promise->Reject(script::kTypeError);
+ return promise;
+ }
+ // Steps 4-7 are not needed for cobalt.
+ // Step 8 is to create a promise (which is already done).
+
+ // Step 9: Construct a list of MediaStreamTracks that we have permission.
+ CreateMicrophoneIfNeeded();
+ if (!microphone_) {
+ promise->Reject(new dom::DOMException(dom::DOMException::kNotFoundErr));
+ return promise;
+ }
+ bool open_success = microphone_->Open();
+ if (!open_success) {
+ // This typically happens if the user did not give permission.
+ // Step 9.8, handle "Permission Failure"
+ DLOG(INFO) << "Unable to open the microphone.";
+ promise->Reject(new dom::DOMException(dom::DOMException::kNotAllowedErr));
+ }
+ using media_stream::MediaStream;
+ scoped_refptr<media_stream::MediaStreamTrack> microphone_track;
+ // TODO: Attach a microphone device to the microphone track.
+ MediaStream::TrackSequences audio_tracks;
+ audio_tracks.push_back(microphone_track);
+ auto media_stream(make_scoped_refptr(new MediaStream(audio_tracks)));
+ promise->Resolve(media_stream);
+
+ // Step 10, return promise.
+ return promise;
+}
+
+void MediaDevices::CreateMicrophoneIfNeeded() {
+ DCHECK(settings_);
+ if (microphone_) {
+ return;
+ }
+
+ scoped_ptr<speech::Microphone> microphone =
+ CreateMicrophone(settings_->microphone_options());
+
+ if (!microphone) {
+ DLOG(INFO) << "Unable to create a microphone.";
+ return;
+ }
+
+ if (!microphone->IsValid()) {
+ DLOG(INFO) << "Ignoring created microphone because it is invalid.";
+ return;
+ }
+
+ DLOG(INFO) << "Created microphone: " << microphone->Label();
+ microphone_ = microphone.Pass();
+}
+
} // namespace media_capture
} // namespace cobalt
diff --git a/src/cobalt/media_capture/media_devices.h b/src/cobalt/media_capture/media_devices.h
index 83739d3..89c0e39 100644
--- a/src/cobalt/media_capture/media_devices.h
+++ b/src/cobalt/media_capture/media_devices.h
@@ -17,13 +17,18 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/dom/dom_settings.h"
#include "cobalt/dom/event_target.h"
#include "cobalt/media_capture/media_device_info.h"
+#include "cobalt/media_stream/media_stream_constraints.h"
+#include "cobalt/script/environment_settings.h"
#include "cobalt/script/promise.h"
#include "cobalt/script/script_value.h"
#include "cobalt/script/script_value_factory.h"
#include "cobalt/script/sequence.h"
#include "cobalt/script/wrappable.h"
+#include "cobalt/speech/microphone.h"
namespace cobalt {
namespace media_capture {
@@ -35,15 +40,29 @@
public:
using MediaInfoSequence = script::Sequence<scoped_refptr<script::Wrappable>>;
using MediaInfoSequencePromise = script::Promise<MediaInfoSequence>;
+ using MediaStreamPromise =
+ script::Promise<script::ScriptValueFactory::WrappablePromise>;
explicit MediaDevices(script::ScriptValueFactory* script_value_factory);
script::Handle<MediaInfoSequencePromise> EnumerateDevices();
+ script::Handle<MediaStreamPromise> GetUserMedia();
+ script::Handle<MediaStreamPromise> GetUserMedia(
+ const media_stream::MediaStreamConstraints& constraints);
+
+ void SetEnvironmentSettings(script::EnvironmentSettings* settings) {
+ settings_ = base::polymorphic_downcast<dom::DOMSettings*>(settings);
+ }
DEFINE_WRAPPABLE_TYPE(MediaDevices);
private:
+ void CreateMicrophoneIfNeeded();
+
script::ScriptValueFactory* script_value_factory_;
+ dom::DOMSettings* settings_ = nullptr;
+
+ scoped_ptr<speech::Microphone> microphone_;
DISALLOW_COPY_AND_ASSIGN(MediaDevices);
};
diff --git a/src/cobalt/media_capture/media_devices.idl b/src/cobalt/media_capture/media_devices.idl
index f697037..2b77fec 100644
--- a/src/cobalt/media_capture/media_devices.idl
+++ b/src/cobalt/media_capture/media_devices.idl
@@ -17,4 +17,5 @@
[Exposed=Window]
interface MediaDevices : EventTarget {
Promise<sequence<MediaDeviceInfo>> enumerateDevices();
+ Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
};
diff --git a/src/cobalt/media_capture/media_recorder.cc b/src/cobalt/media_capture/media_recorder.cc
new file mode 100644
index 0000000..638048c
--- /dev/null
+++ b/src/cobalt/media_capture/media_recorder.cc
@@ -0,0 +1,262 @@
+// Copyright 2018 Google Inc. 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 "cobalt/media_capture/media_recorder.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/message_loop.h"
+#include "base/string_piece.h"
+#include "cobalt/dom/array_buffer.h"
+#include "cobalt/dom/blob.h"
+#include "cobalt/dom/dom_exception.h"
+#include "cobalt/media_stream/media_stream_track.h"
+#include "cobalt/media_stream/media_track_settings.h"
+
+namespace {
+
+// See https://tools.ietf.org/html/rfc2586 for MIME type
+const char kLinear16MimeType[] = "audio/L16";
+
+const int32 kMinimumTimeSliceInMilliseconds = 1;
+
+// Read Microphone input every few milliseconds, so that
+// the input buffer doesn't fill up.
+const int32 kDefaultMicrophoneReadThresholdMilliseconds = 50;
+
+const double kSchedulingLatencyBufferSeconds = 0.20;
+
+} // namespace
+
+namespace cobalt {
+namespace media_capture {
+
+void MediaRecorder::Start(int32 timeslice,
+ script::ExceptionState* exception_state) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // Following the spec at
+ // https://www.w3.org/TR/mediastream-recording/#mediarecorder-methods:
+
+ // Step #1, not needed for Cobalt.
+
+ // Step #2, done by Cobalt bindings.
+
+ // Step #3
+ if (recording_state_ != kRecordingStateInactive) {
+ dom::DOMException::Raise(dom::DOMException::kInvalidStateErr,
+ "Internal error: Unable to get DOM settings.",
+ exception_state);
+ return;
+ }
+
+ // Step #4-5.3, not needed for Cobalt.
+ recording_state_ = kRecordingStateRecording;
+
+ // Step #5.4, create a new Blob, and start collecting data.
+
+ // If timeslice is not undefined, then once a minimum of timeslice
+ // milliseconds of data have been collected, or some minimum time slice
+ // imposed by the UA, whichever is greater, start gathering data into a new
+ // Blob blob, and queue a task, using the DOM manipulation task source, that
+ // fires a blob event named |dataavailable| at target.
+
+ // We need to drain the media frequently, so try to read atleast once every
+ // |read_frequency_| interval.
+ int32 effective_time_slice_milliseconds =
+ std::min(kDefaultMicrophoneReadThresholdMilliseconds, timeslice);
+ // Avoid rounding down to 0 milliseconds.
+ effective_time_slice_milliseconds = std::max(
+ kMinimumTimeSliceInMilliseconds, effective_time_slice_milliseconds);
+ read_frequency_ =
+ base::TimeDelta::FromMilliseconds(effective_time_slice_milliseconds);
+
+ // This is the frequency we will callback to Javascript.
+ callback_frequency_ = base::TimeDelta::FromMilliseconds(timeslice);
+
+ int64 buffer_size_hint = GetRecommendedBufferSize(callback_frequency_);
+ recorded_buffer_.HintTypicalSize(static_cast<size_t>(buffer_size_hint));
+
+ ResetLastCallbackTime();
+ ReadStreamAndDoCallback();
+
+ stream_reader_callback_.Reset(
+ base::Bind(&MediaRecorder::ReadStreamAndDoCallback, this));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ stream_reader_callback_.callback());
+
+ // Step #5.5, not needed.
+
+ // Step #6, return undefined.
+}
+
+void MediaRecorder::Stop(script::ExceptionState* exception_state) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ UNREFERENCED_PARAMETER(exception_state);
+ NOTREACHED();
+}
+
+MediaRecorder::MediaRecorder(
+ script::EnvironmentSettings* settings,
+ const scoped_refptr<media_stream::MediaStream>& stream,
+ const MediaRecorderOptions& options)
+ : settings_(settings), stream_(stream) {
+ // Per W3C spec, the default value of this is platform-specific,
+ // so Linear16 was chosen. Spec url:
+ // https://www.w3.org/TR/mediastream-recording/#dom-mediarecorder-mediarecorder
+ mime_type_ =
+ options.has_mime_type() ? options.mime_type() : kLinear16MimeType;
+}
+
+void MediaRecorder::ReadStreamAndDoCallback() {
+ DCHECK(stream_);
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ size_t number_audio_tracks = stream_->GetAudioTracks().size();
+ if (number_audio_tracks == 0) {
+ LOG(WARNING) << "Audio Tracks are empty.";
+ return;
+ }
+ LOG_IF(WARNING, number_audio_tracks > 1)
+ << "Only recording the first audio track.";
+
+ base::TimeTicks current_time = base::TimeTicks::Now();
+ base::TimeDelta time_difference = last_callback_time_ - current_time;
+
+ int64 recommended_buffer_size = GetRecommendedBufferSize(time_difference);
+ base::StringPiece writeable_buffer = recorded_buffer_.GetWriteCursor(
+ static_cast<size_t>(recommended_buffer_size));
+
+ media_stream::MediaStreamTrack* track =
+ stream_->GetAudioTracks().begin()->get();
+
+ int64 bytes_read = track->Read(writeable_buffer);
+
+ if (bytes_read < 0) {
+ // An error occured, so do not post another read.
+ DoOnDataCallback();
+ return;
+ }
+
+ DCHECK_LE(bytes_read, static_cast<int64>(writeable_buffer.size()));
+ recorded_buffer_.IncrementCursorPosition(static_cast<size_t>(bytes_read));
+
+ if (current_time >= GetNextCallbackTime()) {
+ DoOnDataCallback();
+ ResetLastCallbackTime();
+ }
+
+ // Note that GetNextCallbackTime() should not be cached, since
+ // ResetLastCallbackTime() above can change its value.
+ base::TimeDelta time_until_expiration = GetNextCallbackTime() - current_time;
+
+ // Consider the scenario where |time_until_expiration| is 4ms, and
+ // read_frequency is 10ms. In this case, just do the read 4 milliseconds
+ // later, and then do the callback.
+ base::TimeDelta delay_until_next_read =
+ std::min(time_until_expiration, read_frequency_);
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, stream_reader_callback_.callback(), delay_until_next_read);
+}
+
+void MediaRecorder::DoOnDataCallback() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (recorded_buffer_.GetWrittenChunk().empty()) {
+ DLOG(WARNING) << "No data was recorded.";
+ return;
+ }
+
+ base::StringPiece written_data = recorded_buffer_.GetWrittenChunk();
+ DCHECK_LE(written_data.size(), kuint32max);
+ uint32 number_of_written_bytes = static_cast<uint32>(written_data.size());
+
+ auto array_buffer = make_scoped_refptr(new dom::ArrayBuffer(
+ settings_, reinterpret_cast<const uint8*>(written_data.data()),
+ number_of_written_bytes));
+ recorded_buffer_.Reset();
+
+ auto blob = make_scoped_refptr(new dom::Blob(settings_, array_buffer));
+ // TODO: Post a task to fire BlobEvent (constructed out of |blob| and
+ // |array_buffer| at target.
+}
+
+void MediaRecorder::ResetLastCallbackTime() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ last_callback_time_ = base::TimeTicks::Now();
+}
+
+void MediaRecorder::CalculateStreamBitrate() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ media_stream::MediaStreamTrack* track =
+ stream_->GetAudioTracks().begin()->get();
+ const media_stream::MediaTrackSettings& settings = track->GetSettings();
+ DCHECK_GT(settings.sample_rate(), 0);
+ DCHECK_GT(settings.sample_size(), 0);
+ DCHECK_GT(settings.channel_count(), 0);
+ bitrate_bps_ = settings.sample_rate() * settings.sample_size() *
+ settings.channel_count();
+ DCHECK_GT(bitrate_bps_, 0);
+}
+
+int64 MediaRecorder::GetRecommendedBufferSize(base::TimeDelta time_span) const {
+ DCHECK_GE(time_span, base::TimeDelta::FromSeconds(0));
+ // Increase buffer slightly to account for the fact that scheduling our
+ // tasks might be a little bit noisy.
+ double buffer_window_span_seconds =
+ time_span.InSecondsF() + kSchedulingLatencyBufferSeconds;
+ int64 recommended_buffer_size =
+ static_cast<int64>(std::ceil(buffer_window_span_seconds * bitrate_bps_));
+ DCHECK_GT(recommended_buffer_size, 0);
+ return recommended_buffer_size;
+}
+
+bool MediaRecorder::IsTypeSupported(const base::StringPiece mime_type) {
+ return mime_type == kLinear16MimeType;
+}
+
+base::StringPiece MediaRecorder::Buffer::GetWriteCursor(
+ size_t number_of_bytes) {
+ size_t minimim_required_size = current_position_ + number_of_bytes;
+ if (minimim_required_size > buffer_.size()) {
+ buffer_.resize(minimim_required_size);
+ }
+ return base::StringPiece(reinterpret_cast<const char*>(buffer_.data()),
+ number_of_bytes);
+}
+
+void MediaRecorder::Buffer::IncrementCursorPosition(size_t number_of_bytes) {
+ size_t new_position = current_position_ + number_of_bytes;
+ DCHECK_LE(new_position, buffer_.size());
+ current_position_ = new_position;
+}
+
+base::StringPiece MediaRecorder::Buffer::GetWrittenChunk() const {
+ return base::StringPiece(reinterpret_cast<const char*>(buffer_.data()),
+ current_position_);
+}
+
+void MediaRecorder::Buffer::Reset() {
+ current_position_ = 0;
+ buffer_.resize(0);
+}
+
+void MediaRecorder::Buffer::HintTypicalSize(size_t number_of_bytes) {
+ // Cap the hint size to be 1 Megabyte.
+ const size_t kMaxBufferSizeHintInBytes = 1024 * 1024;
+ buffer_.reserve(std::min(number_of_bytes, kMaxBufferSizeHintInBytes));
+}
+
+} // namespace media_capture
+} // namespace cobalt
diff --git a/src/cobalt/media_capture/media_recorder.h b/src/cobalt/media_capture/media_recorder.h
new file mode 100644
index 0000000..cebaa73
--- /dev/null
+++ b/src/cobalt/media_capture/media_recorder.h
@@ -0,0 +1,135 @@
+// Copyright 2018 Google Inc. 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 COBALT_MEDIA_CAPTURE_MEDIA_RECORDER_H_
+#define COBALT_MEDIA_CAPTURE_MEDIA_RECORDER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/cancelable_callback.h"
+#include "base/optional.h"
+#include "base/string_piece.h"
+#include "base/threading/thread_checker.h"
+#include "base/time.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/media_capture/media_recorder_options.h"
+#include "cobalt/media_capture/recording_state.h"
+#include "cobalt/media_stream/media_stream.h"
+#include "cobalt/script/environment_settings.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace media_capture {
+
+// This class represents a MediaRecorder, and implements the specification at:
+// https://www.w3.org/TR/mediastream-recording/#mediarecorder-api
+class MediaRecorder : public dom::EventTarget {
+ public:
+ // Constructors.
+ explicit MediaRecorder(
+ script::EnvironmentSettings* settings,
+ const scoped_refptr<media_stream::MediaStream>& stream,
+ const MediaRecorderOptions& options = MediaRecorderOptions());
+
+ // Readonly attributes.
+ const std::string& mime_type() const { return mime_type_; }
+
+ // Functions
+ static bool IsTypeSupported(const base::StringPiece mime_type);
+
+ void Start(int32 timeslice, script::ExceptionState* exception_state);
+
+ void Start(script::ExceptionState* exception_state) {
+ Start(kint32max, exception_state);
+ }
+
+ void Stop(script::ExceptionState* exception_state);
+
+ // EventHandlers.
+ const EventListenerScriptValue* onerror() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return GetAttributeEventListener(base::Tokens::error());
+ }
+
+ void set_onerror(const EventListenerScriptValue& event_listener) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ SetAttributeEventListener(base::Tokens::error(), event_listener);
+ }
+
+ const EventListenerScriptValue* ondataavailable() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return GetAttributeEventListener(base::Tokens::dataavailable());
+ }
+
+ void set_ondataavailable(const EventListenerScriptValue& event_listener) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ SetAttributeEventListener(base::Tokens::dataavailable(), event_listener);
+ }
+
+ DEFINE_WRAPPABLE_TYPE(MediaRecorder);
+
+ private:
+ MediaRecorder(const MediaRecorder&) = delete;
+ MediaRecorder& operator=(const MediaRecorder&) = delete;
+
+ class Buffer {
+ public:
+ base::StringPiece GetWriteCursor(size_t number_of_bytes);
+ void IncrementCursorPosition(size_t number_of_bytes);
+ base::StringPiece GetWrittenChunk() const;
+ void Reset();
+ void HintTypicalSize(size_t number_of_bytes);
+
+ private:
+ size_t current_position_ = 0;
+ std::vector<uint8> buffer_;
+ };
+
+ void ReadStreamAndDoCallback();
+ void DoOnDataCallback();
+ void ScheduleOnDataAvailableCallback();
+ void ResetLastCallbackTime();
+ void CalculateStreamBitrate();
+ int64 GetRecommendedBufferSize(base::TimeDelta time_span) const;
+ base::TimeTicks GetNextCallbackTime() const {
+ return last_callback_time_ + callback_frequency_;
+ }
+
+ base::ThreadChecker thread_checker_;
+
+ RecordingState recording_state_ = kRecordingStateInactive;
+
+ script::EnvironmentSettings* settings_;
+ std::string mime_type_;
+ scoped_refptr<media_stream::MediaStream> stream_;
+
+ base::TimeTicks last_callback_time_;
+ // Frequency we will callback to Javascript.
+ base::TimeDelta callback_frequency_;
+
+ // Frequency we will read the stream.
+ base::TimeDelta read_frequency_;
+
+ int64 bitrate_bps_;
+ Buffer recorded_buffer_;
+
+ base::CancelableClosure stream_reader_callback_;
+};
+
+} // namespace media_capture
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_CAPTURE_MEDIA_RECORDER_H_
diff --git a/src/cobalt/media_capture/media_recorder.idl b/src/cobalt/media_capture/media_recorder.idl
new file mode 100644
index 0000000..45b513f
--- /dev/null
+++ b/src/cobalt/media_capture/media_recorder.idl
@@ -0,0 +1,29 @@
+// Copyright 2018 Google Inc. 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.
+
+// Implements a subset of the specification at:
+// https://www.w3.org/TR/mediastream-recording/#mediarecorder-api
+[Constructor(MediaStream stream, optional MediaRecorderOptions options),
+ ConstructorCallWith = EnvironmentSettings]
+interface MediaRecorder : EventTarget {
+ readonly attribute DOMString mimeType;
+ attribute EventHandler ondataavailable;
+ attribute EventHandler onerror;
+
+ [RaisesException] void start(optional long timeslice);
+ [RaisesException] void stop();
+
+ static boolean isTypeSupported(DOMString type);
+};
+
diff --git a/src/cobalt/media_capture/media_recorder_options.idl b/src/cobalt/media_capture/media_recorder_options.idl
new file mode 100644
index 0000000..a0a9aa0
--- /dev/null
+++ b/src/cobalt/media_capture/media_recorder_options.idl
@@ -0,0 +1,19 @@
+// Copyright 2018 Google Inc. 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.
+
+// https://www.w3.org/TR/mediastream-recording/#dictdef-mediarecorderoptions
+dictionary MediaRecorderOptions {
+ DOMString mimeType;
+};
+
diff --git a/src/cobalt/media_capture/recording_state.idl b/src/cobalt/media_capture/recording_state.idl
new file mode 100644
index 0000000..3f9a955
--- /dev/null
+++ b/src/cobalt/media_capture/recording_state.idl
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// Implements a subset of the specification at:
+// https://www.w3.org/TR/mediastream-recording/#mediarecorder-api
+
+enum RecordingState {
+ "inactive",
+ "recording",
+ "paused"
+};
+
diff --git a/src/cobalt/media_session/media_session.h b/src/cobalt/media_session/media_session.h
index ec74cef..9c838a1 100644
--- a/src/cobalt/media_session/media_session.h
+++ b/src/cobalt/media_session/media_session.h
@@ -24,9 +24,11 @@
#include "base/logging.h"
#include "base/message_loop_proxy.h"
#include "cobalt/media_session/media_session_action.h"
+#include "cobalt/media_session/media_session_action_details.h"
#include "cobalt/media_session/media_session_playback_state.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/script_value.h"
+#include "cobalt/script/wrappable.h"
namespace cobalt {
namespace media_session {
@@ -37,7 +39,9 @@
friend class MediaSessionClient;
public:
- typedef script::CallbackFunction<void()> MediaSessionActionHandler;
+ typedef script::CallbackFunction<void(
+ const scoped_refptr<MediaSessionActionDetails>& action_details)>
+ MediaSessionActionHandler;
typedef script::ScriptValue<MediaSessionActionHandler>
MediaSessionActionHandlerHolder;
typedef script::ScriptValue<MediaSessionActionHandler>::Reference
diff --git a/src/cobalt/media_session/media_session.idl b/src/cobalt/media_session/media_session.idl
index ffb06e2..5d8d1d7 100644
--- a/src/cobalt/media_session/media_session.idl
+++ b/src/cobalt/media_session/media_session.idl
@@ -15,7 +15,8 @@
// MediaSession interface
// https://wicg.github.io/mediasession
-callback MediaSessionActionHandler = void();
+// Cobalt customization - details parameter is not part of spec.
+callback MediaSessionActionHandler = void(MediaSessionActionDetails details);
interface MediaSession {
attribute MediaMetadata? metadata;
diff --git a/src/cobalt/media_session/media_session_action.idl b/src/cobalt/media_session/media_session_action.idl
index c57a01b..24dd691 100644
--- a/src/cobalt/media_session/media_session_action.idl
+++ b/src/cobalt/media_session/media_session_action.idl
@@ -18,6 +18,7 @@
enum MediaSessionAction {
"play",
"pause",
+ "seek", // Cobalt customization.
"seekbackward",
"seekforward",
"previoustrack",
diff --git a/src/cobalt/media_session/media_session_action_details.h b/src/cobalt/media_session/media_session_action_details.h
new file mode 100644
index 0000000..2c910ee
--- /dev/null
+++ b/src/cobalt/media_session/media_session_action_details.h
@@ -0,0 +1,71 @@
+// Copyright 2018 Google Inc. 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 COBALT_MEDIA_SESSION_MEDIA_SESSION_ACTION_DETAILS_H_
+#define COBALT_MEDIA_SESSION_MEDIA_SESSION_ACTION_DETAILS_H_
+
+#include "base/logging.h"
+#include "cobalt/media_session/media_session_action.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace media_session {
+
+class MediaSessionActionDetails : public script::Wrappable {
+ public:
+ // Non-Wrappable class holding the data needed for selected actions. This can
+ // be constructed on any thread, and then it's used internally to construct
+ // MediaSessionActionDetails on the web module thread.
+ class Data {
+ public:
+ explicit Data(MediaSessionAction action, double seek = 0.0)
+ : action_(action),
+ seek_time_(action == kMediaSessionActionSeek ? seek : 0.0),
+ seek_offset_(
+ (action == kMediaSessionActionSeekforward
+ || action == kMediaSessionActionSeekbackward) ? seek : 0.0) {
+ DCHECK(seek >= 0.0);
+ DCHECK(seek == 0.0
+ || action == kMediaSessionActionSeek
+ || action == kMediaSessionActionSeekforward
+ || action == kMediaSessionActionSeekbackward);
+ }
+ Data(const Data&) = default;
+
+ MediaSessionAction action() const { return action_; }
+
+ private:
+ friend class MediaSessionActionDetails;
+
+ MediaSessionAction action_;
+ double seek_time_;
+ double seek_offset_;
+ };
+
+ explicit MediaSessionActionDetails(const Data& data) : data_(data) {}
+
+ MediaSessionAction action() const { return data_.action_; }
+ double seek_time() const { return data_.seek_time_; }
+ double seek_offset() const { return data_.seek_offset_; }
+
+ DEFINE_WRAPPABLE_TYPE(MediaSessionActionDetails);
+
+ private:
+ Data data_;
+};
+
+} // namespace media_session
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_SESSION_MEDIA_SESSION_ACTION_DETAILS_H_
diff --git a/src/cobalt/media_session/media_session_action_details.idl b/src/cobalt/media_session/media_session_action_details.idl
new file mode 100644
index 0000000..8846df5
--- /dev/null
+++ b/src/cobalt/media_session/media_session_action_details.idl
@@ -0,0 +1,27 @@
+// Copyright 2018 Google Inc. 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.
+
+// MediaSession interface (Cobalt customization)
+// https://wicg.github.io/mediasession
+
+interface MediaSessionActionDetails {
+ readonly attribute MediaSessionAction action;
+
+ // Time in seconds relative to the beginning of the media for "seek" action.
+ readonly attribute double seekTime;
+
+ // Time in seconds relative to the current media time for "seekforward" and
+ // "seekbackward" actions.
+ readonly attribute double seekOffset;
+};
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
index 6cf769c..d141611 100644
--- a/src/cobalt/media_session/media_session_client.cc
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -98,22 +98,23 @@
}
}
-void MediaSessionClient::InvokeAction(MediaSessionAction action) {
+void MediaSessionClient::InvokeActionInternal(
+ scoped_ptr<MediaSessionActionDetails::Data> data) {
if (base::MessageLoopProxy::current() != media_session_->message_loop_) {
media_session_->message_loop_->PostTask(
- FROM_HERE, base::Bind(&MediaSessionClient::InvokeAction,
- base::Unretained(this), action));
+ FROM_HERE, base::Bind(&MediaSessionClient::InvokeActionInternal,
+ base::Unretained(this), base::Passed(&data)));
return;
}
MediaSession::ActionMap::iterator it =
- media_session_->action_map_.find(action);
+ media_session_->action_map_.find(data->action());
if (it == media_session_->action_map_.end()) {
return;
}
- it->second->value().Run();
+ it->second->value().Run(new MediaSessionActionDetails(*data));
}
} // namespace media_session
diff --git a/src/cobalt/media_session/media_session_client.h b/src/cobalt/media_session/media_session_client.h
index d838785..1763d6c 100644
--- a/src/cobalt/media_session/media_session_client.h
+++ b/src/cobalt/media_session/media_session_client.h
@@ -17,9 +17,11 @@
#include <bitset>
+#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
#include "cobalt/media_session/media_session.h"
+#include "cobalt/media_session/media_session_action_details.h"
namespace cobalt {
namespace media_session {
@@ -60,7 +62,15 @@
// Invokes a given media session action
// https://wicg.github.io/mediasession/#actions-model
// Can be invoked from any thread.
- void InvokeAction(MediaSessionAction action);
+ void InvokeAction(MediaSessionAction action) {
+ InvokeActionInternal(scoped_ptr<MediaSessionActionDetails::Data>(
+ new MediaSessionActionDetails::Data(action)));
+ }
+
+ // Invokes a given media session action that takes additional data.
+ void InvokeAction(scoped_ptr<MediaSessionActionDetails::Data> data) {
+ InvokeActionInternal(data.Pass());
+ }
// Invoked on the browser thread when any metadata, playback state,
// or supported session actions change.
@@ -71,6 +81,8 @@
scoped_refptr<MediaSession> media_session_;
MediaSessionPlaybackState platform_playback_state_;
+ void InvokeActionInternal(scoped_ptr<MediaSessionActionDetails::Data> data);
+
DISALLOW_COPY_AND_ASSIGN(MediaSessionClient);
};
diff --git a/src/cobalt/media_session/media_session_test.cc b/src/cobalt/media_session/media_session_test.cc
index 345dbd9..6c9f7bf 100644
--- a/src/cobalt/media_session/media_session_test.cc
+++ b/src/cobalt/media_session/media_session_test.cc
@@ -44,7 +44,8 @@
class MockCallbackFunction : public MediaSession::MediaSessionActionHandler {
public:
- MOCK_CONST_METHOD0(Run, ReturnValue());
+ MOCK_CONST_METHOD1(Run, ReturnValue(
+ const scoped_refptr<MediaSessionActionDetails>& action_details));
};
class MockMediaSessionClient : public MediaSessionClient {
@@ -52,6 +53,16 @@
MOCK_METHOD0(OnMediaSessionChanged, void());
};
+MATCHER_P(SeekTime, time, "") {
+ return arg->action() == kMediaSessionActionSeek
+ && arg->seek_time() == time;
+}
+
+MATCHER_P2(SeekOffset, action, offset, "") {
+ return arg->action() == action
+ && arg->seek_offset() == offset;
+}
+
TEST(MediaSessionTest, MediaSessionTest) {
MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
base::RunLoop run_loop;
@@ -130,7 +141,7 @@
EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
MockCallbackFunction cf;
- EXPECT_CALL(cf, Run())
+ EXPECT_CALL(cf, Run(_))
.Times(1)
.WillRepeatedly(Return(CallbackResult<void>()));
FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
@@ -162,7 +173,7 @@
EXPECT_EQ(0, client.GetAvailableActions().to_ulong());
MockCallbackFunction cf;
- EXPECT_CALL(cf, Run()).Times(0);
+ EXPECT_CALL(cf, Run(_)).Times(0);
FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
session->SetActionHandler(kMediaSessionActionPlay, holder);
@@ -173,7 +184,7 @@
EXPECT_EQ(1, client.GetAvailableActions().to_ulong());
- session->SetActionHandler(kMediaSessionActionSeekbackward, holder);
+ session->SetActionHandler(kMediaSessionActionSeek, holder);
EXPECT_EQ(5, client.GetAvailableActions().to_ulong());
@@ -220,6 +231,55 @@
run_loop.Run();
}
+TEST(MediaSessionTest, SeekDetails) {
+ MessageLoop message_loop(MessageLoop::TYPE_DEFAULT);
+ base::RunLoop run_loop;
+
+ MockMediaSessionClient client;
+
+ ON_CALL(client, OnMediaSessionChanged())
+ .WillByDefault(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+ EXPECT_CALL(client, OnMediaSessionChanged()).Times(AtLeast(0));
+
+ scoped_refptr<MediaSession> session = client.GetMediaSession();
+
+ MockCallbackFunction cf;
+ FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
+
+ session->SetActionHandler(kMediaSessionActionSeek, holder);
+ session->SetActionHandler(kMediaSessionActionSeekforward, holder);
+ session->SetActionHandler(kMediaSessionActionSeekbackward, holder);
+
+ EXPECT_CALL(cf, Run(SeekTime(0.0)))
+ .WillOnce(Return(CallbackResult<void>()));
+ client.InvokeAction(kMediaSessionActionSeek);
+
+ EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekforward, 0.0)))
+ .WillOnce(Return(CallbackResult<void>()));
+ client.InvokeAction(kMediaSessionActionSeekforward);
+
+ EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekbackward, 0.0)))
+ .WillOnce(Return(CallbackResult<void>()));
+ client.InvokeAction(kMediaSessionActionSeekbackward);
+
+ EXPECT_CALL(cf, Run(SeekTime(1.2)))
+ .WillOnce(Return(CallbackResult<void>()));
+ client.InvokeAction(scoped_ptr<MediaSessionActionDetails::Data>(
+ new MediaSessionActionDetails::Data(kMediaSessionActionSeek, 1.2)));
+
+ EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekforward, 3.4)))
+ .WillOnce(Return(CallbackResult<void>()));
+ client.InvokeAction(scoped_ptr<MediaSessionActionDetails::Data>(
+ new MediaSessionActionDetails::Data(
+ kMediaSessionActionSeekforward, 3.4)));
+
+ EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekbackward, 5.6)))
+ .WillOnce(Return(CallbackResult<void>()));
+ client.InvokeAction(scoped_ptr<MediaSessionActionDetails::Data>(
+ new MediaSessionActionDetails::Data(
+ kMediaSessionActionSeekbackward, 5.6)));
+}
+
} // namespace
} // namespace media_session
} // namespace cobalt
diff --git a/src/cobalt/media_stream/audio_parameters.cc b/src/cobalt/media_stream/audio_parameters.cc
new file mode 100644
index 0000000..8973f6f
--- /dev/null
+++ b/src/cobalt/media_stream/audio_parameters.cc
@@ -0,0 +1,31 @@
+// Copyright 2018 Google Inc. 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 "cobalt/media_stream/audio_parameters.h"
+
+#include <sstream>
+
+namespace cobalt {
+namespace media_stream {
+
+std::string AudioParameters::AsHumanReadableString() const {
+ std::ostringstream os;
+ os << "channel_count: " << channel_count()
+ << " sample_rate: " << sample_rate()
+ << " bits_per_sample: " << bits_per_sample();
+ return os.str();
+}
+
+} // namespace media_stream
+} // namespace cobalt
diff --git a/src/cobalt/media_stream/audio_parameters.h b/src/cobalt/media_stream/audio_parameters.h
new file mode 100644
index 0000000..5f1e012
--- /dev/null
+++ b/src/cobalt/media_stream/audio_parameters.h
@@ -0,0 +1,76 @@
+// Copyright 2018 Google Inc. 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 COBALT_MEDIA_STREAM_AUDIO_PARAMETERS_H_
+#define COBALT_MEDIA_STREAM_AUDIO_PARAMETERS_H_
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace media_stream {
+
+class AudioParameters {
+ public:
+ AudioParameters(int channel_count, int sample_rate, int bits_per_sample)
+ : channel_count_(channel_count),
+ sample_rate_(sample_rate),
+ bits_per_sample_(bits_per_sample) {
+ DCHECK(IsValid());
+ }
+
+ AudioParameters(const AudioParameters&) = default;
+ AudioParameters& operator=(const AudioParameters&) = default;
+
+ int channel_count() const { return channel_count_; }
+ int sample_rate() const { return sample_rate_; }
+ int bits_per_sample() const { return bits_per_sample_; }
+
+ std::string AsHumanReadableString() const;
+
+ bool IsValid() const {
+ return (channel_count_ > 0) && (bits_per_sample_ > 0) && (sample_rate_ > 0);
+ }
+
+ int GetBitsPerSecond() const {
+ return sample_rate_ * channel_count_ * bits_per_sample_;
+ }
+
+ private:
+ int channel_count_;
+ int sample_rate_;
+ int bits_per_sample_;
+};
+
+inline bool operator==(const AudioParameters& lhs, const AudioParameters& rhs) {
+ return ((lhs.channel_count() == rhs.channel_count()) &&
+ (lhs.sample_rate() == rhs.sample_rate()) &&
+ (lhs.bits_per_sample() == rhs.bits_per_sample()));
+}
+
+inline bool operator!=(const AudioParameters& lhs, const AudioParameters& rhs) {
+ return !(lhs == rhs);
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const AudioParameters& params) {
+ os << params.AsHumanReadableString();
+ return os;
+}
+
+} // namespace media_stream
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_STREAM_AUDIO_PARAMETERS_H_
diff --git a/src/cobalt/media_stream/audio_parameters_test.cc b/src/cobalt/media_stream/audio_parameters_test.cc
new file mode 100644
index 0000000..107f784
--- /dev/null
+++ b/src/cobalt/media_stream/audio_parameters_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2018 Google Inc. 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 "cobalt/media_stream/audio_parameters.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace media_stream {
+
+TEST(AudioParameters, Constructor_ParametersValues) {
+ int channel_count = 6;
+ int sample_rate = 20000;
+ int bits_per_sample = 24;
+ AudioParameters params(channel_count, sample_rate, bits_per_sample);
+
+ EXPECT_EQ(channel_count, params.channel_count());
+ EXPECT_EQ(bits_per_sample, params.bits_per_sample());
+ EXPECT_EQ(sample_rate, params.sample_rate());
+}
+
+TEST(AudioParameters, AsHumanReadableString) {
+ int channel_count = 6;
+ int sample_rate = 20000;
+ int bits_per_sample = 24;
+ AudioParameters params(channel_count, sample_rate, bits_per_sample);
+ std::string params_string = params.AsHumanReadableString();
+ EXPECT_EQ("channel_count: 6 sample_rate: 20000 bits_per_sample: 24",
+ params_string);
+}
+
+TEST(AudioParameters, IsValid_Parameterized) {
+ int channel_count = 1;
+ int sample_rate = 44100;
+ int bits_per_sample = 16;
+ AudioParameters params(channel_count, sample_rate, bits_per_sample);
+ EXPECT_TRUE(params.IsValid());
+}
+
+TEST(AudioParameters, GetBitsPerSecond) {
+ int channel_count = 2;
+ int sample_rate = 44100;
+ int bits_per_sample = 16;
+ AudioParameters params(channel_count, sample_rate, bits_per_sample);
+ EXPECT_EQ(2 * 44100 * 16, params.GetBitsPerSecond());
+}
+
+TEST(AudioParameters, Compare) {
+ int channel_count = 1;
+ int sample_rate = 44100;
+ int bits_per_sample = 16;
+ AudioParameters params1(channel_count, sample_rate, bits_per_sample);
+ AudioParameters params2(channel_count, sample_rate, bits_per_sample);
+ AudioParameters params3(channel_count, sample_rate + 1, bits_per_sample);
+ EXPECT_EQ(params1, params2);
+ EXPECT_NE(params2, params3);
+}
+
+} // namespace media_stream
+} // namespace cobalt
diff --git a/src/cobalt/media_stream/media_stream.gyp b/src/cobalt/media_stream/media_stream.gyp
new file mode 100644
index 0000000..8581291
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream.gyp
@@ -0,0 +1,38 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'media_stream',
+ 'type': 'static_library',
+ 'sources': [
+ 'audio_parameters.cc',
+ 'audio_parameters.h',
+ 'media_stream.h',
+ 'media_stream_track.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ ],
+ 'export_dependent_settings': [
+ '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+ ],
+ },
+ ],
+}
+
diff --git a/src/cobalt/media_stream/media_stream.h b/src/cobalt/media_stream/media_stream.h
new file mode 100644
index 0000000..491072b
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream.h
@@ -0,0 +1,65 @@
+// Copyright 2018 Google Inc. 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 COBALT_MEDIA_STREAM_MEDIA_STREAM_H_
+#define COBALT_MEDIA_STREAM_MEDIA_STREAM_H_
+
+#include "base/memory/ref_counted.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/media_stream/media_stream_track.h"
+#include "cobalt/script/sequence.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace media_stream {
+
+// This class represents a MediaStream, and implements the specification at:
+// https://www.w3.org/TR/mediacapture-streams/#dom-mediastream
+class MediaStream : public dom::EventTarget {
+ public:
+ using TrackSequences = script::Sequence<scoped_refptr<MediaStreamTrack>>;
+
+ // Constructors.
+ MediaStream() = default;
+
+ explicit MediaStream(TrackSequences tracks): tracks_(std::move(tracks)) {
+ }
+
+ // Functions.
+ script::Sequence<scoped_refptr<MediaStreamTrack>>& GetAudioTracks() {
+ return tracks_;
+ }
+
+ script::Sequence<scoped_refptr<MediaStreamTrack>>& GetTracks() {
+ return GetAudioTracks();
+ }
+
+ void TraceMembers(script::Tracer* tracer) override {
+ EventTarget::TraceMembers(tracer);
+ tracer->TraceItems(tracks_);
+ }
+
+ DEFINE_WRAPPABLE_TYPE(MediaStream);
+
+ private:
+ MediaStream(const MediaStream&) = delete;
+ MediaStream& operator=(const MediaStream&) = delete;
+
+ script::Sequence<scoped_refptr<MediaStreamTrack>> tracks_;
+};
+
+} // namespace media_stream
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_STREAM_MEDIA_STREAM_H_
diff --git a/src/cobalt/media_stream/media_stream.idl b/src/cobalt/media_stream/media_stream.idl
new file mode 100644
index 0000000..d98bade
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream.idl
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// Implements a subset of the specification at:
+// https://www.w3.org/TR/mediacapture-streams/#dom-mediastream
+[Exposed=Window,
+ Constructor
+]
+interface MediaStream : EventTarget {
+ sequence<MediaStreamTrack> getAudioTracks();
+ sequence<MediaStreamTrack> getTracks();
+};
diff --git a/src/cobalt/media_stream/media_stream_constraints.idl b/src/cobalt/media_stream/media_stream_constraints.idl
new file mode 100644
index 0000000..72bf8f8
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream_constraints.idl
@@ -0,0 +1,19 @@
+// Copyright 2018 Google Inc. 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.
+
+// https://www.w3.org/TR/mediacapture-streams/#mediastreamconstraints
+
+dictionary MediaStreamConstraints {
+ boolean audio = false;
+};
diff --git a/src/cobalt/media_stream/media_stream_test.cc b/src/cobalt/media_stream/media_stream_test.cc
new file mode 100644
index 0000000..53d962d
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 Google Inc. 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 "cobalt/media_stream/media_stream.h"
+
+#include "base/memory/ref_counted.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace media_stream {
+
+class MediaStreamTest : public ::testing::Test {
+ public:
+ MediaStreamTest() : media_stream_(new MediaStream()) {}
+
+ protected:
+ scoped_refptr<MediaStream> media_stream_;
+};
+
+TEST_F(MediaStreamTest, TestConstruction) {
+ ASSERT_TRUE(media_stream_);
+ EXPECT_TRUE(media_stream_->GetAudioTracks().empty());
+ EXPECT_TRUE(media_stream_->GetTracks().empty());
+}
+
+} // namespace media_stream
+} // namespace cobalt
diff --git a/src/cobalt/media_stream/media_stream_test.gyp b/src/cobalt/media_stream/media_stream_test.gyp
new file mode 100644
index 0000000..642166b
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream_test.gyp
@@ -0,0 +1,49 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'variables': {
+ 'sb_pedantic_warnings': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'media_stream_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'audio_parameters_test.cc',
+ 'media_stream_test.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/dom/dom.gyp:dom',
+ '<(DEPTH)/cobalt/media_stream/media_stream.gyp:media_stream',
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ },
+
+ {
+ 'target_name': 'media_stream_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'media_stream_test',
+ ],
+ 'variables': {
+ 'executable_name': 'media_stream_test',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+ },
+ ],
+}
+
diff --git a/src/cobalt/media_stream/media_stream_track.h b/src/cobalt/media_stream/media_stream_track.h
new file mode 100644
index 0000000..bb060b8
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream_track.h
@@ -0,0 +1,55 @@
+// Copyright 2018 Google Inc. 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 COBALT_MEDIA_STREAM_MEDIA_STREAM_TRACK_H_
+#define COBALT_MEDIA_STREAM_MEDIA_STREAM_TRACK_H_
+
+#include "base/string_piece.h"
+#include "cobalt/dom/event_target.h"
+#include "cobalt/media_stream/media_track_settings.h"
+
+namespace cobalt {
+namespace media_stream {
+
+// This class represents a MediaStreamTrack, and implements the specification
+// at: https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack
+class MediaStreamTrack : public dom::EventTarget {
+ public:
+ // Function exposed to Javascript via IDL.
+ const MediaTrackSettings& GetSettings() { return settings_; }
+
+ // The following function is not exposed to Javascript.
+
+ // Returns number of bytes read. If the return value is 0, the user
+ // is encouraged to try again later, as there might be no additional
+ // data available. Returns a value < 0 on error.
+ int64 Read(base::StringPiece output_buffer) {
+ UNREFERENCED_PARAMETER(output_buffer);
+ NOTIMPLEMENTED();
+ return 0;
+ }
+
+ DEFINE_WRAPPABLE_TYPE(MediaStreamTrack);
+
+ private:
+ MediaStreamTrack(const MediaStreamTrack&) = delete;
+ MediaStreamTrack& operator=(const MediaStreamTrack&) = delete;
+
+ MediaTrackSettings settings_;
+};
+
+} // namespace media_stream
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_STREAM_MEDIA_STREAM_TRACK_H_
diff --git a/src/cobalt/media_stream/media_stream_track.idl b/src/cobalt/media_stream/media_stream_track.idl
new file mode 100644
index 0000000..77a3bbb
--- /dev/null
+++ b/src/cobalt/media_stream/media_stream_track.idl
@@ -0,0 +1,19 @@
+// Copyright 2018 Google Inc. 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.
+
+// https://www.w3.org/TR/mediacapture-streams/#dom-mediastreamtrack
+[Exposed=Window]
+interface MediaStreamTrack : EventTarget {
+ MediaTrackSettings getSettings();
+};
diff --git a/src/cobalt/media_stream/media_track_settings.idl b/src/cobalt/media_stream/media_track_settings.idl
new file mode 100644
index 0000000..4647452
--- /dev/null
+++ b/src/cobalt/media_stream/media_track_settings.idl
@@ -0,0 +1,20 @@
+// Copyright 2018 Google Inc. 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.
+
+// https://www.w3.org/TR/mediacapture-streams/#media-track-settings
+dictionary MediaTrackSettings {
+ long sampleRate;
+ long sampleSize;
+ long channelCount;
+};
diff --git a/src/cobalt/network/persistent_cookie_store.cc b/src/cobalt/network/persistent_cookie_store.cc
index 5c2ae8c..fdfb687 100644
--- a/src/cobalt/network/persistent_cookie_store.cc
+++ b/src/cobalt/network/persistent_cookie_store.cc
@@ -20,171 +20,59 @@
#include "base/debug/trace_event.h"
#include "base/message_loop.h"
#include "googleurl/src/gurl.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
namespace cobalt {
namespace network {
namespace {
-const int kOriginalCookieSchemaVersion = 1;
-const int kLatestCookieSchemaVersion = 1;
const base::TimeDelta kMaxCookieLifetime = base::TimeDelta::FromDays(365 * 2);
-std::vector<net::CanonicalCookie*> GetAllCookies(sql::Connection* conn) {
+std::vector<net::CanonicalCookie*> GetAllCookies(
+ const storage::MemoryStore& memory_store) {
std::vector<net::CanonicalCookie*> actual_cookies;
- sql::Statement get_all(conn->GetCachedStatement(
- SQL_FROM_HERE,
- "SELECT url, name, value, domain, path, mac_key, mac_algorithm, "
- "creation, expiration, last_access, secure, http_only "
- "FROM CookieTable"));
- while (get_all.Step()) {
- // We create a CanonicalCookie directly through its constructor instead of
- // through CanonicalCookie::Create() and its sanitization because these
- // values are just serialized from a former instance of a CanonicalCookie
- // object that *was* created through CanonicalCookie::Create().
- net::CanonicalCookie* cookie = new net::CanonicalCookie(
- GURL(get_all.ColumnString(0)), get_all.ColumnString(1),
- get_all.ColumnString(2), get_all.ColumnString(3),
- get_all.ColumnString(4), get_all.ColumnString(5),
- get_all.ColumnString(6),
- base::Time::FromInternalValue(get_all.ColumnInt64(7)),
- base::Time::FromInternalValue(get_all.ColumnInt64(8)),
- base::Time::FromInternalValue(get_all.ColumnInt64(9)),
- get_all.ColumnBool(10), get_all.ColumnBool(11));
- actual_cookies.push_back(cookie);
- }
+ memory_store.GetAllCookies(&actual_cookies);
return actual_cookies;
}
-void SqlInit(const PersistentCookieStore::LoadedCallback& loaded_callback,
- storage::SqlContext* sql_context) {
- TRACE_EVENT0("cobalt::network", "PersistentCookieStore::SqlInit()");
-
- sql::Connection* conn = sql_context->sql_connection();
-
- // Check the table's schema version.
- int schema_version;
- bool table_exists =
- sql_context->GetSchemaVersion("CookieTable", &schema_version);
-
- if (table_exists) {
- if (schema_version == storage::StorageManager::kSchemaTableIsNew) {
- // This savegame predates the existence of the schema table.
- // Since the cookie table did not change between the initial release of
- // the app and the introduction of the schema table, assume that this
- // existing cookie table is schema version 1. This avoids a loss of
- // cookies on upgrade.
- DLOG(INFO) << "Updating CookieTable schema version to "
- << kOriginalCookieSchemaVersion;
- sql_context->UpdateSchemaVersion("CookieTable",
- kOriginalCookieSchemaVersion);
- } else if (schema_version == storage::StorageManager::kSchemaVersionLost) {
- // Since there has only been one schema so far, treat this the same as
- // kSchemaTableIsNew. When there are multiple schemas in the wild,
- // we may want to drop the table instead.
- sql_context->UpdateSchemaVersion("CookieTable",
- kOriginalCookieSchemaVersion);
- }
- } else {
- // The table does not exist, so create it in its latest form.
- sql::Statement create_table(conn->GetUniqueStatement(
- "CREATE TABLE CookieTable ("
- "url TEXT, "
- "name TEXT, "
- "value TEXT, "
- "domain TEXT, "
- "path TEXT, "
- "mac_key TEXT, "
- "mac_algorithm TEXT, "
- "creation INTEGER, "
- "expiration INTEGER, "
- "last_access INTEGER, "
- "secure INTEGER, "
- "http_only INTEGER, "
- "UNIQUE(name, domain, path) ON CONFLICT REPLACE)"));
- bool ok = create_table.Run();
- DCHECK(ok);
- sql_context->UpdateSchemaVersion("CookieTable", kLatestCookieSchemaVersion);
- }
-
- loaded_callback.Run(GetAllCookies(conn));
-}
-
-void SqlAddCookie(const net::CanonicalCookie& cc,
- storage::SqlContext* sql_context) {
- base::Time maximum_expiry = base::Time::Now() + kMaxCookieLifetime;
- base::Time expiry = cc.ExpiryDate();
- if (expiry > maximum_expiry) {
- expiry = maximum_expiry;
- }
- sql::Connection* conn = sql_context->sql_connection();
- sql::Statement insert_cookie(conn->GetCachedStatement(
- SQL_FROM_HERE,
- "INSERT INTO CookieTable ("
- "url, name, value, domain, path, mac_key, mac_algorithm, "
- "creation, expiration, last_access, secure, http_only"
- ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
-
- insert_cookie.BindString(0, cc.Source());
- insert_cookie.BindString(1, cc.Name());
- insert_cookie.BindString(2, cc.Value());
- insert_cookie.BindString(3, cc.Domain());
- insert_cookie.BindString(4, cc.Path());
- insert_cookie.BindString(5, cc.MACKey());
- insert_cookie.BindString(6, cc.MACAlgorithm());
- insert_cookie.BindInt64(7, cc.CreationDate().ToInternalValue());
- insert_cookie.BindInt64(8, expiry.ToInternalValue());
- insert_cookie.BindInt64(9, cc.LastAccessDate().ToInternalValue());
- insert_cookie.BindBool(10, cc.IsSecure());
- insert_cookie.BindBool(11, cc.IsHttpOnly());
- bool ok = insert_cookie.Run();
- DCHECK(ok);
- sql_context->FlushOnChange();
-}
-
-void SqlUpdateCookieAccessTime(const net::CanonicalCookie& cc,
- storage::SqlContext* sql_context) {
- sql::Connection* conn = sql_context->sql_connection();
- sql::Statement touch_cookie(
- conn->GetCachedStatement(SQL_FROM_HERE,
- "UPDATE CookieTable SET last_access = ? WHERE "
- "name = ? AND domain = ? AND path = ?"));
-
- base::Time maximum_expiry = base::Time::Now() + kMaxCookieLifetime;
- base::Time expiry = cc.ExpiryDate();
- if (expiry > maximum_expiry) {
- expiry = maximum_expiry;
- }
-
- touch_cookie.BindInt64(0, expiry.ToInternalValue());
- touch_cookie.BindString(1, cc.Name());
- touch_cookie.BindString(2, cc.Domain());
- touch_cookie.BindString(3, cc.Path());
- bool ok = touch_cookie.Run();
- DCHECK(ok);
- sql_context->FlushOnChange();
-}
-
-void SqlDeleteCookie(const net::CanonicalCookie& cc,
- storage::SqlContext* sql_context) {
- sql::Connection* conn = sql_context->sql_connection();
- sql::Statement delete_cookie(conn->GetCachedStatement(
- SQL_FROM_HERE,
- "DELETE FROM CookieTable WHERE name = ? AND domain = ? AND path = ?"));
- delete_cookie.BindString(0, cc.Name());
- delete_cookie.BindString(1, cc.Domain());
- delete_cookie.BindString(2, cc.Path());
- bool ok = delete_cookie.Run();
- DCHECK(ok);
- sql_context->FlushOnChange();
-}
-
-void SqlSendEmptyCookieList(
+void CookieStorageInit(
const PersistentCookieStore::LoadedCallback& loaded_callback,
- storage::SqlContext* sql_context) {
- UNREFERENCED_PARAMETER(sql_context);
+ const storage::MemoryStore& memory_store) {
+ TRACE_EVENT0("cobalt::network", "PersistentCookieStore::CookieStorageInit()");
+ loaded_callback.Run(GetAllCookies(memory_store));
+}
+
+void CookieStorageAddCookie(const net::CanonicalCookie& cc,
+ storage::MemoryStore* memory_store) {
+ base::Time maximum_expiry = base::Time::Now() + kMaxCookieLifetime;
+ base::Time expiry = cc.ExpiryDate();
+ if (expiry > maximum_expiry) {
+ expiry = maximum_expiry;
+ }
+
+ memory_store->AddCookie(cc, expiry.ToInternalValue());
+}
+
+void CookieStorageCookieAccessTime(const net::CanonicalCookie& cc,
+ storage::MemoryStore* memory_store) {
+ base::Time maximum_expiry = base::Time::Now() + kMaxCookieLifetime;
+ base::Time expiry = cc.ExpiryDate();
+ if (expiry > maximum_expiry) {
+ expiry = maximum_expiry;
+ }
+
+ memory_store->UpdateCookieAccessTime(cc, expiry.ToInternalValue());
+}
+
+void CookieStorageDeleteCookie(const net::CanonicalCookie& cc,
+ storage::MemoryStore* memory_store) {
+ memory_store->DeleteCookie(cc);
+}
+
+void SendEmptyCookieList(
+ const PersistentCookieStore::LoadedCallback& loaded_callback,
+ const storage::MemoryStore& memory_store) {
+ UNREFERENCED_PARAMETER(memory_store);
std::vector<net::CanonicalCookie*> empty_cookie_list;
loaded_callback.Run(empty_cookie_list);
}
@@ -197,33 +85,34 @@
PersistentCookieStore::~PersistentCookieStore() {}
void PersistentCookieStore::Load(const LoadedCallback& loaded_callback) {
- storage_->GetSqlContext(base::Bind(&SqlInit, loaded_callback));
+ storage_->WithReadOnlyMemoryStore(
+ base::Bind(&CookieStorageInit, loaded_callback));
}
void PersistentCookieStore::LoadCookiesForKey(
const std::string& key, const LoadedCallback& loaded_callback) {
UNREFERENCED_PARAMETER(key);
// We don't support loading of individual cookies.
- // This is always called after Load(), so just post the callback to the SQL
- // thread to make sure it is run after Load() has finished.
- // See comments in net/cookie_monster.cc for more information.
- storage_->GetSqlContext(base::Bind(&SqlSendEmptyCookieList,
- loaded_callback));
+ // This is always called after Load(), so just post the callback to the
+ // Storage thread to make sure it is run after Load() has finished. See
+ // comments in net/cookie_monster.cc for more information.
+ storage_->WithReadOnlyMemoryStore(
+ base::Bind(&SendEmptyCookieList, loaded_callback));
}
void PersistentCookieStore::AddCookie(const net::CanonicalCookie& cc) {
// We expect that all cookies we are fed are meant to persist.
DCHECK(cc.IsPersistent());
- storage_->GetSqlContext(base::Bind(&SqlAddCookie, cc));
+ storage_->WithMemoryStore(base::Bind(&CookieStorageAddCookie, cc));
}
void PersistentCookieStore::UpdateCookieAccessTime(
const net::CanonicalCookie& cc) {
- storage_->GetSqlContext(base::Bind(&SqlUpdateCookieAccessTime, cc));
+ storage_->WithMemoryStore(base::Bind(&CookieStorageCookieAccessTime, cc));
}
void PersistentCookieStore::DeleteCookie(const net::CanonicalCookie& cc) {
- storage_->GetSqlContext(base::Bind(&SqlDeleteCookie, cc));
+ storage_->WithMemoryStore(base::Bind(&CookieStorageDeleteCookie, cc));
}
void PersistentCookieStore::SetForceKeepSessionState() {
diff --git a/src/cobalt/network/persistent_cookie_store_test.cc b/src/cobalt/network/persistent_cookie_store_test.cc
index da8c759..68bf54a 100644
--- a/src/cobalt/network/persistent_cookie_store_test.cc
+++ b/src/cobalt/network/persistent_cookie_store_test.cc
@@ -28,8 +28,6 @@
#include "cobalt/storage/storage_manager.h"
#include "googleurl/src/gurl.h"
#include "net/cookies/canonical_cookie.h"
-#include "sql/connection.h"
-#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cobalt {
@@ -97,27 +95,6 @@
DISALLOW_COPY_AND_ASSIGN(CookieLoader);
};
-class CookieVerifier : public CallbackWaiter {
- public:
- explicit CookieVerifier(net::CanonicalCookie* test_cookie)
- : test_cookie(test_cookie) {}
- void SqlSelect(storage::SqlContext* sql_context) {
- std::string statement =
- base::StringPrintf("SELECT url FROM CookieTable WHERE url = \"%s\";",
- test_cookie->Source().c_str());
- sql::Statement get_cookie(
- sql_context->sql_connection()->GetUniqueStatement(statement.c_str()));
- EXPECT_EQ(true, get_cookie.Step());
- EXPECT_EQ(test_cookie->Source(), get_cookie.ColumnString(0));
- Signal();
- }
-
- net::CanonicalCookie* test_cookie;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CookieVerifier);
-};
-
class DummyUpgradeHandler : public storage::StorageManager::UpgradeHandler {
void OnUpgrade(storage::StorageManager* /*storage*/, const char* /*data*/,
int /*size*/) override {}
@@ -155,7 +132,7 @@
};
} // namespace
-TEST_F(PersistentCookieStoreTest, LoadGetsCookies) {
+TEST_F(PersistentCookieStoreTest, LoadGetsAddedCookies) {
// Put a cookie into the database. Flush, then reload and make sure
// the Loaded callback contains it.
scoped_ptr<net::CanonicalCookie> test_cookie(CreateTestCookie());
@@ -175,22 +152,5 @@
ASSERT_EQ(1, loader.cookies.size());
EXPECT_TRUE(test_cookie->IsEquivalent(*loader.cookies[0]));
}
-
-TEST_F(PersistentCookieStoreTest, AddCookie) {
- CookieLoader loader;
- cookie_store_->Load(
- base::Bind(&CookieLoader::OnCookieLoad, base::Unretained(&loader)));
- message_loop_.RunUntilIdle();
-
- scoped_ptr<net::CanonicalCookie> test_cookie(CreateTestCookie());
- cookie_store_->AddCookie(*test_cookie);
-
- CookieVerifier verifier(test_cookie.get());
- storage_manager_->GetSqlContext(
- base::Bind(&CookieVerifier::SqlSelect, base::Unretained(&verifier)));
- message_loop_.RunUntilIdle();
- EXPECT_TRUE(verifier.TimedWait());
-}
-
} // namespace network
} // namespace cobalt
diff --git a/src/cobalt/overlay_info/qr_code_overlay.cc b/src/cobalt/overlay_info/qr_code_overlay.cc
index 7d91e84..a3388d8 100644
--- a/src/cobalt/overlay_info/qr_code_overlay.cc
+++ b/src/cobalt/overlay_info/qr_code_overlay.cc
@@ -147,7 +147,7 @@
TRACE_EVENT0("cobalt::overlay_info", "AnimateCB()");
- OverlayInfoRegistry::Register("overlay_info", &s_frame_count_,
+ OverlayInfoRegistry::Register("overlay_info:frame_count", &s_frame_count_,
sizeof(s_frame_count_));
++s_frame_count_;
@@ -159,20 +159,14 @@
return;
}
- // TODO: Explore the option to store the whole |infos| binary into one giant
- // QR code or combine the values into several large QR codes.
+ // Use a vector in case we decide to switch back to multiple qr codes.
std::vector<QrCode> qr_codes;
- for (size_t i = 0; i < infos.size(); ++i) {
- DCHECK_LT(infos[i] + i, infos.size());
- std::vector<uint8_t> data(infos.begin() + i + 1,
- infos.begin() + i + 1 + infos[i]);
- qr_codes.emplace_back(QrCode::encodeBinary(data, QrCode::Ecc::LOW));
- i += infos[i];
- }
+ qr_codes.emplace_back(QrCode::encodeBinary(infos, QrCode::Ecc::LOW));
image_node->source =
CreateImageForQrCodes(qr_codes, screen_size, resource_provider);
auto image_size = image_node->source->GetSize();
+ // TODO: Move the QR code between draws to avoid tearing.
image_node->destination_rect =
math::RectF(kScreenMarginInPixels, kScreenMarginInPixels,
image_size.width(), image_size.height());
diff --git a/src/cobalt/renderer/default_options_starboard.gyp b/src/cobalt/renderer/default_options_starboard.gyp
index 990ffa6..48df3fa 100644
--- a/src/cobalt/renderer/default_options_starboard.gyp
+++ b/src/cobalt/renderer/default_options_starboard.gyp
@@ -21,7 +21,7 @@
'renderer_parameters_setup.gypi',
],
'sources': [
- '<(DEPTH)/cobalt/renderer/renderer_module_default_options_starboard.cc',
+ '<(DEPTH)/cobalt/renderer/get_default_rasterizer_for_platform.cc',
],
},
],
diff --git a/src/cobalt/renderer/get_default_rasterizer_for_platform.cc b/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
new file mode 100644
index 0000000..608110d
--- /dev/null
+++ b/src/cobalt/renderer/get_default_rasterizer_for_platform.cc
@@ -0,0 +1,127 @@
+// Copyright 2018 Google Inc. 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 "cobalt/renderer/get_default_rasterizer_for_platform.h"
+
+#include "cobalt/renderer/backend/graphics_context.h"
+#include "cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h"
+#include "cobalt/renderer/rasterizer/blitter/software_rasterizer.h"
+#include "cobalt/renderer/rasterizer/egl/hardware_rasterizer.h"
+#include "cobalt/renderer/rasterizer/egl/software_rasterizer.h"
+#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
+#include "cobalt/renderer/rasterizer/stub/rasterizer.h"
+#include "cobalt/renderer/renderer_module.h"
+
+namespace cobalt {
+namespace renderer {
+
+namespace {
+
+#if COBALT_FORCE_STUB_RASTERIZER
+scoped_ptr<rasterizer::Rasterizer> CreateStubRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ UNREFERENCED_PARAMETER(graphics_context);
+ return scoped_ptr<rasterizer::Rasterizer>(new rasterizer::stub::Rasterizer());
+}
+#endif // COBALT_FORCE_STUB_RASTERIZER
+
+#if SB_HAS(GLES2)
+scoped_ptr<rasterizer::Rasterizer> CreateGLESSoftwareRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ return scoped_ptr<rasterizer::Rasterizer>(
+ new rasterizer::egl::SoftwareRasterizer(
+ graphics_context, options.purge_skia_font_caches_on_destruction));
+}
+
+scoped_ptr<rasterizer::Rasterizer> CreateGLESHardwareRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ return scoped_ptr<rasterizer::Rasterizer>(
+ new rasterizer::egl::HardwareRasterizer(
+ graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
+ options.skia_glyph_texture_atlas_dimensions.height(),
+ options.skia_cache_size_in_bytes,
+ options.scratch_surface_cache_size_in_bytes,
+ options.offscreen_target_cache_size_in_bytes,
+ options.purge_skia_font_caches_on_destruction,
+ options.force_deterministic_rendering));
+}
+
+scoped_ptr<rasterizer::Rasterizer> CreateSkiaHardwareRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ return scoped_ptr<rasterizer::Rasterizer>(
+ new rasterizer::skia::HardwareRasterizer(
+ graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
+ options.skia_glyph_texture_atlas_dimensions.height(),
+ options.skia_cache_size_in_bytes,
+ options.scratch_surface_cache_size_in_bytes,
+ options.purge_skia_font_caches_on_destruction,
+ options.force_deterministic_rendering));
+}
+#endif // #if SB_HAS(GLES2)
+
+#if SB_HAS(BLITTER)
+scoped_ptr<rasterizer::Rasterizer> CreateBlitterSoftwareRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ return scoped_ptr<rasterizer::Rasterizer>(
+ new rasterizer::blitter::SoftwareRasterizer(
+ graphics_context, options.purge_skia_font_caches_on_destruction));
+}
+
+scoped_ptr<rasterizer::Rasterizer> CreateBlitterHardwareRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ return scoped_ptr<rasterizer::Rasterizer>(
+ new rasterizer::blitter::HardwareRasterizer(
+ graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
+ options.skia_glyph_texture_atlas_dimensions.height(),
+ options.scratch_surface_cache_size_in_bytes,
+ options.software_surface_cache_size_in_bytes,
+ options.purge_skia_font_caches_on_destruction));
+}
+#endif // SB_HAS(BLITTER)
+
+} // namespace
+
+RasterizerInfo GetDefaultRasterizerForPlatform() {
+#if COBALT_FORCE_STUB_RASTERIZER
+ return {"stub", base::Bind(&CreateStubRasterizer)};
+#else
+#if SB_HAS(GLES2)
+#if COBALT_FORCE_SOFTWARE_RASTERIZER
+ return {"gles-software", base::Bind(&CreateGLESSoftwareRasterizer)};
+#elif defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
+ return {"gles", base::Bind(&CreateGLESHardwareRasterizer)};
+#else
+ return {"skia", base::Bind(&CreateSkiaHardwareRasterizer)};
+#endif // COBALT_FORCE_SOFTWARE_RASTERIZER
+#elif SB_HAS(BLITTER)
+#if COBALT_FORCE_SOFTWARE_RASTERIZER
+ return {"blitter-software", base::Bind(&CreateBlitterSoftwareRasterizer)};
+#else
+ return {"blitter", base::Bind(&CreateBlitterHardwareRasterizer)};
+#endif // COBALT_FORCE_SOFTWARE_RASTERIZER
+#else
+#error "Either GLES2 or the Starboard Blitter API must be available."
+ return {"", NULL};
+#endif
+#endif // #if COBALT_FORCE_STUB_RASTERIZER
+}
+
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/get_default_rasterizer_for_platform.h b/src/cobalt/renderer/get_default_rasterizer_for_platform.h
new file mode 100644
index 0000000..0d6ed26
--- /dev/null
+++ b/src/cobalt/renderer/get_default_rasterizer_for_platform.h
@@ -0,0 +1,35 @@
+// Copyright 2018 Google Inc. 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 COBALT_RENDERER_GET_DEFAULT_RASTERIZER_FOR_PLATFORM_H_
+#define COBALT_RENDERER_GET_DEFAULT_RASTERIZER_FOR_PLATFORM_H_
+
+#include <string>
+
+#include "cobalt/renderer/renderer_module.h"
+
+namespace cobalt {
+namespace renderer {
+
+struct RasterizerInfo {
+ std::string rasterizer_name;
+ RendererModule::Options::CreateRasterizerCallback create_rasterizer_callback;
+};
+
+RasterizerInfo GetDefaultRasterizerForPlatform();
+
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_GET_DEFAULT_RASTERIZER_FOR_PLATFORM_H_
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 3ec36c1..c7a8f61 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -363,36 +363,26 @@
bool animations_active =
!are_stat_tracked_animations_expired && did_rasterize;
+ // Rasterizations are only tracked by the timers when there are animations.
+ // This applies when animations were active during either the last
+ // rasterization or the current one. The reason for including the last one is
+ // that if animations have just expired, then this rasterization produces the
+ // final state of the animated tree.
if (last_animations_active || animations_active) {
- // The rasterization is only timed with the animations timer when there are
- // animations to track. This applies when animations were active during
- // either the last rasterization or the current one. The reason for
- // including the last one is that if animations have just expired, then this
- // rasterization produces the final state of the animated tree.
if (did_rasterize) {
rasterize_animations_timer_.Start(start_time);
rasterize_animations_timer_.Stop(end_time);
- }
- // If animations are going from being inactive to active, then set the c_val
- // prior to starting the animation so that it's in the correct state while
- // the tree is being rendered.
- // Also, start the interval timer now. While the first entry only captures a
- // partial interval, it's recorded to include the duration of the first
- // submission. All subsequent entries will record a full interval.
- if (!last_animations_active && animations_active) {
- has_active_animations_c_val_ = true;
- rasterize_periodic_interval_timer_.Start(start_time);
- rasterize_animations_interval_timer_.Start(start_time);
- }
-
- if (!did_rasterize) {
- // If we didn't actually rasterize anything, don't count this sample.
+ // The interval timers require animations to have been active the previous
+ // frame so that there is a full interval to record.
+ if (last_animations_active) {
+ rasterize_periodic_interval_timer_.Stop(end_time);
+ rasterize_animations_interval_timer_.Stop(end_time);
+ }
+ } else {
+ // If we didn't actually rasterize anything, then don't count this sample.
rasterize_periodic_interval_timer_.Cancel();
rasterize_animations_interval_timer_.Cancel();
- } else {
- rasterize_periodic_interval_timer_.Stop(end_time);
- rasterize_animations_interval_timer_.Stop(end_time);
}
// If animations are active, then they are guaranteed at least one more
@@ -405,11 +395,12 @@
// Check for if the animations are starting or ending.
if (!last_animations_active && animations_active) {
animations_start_time_ = end_time.ToInternalValue();
+ has_active_animations_c_val_ = true;
} else if (last_animations_active && !animations_active) {
animations_end_time_ = end_time.ToInternalValue();
- has_active_animations_c_val_ = false;
rasterize_animations_interval_timer_.Flush();
rasterize_animations_timer_.Flush();
+ has_active_animations_c_val_ = false;
}
}
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
index 901661d..189a4fe 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.cc
@@ -51,7 +51,7 @@
int scratch_surface_cache_size_in_bytes,
int offscreen_target_cache_size_in_bytes,
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching);
+ bool force_deterministic_rendering);
~Impl();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
@@ -82,6 +82,7 @@
const math::Rect& content_rect);
sk_sp<SkSurface> CreateFallbackSurface(
+ bool force_deterministic_rendering,
const backend::RenderTarget* render_target);
scoped_ptr<skia::HardwareRasterizer> fallback_rasterizer_;
@@ -99,11 +100,12 @@
int scratch_surface_cache_size_in_bytes,
int offscreen_target_cache_size_in_bytes,
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching)
+ bool force_deterministic_rendering)
: fallback_rasterizer_(new skia::HardwareRasterizer(
graphics_context, skia_atlas_width, skia_atlas_height,
skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
- purge_skia_font_caches_on_destruction)),
+ purge_skia_font_caches_on_destruction,
+ force_deterministic_rendering)),
graphics_context_(
base::polymorphic_downcast<backend::GraphicsContextEGL*>(
graphics_context)) {
@@ -117,9 +119,9 @@
offscreen_target_manager_.reset(new OffscreenTargetManager(
graphics_context_,
base::Bind(&HardwareRasterizer::Impl::CreateFallbackSurface,
- base::Unretained(this)),
+ base::Unretained(this), force_deterministic_rendering),
offscreen_target_cache_size_in_bytes));
- if (disable_rasterizer_caching) {
+ if (force_deterministic_rendering) {
offscreen_target_manager_->SetCacheErrorThreshold(0.0f);
}
}
@@ -281,6 +283,7 @@
}
sk_sp<SkSurface> HardwareRasterizer::Impl::CreateFallbackSurface(
+ bool force_deterministic_rendering,
const backend::RenderTarget* render_target) {
// Wrap the given render target in a new skia surface.
GrBackendRenderTargetDesc skia_desc;
@@ -292,9 +295,16 @@
skia_desc.fStencilBits = 0;
skia_desc.fRenderTargetHandle = render_target->GetPlatformHandle();
- SkSurfaceProps skia_surface_props(
- SkSurfaceProps::kUseDistanceFieldFonts_Flag,
- SkSurfaceProps::kLegacyFontHost_InitType);
+ uint32_t flags = 0;
+ if (!force_deterministic_rendering) {
+ // Distance field fonts are known to result in non-deterministic graphical,
+ // since the output depends on the size of the glyph that enters the atlas
+ // first (which would get re-used for similarly but unequal sized
+ // subsequent glyphs).
+ flags = SkSurfaceProps::kUseDistanceFieldFonts_Flag;
+ }
+ SkSurfaceProps skia_surface_props(flags,
+ SkSurfaceProps::kLegacyFontHost_InitType);
return SkSurface::MakeFromBackendRenderTarget(GetFallbackContext(), skia_desc,
&skia_surface_props);
}
@@ -305,13 +315,13 @@
int scratch_surface_cache_size_in_bytes,
int offscreen_target_cache_size_in_bytes,
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching)
- : impl_(new Impl(
- graphics_context, skia_atlas_width, skia_atlas_height,
- skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
- offscreen_target_cache_size_in_bytes,
- purge_skia_font_caches_on_destruction, disable_rasterizer_caching)) {
-}
+ bool force_deterministic_rendering)
+ : impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
+ skia_cache_size_in_bytes,
+ scratch_surface_cache_size_in_bytes,
+ offscreen_target_cache_size_in_bytes,
+ purge_skia_font_caches_on_destruction,
+ force_deterministic_rendering)) {}
HardwareRasterizer::~HardwareRasterizer() {}
diff --git a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
index 4f1e85d..bb60e9e 100644
--- a/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/hardware_rasterizer.h
@@ -46,16 +46,16 @@
// how much GPU memory is used to save the results of render tree nodes to
// improve performance. This should be non-zero, otherwise performance
// degrades drastically.
- // |disable_rasterizer_caching| disables caching of render tree node outputs.
- // This significantly degrades performance but provides sub-pixel correctness.
- // This should only be done for testing purposes.
+ // |force_deterministic_rendering| disables caching of render tree node
+ // outputs. This significantly degrades performance but provides sub-pixel
+ // correctness. This should only be done for testing purposes.
explicit HardwareRasterizer(backend::GraphicsContext* graphics_context,
int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
int offscreen_target_cache_size_in_bytes,
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching);
+ bool force_deterministic_rendering);
virtual ~HardwareRasterizer();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
index 2244592..a015e94 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.cc
@@ -180,7 +180,7 @@
int offscreen_target_cache_size_in_bytes,
#endif
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching);
+ bool force_deterministic_rendering);
~Impl();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
@@ -254,23 +254,18 @@
int offscreen_target_cache_size_in_bytes,
#endif
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching)
+ bool force_deterministic_rendering)
: graphics_context_(
base::polymorphic_downcast<backend::GraphicsContextEGL*>(
graphics_context)),
- hardware_rasterizer_(graphics_context, skia_atlas_width,
- skia_atlas_height, skia_cache_size_in_bytes,
- scratch_surface_cache_size_in_bytes,
+ hardware_rasterizer_(
+ graphics_context, skia_atlas_width, skia_atlas_height,
+ skia_cache_size_in_bytes, scratch_surface_cache_size_in_bytes,
#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
- offscreen_target_cache_size_in_bytes,
+ offscreen_target_cache_size_in_bytes,
#endif
- purge_skia_font_caches_on_destruction
-#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
- ,
- disable_rasterizer_caching
-#endif
- ),
+ purge_skia_font_caches_on_destruction, force_deterministic_rendering),
render_tree_temp_(nullptr),
main_render_target_temp_(nullptr),
video_projection_type_(kCbLibVideoProjectionTypeNone),
@@ -649,7 +644,7 @@
int offscreen_target_cache_size_in_bytes,
#endif
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching)
+ bool force_deterministic_rendering)
: impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
skia_cache_size_in_bytes,
scratch_surface_cache_size_in_bytes,
@@ -657,7 +652,8 @@
offscreen_target_cache_size_in_bytes,
#endif
purge_skia_font_caches_on_destruction,
- disable_rasterizer_caching)) {}
+ force_deterministic_rendering)) {
+}
ExternalRasterizer::~ExternalRasterizer() {}
diff --git a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
index 1a6a5a2..84f9f5e 100644
--- a/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/lib/external_rasterizer.h
@@ -41,7 +41,7 @@
int rasterizer_gpu_cache_size_in_bytes,
#endif
bool purge_skia_font_caches_on_destruction,
- bool disable_rasterizer_caching);
+ bool force_deterministic_rendering);
virtual ~ExternalRasterizer();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
diff --git a/src/cobalt/renderer/rasterizer/lib/get_default_rasterizer_for_platform_lib.cc b/src/cobalt/renderer/rasterizer/lib/get_default_rasterizer_for_platform_lib.cc
new file mode 100644
index 0000000..00cd166
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/lib/get_default_rasterizer_for_platform_lib.cc
@@ -0,0 +1,45 @@
+// Copyright 2018 Google Inc. 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 "cobalt/renderer/get_default_rasterizer_for_platform.h"
+#include "cobalt/renderer/rasterizer/lib/external_rasterizer.h"
+#include "cobalt/renderer/renderer_module.h"
+
+namespace cobalt {
+namespace renderer {
+
+namespace {
+scoped_ptr<rasterizer::Rasterizer> CreateRasterizer(
+ backend::GraphicsContext* graphics_context,
+ const RendererModule::Options& options) {
+ return scoped_ptr<rasterizer::Rasterizer>(
+ new rasterizer::lib::ExternalRasterizer(
+ graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
+ options.skia_glyph_texture_atlas_dimensions.height(),
+ options.skia_cache_size_in_bytes,
+ options.scratch_surface_cache_size_in_bytes,
+#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
+ options.offscreen_target_cache_size_in_bytes,
+#endif
+ options.purge_skia_font_caches_on_destruction,
+ options.force_deterministic_rendering));
+}
+} // namespace
+
+RasterizerInfo GetDefaultRasterizerForPlatform() {
+ return {"external", base::Bind(&CreateRasterizer)};
+}
+
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/lib/lib.gyp b/src/cobalt/renderer/rasterizer/lib/lib.gyp
index 03cead1..c298b5d 100644
--- a/src/cobalt/renderer/rasterizer/lib/lib.gyp
+++ b/src/cobalt/renderer/rasterizer/lib/lib.gyp
@@ -22,7 +22,7 @@
],
'sources': [
'external_rasterizer.cc',
- 'renderer_module_default_options_lib.cc'
+ 'get_default_rasterizer_for_platform_lib.cc'
],
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
diff --git a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc b/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
deleted file mode 100644
index 03c17e6..0000000
--- a/src/cobalt/renderer/rasterizer/lib/renderer_module_default_options_lib.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 Google Inc. 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 "cobalt/renderer/renderer_module.h"
-#include "cobalt/renderer/rasterizer/lib/external_rasterizer.h"
-
-namespace cobalt {
-namespace renderer {
-
-namespace {
-scoped_ptr<rasterizer::Rasterizer> CreateRasterizer(
- backend::GraphicsContext* graphics_context,
- const RendererModule::Options& options) {
- return scoped_ptr<rasterizer::Rasterizer>(
- new rasterizer::lib::ExternalRasterizer(
- graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
- options.skia_glyph_texture_atlas_dimensions.height(),
- options.skia_cache_size_in_bytes,
- options.scratch_surface_cache_size_in_bytes,
-#if defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
- options.offscreen_target_cache_size_in_bytes,
-#endif
- options.purge_skia_font_caches_on_destruction,
- options.disable_rasterizer_caching));
-}
-} // namespace
-
-void RendererModule::Options::SetPerPlatformDefaultOptions() {
- // Ensure the scene is re-rasterized even if the render tree is unchanged so
- // that headset look changes are properly rendered.
- submit_even_if_render_tree_is_unchanged = true;
-
- create_rasterizer_function = base::Bind(&CreateRasterizer);
-}
-
-} // namespace renderer
-} // namespace cobalt
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index 0f17aa0..cdf4e81 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -60,7 +60,8 @@
Impl(backend::GraphicsContext* graphics_context, int skia_atlas_width,
int skia_atlas_height, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
- bool purge_skia_font_caches_on_destruction);
+ bool purge_skia_font_caches_on_destruction,
+ bool force_deterministic_rendering);
~Impl();
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
@@ -133,13 +134,25 @@
// or not since Skia does not let us pull that information out of the
// SkCanvas object (which Skia would internally use to get this information).
base::optional<GrSurfaceOrigin> current_surface_origin_;
+
+ // If true, rasterizer will eschew performance optimizations in favor of
+ // ensuring that each rasterization is pixel-wise deterministic, given
+ // the same render tree.
+ bool force_deterministic_rendering_;
};
namespace {
-SkSurfaceProps GetRenderTargetSurfaceProps() {
- return SkSurfaceProps(SkSurfaceProps::kUseDistanceFieldFonts_Flag,
- SkSurfaceProps::kLegacyFontHost_InitType);
+SkSurfaceProps GetRenderTargetSurfaceProps(bool force_deterministic_rendering) {
+ uint32_t flags = 0;
+ if (!force_deterministic_rendering) {
+ // Distance field fonts are known to result in non-deterministic graphical
+ // output since the output depends on the size of the glyph that enters the
+ // atlas first (which would get re-used for similarly but unequal sized
+ // subsequent glyphs).
+ flags = SkSurfaceProps::kUseDistanceFieldFonts_Flag;
+ }
+ return SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
}
// Takes meta-data from a Cobalt RenderTarget object and uses it to fill out
@@ -431,6 +444,10 @@
// order.
draw_state->render_target->flush();
+ // This may be the first use of the render target, so ensure it is bound.
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER,
+ draw_state->render_target->getRenderTargetHandle()));
+
SkISize canvas_size = draw_state->render_target->getBaseLayerSize();
GL_CALL(glViewport(0, 0, canvas_size.width(), canvas_size.height()));
@@ -501,6 +518,10 @@
// order.
draw_state->render_target->flush();
+ // This may be the first use of the render target, so ensure it is bound.
+ GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER,
+ draw_state->render_target->getRenderTargetHandle()));
+
// We setup our viewport to fill the entire canvas.
GL_CALL(glViewport(0, 0, canvas_size.width(), canvas_size.height()));
GL_CALL(glScissor(0, 0, canvas_size.width(), canvas_size.height()));
@@ -545,10 +566,12 @@
int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
- bool purge_skia_font_caches_on_destruction)
+ bool purge_skia_font_caches_on_destruction,
+ bool force_deterministic_rendering)
: graphics_context_(
base::polymorphic_downcast<backend::GraphicsContextEGL*>(
- graphics_context)) {
+ graphics_context)),
+ force_deterministic_rendering_(force_deterministic_rendering) {
TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::Impl::Impl()");
DLOG(INFO) << "skia_cache_size_in_bytes: " << skia_cache_size_in_bytes;
@@ -626,11 +649,6 @@
backend::GraphicsContextEGL::ScopedMakeCurrent scoped_make_current(
graphics_context_, render_target_egl);
- // Make sure the render target's framebuffer is bound before continuing.
- // Skia will usually do this, but it is possible for some render trees to
- // have non-skia draw calls only, in which case this needs to be done.
- GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER,
- render_target_egl->GetPlatformHandle()));
// First reset the graphics context state for the pending render tree
// draw calls, in case we have modified state in between.
@@ -690,7 +708,8 @@
CobaltRenderTargetToSkiaBackendRenderTargetDesc(*render_target);
skia_desc.fOrigin = kTopLeft_GrSurfaceOrigin;
- SkSurfaceProps surface_props = GetRenderTargetSurfaceProps();
+ SkSurfaceProps surface_props =
+ GetRenderTargetSurfaceProps(force_deterministic_rendering_);
sk_sp<SkSurface> sk_output_surface = SkSurface::MakeFromBackendRenderTarget(
gr_context_.get(), skia_desc, &surface_props);
SkCanvas* canvas = sk_output_surface->getCanvas();
@@ -761,7 +780,8 @@
// Create an SkSurface from the render target so that we can acquire a
// SkCanvas object from it in Submit().
- SkSurfaceProps surface_props = GetRenderTargetSurfaceProps();
+ SkSurfaceProps surface_props =
+ GetRenderTargetSurfaceProps(force_deterministic_rendering_);
// Create a canvas from the render target.
GrBackendRenderTargetDesc skia_desc =
CobaltRenderTargetToSkiaBackendRenderTargetDesc(*render_target);
@@ -815,11 +835,13 @@
backend::GraphicsContext* graphics_context, int skia_atlas_width,
int skia_atlas_height, int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
- bool purge_skia_font_caches_on_destruction)
+ bool purge_skia_font_caches_on_destruction,
+ bool force_deterministic_rendering)
: impl_(new Impl(graphics_context, skia_atlas_width, skia_atlas_height,
skia_cache_size_in_bytes,
scratch_surface_cache_size_in_bytes,
- purge_skia_font_caches_on_destruction)) {}
+ purge_skia_font_caches_on_destruction,
+ force_deterministic_rendering)) {}
HardwareRasterizer::~HardwareRasterizer() {}
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
index d662b87..8bcad77 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
@@ -54,7 +54,8 @@
int skia_atlas_width, int skia_atlas_height,
int skia_cache_size_in_bytes,
int scratch_surface_cache_size_in_bytes,
- bool purge_skia_font_caches_on_destruction);
+ bool purge_skia_font_caches_on_destruction,
+ bool force_deterministic_rendering);
virtual ~HardwareRasterizer();
// Consume the render tree and output the results to the render target passed
diff --git a/src/cobalt/renderer/renderer.gyp b/src/cobalt/renderer/renderer.gyp
index ea26345..912ac33 100644
--- a/src/cobalt/renderer/renderer.gyp
+++ b/src/cobalt/renderer/renderer.gyp
@@ -28,6 +28,7 @@
'pipeline.h',
'renderer_module.cc',
'renderer_module.h',
+ 'renderer_module_default_options.cc',
'smoothed_value.cc',
'smoothed_value.h',
'submission.h',
diff --git a/src/cobalt/renderer/renderer_module.cc b/src/cobalt/renderer/renderer_module.cc
index 99d6a09..5bbc79b 100644
--- a/src/cobalt/renderer/renderer_module.cc
+++ b/src/cobalt/renderer/renderer_module.cc
@@ -24,7 +24,7 @@
namespace renderer {
RendererModule::Options::Options()
- : disable_rasterizer_caching(false),
+ : force_deterministic_rendering(false),
purge_skia_font_caches_on_destruction(true),
enable_fps_stdout(false),
enable_fps_overlay(false) {
diff --git a/src/cobalt/renderer/renderer_module.h b/src/cobalt/renderer/renderer_module.h
index e4c39ee..799726c 100644
--- a/src/cobalt/renderer/renderer_module.h
+++ b/src/cobalt/renderer/renderer_module.h
@@ -80,7 +80,7 @@
// a sub-pixel offset that is different from when the cache was created.
// This caching mechanism should only be disabled for testing purposes
// (e.g. screenshot diff tools).
- bool disable_rasterizer_caching;
+ bool force_deterministic_rendering;
// If this flag is set to true, the pipeline will not re-submit a render
// tree if it has not changed from the previous submission. This can save
diff --git a/src/cobalt/renderer/renderer_module_default_options.cc b/src/cobalt/renderer/renderer_module_default_options.cc
new file mode 100644
index 0000000..c835e9f
--- /dev/null
+++ b/src/cobalt/renderer/renderer_module_default_options.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 Google Inc. 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 "cobalt/renderer/get_default_rasterizer_for_platform.h"
+#include "cobalt/renderer/renderer_module.h"
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace renderer {
+
+void RendererModule::Options::SetPerPlatformDefaultOptions() {
+ // If there is no need to frequently flip the display buffer, then enable
+ // support for an optimization where the scene is not re-rasterized each frame
+ // if it has not changed from the last frame.
+ submit_even_if_render_tree_is_unchanged =
+ SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER;
+
+ create_rasterizer_function =
+ GetDefaultRasterizerForPlatform().create_rasterizer_callback;
+}
+
+} // namespace renderer
+} // namespace cobalt
diff --git a/src/cobalt/renderer/renderer_module_default_options_starboard.cc b/src/cobalt/renderer/renderer_module_default_options_starboard.cc
deleted file mode 100644
index 8c0c16e..0000000
--- a/src/cobalt/renderer/renderer_module_default_options_starboard.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2015 Google Inc. 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 "cobalt/renderer/renderer_module.h"
-
-#include "base/debug/trace_event.h"
-#include "base/logging.h"
-#include "cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h"
-#include "cobalt/renderer/rasterizer/blitter/software_rasterizer.h"
-#include "cobalt/renderer/rasterizer/egl/hardware_rasterizer.h"
-#include "cobalt/renderer/rasterizer/egl/software_rasterizer.h"
-#include "cobalt/renderer/rasterizer/skia/hardware_rasterizer.h"
-#include "cobalt/renderer/rasterizer/stub/rasterizer.h"
-
-namespace cobalt {
-namespace renderer {
-
-namespace {
-
-scoped_ptr<rasterizer::Rasterizer> CreateRasterizer(
- backend::GraphicsContext* graphics_context,
- const RendererModule::Options& options) {
- TRACE_EVENT0("cobalt::renderer", "CreateRasterizer");
-#if COBALT_FORCE_STUB_RASTERIZER
- return scoped_ptr<rasterizer::Rasterizer>(new rasterizer::stub::Rasterizer());
-#else
-#if SB_HAS(GLES2)
-#if COBALT_FORCE_SOFTWARE_RASTERIZER
- return scoped_ptr<rasterizer::Rasterizer>(
- new rasterizer::egl::SoftwareRasterizer(
- graphics_context, options.purge_skia_font_caches_on_destruction));
-#elif defined(COBALT_FORCE_DIRECT_GLES_RASTERIZER)
- return scoped_ptr<rasterizer::Rasterizer>(
- new rasterizer::egl::HardwareRasterizer(
- graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
- options.skia_glyph_texture_atlas_dimensions.height(),
- options.skia_cache_size_in_bytes,
- options.scratch_surface_cache_size_in_bytes,
- options.offscreen_target_cache_size_in_bytes,
- options.purge_skia_font_caches_on_destruction,
- options.disable_rasterizer_caching));
-#else
- return scoped_ptr<rasterizer::Rasterizer>(
- new rasterizer::skia::HardwareRasterizer(
- graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
- options.skia_glyph_texture_atlas_dimensions.height(),
- options.skia_cache_size_in_bytes,
- options.scratch_surface_cache_size_in_bytes,
- options.purge_skia_font_caches_on_destruction));
-#endif // COBALT_FORCE_SOFTWARE_RASTERIZER
-#elif SB_HAS(BLITTER)
-#if COBALT_FORCE_SOFTWARE_RASTERIZER
- return scoped_ptr<rasterizer::Rasterizer>(
- new rasterizer::blitter::SoftwareRasterizer(
- graphics_context, options.purge_skia_font_caches_on_destruction));
-#else
- return scoped_ptr<rasterizer::Rasterizer>(
- new rasterizer::blitter::HardwareRasterizer(
- graphics_context, options.skia_glyph_texture_atlas_dimensions.width(),
- options.skia_glyph_texture_atlas_dimensions.height(),
- options.scratch_surface_cache_size_in_bytes,
- options.software_surface_cache_size_in_bytes,
- options.purge_skia_font_caches_on_destruction));
-#endif // COBALT_FORCE_SOFTWARE_RASTERIZER
-#else
-#error "Either GLES2 or the Starboard Blitter API must be available."
- return scoped_ptr<rasterizer::Rasterizer>();
-#endif
-#endif // #if COBALT_FORCE_STUB_RASTERIZER
-}
-} // namespace
-
-void RendererModule::Options::SetPerPlatformDefaultOptions() {
- // If there is no need to frequently flip the display buffer, then enable
- // support for an optimization where the scene is not re-rasterized each frame
- // if it has not changed from the last frame.
- submit_even_if_render_tree_is_unchanged =
- SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER;
-
- create_rasterizer_function = base::Bind(&CreateRasterizer);
-}
-
-} // namespace renderer
-} // namespace cobalt
diff --git a/src/cobalt/renderer/test/png_utils/png_encode.cc b/src/cobalt/renderer/test/png_utils/png_encode.cc
index 934dd3c..c953420 100644
--- a/src/cobalt/renderer/test/png_utils/png_encode.cc
+++ b/src/cobalt/renderer/test/png_utils/png_encode.cc
@@ -16,6 +16,7 @@
#include <vector>
+#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "third_party/libpng/png.h"
@@ -58,6 +59,7 @@
scoped_array<uint8> EncodeRGBAToBuffer(const uint8_t* pixel_data, int width,
int height, int pitch_in_bytes,
size_t* out_size) {
+ TRACE_EVENT0("cobalt::renderer", "PNGEncode::EncodeRGBAToBuffer()");
// Initialize png library and headers for writing.
png_structp png =
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
diff --git a/src/cobalt/script/engine.gyp b/src/cobalt/script/engine.gyp
index 342bcb5..cfacbea 100644
--- a/src/cobalt/script/engine.gyp
+++ b/src/cobalt/script/engine.gyp
@@ -28,11 +28,12 @@
],
},
{
- # Empty target to ensure all targets for enabled engines are built when
- # building 'all'.
- 'target_name': 'all_engines',
+ 'target_name': 'engine_shell',
'type': 'none',
- 'dependencies': [],
+ 'conditions': [
+ [ 'javascript_engine == "mozjs-45"', { 'dependencies': ['mozjs-45/mozjs-45.gyp:mozjs-45', ], }, ],
+ [ 'javascript_engine == "v8"', { 'dependencies': ['v8c/v8c.gyp:v8c', ], }, ],
+ ],
},
],
}
diff --git a/src/cobalt/script/mozjs-45/mozjs.cc b/src/cobalt/script/mozjs-45/mozjs.cc
index f54f449..eefa9fe 100644
--- a/src/cobalt/script/mozjs-45/mozjs.cc
+++ b/src/cobalt/script/mozjs-45/mozjs.cc
@@ -88,8 +88,7 @@
// Execute the script and get the results of execution.
std::string result;
- bool success = global_environment->EvaluateScript(
- source, false /*mute_errors*/, &result);
+ bool success = global_environment->EvaluateScript(source, &result);
// Echo the results to stdout.
if (!success) {
std::cout << "Exception: ";
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 12ada40..fa7a127 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -352,11 +352,15 @@
}
void MozjsGlobalEnvironment::AddRoot(Traceable* traceable) {
+ DCHECK(traceable);
roots_.insert(traceable);
}
void MozjsGlobalEnvironment::RemoveRoot(Traceable* traceable) {
- roots_.erase(traceable);
+ DCHECK(traceable);
+ auto it = roots_.find(traceable);
+ DCHECK(it != roots_.end());
+ roots_.erase(it);
}
void MozjsGlobalEnvironment::DisableEval(const std::string& message) {
diff --git a/src/cobalt/script/promise.h b/src/cobalt/script/promise.h
index 5c6a1fb..2d01a2b 100644
--- a/src/cobalt/script/promise.h
+++ b/src/cobalt/script/promise.h
@@ -67,6 +67,27 @@
void Resolve() const { Resolve(PromiseResultUndefined()); }
};
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+
+inline std::ostream& operator<<(std::ostream& os, const PromiseState state) {
+ switch (state) {
+ case PromiseState::kFulfilled:
+ os << "Fulfilled";
+ return os;
+ case PromiseState::kPending:
+ os << "Pending";
+ return os;
+ case PromiseState::kRejected:
+ os << "Rejected";
+ return os;
+ }
+ DCHECK(false);
+ os << "Unknown promise state";
+ return os;
+}
+
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
+
} // namespace script
} // namespace cobalt
diff --git a/src/cobalt/script/sequence.h b/src/cobalt/script/sequence.h
index 8fa5c31..557559b 100644
--- a/src/cobalt/script/sequence.h
+++ b/src/cobalt/script/sequence.h
@@ -33,31 +33,26 @@
template <typename T>
class Sequence {
public:
- Sequence() {}
- Sequence(const Sequence& other) { *this = other; }
-
- void clear() { sequence_.erase(sequence_.begin(), sequence_.end()); }
-
- Sequence& operator=(const Sequence& other) {
- clear();
- size_type max = other.size();
- for (size_type i = 0; i < max; ++i) {
- push_back(other.at(i));
- }
- return *this;
- }
-
typedef std::vector<T> SequenceType;
// --- Vector partial emulation ---
typedef typename SequenceType::size_type size_type;
typedef typename SequenceType::reference reference;
typedef typename SequenceType::const_reference const_reference;
+ typedef typename SequenceType::iterator iterator;
+ typedef typename SequenceType::const_iterator const_iterator;
void push_back(const_reference value) { sequence_.push_back(value); }
size_type size() const { return sequence_.size(); }
bool empty() const { return sequence_.empty(); }
const_reference at(size_type index) const { return sequence_.at(index); }
void swap(Sequence<T>& other) { sequence_.swap(other.sequence_); }
+ iterator begin() { return sequence_.begin(); }
+ const_iterator begin() const { return sequence_.begin(); }
+ const_iterator cbegin() const { return sequence_.cbegin(); }
+ iterator end() { return sequence_.end(); }
+ const_iterator end() const { return sequence_.end(); }
+ const_iterator cend() const { return sequence_.cend(); }
+ void clear() { sequence_.clear(); }
private:
SequenceType sequence_;
diff --git a/src/cobalt/script/tracer.h b/src/cobalt/script/tracer.h
index c4fdcab..1828a06 100644
--- a/src/cobalt/script/tracer.h
+++ b/src/cobalt/script/tracer.h
@@ -89,15 +89,6 @@
}
}
- template <typename T>
- void TraceSequence(const Sequence<T>& sequence) {
- // TODO: Consider making |Sequence| more vector-like and just using the
- // iterator version.
- for (size_t i = 0; i < sequence.size(); i++) {
- Trace(sequence.at(i));
- }
- }
-
// Note: If your new class wants to own a container of |Traceable|s that
// does not have an iteration helper function here, you should add one.
};
diff --git a/src/cobalt/script/v8c/v8c.cc b/src/cobalt/script/v8c/v8c.cc
index 10c5b0c..a522c60 100644
--- a/src/cobalt/script/v8c/v8c.cc
+++ b/src/cobalt/script/v8c/v8c.cc
@@ -97,8 +97,7 @@
// Execute the script and get the results of execution.
std::string result;
- bool success = global_environment->EvaluateScript(
- source, false /*mute_errors*/, &result);
+ bool success = global_environment->EvaluateScript(source, &result);
// Echo the results to stdout.
if (!success) {
std::cout << "Exception: ";
diff --git a/src/cobalt/script/v8c/v8c_array_buffer_view.h b/src/cobalt/script/v8c/v8c_array_buffer_view.h
index e92ad7e..207b4c9 100644
--- a/src/cobalt/script/v8c/v8c_array_buffer_view.h
+++ b/src/cobalt/script/v8c/v8c_array_buffer_view.h
@@ -19,9 +19,9 @@
#include "cobalt/script/array_buffer_view.h"
#include "cobalt/script/v8c/entry_scope.h"
#include "cobalt/script/v8c/type_traits.h"
+#include "cobalt/script/v8c/v8c_array_buffer.h"
#include "cobalt/script/v8c/v8c_exception_state.h"
#include "cobalt/script/v8c/v8c_user_object_holder.h"
-#include "cobalt/script/v8c/weak_heap_object.h"
namespace cobalt {
namespace script {
@@ -32,8 +32,8 @@
public:
using BaseType = ArrayBufferView;
- V8cArrayBufferView(v8::Isolate* isolate, JS::HandleValue value)
- : isolate_(isolate), weak_heap_object_(isolate, value) {
+ V8cArrayBufferView(v8::Isolate* isolate, v8::Local<v8::Value> value)
+ : isolate_(isolate), ScopedPersistent(isolate, value) {
DCHECK(value->IsArrayBufferView());
}
@@ -79,7 +79,6 @@
v8::Isolate* isolate,
const ScriptValue<ArrayBufferView>* array_buffer_view_value,
v8::Local<v8::Value>* out_value) {
- TRACK_MEMORY_SCOPE("Javascript");
if (!array_buffer_view_value) {
*out_value = v8::Null(isolate);
@@ -95,7 +94,6 @@
v8::Isolate* isolate, v8::Local<v8::Value> value, int conversion_flags,
ExceptionState* exception_state,
V8cUserObjectHolder<V8cArrayBufferView>* out_array_buffer_view) {
- TRACK_MEMORY_SCOPE("Javascript");
DCHECK_EQ(0, conversion_flags);
DCHECK(out_array_buffer_view);
diff --git a/src/cobalt/script/v8c/v8c_engine.cc b/src/cobalt/script/v8c/v8c_engine.cc
index d1c7046..c435b23 100644
--- a/src/cobalt/script/v8c/v8c_engine.cc
+++ b/src/cobalt/script/v8c/v8c_engine.cc
@@ -132,6 +132,15 @@
isolate_->AddGCPrologueCallback(GCPrologueCallback);
isolate_->AddGCEpilogueCallback(GCEpilogueCallback);
+
+ // The V8 |SetStackLimit|'s parameter is the memory address that it should not
+ // pass, as opposed to the size of the stack that it should use. We set it
+ // to 3/4 of the main thread's stack size to cover for the space underneath
+ // the stack currently, and the space we want to reserve on top of the stack
+ // for when JavaScript calls back into C++ bindings.
+ uintptr_t here = reinterpret_cast<uintptr_t>(&here);
+ isolate_->SetStackLimit(here -
+ (3 * cobalt::browser::kWebModuleStackSize) / 4);
}
V8cEngine::~V8cEngine() {
diff --git a/src/cobalt/site/docs/reference/starboard/gyp-configuration.md b/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
index 4f85abd..864f562 100644
--- a/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
+++ b/src/cobalt/site/docs/reference/starboard/gyp-configuration.md
@@ -34,7 +34,7 @@
| **`cobalt_media_buffer_storage_type`**<br><br> This can be set to "memory" or "file". When it is set to "memory", the media buffers will be stored in main memory allocated by SbMemory functions. When it is set to "file", the media buffers will be stored in a temporary file in the system cache folder acquired by calling SbSystemGetPath() with "kSbSystemPathCacheDirectory". Note that when its value is "file" the media stack will still allocate memory to cache the the buffers in use.<br><br>The default value is `'memory'`. |
| **`cobalt_media_buffer_video_budget_1080p`**<br><br> Specifies the maximum amount of memory used by video buffers of media source before triggering a garbage collection when the video resolution is lower than 1080p (1920x1080). A large value will cause more memory being used by video buffers but will also make JavaScript app less likely to re-download video data. Note that the JavaScript app may experience significant difficulty if this value is too low.<br><br>The default value is `16 * 1024 * 1024`. |
| **`cobalt_media_buffer_video_budget_4k`**<br><br> Specifies the maximum amount of memory used by video buffers of media source before triggering a garbage collection when the video resolution is lower than 4k (3840x2160). A large value will cause more memory being used by video buffers but will also make JavaScript app less likely to re-download video data. Note that the JavaScript app may experience significant difficulty if this value is too low.<br><br>The default value is `60 * 1024 * 1024`. |
-| **`cobalt_media_source_2016`**<br><br> Use media source extension implementation that is conformed to the Candidate Recommandation of July 5th 2016.<br><br>The default value is `'<(cobalt_media_source_2016)'`. |
+| **`cobalt_media_source_2016`**<br><br> Use media source extension implementation that is conformed to the Candidate Recommendation of July 5th 2016.<br><br>The default value is `'<(cobalt_media_source_2016)'`. |
| **`cobalt_minimum_frame_time_in_milliseconds`**<br><br> Allow throttling of the frame rate. This is expressed in terms of milliseconds and can be a floating point number. Keep in mind that swapping frames may take some additional processing time, so it may be better to specify a lower delay. For example, '33' instead of '33.33' for 30 Hz refresh.<br><br>The default value is `'16.0'`. |
| **`cobalt_platform_dependencies`**<br><br> List of platform-specific targets that get compiled into cobalt.<br><br>The default value is `[]`. |
| **`cobalt_user_on_exit_strategy`**<br><br> This variable defines what Cobalt's preferred strategy should be for handling internally triggered application exit requests (e.g. the user chooses to back out of the application).<ul><li><code>stop</code> - The application should call SbSystemRequestStop() on exit, resulting in a complete shutdown of the application.<li><code>suspend</code> - The application should call SbSystemRequestSuspend() on exit, resulting in the application being "minimized".<li><code>noexit</code> - The application should never allow the user to trigger an exit, this will be managed by the system.<br><br>The default value is `'stop'`. |
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.cc b/src/cobalt/speech/cobalt_speech_recognizer.cc
index 0a8c161..3cee822 100644
--- a/src/cobalt/speech/cobalt_speech_recognizer.cc
+++ b/src/cobalt/speech/cobalt_speech_recognizer.cc
@@ -21,6 +21,7 @@
#include "cobalt/speech/url_fetcher_fake.h"
#endif // defined(ENABLE_FAKE_MICROPHONE)
#include "cobalt/speech/microphone_manager.h"
+#include "cobalt/speech/speech_recognition_error.h"
#if defined(SB_USE_SB_MICROPHONE)
#include "cobalt/speech/microphone_starboard.h"
#endif // defined(SB_USE_SB_MICROPHONE)
@@ -91,8 +92,9 @@
#endif // defined(SB_USE_SB_MICROPHONE)
service_.reset(new GoogleSpeechService(
- network_module, base::Bind(&CobaltSpeechRecognizer::OnRecognizerEvent,
- base::Unretained(this)),
+ network_module,
+ base::Bind(&CobaltSpeechRecognizer::OnRecognizerEvent,
+ base::Unretained(this)),
url_fetcher_creator));
microphone_manager_.reset(new MicrophoneManager(
base::Bind(&CobaltSpeechRecognizer::OnDataReceived,
@@ -143,8 +145,22 @@
}
void CobaltSpeechRecognizer::OnMicError(
- const scoped_refptr<dom::Event>& event) {
+ MicrophoneManager::MicrophoneError error,
+ const std::string& error_message) {
// An error is occured in Mic, so stop the energy endpointer and recognizer.
+
+ SpeechRecognitionErrorCode speech_error_code =
+ kSpeechRecognitionErrorCodeAborted;
+ switch (error) {
+ case MicrophoneManager::MicrophoneError::kAborted:
+ speech_error_code = kSpeechRecognitionErrorCodeAborted;
+ break;
+ case MicrophoneManager::MicrophoneError::kAudioCapture:
+ speech_error_code = kSpeechRecognitionErrorCodeAudioCapture;
+ break;
+ }
+ scoped_refptr<dom::Event> event(
+ new SpeechRecognitionError(speech_error_code, error_message));
endpointer_delegate_.Stop();
service_->Stop();
RunEventCallback(event);
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.h b/src/cobalt/speech/cobalt_speech_recognizer.h
index 004c68d..c77980a 100644
--- a/src/cobalt/speech/cobalt_speech_recognizer.h
+++ b/src/cobalt/speech/cobalt_speech_recognizer.h
@@ -15,6 +15,8 @@
#ifndef COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
#define COBALT_SPEECH_COBALT_SPEECH_RECOGNIZER_H_
+#include <string>
+
#include "cobalt/network/network_module.h"
#include "cobalt/speech/endpointer_delegate.h"
#include "cobalt/speech/google_speech_service.h"
@@ -58,7 +60,8 @@
// Callbacks from mic.
void OnDataReceived(scoped_ptr<ShellAudioBus> audio_bus);
void OnDataCompletion();
- void OnMicError(const scoped_refptr<dom::Event>& event);
+ void OnMicError(MicrophoneManager::MicrophoneError error,
+ const std::string& error_message);
// Callbacks from recognizer.
void OnRecognizerEvent(const scoped_refptr<dom::Event>& event);
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc
index c54d36c..779a8fe 100644
--- a/src/cobalt/speech/microphone_fake.cc
+++ b/src/cobalt/speech/microphone_fake.cc
@@ -30,11 +30,7 @@
namespace cobalt {
namespace speech {
-#if defined(COBALT_MEDIA_SOURCE_2016)
-typedef media::ShellAudioBus ShellAudioBus;
-#else // defined(COBALT_MEDIA_SOURCE_2016)
-typedef ::media::ShellAudioBus ShellAudioBus;
-#endif // defined(COBALT_MEDIA_SOURCE_2016)
+typedef audio::ShellAudioBus ShellAudioBus;
namespace {
@@ -49,6 +45,8 @@
// The possiblity of microphone close failed is 1/20.
const int kCloseRange = 20;
const int kFailureNumber = 5;
+const int kSupportedMonoChannel = 1;
+const char kMicrophoneLabel[] = "FakeMicrophone";
bool ShouldFail(int range) {
return base::RandGenerator(range) == kFailureNumber;
@@ -89,9 +87,13 @@
}
} else {
file_length_ = std::min(options.audio_data_size, kMaxBufferSize);
- DCHECK(file_length_ > 0);
- file_buffer_.reset(new uint8[file_length_]);
- SbMemoryCopy(file_buffer_.get(), options.external_audio_data, file_length_);
+ DCHECK_GT(file_length_, 0);
+ audio_bus_.reset(new ShellAudioBus(
+ kSupportedMonoChannel,
+ file_length_ / audio::GetSampleTypeSize(ShellAudioBus::kInt16),
+ ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ SbMemoryCopy(audio_bus_->interleaved_data(), options.external_audio_data,
+ file_length_);
}
}
@@ -105,7 +107,7 @@
if (read_data_from_file_) {
// Read from local files.
- DCHECK(file_paths_.size() != 0);
+ DCHECK_NE(file_paths_.size(), 0u);
size_t random_index =
static_cast<size_t>(base::RandGenerator(file_paths_.size()));
starboard::ScopedFile file(file_paths_[random_index].value().c_str(),
@@ -113,7 +115,7 @@
DCHECK(file.IsValid());
int file_buffer_size =
std::min(static_cast<int>(file.GetSize()), kMaxBufferSize);
- DCHECK(file_buffer_size > 0);
+ DCHECK_GT(file_buffer_size, 0);
scoped_array<char> audio_input(new char[file_buffer_size]);
int read_bytes = file.ReadAll(audio_input.get(), file_buffer_size);
@@ -125,11 +127,14 @@
reinterpret_cast<const uint8*>(audio_input.get()), file_buffer_size,
audio::kSampleTypeInt16));
const float kSupportedSampleRate = 16000.0f;
- const int kSupportedMonoChannel = 1;
if (!reader) {
// If it is not a WAV file, read audio data as raw audio.
- file_buffer_.reset(new uint8[file_buffer_size]);
- SbMemoryCopy(file_buffer_.get(), audio_input.get(), file_buffer_size);
+ audio_bus_.reset(new ShellAudioBus(
+ kSupportedMonoChannel,
+ file_buffer_size / audio::GetSampleTypeSize(ShellAudioBus::kInt16),
+ ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ SbMemoryCopy(audio_bus_->interleaved_data(), audio_input.get(),
+ file_buffer_size);
file_length_ = file_buffer_size;
} else if (reader->sample_type() != ShellAudioBus::kInt16 ||
reader->sample_rate() != kSupportedSampleRate ||
@@ -138,13 +143,10 @@
// it as an error.
return false;
} else {
- // Read WAV PCM16 audio data.
- int size =
+ audio_bus_ = reader->ResetAndReturnAudioBus().Pass();
+ file_length_ =
static_cast<int>(reader->number_of_frames() *
audio::GetSampleTypeSize(reader->sample_type()));
- file_buffer_.reset(new uint8[size]);
- SbMemoryCopy(file_buffer_.get(), reader->sample_data().get(), size);
- file_length_ = size;
}
}
return true;
@@ -159,7 +161,8 @@
}
int copy_bytes = std::min(file_length_ - read_index_, data_size);
- SbMemoryCopy(out_data, file_buffer_.get() + read_index_, copy_bytes);
+ SbMemoryCopy(out_data, audio_bus_->interleaved_data() + read_index_,
+ copy_bytes);
read_index_ += copy_bytes;
if (read_index_ == file_length_) {
read_index_ = 0;
@@ -172,7 +175,7 @@
DCHECK(thread_checker_.CalledOnValidThread());
if (read_data_from_file_) {
- file_buffer_.reset();
+ audio_bus_.reset();
file_length_ = -1;
}
@@ -190,6 +193,8 @@
return kMinMicrophoneReadInBytes;
}
+const char* MicrophoneFake::Label() { return kMicrophoneLabel; }
+
} // namespace speech
} // namespace cobalt
diff --git a/src/cobalt/speech/microphone_fake.h b/src/cobalt/speech/microphone_fake.h
index c755071..26cfea5 100644
--- a/src/cobalt/speech/microphone_fake.h
+++ b/src/cobalt/speech/microphone_fake.h
@@ -26,6 +26,7 @@
#include "base/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
+#include "cobalt/audio/audio_helpers.h"
#include "cobalt/speech/microphone.h"
namespace cobalt {
@@ -43,14 +44,14 @@
bool Close() override;
int MinMicrophoneReadInBytes() override;
bool IsValid() override { return is_valid_; }
- const char* Label() override { return ""; }
+ const char* Label() override;
private:
base::ThreadChecker thread_checker_;
bool read_data_from_file_;
std::vector<FilePath> file_paths_;
- scoped_array<uint8> file_buffer_;
+ scoped_ptr<audio::ShellAudioBus> audio_bus_;
int file_length_;
int read_index_;
bool is_valid_;
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
index ac38787..16daed2 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -14,8 +14,6 @@
#include "cobalt/speech/microphone_manager.h"
-#include "cobalt/speech/speech_recognition_error.h"
-
namespace cobalt {
namespace speech {
@@ -72,8 +70,8 @@
DLOG(WARNING) << "Microphone creation failed.";
microphone_.reset();
state_ = kError;
- error_callback_.Run(new SpeechRecognitionError(
- kSpeechRecognitionErrorCodeAudioCapture, "No microphone available."));
+ error_callback_.Run(MicrophoneError::kAudioCapture,
+ "No microphone available.");
return false;
}
}
@@ -89,8 +87,8 @@
DCHECK(microphone_);
if (!microphone_->Open()) {
state_ = kError;
- error_callback_.Run(new SpeechRecognitionError(
- kSpeechRecognitionErrorCodeAborted, "Microphone open failed."));
+ error_callback_.Run(MicrophoneError::kAborted,
+ "Microphone open failed.");
return;
}
@@ -117,8 +115,8 @@
if (microphone_) {
if (!microphone_->Close()) {
state_ = kError;
- error_callback_.Run(new SpeechRecognitionError(
- kSpeechRecognitionErrorCodeAborted, "Microphone close failed."));
+ error_callback_.Run(MicrophoneError::kAborted,
+ "Microphone close failed.");
return;
}
completion_callback_.Run();
@@ -146,8 +144,8 @@
data_received_callback_.Run(output_audio_bus.Pass());
} else if (read_bytes != 0) {
state_ = kError;
- error_callback_.Run(new SpeechRecognitionError(
- kSpeechRecognitionErrorCodeAborted, "Microphone read failed."));
+ error_callback_.Run(MicrophoneError::kAborted,
+ "Microphone read failed.");
poll_mic_events_timer_->Stop();
}
}
diff --git a/src/cobalt/speech/microphone_manager.h b/src/cobalt/speech/microphone_manager.h
index db07962..4df00f4 100644
--- a/src/cobalt/speech/microphone_manager.h
+++ b/src/cobalt/speech/microphone_manager.h
@@ -15,6 +15,8 @@
#ifndef COBALT_SPEECH_MICROPHONE_MANAGER_H_
#define COBALT_SPEECH_MICROPHONE_MANAGER_H_
+#include <string>
+
#include "cobalt/speech/speech_configuration.h"
#include "base/callback.h"
@@ -36,6 +38,10 @@
// a self-managed poller to fetch audio data from microphone.
class MicrophoneManager {
public:
+ enum class MicrophoneError {
+ kAudioCapture,
+ kAborted,
+ };
#if defined(COBALT_MEDIA_SOURCE_2016)
typedef media::ShellAudioBus ShellAudioBus;
#else // defined(COBALT_MEDIA_SOURCE_2016)
@@ -43,7 +49,8 @@
#endif // defined(COBALT_MEDIA_SOURCE_2016)
typedef base::Callback<void(scoped_ptr<ShellAudioBus>)> DataReceivedCallback;
typedef base::Callback<void(void)> CompletionCallback;
- typedef base::Callback<void(const scoped_refptr<dom::Event>&)> ErrorCallback;
+ typedef base::Callback<void(MicrophoneError, const std::string&)>
+ ErrorCallback;
typedef base::Callback<scoped_ptr<Microphone>(int)> MicrophoneCreator;
MicrophoneManager(const DataReceivedCallback& data_received,
diff --git a/src/cobalt/speech/microphone_starboard.h b/src/cobalt/speech/microphone_starboard.h
index 2729441..3df50dd 100644
--- a/src/cobalt/speech/microphone_starboard.h
+++ b/src/cobalt/speech/microphone_starboard.h
@@ -30,6 +30,8 @@
class MicrophoneStarboard : public Microphone {
public:
static const int kDefaultSampleRate = 16000;
+ // Starboard uses int16 as sample size.
+ static const int16 kSbMicrophoneSampleSizeInBytes = 2;
MicrophoneStarboard(int sample_rate, int buffer_size_bytes);
~MicrophoneStarboard() override;
diff --git a/src/cobalt/speech/sandbox/sandbox.gyp b/src/cobalt/speech/sandbox/sandbox.gyp
index f238410..a4ba5c7 100644
--- a/src/cobalt/speech/sandbox/sandbox.gyp
+++ b/src/cobalt/speech/sandbox/sandbox.gyp
@@ -30,6 +30,7 @@
'dependencies': [
'<(DEPTH)/cobalt/audio/audio.gyp:audio',
'<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/dom/dom.gyp:dom',
'<(DEPTH)/cobalt/loader/loader.gyp:loader',
'<(DEPTH)/cobalt/network/network.gyp:network',
'<(DEPTH)/cobalt/script/engine.gyp:engine',
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index 318f044..c392ec7 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -72,7 +72,6 @@
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
- '<(DEPTH)/cobalt/dom/dom.gyp:dom',
'<(DEPTH)/content/browser/speech/speech.gyp:speech',
'<(DEPTH)/third_party/flac/flac.gyp:libflac',
'<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
@@ -101,6 +100,9 @@
'url_fetcher_fake.cc',
'url_fetcher_fake.h',
],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/audio/audio.gyp:audio',
+ ],
'defines': [
'ENABLE_FAKE_MICROPHONE',
],
diff --git a/src/cobalt/speech/speech_synthesis.cc b/src/cobalt/speech/speech_synthesis.cc
index 3342b94..1b29b7f 100644
--- a/src/cobalt/speech/speech_synthesis.cc
+++ b/src/cobalt/speech/speech_synthesis.cc
@@ -70,7 +70,7 @@
dom::EventTarget::TraceMembers(tracer);
tracer->TraceItems(utterances_);
- tracer->TraceSequence(voices_);
+ tracer->TraceItems(voices_);
tracer->Trace(navigator_);
}
diff --git a/src/cobalt/storage/savegame_starboard.cc b/src/cobalt/storage/savegame_starboard.cc
index a2fe362..255176c 100644
--- a/src/cobalt/storage/savegame_starboard.cc
+++ b/src/cobalt/storage/savegame_starboard.cc
@@ -21,6 +21,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/optional.h"
#include "base/path_service.h"
+#include "cobalt/storage/store_upgrade/upgrade.h"
#include "starboard/storage.h"
#include "starboard/user.h"
@@ -28,10 +29,31 @@
namespace storage {
namespace {
+using cobalt::storage::store_upgrade::IsUpgradeRequired;
+using cobalt::storage::store_upgrade::UpgradeStore;
+
// An arbitrary max size for the save game file so that, for example, a corrupt
// filesystem cannot cause us to allocate a fatally large memory buffer.
size_t kMaxSaveGameSizeBytes = 4 * 1024 * 1024;
+bool WriteRecord(const scoped_ptr<starboard::StorageRecord>& record,
+ const Savegame::ByteVector& bytes);
+
+bool Upgrade(Savegame::ByteVector* bytes_ptr,
+ const scoped_ptr<starboard::StorageRecord>& record) {
+ DLOG(INFO) << "UPGRADING Record with size=" << bytes_ptr->size();
+ if (IsUpgradeRequired(*bytes_ptr)) {
+ if (!UpgradeStore(bytes_ptr)) {
+ DLOG(ERROR) << "Upgrade failed";
+ return false;
+ }
+ }
+
+ WriteRecord(record, *bytes_ptr);
+ DLOG(INFO) << "UPGRADING bytes_ptr.size=" << bytes_ptr->size();
+ return true;
+}
+
bool ReadRecord(Savegame::ByteVector* bytes_ptr, size_t max_to_read,
const scoped_ptr<starboard::StorageRecord>& record) {
if (!record->IsValid()) {
@@ -65,6 +87,10 @@
if (success) {
DLOG(INFO) << "Successfully read storage record.";
}
+ if (!Upgrade(bytes_ptr, record)) {
+ DLOG(WARNING) << __FUNCTION__ << ": Upgrade Failed";
+ return false;
+ }
return success;
}
@@ -117,7 +143,6 @@
private:
bool MigrateFromFallback();
-
scoped_ptr<starboard::StorageRecord> record_;
};
diff --git a/src/cobalt/storage/sql_vfs.cc b/src/cobalt/storage/sql_vfs.cc
deleted file mode 100644
index 262f877..0000000
--- a/src/cobalt/storage/sql_vfs.cc
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2015 Google Inc. 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 "cobalt/storage/sql_vfs.h"
-
-#include "base/logging.h"
-#include "base/rand_util.h"
-#include "base/string_util.h"
-#include "base/synchronization/lock.h"
-#include "cobalt/storage/virtual_file.h"
-#include "cobalt/storage/virtual_file_system.h"
-#include "third_party/sqlite/sqlite3.h"
-
-namespace cobalt {
-namespace storage {
-
-namespace {
-
-// A "subclass" of sqlite3_file with our required data structures added.
-struct virtual_file {
- sqlite3_file sql_internal_file;
- VirtualFile* file;
- base::Lock* lock;
- int current_lock;
- int shared;
-};
-
-int VfsClose(sqlite3_file* file) {
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- delete vfile->lock;
- return SQLITE_OK;
-}
-
-int VfsRead(sqlite3_file* file, void* out, int bytes, sqlite_int64 offset) {
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- vfile->file->Read(out, bytes, static_cast<int>(offset));
- return SQLITE_OK;
-}
-
-int VfsWrite(sqlite3_file* file, const void* data, int bytes,
- sqlite3_int64 offset) {
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- vfile->file->Write(data, bytes, static_cast<int>(offset));
- return SQLITE_OK;
-}
-
-int VfsSync(sqlite3_file* pFile, int flags) {
- UNREFERENCED_PARAMETER(pFile);
- UNREFERENCED_PARAMETER(flags);
- return SQLITE_OK;
-}
-
-int VfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
- UNREFERENCED_PARAMETER(pFile);
- UNREFERENCED_PARAMETER(op);
- UNREFERENCED_PARAMETER(pArg);
- return SQLITE_OK;
-}
-
-int VfsSectorSize(sqlite3_file* file) {
- // The number of bytes that can be read without disturbing other bytes in the
- // file.
- UNREFERENCED_PARAMETER(file);
- return 1;
-}
-
-int VfsLock(sqlite3_file* file, const int mode) {
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- base::AutoLock lock(*vfile->lock);
-
- // If there is already a lock of this type or more restrictive, do nothing
- if (vfile->current_lock >= mode) {
- return SQLITE_OK;
- }
-
- if (mode == SQLITE_LOCK_SHARED) {
- DCHECK_EQ(vfile->current_lock, SQLITE_LOCK_NONE);
- vfile->shared++;
- vfile->current_lock = SQLITE_LOCK_SHARED;
- }
-
- if (mode == SQLITE_LOCK_RESERVED) {
- DCHECK_EQ(vfile->current_lock, SQLITE_LOCK_SHARED);
- vfile->current_lock = SQLITE_LOCK_RESERVED;
- }
-
- if (mode == SQLITE_LOCK_EXCLUSIVE) {
- if (vfile->current_lock >= SQLITE_LOCK_PENDING) {
- return SQLITE_BUSY;
- }
- vfile->current_lock = SQLITE_LOCK_PENDING;
- if (vfile->shared > 1) {
- // There are some outstanding shared locks (greater than one because the
- // pending lock is an "upgraded" shared lock)
- return SQLITE_BUSY;
- }
- // Acquire the exclusive lock
- vfile->current_lock = SQLITE_LOCK_EXCLUSIVE;
- }
-
- return SQLITE_OK;
-}
-
-int VfsUnlock(sqlite3_file* file, int mode) {
- DCHECK_LE(mode, SQLITE_LOCK_SHARED);
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- base::AutoLock lock(*vfile->lock);
-
- COMPILE_ASSERT(SQLITE_LOCK_NONE < SQLITE_LOCK_SHARED,
- sqlite_lock_constants_order_has_changed);
- COMPILE_ASSERT(SQLITE_LOCK_SHARED < SQLITE_LOCK_RESERVED,
- sqlite_lock_constants_order_has_changed);
- COMPILE_ASSERT(SQLITE_LOCK_RESERVED < SQLITE_LOCK_PENDING,
- sqlite_lock_constants_order_has_changed);
- COMPILE_ASSERT(SQLITE_LOCK_PENDING < SQLITE_LOCK_EXCLUSIVE,
- sqlite_lock_constants_order_has_changed);
-
- if (mode == SQLITE_LOCK_NONE && vfile->current_lock >= SQLITE_LOCK_SHARED) {
- vfile->shared--;
- }
-
- vfile->current_lock = mode;
- return SQLITE_OK;
-}
-
-int VfsCheckReservedLock(sqlite3_file* file, int* result) {
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- base::AutoLock lock(*vfile->lock);
-
- // The function expects a result is 1 if the lock is reserved, pending, or
- // exclusive; 0 otherwise.
- *result = vfile->current_lock >= SQLITE_LOCK_RESERVED ? 1 : 0;
- return SQLITE_OK;
-}
-
-int VfsFileSize(sqlite3_file* file, sqlite3_int64* out_size) {
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- *out_size = vfile->file->Size();
- return SQLITE_OK;
-}
-
-int VfsTruncate(sqlite3_file* file, sqlite3_int64 size) {
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- vfile->file->Truncate(static_cast<int>(size));
- return SQLITE_OK;
-}
-
-int VfsDeviceCharacteristics(sqlite3_file* file) {
- UNREFERENCED_PARAMETER(file);
- return 0;
-}
-
-const sqlite3_io_methods s_cobalt_vfs_io = {
- 1, // Structure version number
- VfsClose, // xClose
- VfsRead, // xRead
- VfsWrite, // xWrite
- VfsTruncate, // xTruncate
- VfsSync, // xSync
- VfsFileSize, // xFileSize
- VfsLock, // xLock
- VfsUnlock, // xUnlock
- VfsCheckReservedLock, // xCheckReservedLock
- VfsFileControl, // xFileControl
- VfsSectorSize, // xSectorSize
- VfsDeviceCharacteristics // xDeviceCharacteristics
-};
-
-int VfsOpen(sqlite3_vfs* sql_vfs, const char* path, sqlite3_file* file,
- int flags, int* out_flags) {
- UNREFERENCED_PARAMETER(flags);
- UNREFERENCED_PARAMETER(out_flags);
- DCHECK(path) << "NULL filename not supported.";
- virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
- vfile->lock = new base::Lock;
- file->pMethods = &s_cobalt_vfs_io;
-
- VirtualFileSystem* vfs =
- reinterpret_cast<VirtualFileSystem*>(sql_vfs->pAppData);
- vfile->file = vfs->Open(path);
- return SQLITE_OK;
-}
-
-int VfsDelete(sqlite3_vfs* sql_vfs, const char* path, int sync_dir) {
- UNREFERENCED_PARAMETER(sync_dir);
- VirtualFileSystem* vfs =
- reinterpret_cast<VirtualFileSystem*>(sql_vfs->pAppData);
- vfs->Delete(path);
- return SQLITE_OK;
-}
-
-int VfsFullPathname(sqlite3_vfs* sql_vfs, const char* path, int out_size,
- char* out_path) {
- UNREFERENCED_PARAMETER(sql_vfs);
- size_t path_size = static_cast<size_t>(out_size);
- if (base::strlcpy(out_path, path, path_size) < path_size) {
- return SQLITE_OK;
- }
- return SQLITE_ERROR;
-}
-
-int VfsAccess(sqlite3_vfs* sql_vfs, const char* name, int flags, int* result) {
- UNREFERENCED_PARAMETER(name);
- UNREFERENCED_PARAMETER(sql_vfs);
- UNREFERENCED_PARAMETER(flags);
- // We should always have a valid, readable/writable file.
- *result |= SQLITE_ACCESS_EXISTS | SQLITE_ACCESS_READWRITE;
- return SQLITE_OK;
-}
-
-int VfsRandomness(sqlite3_vfs* sql_vfs, int bytes, char* out) {
- UNREFERENCED_PARAMETER(sql_vfs);
- base::RandBytes(out, static_cast<size_t>(bytes));
- return SQLITE_OK;
-}
-
-} // namespace
-
-SqlVfs::SqlVfs(const std::string& name, VirtualFileSystem* vfs)
- : sql_vfs_(new sqlite3_vfs()) {
- memset(sql_vfs_.get(), 0, sizeof(sqlite3_vfs));
- sql_vfs_->iVersion = 1;
- sql_vfs_->szOsFile = sizeof(virtual_file);
- sql_vfs_->mxPathname = VirtualFile::kMaxVfsPathname;
- sql_vfs_->pNext = NULL;
- sql_vfs_->zName = name.c_str();
- sql_vfs_->pAppData = vfs;
- sql_vfs_->xOpen = VfsOpen;
- sql_vfs_->xDelete = VfsDelete;
- sql_vfs_->xAccess = VfsAccess;
- sql_vfs_->xFullPathname = VfsFullPathname;
- sql_vfs_->xRandomness = VfsRandomness;
-
- // Ensure we are not registering multiple of these with the same name.
- // Behavior is undefined in that case.
- DCHECK(sqlite3_vfs_find(name.c_str()) == NULL);
-
- int ret = sqlite3_vfs_register(sql_vfs_.get(), 1 /* make_default */);
- DCHECK_EQ(ret, SQLITE_OK);
-}
-
-SqlVfs::~SqlVfs() {
- int ret = sqlite3_vfs_unregister(sql_vfs_.get());
- DCHECK_EQ(ret, SQLITE_OK);
-}
-
-} // namespace storage
-} // namespace cobalt
diff --git a/src/cobalt/storage/sql_vfs.h b/src/cobalt/storage/sql_vfs.h
deleted file mode 100644
index 8540dcf..0000000
--- a/src/cobalt/storage/sql_vfs.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 Google Inc. 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 COBALT_STORAGE_SQL_VFS_H_
-#define COBALT_STORAGE_SQL_VFS_H_
-
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-
-struct sqlite3_vfs;
-
-namespace cobalt {
-namespace storage {
-
-class VirtualFileSystem;
-
-// Implement the necessary APIs for a Sqlite virtual file system.
-// Dispatch calls to the owning VirtualFileSystem.
-class SqlVfs {
- public:
- SqlVfs(const std::string& name, VirtualFileSystem* vfs);
- ~SqlVfs();
-
- private:
- scoped_ptr<sqlite3_vfs> sql_vfs_;
-};
-
-} // namespace storage
-} // namespace cobalt
-
-#endif // COBALT_STORAGE_SQL_VFS_H_
diff --git a/src/cobalt/storage/storage.gyp b/src/cobalt/storage/storage.gyp
index 5d1d6e0..fcab91c 100644
--- a/src/cobalt/storage/storage.gyp
+++ b/src/cobalt/storage/storage.gyp
@@ -27,24 +27,19 @@
'savegame.h',
'savegame_thread.cc',
'savegame_thread.h',
- 'sql_vfs.cc',
- 'sql_vfs.h',
'savegame_fake.cc',
'savegame_starboard.cc',
'storage_manager.cc',
'storage_manager.h',
'upgrade/upgrade_reader.cc',
'upgrade/upgrade_reader.h',
- 'virtual_file.cc',
- 'virtual_file.h',
- 'virtual_file_system.cc',
- 'virtual_file_system.h',
],
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
'<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/storage/store/store.gyp:memory_store',
+ '<(DEPTH)/cobalt/storage/store_upgrade/upgrade.gyp:storage_upgrade',
'<(DEPTH)/net/net.gyp:net',
- '<(DEPTH)/sql/sql.gyp:sql',
],
},
{
@@ -54,7 +49,6 @@
'savegame_test.cc',
'storage_manager_test.cc',
'upgrade/storage_upgrade_test.cc',
- 'virtual_file_system_test.cc',
],
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
diff --git a/src/cobalt/storage/storage_constants.cc b/src/cobalt/storage/storage_constants.cc
new file mode 100644
index 0000000..766553c
--- /dev/null
+++ b/src/cobalt/storage/storage_constants.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 Google Inc. 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 "cobalt/storage/storage_constants.h"
+
+namespace cobalt {
+namespace storage {
+
+const char kStorageHeader[] = "SAV1";
+const char kOldStorageHeader[] = "SAV0";
+
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/storage_constants.gyp b/src/cobalt/storage/storage_constants.gyp
new file mode 100644
index 0000000..90b58e2
--- /dev/null
+++ b/src/cobalt/storage/storage_constants.gyp
@@ -0,0 +1,26 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'storage_constants',
+ 'type': 'static_library',
+ 'sources': [
+ 'storage_constants.cc',
+ 'storage_constants.h',
+ ],
+ },
+ ],
+}
diff --git a/src/cobalt/storage/storage_constants.h b/src/cobalt/storage/storage_constants.h
new file mode 100644
index 0000000..d6a6b0d
--- /dev/null
+++ b/src/cobalt/storage/storage_constants.h
@@ -0,0 +1,29 @@
+// Copyright 2018 Google Inc. 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 COBALT_STORAGE_STORAGE_CONSTANTS_H_
+#define COBALT_STORAGE_STORAGE_CONSTANTS_H_
+
+namespace cobalt {
+namespace storage {
+
+extern const char kStorageHeader[];
+extern const char kOldStorageHeader[];
+const int kStorageHeaderSize = 4;
+const int kStorageHeaderVersionIndex = 3;
+
+} // namespace storage
+} // namespace cobalt
+
+#endif // COBALT_STORAGE_STORAGE_CONSTANTS_H_
diff --git a/src/cobalt/storage/storage_manager.cc b/src/cobalt/storage/storage_manager.cc
index 7b75647..62b91db 100644
--- a/src/cobalt/storage/storage_manager.cc
+++ b/src/cobalt/storage/storage_manager.cc
@@ -22,10 +22,6 @@
#include "base/stringprintf.h"
#include "cobalt/storage/savegame_thread.h"
#include "cobalt/storage/upgrade/upgrade_reader.h"
-#include "cobalt/storage/virtual_file.h"
-#include "cobalt/storage/virtual_file_system.h"
-#include "sql/statement.h"
-#include "third_party/sqlite/sqlite3.h"
namespace cobalt {
namespace storage {
@@ -36,111 +32,14 @@
// get several FlushOnChange() calls in a row.
const int kDatabaseFlushOnLastChangeDelayMs = 500;
const int kDatabaseFlushOnChangeMaxDelayMs = 2000;
-
-const char kDefaultSaveFile[] = "cobalt_save.bin";
-
-void SqlDisableJournal(sql::Connection* connection) {
- // Disable journaling for our in-memory database.
- sql::Statement disable_journal(
- connection->GetUniqueStatement("PRAGMA journal_mode=OFF"));
- bool ok = disable_journal.Step();
- DCHECK(ok);
-}
-
-int SqlQueryUserVersion(sql::Connection* connection) {
- sql::Statement get_db_version(
- connection->GetUniqueStatement("PRAGMA user_version"));
- bool ok = get_db_version.Step();
- DCHECK(ok);
- return get_db_version.ColumnInt(0);
-}
-
-bool SqlQueryTableExists(sql::Connection* connection, const char* table_name) {
- sql::Statement get_exists(connection->GetUniqueStatement(
- "SELECT name FROM sqlite_master WHERE name = ? AND type = 'table'"));
- get_exists.BindString(0, table_name);
- return get_exists.Step();
-}
-
-int SqlQuerySchemaVersion(sql::Connection* connection, const char* table_name) {
- sql::Statement get_version(connection->GetUniqueStatement(
- "SELECT version FROM SchemaTable WHERE name = ?"));
- get_version.BindString(0, table_name);
- bool row_found = get_version.Step();
- if (row_found) {
- return get_version.ColumnInt(0);
- } else {
- return -1;
- }
-}
-
-void SqlUpdateSchemaVersion(sql::Connection* connection, const char* table_name,
- int version) {
- sql::Statement update_version(connection->GetUniqueStatement(
- "INSERT INTO SchemaTable (name, version)"
- "VALUES (?, ?)"));
- update_version.BindString(0, table_name);
- update_version.BindInt(1, version);
- bool ok = update_version.Run();
- DCHECK(ok);
-}
-
-void SqlCreateSchemaTable(sql::Connection* connection) {
- // Create the schema table.
- sql::Statement create_table(connection->GetUniqueStatement(
- "CREATE TABLE IF NOT EXISTS SchemaTable ("
- "name TEXT, "
- "version INTEGER, "
- "UNIQUE(name, version) ON CONFLICT REPLACE)"));
- bool ok = create_table.Run();
- DCHECK(ok);
-}
-
-void SqlUpdateDatabaseUserVersion(sql::Connection* connection) {
- // Update the DB version which will be read in next time.
- // NOTE: Pragma statements cannot be bound, so we must construct the string
- // in full.
- std::string set_db_version_str = base::StringPrintf(
- "PRAGMA user_version = %d", StorageManager::kDatabaseUserVersion);
- sql::Statement set_db_version(
- connection->GetUniqueStatement(set_db_version_str.c_str()));
- bool ok = set_db_version.Run();
- DCHECK(ok);
-}
-
-const std::string& GetFirstValidDatabaseFile(
- const std::vector<std::string>& filenames) {
- // Caller must ensure at least one file exists.
- DCHECK_GT(filenames.size(), size_t(0));
-
- for (size_t i = 0; i < filenames.size(); ++i) {
- sql::Connection connection;
- bool is_opened = connection.Open(FilePath(filenames[i]));
- if (!is_opened) {
- continue;
- }
- int err = connection.ExecuteAndReturnErrorCode("pragma schema_version;");
- if (err != SQLITE_OK) {
- continue;
- }
- // File can be opened as a database.
- return filenames[i];
- }
-
- // Caller must handle case where a valid database file cannot be found.
- DLOG(WARNING) << "Cannot find valid database file in save data";
- return filenames[0];
-}
-
} // namespace
StorageManager::StorageManager(scoped_ptr<UpgradeHandler> upgrade_handler,
const Options& options)
: upgrade_handler_(upgrade_handler.Pass()),
options_(options),
- sql_thread_(new base::Thread("StorageManager SQL")),
- ALLOW_THIS_IN_INITIALIZER_LIST(sql_context_(new SqlContext(this))),
- connection_(new sql::Connection()),
+ storage_thread_(new base::Thread("StorageManager")),
+ memory_store_(new MemoryStore()),
loaded_database_version_(0),
initialized_(false),
flush_processing_(false),
@@ -149,10 +48,12 @@
true /* initially signalled */) {
DCHECK(upgrade_handler_);
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
+
savegame_thread_.reset(new SavegameThread(options_.savegame_options));
+
// Start the savegame load immediately.
- sql_thread_->Start();
- sql_message_loop_ = sql_thread_->message_loop_proxy();
+ storage_thread_->Start();
+ storage_message_loop_ = storage_thread_->message_loop_proxy();
flush_on_last_change_timer_.reset(new base::OneShotTimer<StorageManager>());
flush_on_change_max_delay_timer_.reset(
@@ -161,37 +62,52 @@
StorageManager::~StorageManager() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(!sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(!storage_message_loop_->BelongsToCurrentThread());
// Wait for all I/O operations to complete.
FinishIO();
// Destroy various objects on the proper thread.
- sql_message_loop_->PostTask(FROM_HERE, base::Bind(&StorageManager::OnDestroy,
- base::Unretained(this)));
+ storage_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&StorageManager::OnDestroy, base::Unretained(this)));
// Force all tasks to finish. Then we can safely let the rest of our
// member variables be destroyed.
- sql_thread_.reset();
+ storage_thread_.reset();
}
-void StorageManager::GetSqlContext(const SqlCallback& callback) {
+void StorageManager::WithReadOnlyMemoryStore(
+ const ReadOnlyMemoryStoreCallback& callback) {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- if (MessageLoop::current()->message_loop_proxy() != sql_message_loop_) {
- sql_message_loop_->PostTask(FROM_HERE,
- base::Bind(&StorageManager::GetSqlContext,
- base::Unretained(this), callback));
+ if (MessageLoop::current()->message_loop_proxy() != storage_message_loop_) {
+ storage_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&StorageManager::WithReadOnlyMemoryStore,
+ base::Unretained(this), callback));
return;
}
- callback.Run(sql_context_.get());
+ callback.Run(*memory_store_.get());
+}
+
+void StorageManager::WithMemoryStore(const MemoryStoreCallback& callback) {
+ TRACE_EVENT0("cobalt::storage", __FUNCTION__);
+ if (MessageLoop::current()->message_loop_proxy() != storage_message_loop_) {
+ storage_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&StorageManager::WithMemoryStore,
+ base::Unretained(this), callback));
+ return;
+ }
+
+ callback.Run(memory_store_.get());
+ FlushOnChange();
}
void StorageManager::FlushOnChange() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
// Make sure this runs on the correct thread.
- if (MessageLoop::current()->message_loop_proxy() != sql_message_loop_) {
- sql_message_loop_->PostTask(
+ if (MessageLoop::current()->message_loop_proxy() != storage_message_loop_) {
+ storage_message_loop_->PostTask(
FROM_HERE,
base::Bind(&StorageManager::FlushOnChange, base::Unretained(this)));
return;
@@ -217,8 +133,8 @@
void StorageManager::FlushNow(const base::Closure& callback) {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
// Make sure this runs on the correct thread.
- if (MessageLoop::current()->message_loop_proxy() != sql_message_loop_) {
- sql_message_loop_->PostTask(
+ if (MessageLoop::current()->message_loop_proxy() != storage_message_loop_) {
+ storage_message_loop_->PostTask(
FROM_HERE, base::Bind(&StorageManager::FlushNow, base::Unretained(this),
callback));
return;
@@ -228,54 +144,15 @@
QueueFlush(callback);
}
-bool StorageManager::GetSchemaVersion(const char* table_name,
- int* schema_version) {
- TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
- DCHECK(schema_version);
-
- if (!SqlQueryTableExists(sql_connection(), table_name)) {
- return false;
- }
-
- int found_version = SqlQuerySchemaVersion(sql_connection(), table_name);
- if (found_version != -1) {
- *schema_version = found_version;
- } else if (loaded_database_version_ != StorageManager::kDatabaseUserVersion) {
- // The schema table did not exist before this session, which is different
- // from the schema table being lost.
- *schema_version = StorageManager::kSchemaTableIsNew;
- } else {
- *schema_version = StorageManager::kSchemaVersionLost;
- }
- return true;
-}
-
-void StorageManager::UpdateSchemaVersion(const char* table_name, int version) {
- TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
- DCHECK_GT(version, 0) << "Schema version numbers must be positive.";
-
- SqlUpdateSchemaVersion(sql_connection(), table_name, version);
-}
-
-sql::Connection* StorageManager::sql_connection() {
- TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- FinishInit();
- return connection_.get();
-}
-
void StorageManager::FinishInit() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(storage_message_loop_->BelongsToCurrentThread());
if (initialized_) {
return;
}
initialized_ = true;
- vfs_.reset(new VirtualFileSystem());
- sql_vfs_.reset(new SqlVfs("cobalt_vfs", vfs_.get()));
// Savegame has finished loading. Now initialize the database connection.
// Check if this is upgrade data, if so, handle it, otherwise:
// Check if the savegame data contains a VFS header.
@@ -285,7 +162,6 @@
savegame_thread_->GetLoadedRawBytes();
DCHECK(loaded_raw_bytes);
Savegame::ByteVector& raw_bytes = *loaded_raw_bytes;
- VirtualFileSystem::SerializedHeader header = {};
bool has_upgrade_data = false;
if (raw_bytes.size() > 0) {
@@ -295,50 +171,13 @@
if (upgrade::UpgradeReader::IsUpgradeData(buffer, buffer_size)) {
has_upgrade_data = true;
} else {
- if (raw_bytes.size() >= sizeof(VirtualFileSystem::SerializedHeader)) {
- memcpy(&header, &raw_bytes[0],
- sizeof(VirtualFileSystem::SerializedHeader));
- }
-
- if (!vfs_->Deserialize(&raw_bytes[0], buffer_size)) {
- VirtualFile* vf = vfs_->Open(kDefaultSaveFile);
- vf->Write(&raw_bytes[0], buffer_size, 0 /* offset */);
- }
+ bool result = memory_store_->Initialize(raw_bytes);
+ LOG(INFO) << "Deserialize result=" << result;
}
}
- std::vector<std::string> filenames = vfs_->ListFiles();
- if (filenames.size() == 0) {
- filenames.push_back(kDefaultSaveFile);
- }
-
// Legacy Steel save data may contain multiple files (e.g. db-journal as well
// as db), so use the first one that looks like a valid database file.
- const std::string& save_name = GetFirstValidDatabaseFile(filenames);
- bool ok = connection_->Open(FilePath(save_name));
- DCHECK(ok);
-
- // Open() is lazy. Run a quick check to see if the database is valid.
- int err = connection_->ExecuteAndReturnErrorCode("pragma schema_version;");
- if (err != SQLITE_OK) {
- // Database seems to be invalid.
- DLOG(WARNING) << "Database " << save_name << " appears to be corrupt.";
- // Try to start again. Delete the file in the VFS and make a
- // new connection.
- vfs_->Delete(save_name);
- vfs_->Open(save_name);
- connection_.reset(new sql::Connection());
- ok = connection_->Open(FilePath(save_name));
- DCHECK(ok);
- err = connection_->ExecuteAndReturnErrorCode("pragma schema_version;");
- DCHECK_EQ(SQLITE_OK, err);
- }
-
- // Configure our SQLite database now that it's open.
- SqlDisableJournal(connection_.get());
- loaded_database_version_ = SqlQueryUserVersion(connection_.get());
- SqlCreateSchemaTable(connection_.get());
- SqlUpdateDatabaseUserVersion(connection_.get());
if (has_upgrade_data) {
const char* buffer = reinterpret_cast<char*>(&raw_bytes[0]);
@@ -358,18 +197,18 @@
void StorageManager::OnFlushOnChangeTimerFired() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(storage_message_loop_->BelongsToCurrentThread());
StopFlushOnChangeTimers();
QueueFlush(base::Closure());
}
-void StorageManager::OnFlushIOCompletedSQLCallback() {
+void StorageManager::OnFlushIOCompletedCallback() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
// Make sure this runs on the SQL message loop.
- if (MessageLoop::current()->message_loop_proxy() != sql_message_loop_) {
- sql_message_loop_->PostTask(
- FROM_HERE, base::Bind(&StorageManager::OnFlushIOCompletedSQLCallback,
+ if (MessageLoop::current()->message_loop_proxy() != storage_message_loop_) {
+ storage_message_loop_->PostTask(
+ FROM_HERE, base::Bind(&StorageManager::OnFlushIOCompletedCallback,
base::Unretained(this)));
return;
}
@@ -397,7 +236,7 @@
void StorageManager::QueueFlush(const base::Closure& callback) {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(storage_message_loop_->BelongsToCurrentThread());
if (!flush_processing_) {
// If no flush is currently in progress, flush immediately.
@@ -418,7 +257,7 @@
void StorageManager::FlushInternal() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(storage_message_loop_->BelongsToCurrentThread());
FinishInit();
flush_processing_ = true;
@@ -426,28 +265,23 @@
// Serialize the database into a buffer. Then send the bytes
// to OnFlushIO for a blocking write to the savegame.
- scoped_ptr<Savegame::ByteVector> raw_bytes_ptr;
- int size = vfs_->Serialize(NULL, true /*dry_run*/);
- raw_bytes_ptr.reset(new Savegame::ByteVector(static_cast<size_t>(size)));
- if (size > 0) {
- Savegame::ByteVector& raw_bytes = *raw_bytes_ptr;
- vfs_->Serialize(&raw_bytes[0], false /*dry_run*/);
- }
+ scoped_ptr<Savegame::ByteVector> raw_bytes_ptr(new Savegame::ByteVector());
+ memory_store_->Serialize(raw_bytes_ptr.get());
// Send the savegame bytes off to the SavegameThread object to be
// asynchronously written to the savegame file.
savegame_thread_->Flush(
raw_bytes_ptr.Pass(),
- base::Bind(&StorageManager::OnFlushIOCompletedSQLCallback,
+ base::Bind(&StorageManager::OnFlushIOCompletedCallback,
base::Unretained(this)));
}
void StorageManager::FinishIO() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(!sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(!storage_message_loop_->BelongsToCurrentThread());
// Make sure that the on change timers fire if they're running.
- sql_message_loop_->PostTask(
+ storage_message_loop_->PostTask(
FROM_HERE, base::Bind(&StorageManager::FireRunningOnChangeTimers,
base::Unretained(this)));
@@ -460,9 +294,9 @@
// This method is called by the destructor, so the only new tasks posted
// after this one will be generated internally. We need to do this because
// it is possible that there are no flushes pending at this instant, but there
- // are tasks queued on |sql_message_loop_| that will begin a flush, and so
+ // are tasks queued on |storage_message_loop_| that will begin a flush, and so
// we make sure that these are executed first.
- sql_message_loop_->WaitForFence();
+ storage_message_loop_->WaitForFence();
// Now wait for all pending flushes to wrap themselves up. This may involve
// the savegame I/O thread and the SQL thread posting tasks to each other.
@@ -471,7 +305,7 @@
void StorageManager::FireRunningOnChangeTimers() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(storage_message_loop_->BelongsToCurrentThread());
if (flush_on_last_change_timer_->IsRunning() ||
flush_on_change_max_delay_timer_->IsRunning()) {
@@ -481,7 +315,7 @@
void StorageManager::OnDestroy() {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
- DCHECK(sql_message_loop_->BelongsToCurrentThread());
+ DCHECK(storage_message_loop_->BelongsToCurrentThread());
// Stop the savegame thread and have it wrap up any pending I/O operations.
savegame_thread_.reset();
@@ -489,8 +323,6 @@
// Ensure these objects are destroyed on the proper thread.
flush_on_last_change_timer_.reset(NULL);
flush_on_change_max_delay_timer_.reset(NULL);
- sql_vfs_.reset(NULL);
- vfs_.reset(NULL);
}
} // namespace storage
diff --git a/src/cobalt/storage/storage_manager.h b/src/cobalt/storage/storage_manager.h
index 9988f92..f5d9ebd 100644
--- a/src/cobalt/storage/storage_manager.h
+++ b/src/cobalt/storage/storage_manager.h
@@ -25,23 +25,20 @@
#include "base/threading/thread_checker.h"
#include "base/timer.h"
#include "cobalt/storage/savegame_thread.h"
-#include "cobalt/storage/sql_vfs.h"
+#include "cobalt/storage/store/memory_store.h"
#include "cobalt/storage/upgrade/upgrade_reader.h"
-#include "cobalt/storage/virtual_file_system.h"
-#include "sql/connection.h"
namespace cobalt {
namespace storage {
-class SqlContext;
-// StorageManager manages a SQLite database containing cookies and local
+// StorageManager manages a store containing cookies and local
// storage data. On most platforms, this is written to disk as a savegame
// using platform APIs. On Linux/Windows, it's a regular file.
// Internally this runs two threads: one thread to perform blocking I/O,
-// and one where SQL operations occur. Users are expected to access the
-// database via an SqlContext, which can be obtained with GetSqlContext().
-// The callback to GetSqlCallback will run on the SQL thread.
-// Operations on SqlContext will block the SQL thread until the savegame
+// and one where store operations occur. Users are expected to access the
+// store via an MemoryStore, which can be obtained with GetReadOnlyMemoryStore()
+// or WithMemoryStore(). The callback to will run on the store thread.
+// Operations on MemoryStore will block the store thread until the savegame
// is loaded.
class StorageManager {
public:
@@ -59,32 +56,17 @@
Savegame::Options savegame_options;
};
- typedef base::Callback<void(SqlContext*)> SqlCallback;
-
- // Database version "2" indicates that this was created by Steel v1.x.
- // Database version "0" indicates that this was created by v2.x beta,
- // patches 0-3. Version "0" is a default from sqlite3 because these
- // versions of the application did not set this value at all.
- // Database version "3" indicates that the schema versions of individual
- // tables should be tracked in SchemaTable.
- static const int kDatabaseUserVersion = 3;
-
- // Schema-related error codes. See GetSchemaVersion().
- enum {
- kSchemaTableIsNew = -1,
- kSchemaVersionLost = -2,
- };
+ typedef base::Callback<void(const MemoryStore&)> ReadOnlyMemoryStoreCallback;
+ typedef base::Callback<void(MemoryStore*)> MemoryStoreCallback;
StorageManager(scoped_ptr<UpgradeHandler> upgrade_handler,
const Options& options);
virtual ~StorageManager();
- // Obtain the SqlContext for our database.
- // |callback| will be called with an SqlContext that can be used to operate on
- // the database. The callback will run on the storage manager's message loop.
- void GetSqlContext(const SqlCallback& callback);
+ void WithReadOnlyMemoryStore(const ReadOnlyMemoryStoreCallback& callback);
+ void WithMemoryStore(const MemoryStoreCallback& callback);
- // Schedule a write of our database to disk to happen at some point in the
+ // Schedule a write of our memory store to disk to happen at some point in the
// future after a change occurs. Multiple calls to Flush() do not necessarily
// result in multiple writes to disk.
// This call returns immediately.
@@ -109,15 +91,13 @@
virtual void QueueFlush(const base::Closure& callback);
private:
- // SqlContext needs access to our internal APIs.
- friend class SqlContext;
// Give StorageManagerTest access, so we can more easily test some internals.
friend class StorageManagerTest;
// Flushes all queued flushes to the savegame thread.
void FlushInternal();
- // Initialize the SQLite database. This blocks until the savegame load is
+ // Initialize the store. This blocks until the savegame load is
// complete.
void FinishInit();
@@ -127,54 +107,38 @@
// Callback when flush timer has elapsed.
void OnFlushOnChangeTimerFired();
- // Logic to be executed on the SQL thread when a flush completes. Will
+ // Logic to be executed on the store thread when a flush completes. Will
// dispatch |flush_processing_callbacks_| callbacks and execute a new flush
// if |flush_requested_| is true.
- void OnFlushIOCompletedSQLCallback();
+ void OnFlushIOCompletedCallback();
// This function will not return until all queued I/O is completed. Since
- // it will require the SQL message loop to process, it must be called from
- // outside the SQL message loop (such as from StorageManager's destructor).
+ // it will require the store message loop to process, it must be called from
+ // outside the store message loop (such as from StorageManager's destructor).
void FinishIO();
// This function will immediately the on change timers if they are running.
void FireRunningOnChangeTimers();
// Called by the destructor, to ensure we destroy certain objects on the
- // sql thread.
+ // store thread
void OnDestroy();
- // Internal API for use by SqlContext.
- sql::Connection* sql_connection();
- bool GetSchemaVersion(const char* table_name, int* schema_version);
- void UpdateSchemaVersion(const char* table_name, int version);
-
// Upgrade handler used if upgrade save data is detected.
scoped_ptr<UpgradeHandler> upgrade_handler_;
// Configuration options for the Storage Manager.
Options options_;
- // Storage manager runs on its own thread. This is where SQL database
+ // Storage manager runs on its own thread. This is where store
// operations are done.
- scoped_ptr<base::Thread> sql_thread_;
- scoped_refptr<base::MessageLoopProxy> sql_message_loop_;
+ scoped_ptr<base::Thread> storage_thread_;
+ scoped_refptr<base::MessageLoopProxy> storage_message_loop_;
- // An interface to the storage manager's SQL database that will run on
- // the correct thread.
- scoped_ptr<SqlContext> sql_context_;
-
- // The in-memory database connection.
- scoped_ptr<sql::Connection> connection_;
-
- // Virtual file system that contains our in-memory SQLite database.
- scoped_ptr<VirtualFileSystem> vfs_;
-
- // An interface between Sqlite and VirtualFileSystem.
- scoped_ptr<SqlVfs> sql_vfs_;
+ scoped_ptr<MemoryStore> memory_store_;
// When the savegame is loaded at startup, we keep the raw data around
- // until we can initialize the database on the correct thread.
+ // until we can initialize the store on the correct thread.
scoped_ptr<Savegame::ByteVector> loaded_raw_bytes_;
// Timers that start running when FlushOnChange() is called. When the time
@@ -191,7 +155,7 @@
// See comments for for kDatabaseUserVersion.
int loaded_database_version_;
- // false until the SQL database is fully configured.
+ // false until the store is fully configured.
bool initialized_;
// True if a flush is currently being processed on the storage message loop.
@@ -222,51 +186,6 @@
DISALLOW_COPY_AND_ASSIGN(StorageManager);
};
-// Proxy for accessing StorageManager's SQL database in a thread-safe way.
-// All access to the StorageManager's database should be done via an SqlContext.
-// Only the StorageManager can create this class.
-class SqlContext {
- public:
- sql::Connection* sql_connection() const {
- return storage_manager_->sql_connection();
- }
-
- // Get the schema version for the given table.
- // Returns false if the table does not exist, otherwise returns true
- // and writes the version number to the schema_version pointer.
- // schema_version will be set to kSchemaTableIsNew if the table exists,
- // but the schema table was newly created in this session.
- // schema_version will be set to kSchemaVersionLost if the table exists,
- // and the schema table existed once before, but has since been lost.
- // In this case, the schema version cannot be known or directly inferred.
- bool GetSchemaVersion(const char* table_name, int* schema_version) {
- return storage_manager_->GetSchemaVersion(table_name, schema_version);
- }
-
- // Updates the schema version for the given table.
- // The version number must be greater than 0.
- void UpdateSchemaVersion(const char* table_name, int version) {
- return storage_manager_->UpdateSchemaVersion(table_name, version);
- }
-
- void FlushOnChange() { storage_manager_->FlushOnChange(); }
-
- void FlushNow(const base::Closure& callback) {
- storage_manager_->FlushNow(callback);
- }
-
- private:
- StorageManager* storage_manager_;
-
- explicit SqlContext(StorageManager* storage_manager)
- : storage_manager_(storage_manager) {}
-
- friend StorageManager::StorageManager(
- scoped_ptr<StorageManager::UpgradeHandler> upgrade_handler,
- const Options& options);
- DISALLOW_COPY_AND_ASSIGN(SqlContext);
-};
-
} // namespace storage
} // namespace cobalt
diff --git a/src/cobalt/storage/storage_manager_test.cc b/src/cobalt/storage/storage_manager_test.cc
index 210c5a2..b199b2a 100644
--- a/src/cobalt/storage/storage_manager_test.cc
+++ b/src/cobalt/storage/storage_manager_test.cc
@@ -79,44 +79,32 @@
DISALLOW_COPY_AND_ASSIGN(FlushWaiter);
};
-class SqlWaiter : public CallbackWaiter {
+class MemoryStoreWaiter : public CallbackWaiter {
public:
- SqlWaiter() {}
- void OnSqlConnection(SqlContext* sql_context) {
- UNREFERENCED_PARAMETER(sql_context);
+ MemoryStoreWaiter() {}
+ void OnMemoryStore(MemoryStore* memory_store) {
+ UNREFERENCED_PARAMETER(memory_store);
Signal();
}
private:
- DISALLOW_COPY_AND_ASSIGN(SqlWaiter);
+ DISALLOW_COPY_AND_ASSIGN(MemoryStoreWaiter);
};
-void FlushCallback(SqlContext* sql_context) {
- sql::Connection* conn = sql_context->sql_connection();
- bool ok = conn->Execute("CREATE TABLE FlushTest(test_name TEXT);");
- EXPECT_TRUE(ok);
-}
+class ReadOnlyMemoryStoreWaiter : public CallbackWaiter {
+ public:
+ ReadOnlyMemoryStoreWaiter() {}
+ void OnMemoryStore(const MemoryStore& memory_store) {
+ UNREFERENCED_PARAMETER(memory_store);
+ Signal();
+ }
-void QuerySchemaCallback(SqlContext* sql_context) {
- int schema_version;
- EXPECT_FALSE(
- sql_context->GetSchemaVersion("Nonexistent table", &schema_version));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReadOnlyMemoryStoreWaiter);
+};
- sql::Connection* conn = sql_context->sql_connection();
- bool ok = conn->Execute("CREATE TABLE TestTable(test_name TEXT);");
- EXPECT_TRUE(ok);
-
- EXPECT_TRUE(sql_context->GetSchemaVersion("TestTable", &schema_version));
- EXPECT_EQ(static_cast<int>(StorageManager::kSchemaTableIsNew),
- schema_version);
-
- sql_context->UpdateSchemaVersion("TestTable", 100);
-}
-
-void InspectSchemaVersionCallback(SqlContext* sql_context) {
- int schema_version;
- EXPECT_TRUE(sql_context->GetSchemaVersion("TestTable", &schema_version));
- EXPECT_EQ(100, schema_version);
+void FlushCallback(MemoryStore* memory_store) {
+ EXPECT_NE(memory_store, nullptr);
}
} // namespace
@@ -155,36 +143,28 @@
scoped_ptr<StorageManager> storage_manager_;
};
-TEST_F(StorageManagerTest, ObtainConnection) {
- // Verify that the SQL connection is non-null.
+TEST_F(StorageManagerTest, WithMemoryStore) {
Init<StorageManager>();
- SqlWaiter waiter;
- storage_manager_->GetSqlContext(
- base::Bind(&SqlWaiter::OnSqlConnection, base::Unretained(&waiter)));
+ MemoryStoreWaiter waiter;
+ storage_manager_->WithMemoryStore(
+ base::Bind(&MemoryStoreWaiter::OnMemoryStore, base::Unretained(&waiter)));
message_loop_.RunUntilIdle();
EXPECT_TRUE(waiter.TimedWait());
}
-TEST_F(StorageManagerTest, QuerySchemaVersion) {
- Init<StorageManager>(false /* delete_savegame */);
- storage_manager_->GetSqlContext(base::Bind(&QuerySchemaCallback));
- message_loop_.RunUntilIdle();
-
- // Force a write to disk and wait until it's done.
- FlushWaiter waiter;
- storage_manager_->FlushNow(
- base::Bind(&FlushWaiter::OnFlushDone, base::Unretained(&waiter)));
- EXPECT_TRUE(waiter.TimedWait());
-
+TEST_F(StorageManagerTest, WithReadOnlyMemoryStore) {
Init<StorageManager>();
- storage_manager_->GetSqlContext(base::Bind(&InspectSchemaVersionCallback));
+ ReadOnlyMemoryStoreWaiter waiter;
+ storage_manager_->WithReadOnlyMemoryStore(base::Bind(
+ &ReadOnlyMemoryStoreWaiter::OnMemoryStore, base::Unretained(&waiter)));
message_loop_.RunUntilIdle();
+ EXPECT_TRUE(waiter.TimedWait());
}
TEST_F(StorageManagerTest, FlushNow) {
// Ensure the Flush callback is called.
Init<StorageManager>();
- storage_manager_->GetSqlContext(base::Bind(&FlushCallback));
+ storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
message_loop_.RunUntilIdle();
FlushWaiter waiter;
storage_manager_->FlushNow(
@@ -197,7 +177,7 @@
// FlushOnChange() and FlushNow().
Init<MockStorageManager>();
- storage_manager_->GetSqlContext(base::Bind(&FlushCallback));
+ storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
message_loop_.RunUntilIdle();
FlushWaiter waiter;
@@ -223,7 +203,7 @@
// FlushOnChange() multiple times.
Init<MockStorageManager>();
- storage_manager_->GetSqlContext(base::Bind(&FlushCallback));
+ storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
message_loop_.RunUntilIdle();
FlushWaiter waiter;
@@ -250,7 +230,7 @@
// there are constant calls to FlushOnChange().
Init<MockStorageManager>();
- storage_manager_->GetSqlContext(base::Bind(&FlushCallback));
+ storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
message_loop_.RunUntilIdle();
FlushWaiter waiter;
@@ -274,7 +254,7 @@
// Test that pending flushes are completed on shutdown.
Init<MockStorageManager>();
- storage_manager_->GetSqlContext(base::Bind(&FlushCallback));
+ storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
message_loop_.RunUntilIdle();
FlushWaiter waiter;
diff --git a/src/cobalt/storage/store/memory_store.cc b/src/cobalt/storage/store/memory_store.cc
new file mode 100644
index 0000000..c92f419
--- /dev/null
+++ b/src/cobalt/storage/store/memory_store.cc
@@ -0,0 +1,268 @@
+// Copyright 2018 Google Inc. 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 "cobalt/storage/store/memory_store.h"
+
+#include <map>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "cobalt/storage/storage_constants.h"
+#include "cobalt/storage/store/storage.pb.h"
+#include "googleurl/src/gurl.h"
+#include "nb/memory_scope.h"
+
+namespace cobalt {
+namespace storage {
+namespace {
+
+bool IsValidFormat(const std::vector<uint8>& buffer) {
+ return buffer.size() >= kStorageHeaderSize &&
+ memcmp(buffer.data(), kStorageHeader, kStorageHeaderSize) == 0;
+}
+
+} // namespace
+
+// Using Pimpl design pattern to hide protobuf implementation details.
+// The generated proto code (including the header) needs special build
+// configuration to work as expected. Which means adding special build settings
+// to any place where the headers is included.
+class MemoryStore::Impl {
+ public:
+ // Initialization
+ Impl();
+ bool Initialize(const std::vector<uint8>& in);
+
+ // Serialization
+ bool Serialize(std::vector<uint8>* out) const;
+
+ // Cookies
+ void GetAllCookies(std::vector<net::CanonicalCookie*>* cookies) const;
+ void AddCookie(const net::CanonicalCookie& cc, int64 expiration_time_us);
+ void UpdateCookieAccessTime(const net::CanonicalCookie& cc, int64 time_us);
+ void DeleteCookie(const net::CanonicalCookie& cc);
+
+ // Local Storage
+ void ReadAllLocalStorage(const std::string& id,
+ LocalStorageMap* local_storage_map) const;
+
+ void WriteToLocalStorage(const std::string& id, const std::string& key,
+ const std::string& value);
+ void DeleteFromLocalStorage(const std::string& id, const std::string& key);
+ void ClearLocalStorage(const std::string& id);
+
+ // Disable copying
+ Impl(const Impl& store) = delete;
+ Impl& operator=(const Impl& store) = delete;
+
+ private:
+ typedef std::tuple<std::string, std::string, std::string> CookieKey;
+ std::map<CookieKey, Cookie> cookies_map_;
+
+ typedef std::map<std::string, std::string> LocalStorageKeyValueMap;
+ typedef std::map<std::string, LocalStorageKeyValueMap> LocalStorageByIdMap;
+
+ LocalStorageByIdMap local_storage_by_id_map_;
+};
+
+MemoryStore::Impl::Impl() {}
+
+bool MemoryStore::Impl::Initialize(const std::vector<uint8>& in) {
+ TRACK_MEMORY_SCOPE("Storage");
+ if (!IsValidFormat(in)) {
+ LOG(ERROR) << "Invalid format with size=" << in.size();
+ return false;
+ }
+ Storage storage_data;
+ if (!storage_data.ParseFromArray(
+ reinterpret_cast<const char*>(in.data() + kStorageHeaderSize),
+ in.size() - kStorageHeaderSize)) {
+ LOG(ERROR) << "Unable to parse storage with size" << in.size();
+ return false;
+ }
+ for (auto& cookie : storage_data.cookies()) {
+ cookies_map_[std::make_tuple(cookie.domain(), cookie.path(),
+ cookie.name())] = cookie;
+ }
+ for (const auto& local_storages : storage_data.local_storages()) {
+ for (const auto& local_storage_entry :
+ local_storages.local_storage_entries()) {
+ local_storage_by_id_map_[local_storages.id()][local_storage_entry.key()] =
+ local_storage_entry.value();
+ }
+ }
+ return true;
+}
+
+bool MemoryStore::Impl::Serialize(std::vector<uint8>* out) const {
+ TRACK_MEMORY_SCOPE("Storage");
+ Storage storage_data;
+ for (const auto& cookie_pair : cookies_map_) {
+ *(storage_data.add_cookies()) = cookie_pair.second;
+ }
+ for (const auto& id_storage_pair : local_storage_by_id_map_) {
+ LocalStorage* local_storage = storage_data.add_local_storages();
+ local_storage->set_id(id_storage_pair.first);
+ for (const auto& key_value_pair : id_storage_pair.second) {
+ LocalStorageEntry* local_storage_entry =
+ local_storage->add_local_storage_entries();
+ local_storage_entry->set_key(key_value_pair.first);
+ local_storage_entry->set_value(key_value_pair.second);
+ }
+ }
+ size_t size = storage_data.ByteSize();
+ out->resize(kStorageHeaderSize + size);
+ char* buffer_ptr = reinterpret_cast<char*>(out->data());
+ memcpy(buffer_ptr, kStorageHeader, kStorageHeaderSize);
+ if (size > 0 &&
+ !storage_data.SerializeToArray(buffer_ptr + kStorageHeaderSize, size)) {
+ LOG(ERROR) << "Failed to serialize message with size=" << size;
+ return false;
+ }
+
+ return true;
+}
+
+void MemoryStore::Impl::GetAllCookies(
+ std::vector<net::CanonicalCookie*>* cookies) const {
+ TRACK_MEMORY_SCOPE("Storage");
+ for (const auto& cookie_pair : cookies_map_) {
+ // We create a CanonicalCookie directly through its constructor instead of
+ // through CanonicalCookie::Create() and its sanitization because these
+ // values are just serialized from a former instance of a CanonicalCookie
+ // object that *was* created through CanonicalCookie::Create().
+ scoped_ptr<net::CanonicalCookie> cookie(new net::CanonicalCookie(
+ GURL(""), cookie_pair.second.name(), cookie_pair.second.value(),
+ cookie_pair.second.domain(), cookie_pair.second.path(),
+ "" /* mac_key */, "" /* mac_algorithm */,
+ base::Time::FromInternalValue(cookie_pair.second.creation_time_us()),
+ base::Time::FromInternalValue(cookie_pair.second.expiration_time_us()),
+ base::Time::FromInternalValue(cookie_pair.second.last_access_time_us()),
+ cookie_pair.second.secure(), cookie_pair.second.http_only()));
+ cookies->push_back(cookie.release());
+ }
+}
+
+void MemoryStore::Impl::AddCookie(const net::CanonicalCookie& cc,
+ int64 expiration_time_us) {
+ TRACK_MEMORY_SCOPE("Storage");
+ Cookie cookie;
+ cookie.set_domain(cc.Domain());
+ cookie.set_path(cc.Path());
+ cookie.set_name(cc.Name());
+ cookie.set_value(cc.Value());
+ cookie.set_secure(cc.IsSecure());
+ cookie.set_http_only(cc.IsHttpOnly());
+
+ cookie.set_creation_time_us(cc.CreationDate().ToInternalValue());
+ cookie.set_expiration_time_us(expiration_time_us);
+ cookie.set_last_access_time_us(cc.LastAccessDate().ToInternalValue());
+
+ CookieKey key = std::make_tuple(cc.Domain(), cc.Path(), cc.Name());
+ cookies_map_[key] = cookie;
+}
+
+void MemoryStore::Impl::UpdateCookieAccessTime(const net::CanonicalCookie& cc,
+ int64 time_us) {
+ CookieKey key = std::make_tuple(cc.Domain(), cc.Path(), cc.Name());
+ cookies_map_[key].set_last_access_time_us(time_us);
+}
+
+void MemoryStore::Impl::DeleteCookie(const net::CanonicalCookie& cc) {
+ CookieKey key = std::make_tuple(cc.Domain(), cc.Path(), cc.Name());
+ cookies_map_.erase(key);
+}
+
+void MemoryStore::Impl::ReadAllLocalStorage(
+ const std::string& id, LocalStorageMap* local_storage_map) const {
+ TRACK_MEMORY_SCOPE("Storage");
+ const auto& it = local_storage_by_id_map_.find(id);
+
+ if (it != local_storage_by_id_map_.end()) {
+ local_storage_map->insert(it->second.begin(), it->second.end());
+ }
+}
+
+void MemoryStore::Impl::WriteToLocalStorage(const std::string& id,
+ const std::string& key,
+ const std::string& value) {
+ TRACK_MEMORY_SCOPE("Storage");
+ local_storage_by_id_map_[id][key] = value;
+}
+
+void MemoryStore::Impl::DeleteFromLocalStorage(const std::string& id,
+ const std::string& key) {
+ TRACK_MEMORY_SCOPE("Storage");
+ local_storage_by_id_map_[id].erase(key);
+}
+
+void MemoryStore::Impl::ClearLocalStorage(const std::string& id) {
+ TRACK_MEMORY_SCOPE("Storage");
+ local_storage_by_id_map_[id].clear();
+}
+
+MemoryStore::MemoryStore() { impl_.reset(new Impl()); }
+
+bool MemoryStore::Initialize(const std::vector<uint8>& in) {
+ return impl_->Initialize(in);
+}
+
+bool MemoryStore::Serialize(std::vector<uint8>* out) const {
+ return impl_->Serialize(out);
+}
+
+void MemoryStore::GetAllCookies(
+ std::vector<net::CanonicalCookie*>* cookies) const {
+ impl_->GetAllCookies(cookies);
+}
+
+void MemoryStore::AddCookie(const net::CanonicalCookie& cc,
+ int64 expiration_time_us) {
+ impl_->AddCookie(cc, expiration_time_us);
+}
+
+void MemoryStore::UpdateCookieAccessTime(const net::CanonicalCookie& cc,
+ int64 time_us) {
+ impl_->UpdateCookieAccessTime(cc, time_us);
+}
+
+void MemoryStore::DeleteCookie(const net::CanonicalCookie& cc) {
+ impl_->DeleteCookie(cc);
+}
+
+void MemoryStore::ReadAllLocalStorage(
+ const std::string& id, LocalStorageMap* local_storage_map) const {
+ impl_->ReadAllLocalStorage(id, local_storage_map);
+}
+
+void MemoryStore::WriteToLocalStorage(const std::string& id,
+ const std::string& key,
+ const std::string& value) {
+ impl_->WriteToLocalStorage(id, key, value);
+}
+
+void MemoryStore::DeleteFromLocalStorage(const std::string& id,
+ const std::string& key) {
+ impl_->DeleteFromLocalStorage(id, key);
+}
+
+void MemoryStore::ClearLocalStorage(const std::string& id) {
+ impl_->ClearLocalStorage(id);
+}
+
+MemoryStore::~MemoryStore() {}
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/store/memory_store.h b/src/cobalt/storage/store/memory_store.h
new file mode 100644
index 0000000..a5f3ccc
--- /dev/null
+++ b/src/cobalt/storage/store/memory_store.h
@@ -0,0 +1,69 @@
+// Copyright 2018 Google Inc. 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 COBALT_STORAGE_STORE_MEMORY_STORE_H_
+#define COBALT_STORAGE_STORE_MEMORY_STORE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/cookies/canonical_cookie.h"
+
+namespace cobalt {
+namespace storage {
+
+// A store for cookies and local storage implemented in memory.
+class MemoryStore {
+ public:
+ typedef base::hash_map<std::string, std::string> LocalStorageMap;
+ typedef const std::vector<net::CanonicalCookie*> Cookies;
+
+ MemoryStore();
+ bool Initialize(const std::vector<uint8>& in);
+
+ // Serialization
+ bool Serialize(std::vector<uint8>* out) const;
+
+ // Cookies
+ void GetAllCookies(std::vector<net::CanonicalCookie*>* cookies) const;
+ void AddCookie(const net::CanonicalCookie& cc, int64 expiration_time_us);
+ void UpdateCookieAccessTime(const net::CanonicalCookie& cc, int64 time_us);
+ void DeleteCookie(const net::CanonicalCookie& cc);
+
+ // Local Storage
+ void ReadAllLocalStorage(const std::string& id,
+ LocalStorageMap* local_storage_map) const;
+
+ void WriteToLocalStorage(const std::string& id, const std::string& key,
+ const std::string& value);
+ void DeleteFromLocalStorage(const std::string& id, const std::string& key);
+ void ClearLocalStorage(const std::string& id);
+
+ // Disable copying
+ MemoryStore(const MemoryStore& store) = delete;
+ MemoryStore& operator=(const MemoryStore& store) = delete;
+
+ ~MemoryStore();
+
+ private:
+ class Impl;
+ scoped_ptr<Impl> impl_;
+};
+
+} // namespace storage
+} // namespace cobalt
+
+#endif // COBALT_STORAGE_STORE_MEMORY_STORE_H_
diff --git a/src/cobalt/storage/store/memory_store_test.cc b/src/cobalt/storage/store/memory_store_test.cc
new file mode 100644
index 0000000..ab7f8cb
--- /dev/null
+++ b/src/cobalt/storage/store/memory_store_test.cc
@@ -0,0 +1,221 @@
+// Copyright 2018 Google Inc. 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 "cobalt/storage/store/memory_store.h"
+
+#include "base/debug/trace_event.h"
+#include "cobalt/storage/storage_constants.h"
+#include "cobalt/storage/store/storage.pb.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace storage {
+
+namespace {
+
+class MemoryStoreTest : public ::testing::Test {
+ protected:
+ MemoryStoreTest() : id1_("a_site_id1"), id2_("a_site_id2") {
+ base::Time current_time = base::Time::FromInternalValue(12345);
+ expiration_time_ = current_time + base::TimeDelta::FromDays(1);
+
+ Cookie* cookie = storage_proto_.add_cookies();
+ cookie->set_name("name");
+ cookie->set_value("value");
+ cookie->set_domain("domain");
+ cookie->set_path("/path/foo");
+ cookie->set_creation_time_us(current_time.ToInternalValue());
+ cookie->set_expiration_time_us(expiration_time_.ToInternalValue());
+ cookie->set_last_access_time_us(current_time.ToInternalValue());
+ cookie->set_secure(true);
+ cookie->set_http_only(true);
+
+ size_t size = storage_proto_.ByteSize();
+ storage_data_.resize(size + kStorageHeaderSize);
+ memcpy(reinterpret_cast<char*>(&storage_data_[0]), kStorageHeader,
+ kStorageHeaderSize);
+ storage_proto_.SerializeToArray(
+ reinterpret_cast<char*>(&storage_data_[kStorageHeaderSize]), size);
+
+ memory_store_.Initialize(storage_data_);
+
+ cookie_.reset(new net::CanonicalCookie(
+ GURL(""), "name", "value", "domain", "/path/foo", "" /* mac_key */,
+ "" /* mac_algorithm */, current_time, expiration_time_, current_time,
+ true /* secure */, true /* http_only */));
+
+ last_access_time_ = current_time + base::TimeDelta::FromDays(50);
+
+ updated_cookie_.reset(new net::CanonicalCookie(
+ GURL(""), "name", "value", "domain", "/path/foo", "" /* mac_key */,
+ "" /* mac_algorithm */, current_time, expiration_time_, current_time,
+ true /* secure */, true /* http_only */));
+
+ new_cookie_.reset(new net::CanonicalCookie(
+ GURL(""), "name1", "value2", "domain2", "/path/foo2", "" /* mac_key */,
+ "" /* mac_algorithm */, current_time, expiration_time_, current_time,
+ false /* secure */, false /* http_only */));
+ }
+ ~MemoryStoreTest() {}
+
+ std::string id1_;
+ std::string id2_;
+ Storage storage_proto_;
+ std::vector<uint8> storage_data_;
+ scoped_ptr<net::CanonicalCookie> cookie_;
+ scoped_ptr<net::CanonicalCookie> updated_cookie_;
+ scoped_ptr<net::CanonicalCookie> new_cookie_;
+ base::Time last_access_time_;
+ base::Time expiration_time_;
+ MemoryStore memory_store_;
+};
+
+TEST_F(MemoryStoreTest, Initialize) {
+ EXPECT_TRUE(memory_store_.Initialize(storage_data_));
+}
+
+TEST_F(MemoryStoreTest, Serialize) {
+ std::vector<uint8> out;
+ memory_store_.Serialize(&out);
+ EXPECT_EQ(storage_data_, out);
+}
+
+TEST_F(MemoryStoreTest, GetAllCookies) {
+ std::vector<net::CanonicalCookie*> cookies;
+ memory_store_.GetAllCookies(&cookies);
+
+ EXPECT_EQ(cookies.size(), 1);
+ EXPECT_TRUE(cookies[0]->IsEquivalent(*cookie_));
+ for (auto* cookie : cookies) {
+ delete cookie;
+ }
+}
+
+TEST_F(MemoryStoreTest, AddCookie) {
+ std::vector<net::CanonicalCookie*> cookies;
+ memory_store_.GetAllCookies(&cookies);
+
+ EXPECT_EQ(cookies.size(), 1);
+ EXPECT_TRUE(cookies[0]->IsEquivalent(*cookie_));
+ for (auto* cookie : cookies) {
+ delete cookie;
+ }
+ cookies.clear();
+
+ memory_store_.AddCookie(*new_cookie_, expiration_time_.ToInternalValue());
+
+ memory_store_.GetAllCookies(&cookies);
+ EXPECT_EQ(cookies.size(), 2);
+ EXPECT_TRUE(cookies[0]->IsEquivalent(*cookie_));
+ EXPECT_TRUE(cookies[1]->IsEquivalent(*new_cookie_));
+ for (auto* cookie : cookies) {
+ delete cookie;
+ }
+ cookies.clear();
+}
+
+TEST_F(MemoryStoreTest, UpdateCookieAccessTime) {
+ memory_store_.UpdateCookieAccessTime(*cookie_,
+ last_access_time_.ToInternalValue());
+ std::vector<net::CanonicalCookie*> cookies;
+ memory_store_.GetAllCookies(&cookies);
+
+ EXPECT_EQ(cookies.size(), 1);
+
+ EXPECT_TRUE(cookies[0]->IsEquivalent(*updated_cookie_));
+ for (auto* cookie : cookies) {
+ delete cookie;
+ }
+}
+
+TEST_F(MemoryStoreTest, DeleteCookie) {
+ std::vector<net::CanonicalCookie*> cookies;
+ memory_store_.GetAllCookies(&cookies);
+ EXPECT_EQ(cookies.size(), 1);
+ for (auto* cookie : cookies) {
+ delete cookie;
+ }
+ cookies.clear();
+
+ memory_store_.DeleteCookie(*cookie_);
+ memory_store_.GetAllCookies(&cookies);
+
+ EXPECT_TRUE(cookies.empty());
+}
+
+TEST_F(MemoryStoreTest, ReadWriteToLocalStorage) {
+ MemoryStore::LocalStorageMap test_vals;
+ test_vals["key0"] = "value0";
+ test_vals["key1"] = "value1";
+
+ for (const auto& it : test_vals) {
+ memory_store_.WriteToLocalStorage(id1_, it.first, it.second);
+ }
+
+ memory_store_.WriteToLocalStorage(id2_, "key0", "value0");
+ MemoryStore::LocalStorageMap read_vals;
+ memory_store_.ReadAllLocalStorage(id1_, &read_vals);
+ EXPECT_EQ(test_vals, read_vals);
+}
+
+TEST_F(MemoryStoreTest, DeleteFromLocalStorage) {
+ MemoryStore::LocalStorageMap test_vals;
+ test_vals["key0"] = "value0";
+ test_vals["key1"] = "value1";
+
+ for (const auto& it : test_vals) {
+ memory_store_.WriteToLocalStorage(id1_, it.first, it.second);
+ }
+
+ memory_store_.DeleteFromLocalStorage(id1_, "key0");
+ test_vals.erase("key0");
+
+ MemoryStore::LocalStorageMap read_vals;
+ memory_store_.ReadAllLocalStorage(id1_, &read_vals);
+
+ EXPECT_EQ(test_vals, read_vals);
+}
+
+TEST_F(MemoryStoreTest, ClearLocalStorage) {
+ MemoryStore::LocalStorageMap test_vals;
+ test_vals["key0"] = "value0";
+ test_vals["key1"] = "value1";
+
+ for (const auto& it : test_vals) {
+ memory_store_.WriteToLocalStorage(id1_, it.first, it.second);
+ }
+
+ memory_store_.ClearLocalStorage(id1_);
+
+ MemoryStore::LocalStorageMap read_vals;
+ memory_store_.ReadAllLocalStorage(id1_, &read_vals);
+
+ EXPECT_TRUE(read_vals.empty());
+}
+
+TEST_F(MemoryStoreTest, EmptyStorage) {
+ MemoryStore empty_store;
+ std::vector<uint8> in(kStorageHeader, kStorageHeader + kStorageHeaderSize);
+ empty_store.Initialize(in);
+
+ std::vector<uint8> out;
+ empty_store.Serialize(&out);
+ EXPECT_EQ(memcmp(kStorageHeader, reinterpret_cast<const char*>(&out[0]),
+ kStorageHeaderSize),
+ 0);
+}
+} // namespace
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/store/storage.pb.cc b/src/cobalt/storage/store/storage.pb.cc
new file mode 100644
index 0000000..b96170c
--- /dev/null
+++ b/src/cobalt/storage/store/storage.pb.cc
@@ -0,0 +1,1763 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: storage.proto
+
+#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+#include "storage.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/port.h>
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/wire_format_lite_inl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+
+namespace cobalt {
+namespace storage {
+
+void protobuf_ShutdownFile_storage_2eproto() {
+ delete Cookie::default_instance_;
+ delete LocalStorageEntry::default_instance_;
+ delete LocalStorage::default_instance_;
+ delete Storage::default_instance_;
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+void protobuf_AddDesc_storage_2eproto_impl() {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#else
+void protobuf_AddDesc_storage_2eproto() {
+ static bool already_here = false;
+ if (already_here) return;
+ already_here = true;
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+#endif
+ Cookie::default_instance_ = new Cookie();
+ LocalStorageEntry::default_instance_ = new LocalStorageEntry();
+ LocalStorage::default_instance_ = new LocalStorage();
+ Storage::default_instance_ = new Storage();
+ Cookie::default_instance_->InitAsDefaultInstance();
+ LocalStorageEntry::default_instance_->InitAsDefaultInstance();
+ LocalStorage::default_instance_->InitAsDefaultInstance();
+ Storage::default_instance_->InitAsDefaultInstance();
+ ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_storage_2eproto);
+}
+
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AddDesc_storage_2eproto_once_);
+void protobuf_AddDesc_storage_2eproto() {
+ ::google::protobuf::GoogleOnceInit(&protobuf_AddDesc_storage_2eproto_once_,
+ &protobuf_AddDesc_storage_2eproto_impl);
+}
+#else
+// Force AddDescriptors() to be called at static initialization time.
+struct StaticDescriptorInitializer_storage_2eproto {
+ StaticDescriptorInitializer_storage_2eproto() {
+ protobuf_AddDesc_storage_2eproto();
+ }
+} static_descriptor_initializer_storage_2eproto_;
+#endif
+
+namespace {
+
+static void MergeFromFail(int line) GOOGLE_ATTRIBUTE_COLD;
+GOOGLE_ATTRIBUTE_NOINLINE static void MergeFromFail(int line) {
+ GOOGLE_CHECK(false) << __FILE__ << ":" << line;
+}
+
+} // namespace
+
+
+// ===================================================================
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int Cookie::kNameFieldNumber;
+const int Cookie::kValueFieldNumber;
+const int Cookie::kDomainFieldNumber;
+const int Cookie::kPathFieldNumber;
+const int Cookie::kCreationTimeUsFieldNumber;
+const int Cookie::kExpirationTimeUsFieldNumber;
+const int Cookie::kLastAccessTimeUsFieldNumber;
+const int Cookie::kSecureFieldNumber;
+const int Cookie::kHttpOnlyFieldNumber;
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+Cookie::Cookie()
+ : ::google::protobuf::MessageLite(), _arena_ptr_(NULL) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:cobalt.storage.Cookie)
+}
+
+void Cookie::InitAsDefaultInstance() {
+ _is_default_instance_ = true;
+}
+
+Cookie::Cookie(const Cookie& from)
+ : ::google::protobuf::MessageLite(),
+ _arena_ptr_(NULL) {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:cobalt.storage.Cookie)
+}
+
+void Cookie::SharedCtor() {
+ _is_default_instance_ = false;
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ name_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ domain_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ path_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ creation_time_us_ = GOOGLE_LONGLONG(0);
+ expiration_time_us_ = GOOGLE_LONGLONG(0);
+ last_access_time_us_ = GOOGLE_LONGLONG(0);
+ secure_ = false;
+ http_only_ = false;
+}
+
+Cookie::~Cookie() {
+ // @@protoc_insertion_point(destructor:cobalt.storage.Cookie)
+ SharedDtor();
+}
+
+void Cookie::SharedDtor() {
+ name_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ domain_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ path_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void Cookie::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const Cookie& Cookie::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_storage_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_storage_2eproto();
+#endif
+ return *default_instance_;
+}
+
+Cookie* Cookie::default_instance_ = NULL;
+
+Cookie* Cookie::New(::google::protobuf::Arena* arena) const {
+ Cookie* n = new Cookie;
+ if (arena != NULL) {
+ arena->Own(n);
+ }
+ return n;
+}
+
+void Cookie::Clear() {
+// @@protoc_insertion_point(message_clear_start:cobalt.storage.Cookie)
+#if defined(__clang__)
+#define ZR_HELPER_(f) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \
+ __builtin_offsetof(Cookie, f) \
+ _Pragma("clang diagnostic pop")
+#else
+#define ZR_HELPER_(f) reinterpret_cast<char*>(\
+ &reinterpret_cast<Cookie*>(16)->f)
+#endif
+
+#define ZR_(first, last) do {\
+ ::memset(&first, 0,\
+ ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\
+} while (0)
+
+ ZR_(creation_time_us_, secure_);
+ name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ domain_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ path_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ http_only_ = false;
+
+#undef ZR_HELPER_
+#undef ZR_
+
+}
+
+bool Cookie::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ // @@protoc_insertion_point(parse_start:cobalt.storage.Cookie)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional string name = 1;
+ case 1: {
+ if (tag == 10) {
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_name()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->name().data(), this->name().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "cobalt.storage.Cookie.name"));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_value;
+ break;
+ }
+
+ // optional string value = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_value:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_value()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->value().data(), this->value().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "cobalt.storage.Cookie.value"));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(26)) goto parse_domain;
+ break;
+ }
+
+ // optional string domain = 3;
+ case 3: {
+ if (tag == 26) {
+ parse_domain:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_domain()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->domain().data(), this->domain().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "cobalt.storage.Cookie.domain"));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(34)) goto parse_path;
+ break;
+ }
+
+ // optional string path = 4;
+ case 4: {
+ if (tag == 34) {
+ parse_path:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_path()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->path().data(), this->path().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "cobalt.storage.Cookie.path"));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(40)) goto parse_creation_time_us;
+ break;
+ }
+
+ // optional int64 creation_time_us = 5;
+ case 5: {
+ if (tag == 40) {
+ parse_creation_time_us:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
+ input, &creation_time_us_)));
+
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(48)) goto parse_expiration_time_us;
+ break;
+ }
+
+ // optional int64 expiration_time_us = 6;
+ case 6: {
+ if (tag == 48) {
+ parse_expiration_time_us:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
+ input, &expiration_time_us_)));
+
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(56)) goto parse_last_access_time_us;
+ break;
+ }
+
+ // optional int64 last_access_time_us = 7;
+ case 7: {
+ if (tag == 56) {
+ parse_last_access_time_us:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
+ input, &last_access_time_us_)));
+
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(64)) goto parse_secure;
+ break;
+ }
+
+ // optional bool secure = 8;
+ case 8: {
+ if (tag == 64) {
+ parse_secure:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &secure_)));
+
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(72)) goto parse_http_only;
+ break;
+ }
+
+ // optional bool http_only = 9;
+ case 9: {
+ if (tag == 72) {
+ parse_http_only:
+ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+ bool, ::google::protobuf::internal::WireFormatLite::TYPE_BOOL>(
+ input, &http_only_)));
+
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:cobalt.storage.Cookie)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:cobalt.storage.Cookie)
+ return false;
+#undef DO_
+}
+
+void Cookie::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:cobalt.storage.Cookie)
+ // optional string name = 1;
+ if (this->name().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->name().data(), this->name().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "cobalt.storage.Cookie.name");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 1, this->name(), output);
+ }
+
+ // optional string value = 2;
+ if (this->value().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->value().data(), this->value().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "cobalt.storage.Cookie.value");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 2, this->value(), output);
+ }
+
+ // optional string domain = 3;
+ if (this->domain().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->domain().data(), this->domain().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "cobalt.storage.Cookie.domain");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 3, this->domain(), output);
+ }
+
+ // optional string path = 4;
+ if (this->path().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->path().data(), this->path().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "cobalt.storage.Cookie.path");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 4, this->path(), output);
+ }
+
+ // optional int64 creation_time_us = 5;
+ if (this->creation_time_us() != 0) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt64(5, this->creation_time_us(), output);
+ }
+
+ // optional int64 expiration_time_us = 6;
+ if (this->expiration_time_us() != 0) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt64(6, this->expiration_time_us(), output);
+ }
+
+ // optional int64 last_access_time_us = 7;
+ if (this->last_access_time_us() != 0) {
+ ::google::protobuf::internal::WireFormatLite::WriteInt64(7, this->last_access_time_us(), output);
+ }
+
+ // optional bool secure = 8;
+ if (this->secure() != 0) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(8, this->secure(), output);
+ }
+
+ // optional bool http_only = 9;
+ if (this->http_only() != 0) {
+ ::google::protobuf::internal::WireFormatLite::WriteBool(9, this->http_only(), output);
+ }
+
+ // @@protoc_insertion_point(serialize_end:cobalt.storage.Cookie)
+}
+
+int Cookie::ByteSize() const {
+// @@protoc_insertion_point(message_byte_size_start:cobalt.storage.Cookie)
+ int total_size = 0;
+
+ // optional string name = 1;
+ if (this->name().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->name());
+ }
+
+ // optional string value = 2;
+ if (this->value().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->value());
+ }
+
+ // optional string domain = 3;
+ if (this->domain().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->domain());
+ }
+
+ // optional string path = 4;
+ if (this->path().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->path());
+ }
+
+ // optional int64 creation_time_us = 5;
+ if (this->creation_time_us() != 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int64Size(
+ this->creation_time_us());
+ }
+
+ // optional int64 expiration_time_us = 6;
+ if (this->expiration_time_us() != 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int64Size(
+ this->expiration_time_us());
+ }
+
+ // optional int64 last_access_time_us = 7;
+ if (this->last_access_time_us() != 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::Int64Size(
+ this->last_access_time_us());
+ }
+
+ // optional bool secure = 8;
+ if (this->secure() != 0) {
+ total_size += 1 + 1;
+ }
+
+ // optional bool http_only = 9;
+ if (this->http_only() != 0) {
+ total_size += 1 + 1;
+ }
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void Cookie::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const Cookie*>(&from));
+}
+
+void Cookie::MergeFrom(const Cookie& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:cobalt.storage.Cookie)
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ if (from.name().size() > 0) {
+
+ name_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.name_);
+ }
+ if (from.value().size() > 0) {
+
+ value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.value_);
+ }
+ if (from.domain().size() > 0) {
+
+ domain_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.domain_);
+ }
+ if (from.path().size() > 0) {
+
+ path_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.path_);
+ }
+ if (from.creation_time_us() != 0) {
+ set_creation_time_us(from.creation_time_us());
+ }
+ if (from.expiration_time_us() != 0) {
+ set_expiration_time_us(from.expiration_time_us());
+ }
+ if (from.last_access_time_us() != 0) {
+ set_last_access_time_us(from.last_access_time_us());
+ }
+ if (from.secure() != 0) {
+ set_secure(from.secure());
+ }
+ if (from.http_only() != 0) {
+ set_http_only(from.http_only());
+ }
+}
+
+void Cookie::CopyFrom(const Cookie& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:cobalt.storage.Cookie)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Cookie::IsInitialized() const {
+
+ return true;
+}
+
+void Cookie::Swap(Cookie* other) {
+ if (other == this) return;
+ InternalSwap(other);
+}
+void Cookie::InternalSwap(Cookie* other) {
+ name_.Swap(&other->name_);
+ value_.Swap(&other->value_);
+ domain_.Swap(&other->domain_);
+ path_.Swap(&other->path_);
+ std::swap(creation_time_us_, other->creation_time_us_);
+ std::swap(expiration_time_us_, other->expiration_time_us_);
+ std::swap(last_access_time_us_, other->last_access_time_us_);
+ std::swap(secure_, other->secure_);
+ std::swap(http_only_, other->http_only_);
+ _unknown_fields_.Swap(&other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+}
+
+::std::string Cookie::GetTypeName() const {
+ return "cobalt.storage.Cookie";
+}
+
+#if PROTOBUF_INLINE_NOT_IN_HEADERS
+// Cookie
+
+// optional string name = 1;
+void Cookie::clear_name() {
+ name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& Cookie::name() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.name)
+ return name_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_name(const ::std::string& value) {
+
+ name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.name)
+}
+ void Cookie::set_name(const char* value) {
+
+ name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.name)
+}
+ void Cookie::set_name(const char* value, size_t size) {
+
+ name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.name)
+}
+ ::std::string* Cookie::mutable_name() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.name)
+ return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* Cookie::release_name() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.name)
+
+ return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_allocated_name(::std::string* name) {
+ if (name != NULL) {
+
+ } else {
+
+ }
+ name_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), name);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.name)
+}
+
+// optional string value = 2;
+void Cookie::clear_value() {
+ value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& Cookie::value() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.value)
+ return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_value(const ::std::string& value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.value)
+}
+ void Cookie::set_value(const char* value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.value)
+}
+ void Cookie::set_value(const char* value, size_t size) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.value)
+}
+ ::std::string* Cookie::mutable_value() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.value)
+ return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* Cookie::release_value() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.value)
+
+ return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_allocated_value(::std::string* value) {
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.value)
+}
+
+// optional string domain = 3;
+void Cookie::clear_domain() {
+ domain_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& Cookie::domain() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.domain)
+ return domain_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_domain(const ::std::string& value) {
+
+ domain_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.domain)
+}
+ void Cookie::set_domain(const char* value) {
+
+ domain_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.domain)
+}
+ void Cookie::set_domain(const char* value, size_t size) {
+
+ domain_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.domain)
+}
+ ::std::string* Cookie::mutable_domain() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.domain)
+ return domain_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* Cookie::release_domain() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.domain)
+
+ return domain_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_allocated_domain(::std::string* domain) {
+ if (domain != NULL) {
+
+ } else {
+
+ }
+ domain_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), domain);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.domain)
+}
+
+// optional string path = 4;
+void Cookie::clear_path() {
+ path_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& Cookie::path() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.path)
+ return path_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_path(const ::std::string& value) {
+
+ path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.path)
+}
+ void Cookie::set_path(const char* value) {
+
+ path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.path)
+}
+ void Cookie::set_path(const char* value, size_t size) {
+
+ path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.path)
+}
+ ::std::string* Cookie::mutable_path() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.path)
+ return path_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* Cookie::release_path() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.path)
+
+ return path_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void Cookie::set_allocated_path(::std::string* path) {
+ if (path != NULL) {
+
+ } else {
+
+ }
+ path_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), path);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.path)
+}
+
+// optional int64 creation_time_us = 5;
+void Cookie::clear_creation_time_us() {
+ creation_time_us_ = GOOGLE_LONGLONG(0);
+}
+ ::google::protobuf::int64 Cookie::creation_time_us() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.creation_time_us)
+ return creation_time_us_;
+}
+ void Cookie::set_creation_time_us(::google::protobuf::int64 value) {
+
+ creation_time_us_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.creation_time_us)
+}
+
+// optional int64 expiration_time_us = 6;
+void Cookie::clear_expiration_time_us() {
+ expiration_time_us_ = GOOGLE_LONGLONG(0);
+}
+ ::google::protobuf::int64 Cookie::expiration_time_us() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.expiration_time_us)
+ return expiration_time_us_;
+}
+ void Cookie::set_expiration_time_us(::google::protobuf::int64 value) {
+
+ expiration_time_us_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.expiration_time_us)
+}
+
+// optional int64 last_access_time_us = 7;
+void Cookie::clear_last_access_time_us() {
+ last_access_time_us_ = GOOGLE_LONGLONG(0);
+}
+ ::google::protobuf::int64 Cookie::last_access_time_us() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.last_access_time_us)
+ return last_access_time_us_;
+}
+ void Cookie::set_last_access_time_us(::google::protobuf::int64 value) {
+
+ last_access_time_us_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.last_access_time_us)
+}
+
+// optional bool secure = 8;
+void Cookie::clear_secure() {
+ secure_ = false;
+}
+ bool Cookie::secure() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.secure)
+ return secure_;
+}
+ void Cookie::set_secure(bool value) {
+
+ secure_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.secure)
+}
+
+// optional bool http_only = 9;
+void Cookie::clear_http_only() {
+ http_only_ = false;
+}
+ bool Cookie::http_only() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.http_only)
+ return http_only_;
+}
+ void Cookie::set_http_only(bool value) {
+
+ http_only_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.http_only)
+}
+
+#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
+
+// ===================================================================
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int LocalStorageEntry::kKeyFieldNumber;
+const int LocalStorageEntry::kValueFieldNumber;
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+LocalStorageEntry::LocalStorageEntry()
+ : ::google::protobuf::MessageLite(), _arena_ptr_(NULL) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:cobalt.storage.LocalStorageEntry)
+}
+
+void LocalStorageEntry::InitAsDefaultInstance() {
+ _is_default_instance_ = true;
+}
+
+LocalStorageEntry::LocalStorageEntry(const LocalStorageEntry& from)
+ : ::google::protobuf::MessageLite(),
+ _arena_ptr_(NULL) {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:cobalt.storage.LocalStorageEntry)
+}
+
+void LocalStorageEntry::SharedCtor() {
+ _is_default_instance_ = false;
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ key_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+
+LocalStorageEntry::~LocalStorageEntry() {
+ // @@protoc_insertion_point(destructor:cobalt.storage.LocalStorageEntry)
+ SharedDtor();
+}
+
+void LocalStorageEntry::SharedDtor() {
+ key_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LocalStorageEntry::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LocalStorageEntry& LocalStorageEntry::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_storage_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_storage_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LocalStorageEntry* LocalStorageEntry::default_instance_ = NULL;
+
+LocalStorageEntry* LocalStorageEntry::New(::google::protobuf::Arena* arena) const {
+ LocalStorageEntry* n = new LocalStorageEntry;
+ if (arena != NULL) {
+ arena->Own(n);
+ }
+ return n;
+}
+
+void LocalStorageEntry::Clear() {
+// @@protoc_insertion_point(message_clear_start:cobalt.storage.LocalStorageEntry)
+ key_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+
+bool LocalStorageEntry::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ // @@protoc_insertion_point(parse_start:cobalt.storage.LocalStorageEntry)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional string key = 1;
+ case 1: {
+ if (tag == 10) {
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_key()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->key().data(), this->key().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "cobalt.storage.LocalStorageEntry.key"));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_value;
+ break;
+ }
+
+ // optional string value = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_value:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_value()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->value().data(), this->value().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "cobalt.storage.LocalStorageEntry.value"));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:cobalt.storage.LocalStorageEntry)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:cobalt.storage.LocalStorageEntry)
+ return false;
+#undef DO_
+}
+
+void LocalStorageEntry::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:cobalt.storage.LocalStorageEntry)
+ // optional string key = 1;
+ if (this->key().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->key().data(), this->key().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "cobalt.storage.LocalStorageEntry.key");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 1, this->key(), output);
+ }
+
+ // optional string value = 2;
+ if (this->value().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->value().data(), this->value().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "cobalt.storage.LocalStorageEntry.value");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 2, this->value(), output);
+ }
+
+ // @@protoc_insertion_point(serialize_end:cobalt.storage.LocalStorageEntry)
+}
+
+int LocalStorageEntry::ByteSize() const {
+// @@protoc_insertion_point(message_byte_size_start:cobalt.storage.LocalStorageEntry)
+ int total_size = 0;
+
+ // optional string key = 1;
+ if (this->key().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->key());
+ }
+
+ // optional string value = 2;
+ if (this->value().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->value());
+ }
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LocalStorageEntry::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LocalStorageEntry*>(&from));
+}
+
+void LocalStorageEntry::MergeFrom(const LocalStorageEntry& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:cobalt.storage.LocalStorageEntry)
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ if (from.key().size() > 0) {
+
+ key_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.key_);
+ }
+ if (from.value().size() > 0) {
+
+ value_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.value_);
+ }
+}
+
+void LocalStorageEntry::CopyFrom(const LocalStorageEntry& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:cobalt.storage.LocalStorageEntry)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LocalStorageEntry::IsInitialized() const {
+
+ return true;
+}
+
+void LocalStorageEntry::Swap(LocalStorageEntry* other) {
+ if (other == this) return;
+ InternalSwap(other);
+}
+void LocalStorageEntry::InternalSwap(LocalStorageEntry* other) {
+ key_.Swap(&other->key_);
+ value_.Swap(&other->value_);
+ _unknown_fields_.Swap(&other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+}
+
+::std::string LocalStorageEntry::GetTypeName() const {
+ return "cobalt.storage.LocalStorageEntry";
+}
+
+#if PROTOBUF_INLINE_NOT_IN_HEADERS
+// LocalStorageEntry
+
+// optional string key = 1;
+void LocalStorageEntry::clear_key() {
+ key_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& LocalStorageEntry::key() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorageEntry.key)
+ return key_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void LocalStorageEntry::set_key(const ::std::string& value) {
+
+ key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.LocalStorageEntry.key)
+}
+ void LocalStorageEntry::set_key(const char* value) {
+
+ key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.LocalStorageEntry.key)
+}
+ void LocalStorageEntry::set_key(const char* value, size_t size) {
+
+ key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.LocalStorageEntry.key)
+}
+ ::std::string* LocalStorageEntry::mutable_key() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorageEntry.key)
+ return key_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* LocalStorageEntry::release_key() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.LocalStorageEntry.key)
+
+ return key_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void LocalStorageEntry::set_allocated_key(::std::string* key) {
+ if (key != NULL) {
+
+ } else {
+
+ }
+ key_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), key);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.LocalStorageEntry.key)
+}
+
+// optional string value = 2;
+void LocalStorageEntry::clear_value() {
+ value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& LocalStorageEntry::value() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorageEntry.value)
+ return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void LocalStorageEntry::set_value(const ::std::string& value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.LocalStorageEntry.value)
+}
+ void LocalStorageEntry::set_value(const char* value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.LocalStorageEntry.value)
+}
+ void LocalStorageEntry::set_value(const char* value, size_t size) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.LocalStorageEntry.value)
+}
+ ::std::string* LocalStorageEntry::mutable_value() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorageEntry.value)
+ return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* LocalStorageEntry::release_value() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.LocalStorageEntry.value)
+
+ return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void LocalStorageEntry::set_allocated_value(::std::string* value) {
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.LocalStorageEntry.value)
+}
+
+#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
+
+// ===================================================================
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int LocalStorage::kIdFieldNumber;
+const int LocalStorage::kLocalStorageEntriesFieldNumber;
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+LocalStorage::LocalStorage()
+ : ::google::protobuf::MessageLite(), _arena_ptr_(NULL) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:cobalt.storage.LocalStorage)
+}
+
+void LocalStorage::InitAsDefaultInstance() {
+ _is_default_instance_ = true;
+}
+
+LocalStorage::LocalStorage(const LocalStorage& from)
+ : ::google::protobuf::MessageLite(),
+ _arena_ptr_(NULL) {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:cobalt.storage.LocalStorage)
+}
+
+void LocalStorage::SharedCtor() {
+ _is_default_instance_ = false;
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ id_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+
+LocalStorage::~LocalStorage() {
+ // @@protoc_insertion_point(destructor:cobalt.storage.LocalStorage)
+ SharedDtor();
+}
+
+void LocalStorage::SharedDtor() {
+ id_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void LocalStorage::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const LocalStorage& LocalStorage::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_storage_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_storage_2eproto();
+#endif
+ return *default_instance_;
+}
+
+LocalStorage* LocalStorage::default_instance_ = NULL;
+
+LocalStorage* LocalStorage::New(::google::protobuf::Arena* arena) const {
+ LocalStorage* n = new LocalStorage;
+ if (arena != NULL) {
+ arena->Own(n);
+ }
+ return n;
+}
+
+void LocalStorage::Clear() {
+// @@protoc_insertion_point(message_clear_start:cobalt.storage.LocalStorage)
+ id_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ local_storage_entries_.Clear();
+}
+
+bool LocalStorage::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ // @@protoc_insertion_point(parse_start:cobalt.storage.LocalStorage)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // optional string id = 1;
+ case 1: {
+ if (tag == 10) {
+ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+ input, this->mutable_id()));
+ DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->id().data(), this->id().length(),
+ ::google::protobuf::internal::WireFormatLite::PARSE,
+ "cobalt.storage.LocalStorage.id"));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_local_storage_entries;
+ break;
+ }
+
+ // repeated .cobalt.storage.LocalStorageEntry local_storage_entries = 2;
+ case 2: {
+ if (tag == 18) {
+ parse_local_storage_entries:
+ DO_(input->IncrementRecursionDepth());
+ parse_loop_local_storage_entries:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth(
+ input, add_local_storage_entries()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_loop_local_storage_entries;
+ input->UnsafeDecrementRecursionDepth();
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:cobalt.storage.LocalStorage)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:cobalt.storage.LocalStorage)
+ return false;
+#undef DO_
+}
+
+void LocalStorage::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:cobalt.storage.LocalStorage)
+ // optional string id = 1;
+ if (this->id().size() > 0) {
+ ::google::protobuf::internal::WireFormatLite::VerifyUtf8String(
+ this->id().data(), this->id().length(),
+ ::google::protobuf::internal::WireFormatLite::SERIALIZE,
+ "cobalt.storage.LocalStorage.id");
+ ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased(
+ 1, this->id(), output);
+ }
+
+ // repeated .cobalt.storage.LocalStorageEntry local_storage_entries = 2;
+ for (unsigned int i = 0, n = this->local_storage_entries_size(); i < n; i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->local_storage_entries(i), output);
+ }
+
+ // @@protoc_insertion_point(serialize_end:cobalt.storage.LocalStorage)
+}
+
+int LocalStorage::ByteSize() const {
+// @@protoc_insertion_point(message_byte_size_start:cobalt.storage.LocalStorage)
+ int total_size = 0;
+
+ // optional string id = 1;
+ if (this->id().size() > 0) {
+ total_size += 1 +
+ ::google::protobuf::internal::WireFormatLite::StringSize(
+ this->id());
+ }
+
+ // repeated .cobalt.storage.LocalStorageEntry local_storage_entries = 2;
+ total_size += 1 * this->local_storage_entries_size();
+ for (int i = 0; i < this->local_storage_entries_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->local_storage_entries(i));
+ }
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void LocalStorage::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const LocalStorage*>(&from));
+}
+
+void LocalStorage::MergeFrom(const LocalStorage& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:cobalt.storage.LocalStorage)
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ local_storage_entries_.MergeFrom(from.local_storage_entries_);
+ if (from.id().size() > 0) {
+
+ id_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.id_);
+ }
+}
+
+void LocalStorage::CopyFrom(const LocalStorage& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:cobalt.storage.LocalStorage)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool LocalStorage::IsInitialized() const {
+
+ return true;
+}
+
+void LocalStorage::Swap(LocalStorage* other) {
+ if (other == this) return;
+ InternalSwap(other);
+}
+void LocalStorage::InternalSwap(LocalStorage* other) {
+ id_.Swap(&other->id_);
+ local_storage_entries_.UnsafeArenaSwap(&other->local_storage_entries_);
+ _unknown_fields_.Swap(&other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+}
+
+::std::string LocalStorage::GetTypeName() const {
+ return "cobalt.storage.LocalStorage";
+}
+
+#if PROTOBUF_INLINE_NOT_IN_HEADERS
+// LocalStorage
+
+// optional string id = 1;
+void LocalStorage::clear_id() {
+ id_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ const ::std::string& LocalStorage::id() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorage.id)
+ return id_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void LocalStorage::set_id(const ::std::string& value) {
+
+ id_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.LocalStorage.id)
+}
+ void LocalStorage::set_id(const char* value) {
+
+ id_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.LocalStorage.id)
+}
+ void LocalStorage::set_id(const char* value, size_t size) {
+
+ id_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.LocalStorage.id)
+}
+ ::std::string* LocalStorage::mutable_id() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorage.id)
+ return id_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ ::std::string* LocalStorage::release_id() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.LocalStorage.id)
+
+ return id_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+ void LocalStorage::set_allocated_id(::std::string* id) {
+ if (id != NULL) {
+
+ } else {
+
+ }
+ id_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), id);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.LocalStorage.id)
+}
+
+// repeated .cobalt.storage.LocalStorageEntry local_storage_entries = 2;
+int LocalStorage::local_storage_entries_size() const {
+ return local_storage_entries_.size();
+}
+void LocalStorage::clear_local_storage_entries() {
+ local_storage_entries_.Clear();
+}
+const ::cobalt::storage::LocalStorageEntry& LocalStorage::local_storage_entries(int index) const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_.Get(index);
+}
+::cobalt::storage::LocalStorageEntry* LocalStorage::mutable_local_storage_entries(int index) {
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_.Mutable(index);
+}
+::cobalt::storage::LocalStorageEntry* LocalStorage::add_local_storage_entries() {
+ // @@protoc_insertion_point(field_add:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_.Add();
+}
+::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorageEntry >*
+LocalStorage::mutable_local_storage_entries() {
+ // @@protoc_insertion_point(field_mutable_list:cobalt.storage.LocalStorage.local_storage_entries)
+ return &local_storage_entries_;
+}
+const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorageEntry >&
+LocalStorage::local_storage_entries() const {
+ // @@protoc_insertion_point(field_list:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_;
+}
+
+#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
+
+// ===================================================================
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+const int Storage::kCookiesFieldNumber;
+const int Storage::kLocalStoragesFieldNumber;
+#endif // !defined(_MSC_VER) || _MSC_VER >= 1900
+
+Storage::Storage()
+ : ::google::protobuf::MessageLite(), _arena_ptr_(NULL) {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:cobalt.storage.Storage)
+}
+
+void Storage::InitAsDefaultInstance() {
+ _is_default_instance_ = true;
+}
+
+Storage::Storage(const Storage& from)
+ : ::google::protobuf::MessageLite(),
+ _arena_ptr_(NULL) {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:cobalt.storage.Storage)
+}
+
+void Storage::SharedCtor() {
+ _is_default_instance_ = false;
+ _cached_size_ = 0;
+}
+
+Storage::~Storage() {
+ // @@protoc_insertion_point(destructor:cobalt.storage.Storage)
+ SharedDtor();
+}
+
+void Storage::SharedDtor() {
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ if (this != &default_instance()) {
+ #else
+ if (this != default_instance_) {
+ #endif
+ }
+}
+
+void Storage::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const Storage& Storage::default_instance() {
+#ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ protobuf_AddDesc_storage_2eproto();
+#else
+ if (default_instance_ == NULL) protobuf_AddDesc_storage_2eproto();
+#endif
+ return *default_instance_;
+}
+
+Storage* Storage::default_instance_ = NULL;
+
+Storage* Storage::New(::google::protobuf::Arena* arena) const {
+ Storage* n = new Storage;
+ if (arena != NULL) {
+ arena->Own(n);
+ }
+ return n;
+}
+
+void Storage::Clear() {
+// @@protoc_insertion_point(message_clear_start:cobalt.storage.Storage)
+ cookies_.Clear();
+ local_storages_.Clear();
+}
+
+bool Storage::MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input) {
+#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure
+ ::google::protobuf::uint32 tag;
+ // @@protoc_insertion_point(parse_start:cobalt.storage.Storage)
+ for (;;) {
+ ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127);
+ tag = p.first;
+ if (!p.second) goto handle_unusual;
+ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+ // repeated .cobalt.storage.Cookie cookies = 1;
+ case 1: {
+ if (tag == 10) {
+ DO_(input->IncrementRecursionDepth());
+ parse_loop_cookies:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth(
+ input, add_cookies()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(10)) goto parse_loop_cookies;
+ if (input->ExpectTag(18)) goto parse_loop_local_storages;
+ input->UnsafeDecrementRecursionDepth();
+ break;
+ }
+
+ // repeated .cobalt.storage.LocalStorage local_storages = 2;
+ case 2: {
+ if (tag == 18) {
+ DO_(input->IncrementRecursionDepth());
+ parse_loop_local_storages:
+ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth(
+ input, add_local_storages()));
+ } else {
+ goto handle_unusual;
+ }
+ if (input->ExpectTag(18)) goto parse_loop_local_storages;
+ input->UnsafeDecrementRecursionDepth();
+ if (input->ExpectAtEnd()) goto success;
+ break;
+ }
+
+ default: {
+ handle_unusual:
+ if (tag == 0 ||
+ ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+ goto success;
+ }
+ DO_(::google::protobuf::internal::WireFormatLite::SkipField(input, tag));
+ break;
+ }
+ }
+ }
+success:
+ // @@protoc_insertion_point(parse_success:cobalt.storage.Storage)
+ return true;
+failure:
+ // @@protoc_insertion_point(parse_failure:cobalt.storage.Storage)
+ return false;
+#undef DO_
+}
+
+void Storage::SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const {
+ // @@protoc_insertion_point(serialize_start:cobalt.storage.Storage)
+ // repeated .cobalt.storage.Cookie cookies = 1;
+ for (unsigned int i = 0, n = this->cookies_size(); i < n; i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 1, this->cookies(i), output);
+ }
+
+ // repeated .cobalt.storage.LocalStorage local_storages = 2;
+ for (unsigned int i = 0, n = this->local_storages_size(); i < n; i++) {
+ ::google::protobuf::internal::WireFormatLite::WriteMessage(
+ 2, this->local_storages(i), output);
+ }
+
+ // @@protoc_insertion_point(serialize_end:cobalt.storage.Storage)
+}
+
+int Storage::ByteSize() const {
+// @@protoc_insertion_point(message_byte_size_start:cobalt.storage.Storage)
+ int total_size = 0;
+
+ // repeated .cobalt.storage.Cookie cookies = 1;
+ total_size += 1 * this->cookies_size();
+ for (int i = 0; i < this->cookies_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->cookies(i));
+ }
+
+ // repeated .cobalt.storage.LocalStorage local_storages = 2;
+ total_size += 1 * this->local_storages_size();
+ for (int i = 0; i < this->local_storages_size(); i++) {
+ total_size +=
+ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+ this->local_storages(i));
+ }
+
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = total_size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+ return total_size;
+}
+
+void Storage::CheckTypeAndMergeFrom(
+ const ::google::protobuf::MessageLite& from) {
+ MergeFrom(*::google::protobuf::down_cast<const Storage*>(&from));
+}
+
+void Storage::MergeFrom(const Storage& from) {
+// @@protoc_insertion_point(class_specific_merge_from_start:cobalt.storage.Storage)
+ if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__);
+ cookies_.MergeFrom(from.cookies_);
+ local_storages_.MergeFrom(from.local_storages_);
+}
+
+void Storage::CopyFrom(const Storage& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:cobalt.storage.Storage)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Storage::IsInitialized() const {
+
+ return true;
+}
+
+void Storage::Swap(Storage* other) {
+ if (other == this) return;
+ InternalSwap(other);
+}
+void Storage::InternalSwap(Storage* other) {
+ cookies_.UnsafeArenaSwap(&other->cookies_);
+ local_storages_.UnsafeArenaSwap(&other->local_storages_);
+ _unknown_fields_.Swap(&other->_unknown_fields_);
+ std::swap(_cached_size_, other->_cached_size_);
+}
+
+::std::string Storage::GetTypeName() const {
+ return "cobalt.storage.Storage";
+}
+
+#if PROTOBUF_INLINE_NOT_IN_HEADERS
+// Storage
+
+// repeated .cobalt.storage.Cookie cookies = 1;
+int Storage::cookies_size() const {
+ return cookies_.size();
+}
+void Storage::clear_cookies() {
+ cookies_.Clear();
+}
+const ::cobalt::storage::Cookie& Storage::cookies(int index) const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Storage.cookies)
+ return cookies_.Get(index);
+}
+::cobalt::storage::Cookie* Storage::mutable_cookies(int index) {
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Storage.cookies)
+ return cookies_.Mutable(index);
+}
+::cobalt::storage::Cookie* Storage::add_cookies() {
+ // @@protoc_insertion_point(field_add:cobalt.storage.Storage.cookies)
+ return cookies_.Add();
+}
+::google::protobuf::RepeatedPtrField< ::cobalt::storage::Cookie >*
+Storage::mutable_cookies() {
+ // @@protoc_insertion_point(field_mutable_list:cobalt.storage.Storage.cookies)
+ return &cookies_;
+}
+const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::Cookie >&
+Storage::cookies() const {
+ // @@protoc_insertion_point(field_list:cobalt.storage.Storage.cookies)
+ return cookies_;
+}
+
+// repeated .cobalt.storage.LocalStorage local_storages = 2;
+int Storage::local_storages_size() const {
+ return local_storages_.size();
+}
+void Storage::clear_local_storages() {
+ local_storages_.Clear();
+}
+const ::cobalt::storage::LocalStorage& Storage::local_storages(int index) const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Storage.local_storages)
+ return local_storages_.Get(index);
+}
+::cobalt::storage::LocalStorage* Storage::mutable_local_storages(int index) {
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Storage.local_storages)
+ return local_storages_.Mutable(index);
+}
+::cobalt::storage::LocalStorage* Storage::add_local_storages() {
+ // @@protoc_insertion_point(field_add:cobalt.storage.Storage.local_storages)
+ return local_storages_.Add();
+}
+::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorage >*
+Storage::mutable_local_storages() {
+ // @@protoc_insertion_point(field_mutable_list:cobalt.storage.Storage.local_storages)
+ return &local_storages_;
+}
+const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorage >&
+Storage::local_storages() const {
+ // @@protoc_insertion_point(field_list:cobalt.storage.Storage.local_storages)
+ return local_storages_;
+}
+
+#endif // PROTOBUF_INLINE_NOT_IN_HEADERS
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace storage
+} // namespace cobalt
+
+// @@protoc_insertion_point(global_scope)
diff --git a/src/cobalt/storage/store/storage.pb.h b/src/cobalt/storage/store/storage.pb.h
new file mode 100644
index 0000000..fe0a29a
--- /dev/null
+++ b/src/cobalt/storage/store/storage.pb.h
@@ -0,0 +1,1044 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: storage.proto
+
+#ifndef PROTOBUF_storage_2eproto__INCLUDED
+#define PROTOBUF_storage_2eproto__INCLUDED
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+#if GOOGLE_PROTOBUF_VERSION < 3000000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3000000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/extension_set.h>
+// @@protoc_insertion_point(includes)
+
+namespace cobalt {
+namespace storage {
+
+// Internal implementation detail -- do not call these.
+void protobuf_AddDesc_storage_2eproto();
+void protobuf_AssignDesc_storage_2eproto();
+void protobuf_ShutdownFile_storage_2eproto();
+
+class Cookie;
+class LocalStorage;
+class LocalStorageEntry;
+class Storage;
+
+// ===================================================================
+
+class Cookie : public ::google::protobuf::MessageLite {
+ public:
+ Cookie();
+ virtual ~Cookie();
+
+ Cookie(const Cookie& from);
+
+ inline Cookie& operator=(const Cookie& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ static const Cookie& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const Cookie* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ GOOGLE_ATTRIBUTE_NOINLINE void Swap(Cookie* other);
+
+ // implements Message ----------------------------------------------
+
+ inline Cookie* New() const { return New(NULL); }
+
+ Cookie* New(::google::protobuf::Arena* arena) const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const Cookie& from);
+ void MergeFrom(const Cookie& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Cookie* other);
+ private:
+ inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+ return _arena_ptr_;
+ }
+ inline ::google::protobuf::Arena* MaybeArenaPtr() const {
+ return _arena_ptr_;
+ }
+ public:
+
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional string name = 1;
+ void clear_name();
+ static const int kNameFieldNumber = 1;
+ const ::std::string& name() const;
+ void set_name(const ::std::string& value);
+ void set_name(const char* value);
+ void set_name(const char* value, size_t size);
+ ::std::string* mutable_name();
+ ::std::string* release_name();
+ void set_allocated_name(::std::string* name);
+
+ // optional string value = 2;
+ void clear_value();
+ static const int kValueFieldNumber = 2;
+ const ::std::string& value() const;
+ void set_value(const ::std::string& value);
+ void set_value(const char* value);
+ void set_value(const char* value, size_t size);
+ ::std::string* mutable_value();
+ ::std::string* release_value();
+ void set_allocated_value(::std::string* value);
+
+ // optional string domain = 3;
+ void clear_domain();
+ static const int kDomainFieldNumber = 3;
+ const ::std::string& domain() const;
+ void set_domain(const ::std::string& value);
+ void set_domain(const char* value);
+ void set_domain(const char* value, size_t size);
+ ::std::string* mutable_domain();
+ ::std::string* release_domain();
+ void set_allocated_domain(::std::string* domain);
+
+ // optional string path = 4;
+ void clear_path();
+ static const int kPathFieldNumber = 4;
+ const ::std::string& path() const;
+ void set_path(const ::std::string& value);
+ void set_path(const char* value);
+ void set_path(const char* value, size_t size);
+ ::std::string* mutable_path();
+ ::std::string* release_path();
+ void set_allocated_path(::std::string* path);
+
+ // optional int64 creation_time_us = 5;
+ void clear_creation_time_us();
+ static const int kCreationTimeUsFieldNumber = 5;
+ ::google::protobuf::int64 creation_time_us() const;
+ void set_creation_time_us(::google::protobuf::int64 value);
+
+ // optional int64 expiration_time_us = 6;
+ void clear_expiration_time_us();
+ static const int kExpirationTimeUsFieldNumber = 6;
+ ::google::protobuf::int64 expiration_time_us() const;
+ void set_expiration_time_us(::google::protobuf::int64 value);
+
+ // optional int64 last_access_time_us = 7;
+ void clear_last_access_time_us();
+ static const int kLastAccessTimeUsFieldNumber = 7;
+ ::google::protobuf::int64 last_access_time_us() const;
+ void set_last_access_time_us(::google::protobuf::int64 value);
+
+ // optional bool secure = 8;
+ void clear_secure();
+ static const int kSecureFieldNumber = 8;
+ bool secure() const;
+ void set_secure(bool value);
+
+ // optional bool http_only = 9;
+ void clear_http_only();
+ static const int kHttpOnlyFieldNumber = 9;
+ bool http_only() const;
+ void set_http_only(bool value);
+
+ // @@protoc_insertion_point(class_scope:cobalt.storage.Cookie)
+ private:
+
+ ::google::protobuf::internal::ArenaStringPtr _unknown_fields_;
+ ::google::protobuf::Arena* _arena_ptr_;
+
+ bool _is_default_instance_;
+ ::google::protobuf::internal::ArenaStringPtr name_;
+ ::google::protobuf::internal::ArenaStringPtr value_;
+ ::google::protobuf::internal::ArenaStringPtr domain_;
+ ::google::protobuf::internal::ArenaStringPtr path_;
+ ::google::protobuf::int64 creation_time_us_;
+ ::google::protobuf::int64 expiration_time_us_;
+ ::google::protobuf::int64 last_access_time_us_;
+ bool secure_;
+ bool http_only_;
+ mutable int _cached_size_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_storage_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_storage_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_storage_2eproto();
+ friend void protobuf_ShutdownFile_storage_2eproto();
+
+ void InitAsDefaultInstance();
+ static Cookie* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LocalStorageEntry : public ::google::protobuf::MessageLite {
+ public:
+ LocalStorageEntry();
+ virtual ~LocalStorageEntry();
+
+ LocalStorageEntry(const LocalStorageEntry& from);
+
+ inline LocalStorageEntry& operator=(const LocalStorageEntry& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ static const LocalStorageEntry& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LocalStorageEntry* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ GOOGLE_ATTRIBUTE_NOINLINE void Swap(LocalStorageEntry* other);
+
+ // implements Message ----------------------------------------------
+
+ inline LocalStorageEntry* New() const { return New(NULL); }
+
+ LocalStorageEntry* New(::google::protobuf::Arena* arena) const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LocalStorageEntry& from);
+ void MergeFrom(const LocalStorageEntry& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(LocalStorageEntry* other);
+ private:
+ inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+ return _arena_ptr_;
+ }
+ inline ::google::protobuf::Arena* MaybeArenaPtr() const {
+ return _arena_ptr_;
+ }
+ public:
+
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional string key = 1;
+ void clear_key();
+ static const int kKeyFieldNumber = 1;
+ const ::std::string& key() const;
+ void set_key(const ::std::string& value);
+ void set_key(const char* value);
+ void set_key(const char* value, size_t size);
+ ::std::string* mutable_key();
+ ::std::string* release_key();
+ void set_allocated_key(::std::string* key);
+
+ // optional string value = 2;
+ void clear_value();
+ static const int kValueFieldNumber = 2;
+ const ::std::string& value() const;
+ void set_value(const ::std::string& value);
+ void set_value(const char* value);
+ void set_value(const char* value, size_t size);
+ ::std::string* mutable_value();
+ ::std::string* release_value();
+ void set_allocated_value(::std::string* value);
+
+ // @@protoc_insertion_point(class_scope:cobalt.storage.LocalStorageEntry)
+ private:
+
+ ::google::protobuf::internal::ArenaStringPtr _unknown_fields_;
+ ::google::protobuf::Arena* _arena_ptr_;
+
+ bool _is_default_instance_;
+ ::google::protobuf::internal::ArenaStringPtr key_;
+ ::google::protobuf::internal::ArenaStringPtr value_;
+ mutable int _cached_size_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_storage_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_storage_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_storage_2eproto();
+ friend void protobuf_ShutdownFile_storage_2eproto();
+
+ void InitAsDefaultInstance();
+ static LocalStorageEntry* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class LocalStorage : public ::google::protobuf::MessageLite {
+ public:
+ LocalStorage();
+ virtual ~LocalStorage();
+
+ LocalStorage(const LocalStorage& from);
+
+ inline LocalStorage& operator=(const LocalStorage& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ static const LocalStorage& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const LocalStorage* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ GOOGLE_ATTRIBUTE_NOINLINE void Swap(LocalStorage* other);
+
+ // implements Message ----------------------------------------------
+
+ inline LocalStorage* New() const { return New(NULL); }
+
+ LocalStorage* New(::google::protobuf::Arena* arena) const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const LocalStorage& from);
+ void MergeFrom(const LocalStorage& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(LocalStorage* other);
+ private:
+ inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+ return _arena_ptr_;
+ }
+ inline ::google::protobuf::Arena* MaybeArenaPtr() const {
+ return _arena_ptr_;
+ }
+ public:
+
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // optional string id = 1;
+ void clear_id();
+ static const int kIdFieldNumber = 1;
+ const ::std::string& id() const;
+ void set_id(const ::std::string& value);
+ void set_id(const char* value);
+ void set_id(const char* value, size_t size);
+ ::std::string* mutable_id();
+ ::std::string* release_id();
+ void set_allocated_id(::std::string* id);
+
+ // repeated .cobalt.storage.LocalStorageEntry local_storage_entries = 2;
+ int local_storage_entries_size() const;
+ void clear_local_storage_entries();
+ static const int kLocalStorageEntriesFieldNumber = 2;
+ const ::cobalt::storage::LocalStorageEntry& local_storage_entries(int index) const;
+ ::cobalt::storage::LocalStorageEntry* mutable_local_storage_entries(int index);
+ ::cobalt::storage::LocalStorageEntry* add_local_storage_entries();
+ ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorageEntry >*
+ mutable_local_storage_entries();
+ const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorageEntry >&
+ local_storage_entries() const;
+
+ // @@protoc_insertion_point(class_scope:cobalt.storage.LocalStorage)
+ private:
+
+ ::google::protobuf::internal::ArenaStringPtr _unknown_fields_;
+ ::google::protobuf::Arena* _arena_ptr_;
+
+ bool _is_default_instance_;
+ ::google::protobuf::internal::ArenaStringPtr id_;
+ ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorageEntry > local_storage_entries_;
+ mutable int _cached_size_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_storage_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_storage_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_storage_2eproto();
+ friend void protobuf_ShutdownFile_storage_2eproto();
+
+ void InitAsDefaultInstance();
+ static LocalStorage* default_instance_;
+};
+// -------------------------------------------------------------------
+
+class Storage : public ::google::protobuf::MessageLite {
+ public:
+ Storage();
+ virtual ~Storage();
+
+ Storage(const Storage& from);
+
+ inline Storage& operator=(const Storage& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ static const Storage& default_instance();
+
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ // Returns the internal default instance pointer. This function can
+ // return NULL thus should not be used by the user. This is intended
+ // for Protobuf internal code. Please use default_instance() declared
+ // above instead.
+ static inline const Storage* internal_default_instance() {
+ return default_instance_;
+ }
+ #endif
+
+ GOOGLE_ATTRIBUTE_NOINLINE void Swap(Storage* other);
+
+ // implements Message ----------------------------------------------
+
+ inline Storage* New() const { return New(NULL); }
+
+ Storage* New(::google::protobuf::Arena* arena) const;
+ void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
+ void CopyFrom(const Storage& from);
+ void MergeFrom(const Storage& from);
+ void Clear();
+ bool IsInitialized() const;
+
+ int ByteSize() const;
+ bool MergePartialFromCodedStream(
+ ::google::protobuf::io::CodedInputStream* input);
+ void SerializeWithCachedSizes(
+ ::google::protobuf::io::CodedOutputStream* output) const;
+ void DiscardUnknownFields();
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Storage* other);
+ private:
+ inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
+ return _arena_ptr_;
+ }
+ inline ::google::protobuf::Arena* MaybeArenaPtr() const {
+ return _arena_ptr_;
+ }
+ public:
+
+ ::std::string GetTypeName() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // repeated .cobalt.storage.Cookie cookies = 1;
+ int cookies_size() const;
+ void clear_cookies();
+ static const int kCookiesFieldNumber = 1;
+ const ::cobalt::storage::Cookie& cookies(int index) const;
+ ::cobalt::storage::Cookie* mutable_cookies(int index);
+ ::cobalt::storage::Cookie* add_cookies();
+ ::google::protobuf::RepeatedPtrField< ::cobalt::storage::Cookie >*
+ mutable_cookies();
+ const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::Cookie >&
+ cookies() const;
+
+ // repeated .cobalt.storage.LocalStorage local_storages = 2;
+ int local_storages_size() const;
+ void clear_local_storages();
+ static const int kLocalStoragesFieldNumber = 2;
+ const ::cobalt::storage::LocalStorage& local_storages(int index) const;
+ ::cobalt::storage::LocalStorage* mutable_local_storages(int index);
+ ::cobalt::storage::LocalStorage* add_local_storages();
+ ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorage >*
+ mutable_local_storages();
+ const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorage >&
+ local_storages() const;
+
+ // @@protoc_insertion_point(class_scope:cobalt.storage.Storage)
+ private:
+
+ ::google::protobuf::internal::ArenaStringPtr _unknown_fields_;
+ ::google::protobuf::Arena* _arena_ptr_;
+
+ bool _is_default_instance_;
+ ::google::protobuf::RepeatedPtrField< ::cobalt::storage::Cookie > cookies_;
+ ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorage > local_storages_;
+ mutable int _cached_size_;
+ #ifdef GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
+ friend void protobuf_AddDesc_storage_2eproto_impl();
+ #else
+ friend void protobuf_AddDesc_storage_2eproto();
+ #endif
+ friend void protobuf_AssignDesc_storage_2eproto();
+ friend void protobuf_ShutdownFile_storage_2eproto();
+
+ void InitAsDefaultInstance();
+ static Storage* default_instance_;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#if !PROTOBUF_INLINE_NOT_IN_HEADERS
+// Cookie
+
+// optional string name = 1;
+inline void Cookie::clear_name() {
+ name_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& Cookie::name() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.name)
+ return name_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_name(const ::std::string& value) {
+
+ name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.name)
+}
+inline void Cookie::set_name(const char* value) {
+
+ name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.name)
+}
+inline void Cookie::set_name(const char* value, size_t size) {
+
+ name_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.name)
+}
+inline ::std::string* Cookie::mutable_name() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.name)
+ return name_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* Cookie::release_name() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.name)
+
+ return name_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_allocated_name(::std::string* name) {
+ if (name != NULL) {
+
+ } else {
+
+ }
+ name_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), name);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.name)
+}
+
+// optional string value = 2;
+inline void Cookie::clear_value() {
+ value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& Cookie::value() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.value)
+ return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_value(const ::std::string& value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.value)
+}
+inline void Cookie::set_value(const char* value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.value)
+}
+inline void Cookie::set_value(const char* value, size_t size) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.value)
+}
+inline ::std::string* Cookie::mutable_value() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.value)
+ return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* Cookie::release_value() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.value)
+
+ return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_allocated_value(::std::string* value) {
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.value)
+}
+
+// optional string domain = 3;
+inline void Cookie::clear_domain() {
+ domain_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& Cookie::domain() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.domain)
+ return domain_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_domain(const ::std::string& value) {
+
+ domain_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.domain)
+}
+inline void Cookie::set_domain(const char* value) {
+
+ domain_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.domain)
+}
+inline void Cookie::set_domain(const char* value, size_t size) {
+
+ domain_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.domain)
+}
+inline ::std::string* Cookie::mutable_domain() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.domain)
+ return domain_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* Cookie::release_domain() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.domain)
+
+ return domain_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_allocated_domain(::std::string* domain) {
+ if (domain != NULL) {
+
+ } else {
+
+ }
+ domain_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), domain);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.domain)
+}
+
+// optional string path = 4;
+inline void Cookie::clear_path() {
+ path_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& Cookie::path() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.path)
+ return path_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_path(const ::std::string& value) {
+
+ path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.path)
+}
+inline void Cookie::set_path(const char* value) {
+
+ path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.Cookie.path)
+}
+inline void Cookie::set_path(const char* value, size_t size) {
+
+ path_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.Cookie.path)
+}
+inline ::std::string* Cookie::mutable_path() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Cookie.path)
+ return path_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* Cookie::release_path() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.Cookie.path)
+
+ return path_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void Cookie::set_allocated_path(::std::string* path) {
+ if (path != NULL) {
+
+ } else {
+
+ }
+ path_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), path);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.Cookie.path)
+}
+
+// optional int64 creation_time_us = 5;
+inline void Cookie::clear_creation_time_us() {
+ creation_time_us_ = GOOGLE_LONGLONG(0);
+}
+inline ::google::protobuf::int64 Cookie::creation_time_us() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.creation_time_us)
+ return creation_time_us_;
+}
+inline void Cookie::set_creation_time_us(::google::protobuf::int64 value) {
+
+ creation_time_us_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.creation_time_us)
+}
+
+// optional int64 expiration_time_us = 6;
+inline void Cookie::clear_expiration_time_us() {
+ expiration_time_us_ = GOOGLE_LONGLONG(0);
+}
+inline ::google::protobuf::int64 Cookie::expiration_time_us() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.expiration_time_us)
+ return expiration_time_us_;
+}
+inline void Cookie::set_expiration_time_us(::google::protobuf::int64 value) {
+
+ expiration_time_us_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.expiration_time_us)
+}
+
+// optional int64 last_access_time_us = 7;
+inline void Cookie::clear_last_access_time_us() {
+ last_access_time_us_ = GOOGLE_LONGLONG(0);
+}
+inline ::google::protobuf::int64 Cookie::last_access_time_us() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.last_access_time_us)
+ return last_access_time_us_;
+}
+inline void Cookie::set_last_access_time_us(::google::protobuf::int64 value) {
+
+ last_access_time_us_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.last_access_time_us)
+}
+
+// optional bool secure = 8;
+inline void Cookie::clear_secure() {
+ secure_ = false;
+}
+inline bool Cookie::secure() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.secure)
+ return secure_;
+}
+inline void Cookie::set_secure(bool value) {
+
+ secure_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.secure)
+}
+
+// optional bool http_only = 9;
+inline void Cookie::clear_http_only() {
+ http_only_ = false;
+}
+inline bool Cookie::http_only() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Cookie.http_only)
+ return http_only_;
+}
+inline void Cookie::set_http_only(bool value) {
+
+ http_only_ = value;
+ // @@protoc_insertion_point(field_set:cobalt.storage.Cookie.http_only)
+}
+
+// -------------------------------------------------------------------
+
+// LocalStorageEntry
+
+// optional string key = 1;
+inline void LocalStorageEntry::clear_key() {
+ key_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& LocalStorageEntry::key() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorageEntry.key)
+ return key_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void LocalStorageEntry::set_key(const ::std::string& value) {
+
+ key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.LocalStorageEntry.key)
+}
+inline void LocalStorageEntry::set_key(const char* value) {
+
+ key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.LocalStorageEntry.key)
+}
+inline void LocalStorageEntry::set_key(const char* value, size_t size) {
+
+ key_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.LocalStorageEntry.key)
+}
+inline ::std::string* LocalStorageEntry::mutable_key() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorageEntry.key)
+ return key_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* LocalStorageEntry::release_key() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.LocalStorageEntry.key)
+
+ return key_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void LocalStorageEntry::set_allocated_key(::std::string* key) {
+ if (key != NULL) {
+
+ } else {
+
+ }
+ key_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), key);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.LocalStorageEntry.key)
+}
+
+// optional string value = 2;
+inline void LocalStorageEntry::clear_value() {
+ value_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& LocalStorageEntry::value() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorageEntry.value)
+ return value_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void LocalStorageEntry::set_value(const ::std::string& value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.LocalStorageEntry.value)
+}
+inline void LocalStorageEntry::set_value(const char* value) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.LocalStorageEntry.value)
+}
+inline void LocalStorageEntry::set_value(const char* value, size_t size) {
+
+ value_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.LocalStorageEntry.value)
+}
+inline ::std::string* LocalStorageEntry::mutable_value() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorageEntry.value)
+ return value_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* LocalStorageEntry::release_value() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.LocalStorageEntry.value)
+
+ return value_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void LocalStorageEntry::set_allocated_value(::std::string* value) {
+ if (value != NULL) {
+
+ } else {
+
+ }
+ value_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.LocalStorageEntry.value)
+}
+
+// -------------------------------------------------------------------
+
+// LocalStorage
+
+// optional string id = 1;
+inline void LocalStorage::clear_id() {
+ id_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline const ::std::string& LocalStorage::id() const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorage.id)
+ return id_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void LocalStorage::set_id(const ::std::string& value) {
+
+ id_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value);
+ // @@protoc_insertion_point(field_set:cobalt.storage.LocalStorage.id)
+}
+inline void LocalStorage::set_id(const char* value) {
+
+ id_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value));
+ // @@protoc_insertion_point(field_set_char:cobalt.storage.LocalStorage.id)
+}
+inline void LocalStorage::set_id(const char* value, size_t size) {
+
+ id_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(),
+ ::std::string(reinterpret_cast<const char*>(value), size));
+ // @@protoc_insertion_point(field_set_pointer:cobalt.storage.LocalStorage.id)
+}
+inline ::std::string* LocalStorage::mutable_id() {
+
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorage.id)
+ return id_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline ::std::string* LocalStorage::release_id() {
+ // @@protoc_insertion_point(field_release:cobalt.storage.LocalStorage.id)
+
+ return id_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+}
+inline void LocalStorage::set_allocated_id(::std::string* id) {
+ if (id != NULL) {
+
+ } else {
+
+ }
+ id_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), id);
+ // @@protoc_insertion_point(field_set_allocated:cobalt.storage.LocalStorage.id)
+}
+
+// repeated .cobalt.storage.LocalStorageEntry local_storage_entries = 2;
+inline int LocalStorage::local_storage_entries_size() const {
+ return local_storage_entries_.size();
+}
+inline void LocalStorage::clear_local_storage_entries() {
+ local_storage_entries_.Clear();
+}
+inline const ::cobalt::storage::LocalStorageEntry& LocalStorage::local_storage_entries(int index) const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_.Get(index);
+}
+inline ::cobalt::storage::LocalStorageEntry* LocalStorage::mutable_local_storage_entries(int index) {
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_.Mutable(index);
+}
+inline ::cobalt::storage::LocalStorageEntry* LocalStorage::add_local_storage_entries() {
+ // @@protoc_insertion_point(field_add:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_.Add();
+}
+inline ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorageEntry >*
+LocalStorage::mutable_local_storage_entries() {
+ // @@protoc_insertion_point(field_mutable_list:cobalt.storage.LocalStorage.local_storage_entries)
+ return &local_storage_entries_;
+}
+inline const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorageEntry >&
+LocalStorage::local_storage_entries() const {
+ // @@protoc_insertion_point(field_list:cobalt.storage.LocalStorage.local_storage_entries)
+ return local_storage_entries_;
+}
+
+// -------------------------------------------------------------------
+
+// Storage
+
+// repeated .cobalt.storage.Cookie cookies = 1;
+inline int Storage::cookies_size() const {
+ return cookies_.size();
+}
+inline void Storage::clear_cookies() {
+ cookies_.Clear();
+}
+inline const ::cobalt::storage::Cookie& Storage::cookies(int index) const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Storage.cookies)
+ return cookies_.Get(index);
+}
+inline ::cobalt::storage::Cookie* Storage::mutable_cookies(int index) {
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Storage.cookies)
+ return cookies_.Mutable(index);
+}
+inline ::cobalt::storage::Cookie* Storage::add_cookies() {
+ // @@protoc_insertion_point(field_add:cobalt.storage.Storage.cookies)
+ return cookies_.Add();
+}
+inline ::google::protobuf::RepeatedPtrField< ::cobalt::storage::Cookie >*
+Storage::mutable_cookies() {
+ // @@protoc_insertion_point(field_mutable_list:cobalt.storage.Storage.cookies)
+ return &cookies_;
+}
+inline const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::Cookie >&
+Storage::cookies() const {
+ // @@protoc_insertion_point(field_list:cobalt.storage.Storage.cookies)
+ return cookies_;
+}
+
+// repeated .cobalt.storage.LocalStorage local_storages = 2;
+inline int Storage::local_storages_size() const {
+ return local_storages_.size();
+}
+inline void Storage::clear_local_storages() {
+ local_storages_.Clear();
+}
+inline const ::cobalt::storage::LocalStorage& Storage::local_storages(int index) const {
+ // @@protoc_insertion_point(field_get:cobalt.storage.Storage.local_storages)
+ return local_storages_.Get(index);
+}
+inline ::cobalt::storage::LocalStorage* Storage::mutable_local_storages(int index) {
+ // @@protoc_insertion_point(field_mutable:cobalt.storage.Storage.local_storages)
+ return local_storages_.Mutable(index);
+}
+inline ::cobalt::storage::LocalStorage* Storage::add_local_storages() {
+ // @@protoc_insertion_point(field_add:cobalt.storage.Storage.local_storages)
+ return local_storages_.Add();
+}
+inline ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorage >*
+Storage::mutable_local_storages() {
+ // @@protoc_insertion_point(field_mutable_list:cobalt.storage.Storage.local_storages)
+ return &local_storages_;
+}
+inline const ::google::protobuf::RepeatedPtrField< ::cobalt::storage::LocalStorage >&
+Storage::local_storages() const {
+ // @@protoc_insertion_point(field_list:cobalt.storage.Storage.local_storages)
+ return local_storages_;
+}
+
+#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace storage
+} // namespace cobalt
+
+// @@protoc_insertion_point(global_scope)
+
+#endif // PROTOBUF_storage_2eproto__INCLUDED
diff --git a/src/cobalt/storage/store/storage.proto b/src/cobalt/storage/store/storage.proto
new file mode 100644
index 0000000..8ec28af
--- /dev/null
+++ b/src/cobalt/storage/store/storage.proto
@@ -0,0 +1,80 @@
+// Copyright 2018 Google Inc. 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.
+
+syntax = "proto3";
+
+option optimize_for = LITE_RUNTIME;
+option java_package = "dev.cobalt.storage";
+option java_outer_classname = "StorageProto";
+
+package cobalt.storage;
+
+// A single cookie representation.
+message Cookie {
+ // The name of the cookie.
+ string name = 1;
+
+ // The value of the cookie.
+ string value = 2;
+
+ // The domain of the url for which the cookie is store.
+ string domain = 3;
+
+ // The path of the url for which the cookie is stored.
+ string path = 4;
+
+ // The creation time for the cookie in microseconds since
+ // Windows epoch - 1/1/1601 UTC.
+ int64 creation_time_us = 5;
+
+ // The expiration time for the cookie in microseconds since
+ // Windows epoch - 1/1/1601 UTC.
+ int64 expiration_time_us = 6;
+
+ // The last access time in microseconds since
+ // Windows epoch - 1/1/1601 UTC.
+ int64 last_access_time_us = 7;
+
+ // Whether the cookie should be transmitted only over secure connection.
+ // Defaults to false.
+ bool secure = 8;
+
+ // Whether this is an HTTP-only cookie. Defaults to false.
+ bool http_only = 9;
+}
+
+// A single local storage entry.
+message LocalStorageEntry {
+ // The key for the local storage entry.
+ string key = 1;
+
+ // The value of the local storage entry.
+ string value = 2;
+}
+
+// Multiple local storages identified by unique id.
+message LocalStorage {
+ // A site id of the local storage.
+ string id = 1;
+ // The local storage entries for individual local storage.
+ repeated LocalStorageEntry local_storage_entries = 2;
+}
+
+// The full storage.
+message Storage {
+ // All the cookies.
+ repeated Cookie cookies = 1;
+ // All local storages.
+ repeated LocalStorage local_storages = 2;
+}
diff --git a/src/cobalt/storage/store/store.gyp b/src/cobalt/storage/store/store.gyp
new file mode 100644
index 0000000..ecc7b44
--- /dev/null
+++ b/src/cobalt/storage/store/store.gyp
@@ -0,0 +1,85 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'memory_store',
+ 'type': 'static_library',
+ 'sources': [
+ 'memory_store.h',
+ 'memory_store.cc',
+ 'storage.pb.cc',
+ 'storage.pb.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/storage/storage_constants.gyp:storage_constants',
+ '<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/net/net.gyp:net',
+ '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
+ ],
+ 'include_dirs': [
+ # Get protobuf headers from the chromium tree.
+ '<(DEPTH)/third_party/protobuf/src',
+ ],
+ 'defines': [
+ # The generated code needs to be compiled with the same flags as the protobuf library.
+ # Otherwise we get static initializers which are not thread safe.
+ # This macro must be defined to suppress the use of dynamic_cast<>,
+ # which requires RTTI.
+ 'GOOGLE_PROTOBUF_NO_RTTI',
+ 'GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER',
+ 'HAVE_PTHREAD',
+ ],
+ },
+ {
+ 'target_name': 'memory_store_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'memory_store_test.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ 'memory_store',
+ ],
+ 'include_dirs': [
+ # Get protobuf headers from the chromium tree.
+ '<(DEPTH)/third_party/protobuf/src',
+ ],
+ 'defines': [
+ # The generated code needs to be compiled with the same flags as the protobuf library.
+ # Otherwise we get static initializers which are not thread safe.
+ # This macro must be defined to suppress the use of dynamic_cast<>,
+ # which requires RTTI.
+ 'GOOGLE_PROTOBUF_NO_RTTI',
+ 'GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER',
+ 'HAVE_PTHREAD',
+ ],
+ },
+ {
+ 'target_name': 'memory_store_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'memory_store_test',
+ ],
+ 'variables': {
+ 'executable_name': 'memory_store_test',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/cobalt/storage/store_upgrade/sql_vfs.cc b/src/cobalt/storage/store_upgrade/sql_vfs.cc
new file mode 100644
index 0000000..72b4342
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/sql_vfs.cc
@@ -0,0 +1,260 @@
+// Copyright 2015 Google Inc. 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 "cobalt/storage/store_upgrade/sql_vfs.h"
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/string_util.h"
+#include "base/synchronization/lock.h"
+#include "cobalt/storage/store_upgrade/virtual_file.h"
+#include "cobalt/storage/store_upgrade/virtual_file_system.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+namespace {
+
+// A "subclass" of sqlite3_file with our required data structures added.
+struct virtual_file {
+ sqlite3_file sql_internal_file;
+ VirtualFile* file;
+ base::Lock* lock;
+ int current_lock;
+ int shared;
+};
+
+int VfsClose(sqlite3_file* file) {
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ delete vfile->lock;
+ return SQLITE_OK;
+}
+
+int VfsRead(sqlite3_file* file, void* out, int bytes, sqlite_int64 offset) {
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ vfile->file->Read(out, bytes, static_cast<int>(offset));
+ return SQLITE_OK;
+}
+
+int VfsWrite(sqlite3_file* file, const void* data, int bytes,
+ sqlite3_int64 offset) {
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ vfile->file->Write(data, bytes, static_cast<int>(offset));
+ return SQLITE_OK;
+}
+
+int VfsSync(sqlite3_file* pFile, int flags) {
+ UNREFERENCED_PARAMETER(pFile);
+ UNREFERENCED_PARAMETER(flags);
+ return SQLITE_OK;
+}
+
+int VfsFileControl(sqlite3_file* pFile, int op, void* pArg) {
+ UNREFERENCED_PARAMETER(pFile);
+ UNREFERENCED_PARAMETER(op);
+ UNREFERENCED_PARAMETER(pArg);
+ return SQLITE_OK;
+}
+
+int VfsSectorSize(sqlite3_file* file) {
+ // The number of bytes that can be read without disturbing other bytes in the
+ // file.
+ UNREFERENCED_PARAMETER(file);
+ return 1;
+}
+
+int VfsLock(sqlite3_file* file, const int mode) {
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ base::AutoLock lock(*vfile->lock);
+
+ // If there is already a lock of this type or more restrictive, do nothing
+ if (vfile->current_lock >= mode) {
+ return SQLITE_OK;
+ }
+
+ if (mode == SQLITE_LOCK_SHARED) {
+ DCHECK_EQ(vfile->current_lock, SQLITE_LOCK_NONE);
+ vfile->shared++;
+ vfile->current_lock = SQLITE_LOCK_SHARED;
+ }
+
+ if (mode == SQLITE_LOCK_RESERVED) {
+ DCHECK_EQ(vfile->current_lock, SQLITE_LOCK_SHARED);
+ vfile->current_lock = SQLITE_LOCK_RESERVED;
+ }
+
+ if (mode == SQLITE_LOCK_EXCLUSIVE) {
+ if (vfile->current_lock >= SQLITE_LOCK_PENDING) {
+ return SQLITE_BUSY;
+ }
+ vfile->current_lock = SQLITE_LOCK_PENDING;
+ if (vfile->shared > 1) {
+ // There are some outstanding shared locks (greater than one because the
+ // pending lock is an "upgraded" shared lock)
+ return SQLITE_BUSY;
+ }
+ // Acquire the exclusive lock
+ vfile->current_lock = SQLITE_LOCK_EXCLUSIVE;
+ }
+
+ return SQLITE_OK;
+}
+
+int VfsUnlock(sqlite3_file* file, int mode) {
+ DCHECK_LE(mode, SQLITE_LOCK_SHARED);
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ base::AutoLock lock(*vfile->lock);
+
+ COMPILE_ASSERT(SQLITE_LOCK_NONE < SQLITE_LOCK_SHARED,
+ sqlite_lock_constants_order_has_changed);
+ COMPILE_ASSERT(SQLITE_LOCK_SHARED < SQLITE_LOCK_RESERVED,
+ sqlite_lock_constants_order_has_changed);
+ COMPILE_ASSERT(SQLITE_LOCK_RESERVED < SQLITE_LOCK_PENDING,
+ sqlite_lock_constants_order_has_changed);
+ COMPILE_ASSERT(SQLITE_LOCK_PENDING < SQLITE_LOCK_EXCLUSIVE,
+ sqlite_lock_constants_order_has_changed);
+
+ if (mode == SQLITE_LOCK_NONE && vfile->current_lock >= SQLITE_LOCK_SHARED) {
+ vfile->shared--;
+ }
+
+ vfile->current_lock = mode;
+ return SQLITE_OK;
+}
+
+int VfsCheckReservedLock(sqlite3_file* file, int* result) {
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ base::AutoLock lock(*vfile->lock);
+
+ // The function expects a result is 1 if the lock is reserved, pending, or
+ // exclusive; 0 otherwise.
+ *result = vfile->current_lock >= SQLITE_LOCK_RESERVED ? 1 : 0;
+ return SQLITE_OK;
+}
+
+int VfsFileSize(sqlite3_file* file, sqlite3_int64* out_size) {
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ *out_size = vfile->file->Size();
+ return SQLITE_OK;
+}
+
+int VfsTruncate(sqlite3_file* file, sqlite3_int64 size) {
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ vfile->file->Truncate(static_cast<int>(size));
+ return SQLITE_OK;
+}
+
+int VfsDeviceCharacteristics(sqlite3_file* file) {
+ UNREFERENCED_PARAMETER(file);
+ return 0;
+}
+
+const sqlite3_io_methods s_cobalt_vfs_io = {
+ 1, // Structure version number
+ VfsClose, // xClose
+ VfsRead, // xRead
+ VfsWrite, // xWrite
+ VfsTruncate, // xTruncate
+ VfsSync, // xSync
+ VfsFileSize, // xFileSize
+ VfsLock, // xLock
+ VfsUnlock, // xUnlock
+ VfsCheckReservedLock, // xCheckReservedLock
+ VfsFileControl, // xFileControl
+ VfsSectorSize, // xSectorSize
+ VfsDeviceCharacteristics // xDeviceCharacteristics
+};
+
+int VfsOpen(sqlite3_vfs* sql_vfs, const char* path, sqlite3_file* file,
+ int flags, int* out_flags) {
+ UNREFERENCED_PARAMETER(flags);
+ UNREFERENCED_PARAMETER(out_flags);
+ DCHECK(path) << "NULL filename not supported.";
+ virtual_file* vfile = reinterpret_cast<virtual_file*>(file);
+ vfile->lock = new base::Lock;
+ file->pMethods = &s_cobalt_vfs_io;
+
+ VirtualFileSystem* vfs =
+ reinterpret_cast<VirtualFileSystem*>(sql_vfs->pAppData);
+ vfile->file = vfs->Open(path);
+ return SQLITE_OK;
+}
+
+int VfsDelete(sqlite3_vfs* sql_vfs, const char* path, int sync_dir) {
+ UNREFERENCED_PARAMETER(sync_dir);
+ VirtualFileSystem* vfs =
+ reinterpret_cast<VirtualFileSystem*>(sql_vfs->pAppData);
+ vfs->Delete(path);
+ return SQLITE_OK;
+}
+
+int VfsFullPathname(sqlite3_vfs* sql_vfs, const char* path, int out_size,
+ char* out_path) {
+ UNREFERENCED_PARAMETER(sql_vfs);
+ size_t path_size = static_cast<size_t>(out_size);
+ if (base::strlcpy(out_path, path, path_size) < path_size) {
+ return SQLITE_OK;
+ }
+ return SQLITE_ERROR;
+}
+
+int VfsAccess(sqlite3_vfs* sql_vfs, const char* name, int flags, int* result) {
+ UNREFERENCED_PARAMETER(name);
+ UNREFERENCED_PARAMETER(sql_vfs);
+ UNREFERENCED_PARAMETER(flags);
+ // We should always have a valid, readable/writable file.
+ *result |= SQLITE_ACCESS_EXISTS | SQLITE_ACCESS_READWRITE;
+ return SQLITE_OK;
+}
+
+int VfsRandomness(sqlite3_vfs* sql_vfs, int bytes, char* out) {
+ UNREFERENCED_PARAMETER(sql_vfs);
+ base::RandBytes(out, static_cast<size_t>(bytes));
+ return SQLITE_OK;
+}
+
+} // namespace
+
+SqlVfs::SqlVfs(const std::string& name, VirtualFileSystem* vfs)
+ : sql_vfs_(new sqlite3_vfs()) {
+ memset(sql_vfs_.get(), 0, sizeof(sqlite3_vfs));
+ sql_vfs_->iVersion = 1;
+ sql_vfs_->szOsFile = sizeof(virtual_file);
+ sql_vfs_->mxPathname = VirtualFile::kMaxVfsPathname;
+ sql_vfs_->pNext = NULL;
+ sql_vfs_->zName = name.c_str();
+ sql_vfs_->pAppData = vfs;
+ sql_vfs_->xOpen = VfsOpen;
+ sql_vfs_->xDelete = VfsDelete;
+ sql_vfs_->xAccess = VfsAccess;
+ sql_vfs_->xFullPathname = VfsFullPathname;
+ sql_vfs_->xRandomness = VfsRandomness;
+
+ // Ensure we are not registering multiple of these with the same name.
+ // Behavior is undefined in that case.
+ DCHECK(sqlite3_vfs_find(name.c_str()) == NULL);
+
+ int ret = sqlite3_vfs_register(sql_vfs_.get(), 1 /* make_default */);
+ DCHECK_EQ(ret, SQLITE_OK);
+}
+
+SqlVfs::~SqlVfs() {
+ int ret = sqlite3_vfs_unregister(sql_vfs_.get());
+ DCHECK_EQ(ret, SQLITE_OK);
+}
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/store_upgrade/sql_vfs.h b/src/cobalt/storage/store_upgrade/sql_vfs.h
new file mode 100644
index 0000000..4a0588c
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/sql_vfs.h
@@ -0,0 +1,45 @@
+// Copyright 2015 Google Inc. 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 COBALT_STORAGE_STORE_UPGRADE_SQL_VFS_H_
+#define COBALT_STORAGE_STORE_UPGRADE_SQL_VFS_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+struct sqlite3_vfs;
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+
+class VirtualFileSystem;
+
+// Implement the necessary APIs for a Sqlite virtual file system.
+// Dispatch calls to the owning VirtualFileSystem.
+class SqlVfs {
+ public:
+ SqlVfs(const std::string& name, VirtualFileSystem* vfs);
+ ~SqlVfs();
+
+ private:
+ scoped_ptr<sqlite3_vfs> sql_vfs_;
+};
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
+
+#endif // COBALT_STORAGE_STORE_UPGRADE_SQL_VFS_H_
diff --git a/src/cobalt/storage/store_upgrade/testdata/test.storage b/src/cobalt/storage/store_upgrade/testdata/test.storage
new file mode 100644
index 0000000..df2c4c8
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/testdata/test.storage
Binary files differ
diff --git a/src/cobalt/storage/store_upgrade/upgrade.cc b/src/cobalt/storage/store_upgrade/upgrade.cc
new file mode 100644
index 0000000..7318104
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/upgrade.cc
@@ -0,0 +1,210 @@
+// Copyright 2018 Google Inc. 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 "cobalt/storage/store_upgrade/upgrade.h"
+
+#include <map>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/file_path.h"
+#include "cobalt/storage/storage_constants.h"
+#include "cobalt/storage/store/storage.pb.h"
+#include "cobalt/storage/store_upgrade/sql_vfs.h"
+#include "cobalt/storage/store_upgrade/virtual_file.h"
+#include "cobalt/storage/store_upgrade/virtual_file_system.h"
+#include "googleurl/src/gurl.h"
+#include "nb/memory_scope.h"
+#include "net/cookies/canonical_cookie.h"
+#include "sql/connection.h"
+#include "sql/statement.h"
+#include "starboard/storage.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+namespace {
+
+constexpr char kDefaultSaveFile[] = "cobalt_save.bin";
+
+typedef std::map<std::string, std::string> StorageMap;
+
+const std::string& GetFirstValidDatabaseFile(
+ const std::vector<std::string>& filenames) {
+ // Caller must ensure at least one file exists.
+ DCHECK_GT(filenames.size(), size_t(0));
+
+ for (size_t i = 0; i < filenames.size(); ++i) {
+ sql::Connection connection;
+ bool is_opened = connection.Open(FilePath(filenames[i]));
+ if (!is_opened) {
+ continue;
+ }
+ int err = connection.ExecuteAndReturnErrorCode("pragma schema_version;");
+ if (err != SQLITE_OK) {
+ continue;
+ }
+ // File can be opened as a database.
+ return filenames[i];
+ }
+
+ // Caller must handle case where a valid database file cannot be found.
+ DLOG(WARNING) << "Cannot find valid database file in save data";
+ return filenames[0];
+}
+
+void GetAllCookies(sql::Connection* conn, Storage* storage) {
+ sql::Statement get_all(conn->GetCachedStatement(
+ SQL_FROM_HERE,
+ "SELECT domain, path, name, value, creation, expiration, last_access, "
+ "secure, http_only FROM CookieTable"));
+ while (get_all.Step()) {
+ Cookie* cookie = storage->add_cookies();
+ cookie->set_domain(get_all.ColumnString(0));
+ cookie->set_path(get_all.ColumnString(1));
+ cookie->set_name(get_all.ColumnString(2));
+ cookie->set_value(get_all.ColumnString(3));
+ cookie->set_creation_time_us(get_all.ColumnInt64(4));
+ cookie->set_expiration_time_us(get_all.ColumnInt64(5));
+ cookie->set_last_access_time_us(get_all.ColumnInt64(6));
+ cookie->set_secure(get_all.ColumnBool(7));
+ cookie->set_http_only(get_all.ColumnBool(8));
+
+ DLOG(INFO) << "GetAllCookies: "
+ << " domain=" << cookie->domain() << " path=" << cookie->path()
+ << " name=" << cookie->name() << " value=" << cookie->value()
+ << " creation=" << cookie->creation_time_us()
+ << " expiration=" << cookie->expiration_time_us()
+ << " last_access=" << cookie->last_access_time_us()
+ << " seucure=" << cookie->secure()
+ << " http_only=" << cookie->http_only();
+ }
+}
+
+void GetLocalStorage(sql::Connection* conn, Storage* storage) {
+ sql::Statement get_all(conn->GetCachedStatement(
+ SQL_FROM_HERE,
+ "SELECT site_identifier, key, value FROM LocalStorageTable"));
+ std::map<std::string, LocalStorage*> map_storage;
+ while (get_all.Step()) {
+ std::string id(get_all.ColumnString(0));
+ DLOG(INFO) << "GetLocalStorage: id=" << id;
+ if (map_storage[id] == nullptr) {
+ LocalStorage* local_storage = storage->add_local_storages();
+ local_storage->set_id(id);
+ map_storage[id] = local_storage;
+ }
+ LocalStorageEntry* local_storage_entry =
+ map_storage[id]->add_local_storage_entries();
+ local_storage_entry->set_key(get_all.ColumnString(1));
+ local_storage_entry->set_value(get_all.ColumnString(2));
+ DLOG(INFO) << "GetLocalStorage: key=" << local_storage_entry->key()
+ << " value=" << local_storage_entry->value();
+ }
+}
+
+bool OpenConnection(const std::vector<uint8>& raw_bytes,
+ const scoped_ptr<VirtualFileSystem>& vfs,
+ const scoped_ptr<SqlVfs>& sql_vfs,
+ const scoped_ptr<sql::Connection>& connection) {
+ VirtualFileSystem::SerializedHeader header = {};
+
+ if (raw_bytes.size() > 0) {
+ const char* buffer = reinterpret_cast<const char*>(raw_bytes.data());
+ int buffer_size = static_cast<int>(raw_bytes.size());
+
+ if (raw_bytes.size() >= sizeof(VirtualFileSystem::SerializedHeader)) {
+ memcpy(&header, buffer, sizeof(VirtualFileSystem::SerializedHeader));
+ }
+
+ if (!vfs->Deserialize(raw_bytes.data(), buffer_size)) {
+ DLOG(ERROR) << "Failed to deserialize vfs";
+ return false;
+ }
+ }
+
+ std::vector<std::string> filenames = vfs->ListFiles();
+ if (filenames.size() == 0) {
+ filenames.push_back(kDefaultSaveFile);
+ }
+
+ // Legacy Steel save data may contain multiple files (e.g. db-journal as well
+ // as db), so use the first one that looks like a valid database file.
+ const std::string& save_name = GetFirstValidDatabaseFile(filenames);
+ bool ok = connection->Open(FilePath(save_name));
+ if (!ok) {
+ DLOG(WARNING) << "Failed to open file: " << save_name;
+ return false;
+ }
+
+ // Open() is lazy. Run a quick check to see if the database is valid.
+ int err = connection->ExecuteAndReturnErrorCode("pragma schema_version;");
+ if (err != SQLITE_OK) {
+ // Database seems to be invalid.
+ DLOG(WARNING) << "Database " << save_name << " appears to be corrupt.";
+ return false;
+ }
+
+ // Disable journaling for our in-memory database.
+ sql::Statement disable_journal(
+ connection->GetUniqueStatement("PRAGMA journal_mode=OFF"));
+ ok = disable_journal.Step();
+ DCHECK(ok);
+
+ return true;
+}
+} // namespace
+
+bool IsUpgradeRequired(const std::vector<uint8>& buffer) {
+ bool result = buffer.size() >= kStorageHeaderSize &&
+ memcmp(reinterpret_cast<const char*>(buffer.data()),
+ kOldStorageHeader, kStorageHeaderSize) == 0;
+ if (result) {
+ DLOG(INFO) << "Store upgrade required";
+ } else {
+ DLOG(INFO) << "Store upgrade not required";
+ }
+ return result;
+}
+
+bool UpgradeStore(std::vector<uint8>* buffer) {
+ scoped_ptr<VirtualFileSystem> vfs(new VirtualFileSystem());
+ scoped_ptr<SqlVfs> sql_vfs(new SqlVfs("cobalt_vfs", vfs.get()));
+ scoped_ptr<sql::Connection> connection(new sql::Connection());
+
+ if (!OpenConnection(*buffer, vfs, sql_vfs, connection)) {
+ return false;
+ }
+
+ Storage storage;
+ GetAllCookies(connection.get(), &storage);
+ GetLocalStorage(connection.get(), &storage);
+ connection->Close();
+ size_t size = storage.ByteSize();
+ buffer->resize(kStorageHeaderSize + size);
+ char* buffer_ptr = reinterpret_cast<char*>(buffer->data());
+ memcpy(buffer_ptr, kStorageHeader, kStorageHeaderSize);
+ if (size > 0 &&
+ !storage.SerializeToArray(buffer_ptr + kStorageHeaderSize, size)) {
+ DLOG(ERROR) << "Failed to serialize message with size=" << size;
+ return false;
+ }
+ return true;
+}
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/store_upgrade/upgrade.gyp b/src/cobalt/storage/store_upgrade/upgrade.gyp
new file mode 100644
index 0000000..a31968e
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/upgrade.gyp
@@ -0,0 +1,98 @@
+# Copyright 2018 Google Inc. 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'storage_upgrade',
+ 'type': 'static_library',
+ 'sources': [
+ '../store/storage.pb.h',
+ 'sql_vfs.cc',
+ 'sql_vfs.h',
+ 'upgrade.cc',
+ 'upgrade.h',
+ 'virtual_file.cc',
+ 'virtual_file.h',
+ 'virtual_file_system.cc',
+ 'virtual_file_system.h'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/storage/storage_constants.gyp:storage_constants',
+ '<(DEPTH)/cobalt/storage/store/store.gyp:memory_store',
+ '<(DEPTH)/net/net.gyp:net',
+ '<(DEPTH)/sql/sql.gyp:sql',
+ '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
+ ],
+ 'defines': [
+ 'GOOGLE_PROTOBUF_NO_RTTI',
+ 'GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER',
+ 'HAVE_PTHREAD',
+ ],
+ 'include_dirs': [
+ # Get protobuf headers from the chromium tree.
+ '<(DEPTH)/third_party/protobuf/src',
+ ],
+ },
+ {
+ 'target_name': 'storage_upgrade_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ '../store/storage.pb.h',
+ 'upgrade_test.cc',
+ ],
+ 'dependencies': [
+ 'storage_upgrade',
+ 'storage_upgrade_copy_test_files',
+ '<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ '<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
+ ],
+ 'defines': [
+ 'GOOGLE_PROTOBUF_NO_RTTI',
+ 'GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER',
+ 'HAVE_PTHREAD',
+ ],
+ 'include_dirs': [
+ # Get protobuf headers from the chromium tree.
+ '<(DEPTH)/third_party/protobuf/src',
+ ],
+ },
+ {
+ 'target_name': 'storage_upgrade_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'storage_upgrade_test',
+ ],
+ 'variables': {
+ 'executable_name': 'storage_upgrade_test',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+ },
+ {
+ 'target_name': 'storage_upgrade_copy_test_files',
+ 'type': 'none',
+ 'variables': {
+ 'content_test_input_files': [
+ '<(DEPTH)/cobalt/storage/store_upgrade/testdata/',
+ ],
+ 'content_test_output_subdir': 'cobalt/storage/store_upgrade/testdata/',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/copy_test_data.gypi' ],
+ },
+ ],
+}
diff --git a/src/cobalt/storage/store_upgrade/upgrade.h b/src/cobalt/storage/store_upgrade/upgrade.h
new file mode 100644
index 0000000..22f3422
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/upgrade.h
@@ -0,0 +1,37 @@
+// Copyright 2018 Google Inc. 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 COBALT_STORAGE_STORE_UPGRADE_UPGRADE_H_
+#define COBALT_STORAGE_STORE_UPGRADE_UPGRADE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+
+// Checks whether store upgrade is required.
+bool IsUpgradeRequired(const std::vector<uint8>& buffer);
+
+// Upgrades the store blob.
+bool UpgradeStore(std::vector<uint8>* buffer);
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
+
+#endif // COBALT_STORAGE_STORE_UPGRADE_UPGRADE_H_
diff --git a/src/cobalt/storage/store_upgrade/upgrade_main.cc b/src/cobalt/storage/store_upgrade/upgrade_main.cc
new file mode 100644
index 0000000..f0a5608
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/upgrade_main.cc
@@ -0,0 +1,65 @@
+// Copyright 2018 Google Inc. 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 "base/path_service.h"
+#include "cobalt/base/get_application_key.h"
+#include "cobalt/base/wrap_main.h"
+#include "cobalt/storage/savegame.h"
+#include "cobalt/storage/store/memory_store.h"
+#include "cobalt/storage/store_upgrade/upgrade.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+
+size_t kMaxSaveGameSizeBytes = 4 * 1024 * 1024;
+const char kDefaultURL[] = "https://www.youtube.com/tv";
+
+int UpgradeMain(int argc, char** argv) {
+ std::vector<uint8> buffer;
+ Savegame::Options options;
+
+ base::optional<std::string> partition_key;
+ partition_key = base::GetApplicationKey(GURL(kDefaultURL));
+
+ options.id = partition_key;
+ options.fallback_to_default_id = true;
+
+ scoped_ptr<Savegame> savegame = Savegame::Create(options);
+ if (!savegame->Read(&buffer, kMaxSaveGameSizeBytes)) {
+ DLOG(ERROR) << "Failed to read storage";
+ return -1;
+ }
+ DLOG(INFO) << "read: size=" << buffer.size();
+ if (IsUpgradeRequired(buffer)) {
+ DLOG(INFO) << "Upgarded required";
+ if (!UpgradeStore(&buffer)) {
+ DLOG(ERROR) << "Failed to upgrade";
+ return -1;
+ }
+ }
+
+ if (!savegame->Write(buffer)) {
+ DLOG(ERROR) << "Failed to save";
+ return -1;
+ }
+ DLOG(INFO) << "SUCCESS: upgarded size=" << buffer.size();
+ return 0;
+}
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
+
+COBALT_WRAP_SIMPLE_MAIN(cobalt::storage::store_upgrade::UpgradeMain);
diff --git a/src/cobalt/storage/store_upgrade/upgrade_test.cc b/src/cobalt/storage/store_upgrade/upgrade_test.cc
new file mode 100644
index 0000000..b97eb91
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/upgrade_test.cc
@@ -0,0 +1,103 @@
+// Copyright 2018 Google Inc. 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 "cobalt/storage/store_upgrade/upgrade.h"
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/platform_file.h"
+#include "base/time.h"
+#include "cobalt/storage/storage_constants.h"
+#include "cobalt/storage/store/storage.pb.h"
+#include "googleurl/src/gurl.h"
+#include "net/cookies/canonical_cookie.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+namespace {
+
+std::vector<uint8> ReadFile(const char* pathname) {
+ FilePath file_path;
+ EXPECT_TRUE(PathService::Get(base::DIR_TEST_DATA, &file_path));
+ file_path = file_path.Append(pathname);
+ std::string string_out;
+ EXPECT_TRUE(file_util::ReadFileToString(file_path, &string_out));
+ std::vector<uint8> buffer(string_out.begin(), string_out.end());
+ return buffer;
+}
+
+void ValidateCookie(const cobalt::storage::Cookie& cookie,
+ const std::string& name, const std::string& value,
+ const std::string domain, const std::string& path,
+ int64 creation_time_us, const int64 expiration_time_us,
+ int64 last_access_time_us, bool http_only) {
+ EXPECT_EQ(cookie.name(), name);
+ EXPECT_EQ(cookie.value(), value);
+ EXPECT_EQ(cookie.domain(), domain);
+ EXPECT_EQ(cookie.path(), path);
+ EXPECT_EQ(cookie.creation_time_us(), creation_time_us);
+ EXPECT_EQ(cookie.expiration_time_us(), expiration_time_us);
+ EXPECT_EQ(cookie.last_access_time_us(), last_access_time_us);
+ EXPECT_EQ(cookie.secure(), false);
+ EXPECT_EQ(cookie.http_only(), http_only);
+}
+
+void ValidateLocalStorageEntry(
+ const cobalt::storage::LocalStorageEntry& local_storage_entry,
+ const std::string& key, const std::string& value) {
+ EXPECT_EQ(local_storage_entry.key(), key);
+ EXPECT_EQ(local_storage_entry.value(), value);
+}
+
+TEST(StorageUpgradeTest, Upgrade) {
+ std::vector<uint8> buffer =
+ ReadFile("cobalt/storage/store_upgrade/testdata/test.storage");
+ EXPECT_TRUE(IsUpgradeRequired(buffer));
+ EXPECT_TRUE(UpgradeStore(&buffer));
+
+ Storage storage;
+ EXPECT_TRUE(storage.ParseFromArray(buffer.data() + kStorageHeaderSize,
+ buffer.size()) -
+ kStorageHeaderSize);
+
+ if (storage.cookies(0).name() == "foo") {
+ ValidateCookie(storage.cookies(0), "foo", "bar", "localhost", "/",
+ 13172275723286776, 13191233280286776, 13191233280286776,
+ false);
+ } else {
+ ValidateCookie(storage.cookies(1), "sessionid", "4242", "localhost", "/",
+ 13172278221139558, 13191319680139558, 13172278221139558,
+ true);
+ }
+
+ EXPECT_EQ(storage.local_storages(0).id(), "http_localhost_8000.localstorage");
+ if (storage.local_storages(0).local_storage_entries(0).key() == "foo") {
+ ValidateLocalStorageEntry(
+ storage.local_storages(0).local_storage_entries(0), "foo", "bar");
+ } else {
+ ValidateLocalStorageEntry(
+ storage.local_storages(0).local_storage_entries(1), "data",
+ "{\"mydata\": \"123\"}");
+ }
+}
+
+} // namespace
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/store_upgrade/upgrade_tool.gyp b/src/cobalt/storage/store_upgrade/upgrade_tool.gyp
new file mode 100644
index 0000000..172fd9f
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/upgrade_tool.gyp
@@ -0,0 +1,29 @@
+# Copyright 2016 Google Inc. 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'storage_upgrade_tool',
+ 'type': '<(final_executable_type)',
+ 'sources': [
+ 'upgrade_main.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/storage/storage.gyp:storage',
+ '<(DEPTH)/cobalt/storage/store_upgrade/upgrade.gyp:storage_upgrade',
+ ],
+ },
+ ],
+}
diff --git a/src/cobalt/storage/store_upgrade/virtual_file.cc b/src/cobalt/storage/store_upgrade/virtual_file.cc
new file mode 100644
index 0000000..6057aa1
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/virtual_file.cc
@@ -0,0 +1,187 @@
+// Copyright 2015 Google Inc. 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 "cobalt/storage/store_upgrade/virtual_file.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+namespace {
+
+// We must use forward declarations to be able to specify the
+// WARN_UNUSED_RESULT attribute.
+
+// Read size bytes from buffer into dest. Updates buffer and buffer_remaining
+// on success.
+// Returns true on success, false on buffer overrun
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+ size_t* buffer_remaining) WARN_UNUSED_RESULT;
+
+// Write size bytes from source into buffer. Return # of bytes written.
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+ bool dry_run) WARN_UNUSED_RESULT;
+
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+ size_t* buffer_remaining) {
+ if (size > *buffer_remaining) {
+ return false;
+ }
+ memcpy(dest, *buffer, size);
+ *buffer_remaining -= size;
+ *buffer += size;
+ return true;
+}
+
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+ bool dry_run) {
+ if (!dry_run) {
+ memcpy(buffer, source, size);
+ }
+ return size;
+}
+
+} // namespace
+
+VirtualFile::VirtualFile(const std::string& name) : name_(name) {}
+VirtualFile::~VirtualFile() {}
+
+int VirtualFile::Read(void* dest, int bytes_in, int offset_in) const {
+ DCHECK_GE(offset_in, 0);
+ DCHECK_GE(bytes_in, 0);
+ size_t offset = static_cast<size_t>(offset_in);
+ size_t bytes = static_cast<size_t>(bytes_in);
+ size_t size = buffer_.size();
+ if (offset > size) {
+ return 0;
+ }
+ size_t bytes_to_read = std::min(static_cast<size_t>(size - offset), bytes);
+ if (bytes_to_read == 0) {
+ return 0;
+ }
+ memcpy(dest, &buffer_[offset], bytes_to_read);
+ return static_cast<int>(bytes_to_read);
+}
+
+int VirtualFile::Write(const void* source, int bytes_in, int offset_in) {
+ DCHECK_GE(offset_in, 0);
+ DCHECK_GE(bytes_in, 0);
+ size_t bytes = static_cast<size_t>(bytes_in);
+ size_t offset = static_cast<size_t>(offset_in);
+ if (buffer_.size() < offset + bytes) {
+ buffer_.resize(offset + bytes);
+ }
+
+ if (!buffer_.empty()) {
+ // std::vector does not define access to underlying array when empty
+ memcpy(&buffer_[offset], source, bytes);
+ }
+ return bytes_in;
+}
+
+int VirtualFile::Truncate(int size) {
+ size_t newsize = std::min(buffer_.size(), static_cast<size_t>(size));
+ buffer_.resize(newsize);
+ return static_cast<int>(newsize);
+}
+
+int VirtualFile::Serialize(uint8* dest, bool dry_run) {
+ uint8* cur_buffer = dest;
+
+ // Write filename length
+ uint64 name_length = name_.length();
+ DCHECK_LT(name_length, kMaxVfsPathname);
+ cur_buffer +=
+ WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(&name_length),
+ sizeof(name_length), dry_run);
+
+ // Write the filename
+ cur_buffer +=
+ WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(name_.c_str()),
+ static_cast<size_t>(name_length), dry_run);
+
+ // NOTE: Ensure the file size is 64-bit for compatibility
+ // with any existing serialized files.
+ uint64 file_size = static_cast<uint64>(buffer_.size());
+ // Write the file contents size
+ cur_buffer +=
+ WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(&file_size),
+ sizeof(file_size), dry_run);
+
+ // Write the file contents
+ if (!buffer_.empty()) {
+ // std::vector does not define access to underlying array when empty
+ cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], buffer_.size(), dry_run);
+ }
+
+ // Return the number of bytes written
+ return static_cast<int>(std::distance(dest, cur_buffer));
+}
+
+int VirtualFile::Deserialize(const uint8* source, size_t buffer_remaining) {
+ // Read in filename length
+ const uint8* cur_buffer = source;
+ uint64 name_length;
+ bool success = ReadBuffer(reinterpret_cast<uint8*>(&name_length), &cur_buffer,
+ sizeof(name_length), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
+
+ if (name_length >= kMaxVfsPathname) {
+ DLOG(ERROR) << "Filename was longer than the maximum allowed.";
+ return -1;
+ }
+ // Read in filename
+ char name[kMaxVfsPathname];
+ success = ReadBuffer(reinterpret_cast<uint8*>(name), &cur_buffer,
+ static_cast<size_t>(name_length), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
+ name_.assign(name, static_cast<size_t>(name_length));
+
+ // Read in file contents size.
+ uint64 file_size;
+ success = ReadBuffer(reinterpret_cast<uint8*>(&file_size), &cur_buffer,
+ sizeof(file_size), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
+ // Read in the file contents
+ buffer_.resize(static_cast<size_t>(file_size));
+
+ if (!buffer_.empty()) {
+ // std::vector does not define access to underlying array when empty
+ success =
+ ReadBuffer(&buffer_[0], &cur_buffer, buffer_.size(), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
+ }
+
+ // Return the number of bytes read
+ return static_cast<int>(std::distance(source, cur_buffer));
+}
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/store_upgrade/virtual_file.h b/src/cobalt/storage/store_upgrade/virtual_file.h
new file mode 100644
index 0000000..007cce3
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/virtual_file.h
@@ -0,0 +1,69 @@
+// Copyright 2015 Google Inc. 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 COBALT_STORAGE_STORE_UPGRADE_VIRTUAL_FILE_H_
+#define COBALT_STORAGE_STORE_UPGRADE_VIRTUAL_FILE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+
+class VirtualFileSystem;
+
+// VirtualFile implements an in-memory file system. These
+// are managed by VirtualFileSystem for use by SQLite3.
+// The format of a serialized VirtualFile is as follows:
+// 8-byte name length
+// <name length> File name string
+// 8-byte file size
+// <file size> raw bytes.
+class VirtualFile {
+ public:
+ enum { kMaxVfsPathname = 512 };
+
+ int Read(void* dest, int bytes, int offset) const;
+ int Write(const void* source, int bytes, int offset);
+ int Truncate(int size);
+ int Size() const { return static_cast<int>(buffer_.size()); }
+
+ private:
+ explicit VirtualFile(const std::string& name);
+ ~VirtualFile();
+
+ // Returns the number of bytes written
+ int Serialize(uint8* dest, const bool dry_run);
+ // Deserializes a file, returning the size of the file or
+ // < 0 on error.
+ // |buffer_remaining| is the maximum size of |source|
+ int Deserialize(const uint8* source, size_t buffer_remaining);
+
+ std::vector<uint8> buffer_;
+
+ std::string name_;
+
+ friend class VirtualFileSystem;
+ DISALLOW_COPY_AND_ASSIGN(VirtualFile);
+};
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
+
+#endif // COBALT_STORAGE_STORE_UPGRADE_VIRTUAL_FILE_H_
diff --git a/src/cobalt/storage/store_upgrade/virtual_file_system.cc b/src/cobalt/storage/store_upgrade/virtual_file_system.cc
new file mode 100644
index 0000000..35dba48
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/virtual_file_system.cc
@@ -0,0 +1,195 @@
+// Copyright 2015 Google Inc. 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 "cobalt/storage/store_upgrade/virtual_file_system.h"
+
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "cobalt/storage/storage_constants.h"
+#include "cobalt/storage/store_upgrade/virtual_file.h"
+
+#include "starboard/client_porting/poem/string_poem.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+
+// static
+int VirtualFileSystem::GetHeaderVersion(const SerializedHeader& header) {
+ // Copy the version int to a char buffer to avoid endian issues.
+ char version[kStorageHeaderSize];
+ memcpy(version, &header.version, sizeof(version));
+
+ if (memcmp(version, kOldStorageHeader, kStorageHeaderSize) != 0) {
+ return -1;
+ } else if (header.file_size < static_cast<int>(sizeof(SerializedHeader))) {
+ return -1;
+ } else {
+ return version[kStorageHeaderVersionIndex] - '0';
+ }
+}
+
+// static
+int VirtualFileSystem::GetCurrentVersion() {
+ int version;
+ memcpy(&version, kStorageHeader, sizeof(version));
+ return version;
+}
+
+VirtualFileSystem::VirtualFileSystem() {}
+
+VirtualFileSystem::~VirtualFileSystem() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::AutoLock lock(file_table_lock_);
+ ClearFileTable();
+}
+
+VirtualFile* VirtualFileSystem::Open(const std::string& filename) {
+ base::AutoLock lock(file_table_lock_);
+ VirtualFile* result = NULL;
+ FileTable::iterator it = table_.find(filename);
+ if (it != table_.end()) {
+ result = it->second;
+ } else {
+ result = new VirtualFile(filename);
+ table_.insert(it, FileTable::value_type(filename, result));
+ }
+ return result;
+}
+
+std::vector<std::string> VirtualFileSystem::ListFiles() {
+ base::AutoLock lock(file_table_lock_);
+ std::vector<std::string> files;
+ for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
+ files.push_back(it->first);
+ }
+ return files;
+}
+
+void VirtualFileSystem::Delete(const std::string& filename) {
+ base::AutoLock lock(file_table_lock_);
+ FileTable::iterator it = table_.find(filename);
+ if (it != table_.end()) {
+ delete it->second;
+ table_.erase(it);
+ }
+}
+
+int VirtualFileSystem::Serialize(uint8* buffer, bool dry_run) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::AutoLock lock(file_table_lock_);
+ uint8* original = buffer;
+
+ // We don't know the total size of the file yet, so defer writing the header.
+ buffer += sizeof(SerializedHeader);
+ int valid_file_count = 0;
+
+ // Serialize each file
+ for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
+ if (it->second->Size() == 0) {
+ continue;
+ }
+ int file_bytes = it->second->Serialize(buffer, dry_run);
+ buffer += file_bytes;
+ valid_file_count++;
+ }
+ const int bytes_written = static_cast<int>(buffer - original);
+ if (!dry_run) {
+ // Now we can write the header to the beginning of the buffer.
+ SerializedHeader header;
+ header.version = GetCurrentVersion();
+ header.file_count = valid_file_count;
+ header.file_size = bytes_written;
+ memcpy(original, &header, sizeof(SerializedHeader));
+ }
+
+ return bytes_written;
+}
+
+bool VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size_in) {
+ const uint8* caller_buffer = buffer;
+
+ base::AutoLock lock(file_table_lock_);
+ ClearFileTable();
+
+ if (buffer_size_in < 0) {
+ DLOG(ERROR) << "Buffer size must be positive: " << buffer_size_in
+ << " < 0.";
+ return false;
+ }
+
+ size_t buffer_size = static_cast<size_t>(buffer_size_in);
+
+ // The size of the buffer must be checked before copying the beginning of it
+ // into a SerializedHeader.
+ if (buffer_size < sizeof(SerializedHeader)) {
+ DLOG(ERROR) << "Buffer size " << buffer_size
+ << " is too small to contain a SerializedHeader of size "
+ << sizeof(SerializedHeader) << "; operation aborted.";
+ return false;
+ }
+
+ // Read in expected number of files
+ SerializedHeader header;
+ memcpy(&header, buffer, sizeof(SerializedHeader));
+ buffer += sizeof(SerializedHeader);
+
+ int buffer_version = GetHeaderVersion(header);
+ if (buffer_version != 0) {
+ // Note: We would handle old versions here, if necessary.
+ DLOG(ERROR) << "Attempted to load a different version; operation aborted.";
+ return false;
+ } else if (static_cast<size_t>(header.file_size) != buffer_size) {
+ DLOG(ERROR) << "Buffer size mismatch: " << header.file_size
+ << " != " << buffer_size;
+ return false;
+ }
+
+ for (int i = 0; i < header.file_count; ++i) {
+ size_t buffer_used = static_cast<size_t>(buffer - caller_buffer);
+ if (buffer_size < buffer_used) {
+ DLOG(ERROR) << "Buffer overrun deserializing files";
+ ClearFileTable();
+ return false;
+ }
+ size_t buffer_remaining = buffer_size - buffer_used;
+
+ VirtualFile* file = new VirtualFile("");
+
+ int bytes = file->Deserialize(buffer, buffer_remaining);
+ if (bytes > 0) {
+ buffer += bytes;
+ table_[file->name_] = file;
+ } else {
+ DLOG(WARNING) << "Failed to deserialize virtual file system.";
+ delete file;
+ ClearFileTable();
+ return false;
+ }
+ }
+ return true;
+}
+
+void VirtualFileSystem::ClearFileTable() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ file_table_lock_.AssertAcquired();
+ for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
+ delete it->second;
+ }
+ table_.clear();
+}
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/store_upgrade/virtual_file_system.h b/src/cobalt/storage/store_upgrade/virtual_file_system.h
new file mode 100644
index 0000000..452d642
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/virtual_file_system.h
@@ -0,0 +1,90 @@
+// Copyright 2015 Google Inc. 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 COBALT_STORAGE_STORE_UPGRADE_VIRTUAL_FILE_SYSTEM_H_
+#define COBALT_STORAGE_STORE_UPGRADE_VIRTUAL_FILE_SYSTEM_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+
+class VirtualFile;
+
+// Implements a simple virtual filesystem, primarily intended to
+// be used for providing an in-memory filesystem that SQLite can write to,
+// and allowing that filesystem to be saved out into a single memory buffer.
+// Contains a mapping from paths to their corresponding memory blocks.
+// VirtualFileSystem must be serialized, deserialized, and destroyed on the
+// same thread it was created on.
+// Open/Delete are thread-safe so that SQL can run on any thread.
+class VirtualFileSystem {
+ public:
+ struct SerializedHeader {
+ int32 version;
+ int32 file_size;
+ int32 file_count;
+ };
+ // Returns the version of the VirtualFileSystem header in buffer.
+ // -1 if buffer is invalid.
+ static int GetHeaderVersion(const SerializedHeader& header);
+ static int GetCurrentVersion();
+
+ VirtualFileSystem();
+ ~VirtualFileSystem();
+
+ // Serializes the file system out to a single contiguous buffer.
+ // A dry run only calculates the number of bytes needed.
+ // Returns the number of bytes written.
+ int Serialize(uint8* buffer, bool dry_run);
+
+ // Deserializes a file system from a memory buffer.
+ // Returns false on failure.
+ bool Deserialize(const uint8* buffer, int buffer_size);
+
+ // Simple file open. Will create a file if it does not exist, and files are
+ // always readable and writable.
+ VirtualFile* Open(const std::string& filename);
+
+ std::vector<std::string> ListFiles();
+
+ void Delete(const std::string& filename);
+
+ private:
+ void ClearFileTable();
+ void SQLRegister();
+ void SQLUnregister();
+
+ typedef std::map<std::string, VirtualFile*> FileTable;
+ FileTable table_;
+ // Certain operations (serialize, deserialize) must run on the same thread.
+ base::ThreadChecker thread_checker_;
+ // Lock to protect the file table. We want to support SQL queries on any
+ // thread.
+ base::Lock file_table_lock_;
+};
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
+
+#endif // COBALT_STORAGE_STORE_UPGRADE_VIRTUAL_FILE_SYSTEM_H_
diff --git a/src/cobalt/storage/store_upgrade/virtual_file_system_test.cc b/src/cobalt/storage/store_upgrade/virtual_file_system_test.cc
new file mode 100644
index 0000000..d6f31a1
--- /dev/null
+++ b/src/cobalt/storage/store_upgrade/virtual_file_system_test.cc
@@ -0,0 +1,255 @@
+// Copyright 2015 Google Inc. 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 "cobalt/storage/virtual_file.h"
+#include "cobalt/storage/virtual_file_system.h"
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "starboard/client_porting/poem/string_poem.h"
+
+namespace cobalt {
+namespace storage {
+namespace store_upgrade {
+
+class VirtualFileSystemTest : public testing::Test {
+ protected:
+ VirtualFileSystem vfs_;
+};
+
+TEST_F(VirtualFileSystemTest, WriteAndRead) {
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(0, file->Size());
+
+ int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ file->Write(expected, sizeof(expected), 0);
+
+ char out_data[128];
+ int bytes = file->Read(out_data, sizeof(out_data), 0);
+ EXPECT_EQ(bytes, sizeof(expected));
+ EXPECT_EQ(0, memcmp(expected, out_data, sizeof(expected)));
+}
+
+TEST_F(VirtualFileSystemTest, ReadWriteOffsets) {
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(0, file->Size());
+
+ char expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ file->Write(expected, sizeof(expected), 0);
+
+ // Seek to position 4 and read bytes.
+ char out_data[128];
+ int bytes = file->Read(out_data, sizeof(out_data), 4);
+
+ const int kDataSize = 6;
+ EXPECT_EQ(kDataSize, bytes);
+ EXPECT_EQ(0, memcmp(&expected[4], out_data, kDataSize));
+
+ // Write some bytes into the middle
+ file->Write(expected, 3, 3);
+ char expected1[] = {0, 1, 2, 0, 1, 2, 6, 7, 8, 9};
+ EXPECT_EQ(sizeof(expected1), file->Size());
+
+ bytes = file->Read(out_data, sizeof(out_data), 0);
+ EXPECT_EQ(0, memcmp(expected1, out_data, static_cast<size_t>(bytes)));
+
+ // Try to read past the end.
+ const int kOffsetPastEnd = 100000;
+ bytes = file->Read(out_data, sizeof(out_data), kOffsetPastEnd);
+ EXPECT_EQ(0, bytes);
+
+ // Try to write past the end.
+ file->Write(expected, sizeof(expected), kOffsetPastEnd);
+ EXPECT_EQ(kOffsetPastEnd + sizeof(expected), file->Size());
+
+ // Make sure the write past the end worked.
+ bytes = file->Read(out_data, sizeof(out_data), kOffsetPastEnd);
+ EXPECT_EQ(0, memcmp(expected, out_data, static_cast<size_t>(bytes)));
+}
+
+TEST_F(VirtualFileSystemTest, Open) {
+ // Create a few files and write some data
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(0, file->Size());
+
+ // Write a few bytes of random data.
+ file->Write("test", 4, 0);
+ EXPECT_EQ(4, file->Size());
+
+ // Open a new file
+ file = vfs_.Open("file2.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(0, file->Size());
+
+ // Try to reopen the existing file.
+ file = vfs_.Open("file1.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(4, file->Size());
+}
+
+TEST_F(VirtualFileSystemTest, Truncate) {
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(0, file->Size());
+
+ const char* data = "test";
+ char file_contents[4];
+ // Write a few bytes of random data.
+ file->Write(data, 4, 0);
+ EXPECT_EQ(4, file->Size());
+ int bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(4, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+ file->Truncate(3);
+ EXPECT_EQ(3, file->Size());
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(3, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+ file->Truncate(0);
+ EXPECT_EQ(0, file->Size());
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(0, bytes);
+}
+
+TEST_F(VirtualFileSystemTest, SerializeDeserialize) {
+ // Create a few files and write some data
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data1[] = "abc";
+ int data1_size = 3;
+ file->Write(data1, data1_size, 0);
+
+ file = vfs_.Open("file2.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data2[] = "defg";
+ int data2_size = 4;
+ file->Write(data2, data2_size, 0);
+
+ file = vfs_.Open("file3.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data3[] = "";
+ int data3_size = 0;
+ file->Write(data3, data3_size, 0);
+
+ // First perform a dry run to figure out how much space we need.
+ int bytes = vfs_.Serialize(NULL, true /*dry run*/);
+ scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
+
+ // Now serialize and deserialize
+ vfs_.Serialize(buffer.get(), false /*dry run*/);
+
+ // Deserialize the data into a new vfs
+ VirtualFileSystem new_vfs;
+ ASSERT_TRUE(new_vfs.Deserialize(buffer.get(), bytes));
+
+ // Make sure the new vfs contains all the expected data.
+ char file_contents[VirtualFile::kMaxVfsPathname];
+ file = new_vfs.Open("file1.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data1_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
+
+ file = new_vfs.Open("file2.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data2_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
+
+ file = new_vfs.Open("file3.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data3_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data3, static_cast<size_t>(bytes)));
+}
+
+TEST_F(VirtualFileSystemTest, DeserializeTruncated) {
+ // Create a few files and write some data
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data1[] = "abc";
+ int data1_size = 3;
+ file->Write(data1, data1_size, 0);
+
+ file = vfs_.Open("file2.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data2[] = "defg";
+ int data2_size = 4;
+ file->Write(data2, data2_size, 0);
+
+ // First perform a dry run to figure out how much space we need.
+ int bytes = vfs_.Serialize(NULL, true /*dry run*/);
+ scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
+
+ // Now serialize and deserialize
+ vfs_.Serialize(buffer.get(), false /*dry run*/);
+
+ for (int i = 1; i < bytes; i++) {
+ // Corrupt the header
+ VirtualFileSystem::SerializedHeader header;
+ memcpy(&header, buffer.get(), sizeof(header));
+ header.file_size = header.file_size - i;
+ memcpy(buffer.get(), &header, sizeof(header));
+
+ // Deserialize the data into a new vfs
+ VirtualFileSystem new_vfs;
+ EXPECT_FALSE(new_vfs.Deserialize(buffer.get(), bytes - i));
+ }
+}
+
+TEST_F(VirtualFileSystemTest, DeserializeBadData) {
+ scoped_array<uint8> buffer(new uint8[0]);
+ EXPECT_FALSE(vfs_.Deserialize(buffer.get(), 0));
+ EXPECT_FALSE(vfs_.Deserialize(buffer.get(), -1));
+ buffer.reset(new uint8[1]);
+ EXPECT_FALSE(vfs_.Deserialize(buffer.get(), 1));
+ buffer.reset(new uint8[sizeof(VirtualFileSystem::SerializedHeader)]);
+ VirtualFileSystem::SerializedHeader header = {};
+ header.version = -1;
+ memcpy(buffer.get(), &header, sizeof(header));
+ EXPECT_FALSE(vfs_.Deserialize(buffer.get(),
+ sizeof(VirtualFileSystem::SerializedHeader)));
+ memcpy(&(header.version), "SAV0", sizeof(header.version));
+ header.file_size = sizeof(VirtualFileSystem::SerializedHeader);
+ memcpy(buffer.get(), &header, sizeof(header));
+ EXPECT_TRUE(vfs_.Deserialize(buffer.get(),
+ sizeof(VirtualFileSystem::SerializedHeader)));
+ ASSERT_EQ(0, vfs_.ListFiles().size());
+}
+
+TEST_F(VirtualFileSystemTest, GetHeaderVersion) {
+ VirtualFileSystem::SerializedHeader header;
+ memset(&header, 0, sizeof(header));
+ header.file_size = sizeof(header);
+
+ COMPILE_ASSERT(sizeof(header.version) == sizeof("SAV0") - 1,
+ Invalid_header_version_size);
+ memcpy(&header.version, "XYZW", sizeof(header.version));
+ EXPECT_EQ(-1, VirtualFileSystem::GetHeaderVersion(header));
+ memcpy(&header.version, "SAV0", sizeof(header.version));
+ EXPECT_EQ(0, VirtualFileSystem::GetHeaderVersion(header));
+ memcpy(&header.version, "SAV1", sizeof(header.version));
+ EXPECT_EQ(1, VirtualFileSystem::GetHeaderVersion(header));
+}
+
+} // namespace store_upgrade
+} // namespace storage
+} // namespace cobalt
diff --git a/src/cobalt/storage/upgrade/schema_v1.proto b/src/cobalt/storage/upgrade/schema_v1.proto
index c2516a5..43d8a15 100644
--- a/src/cobalt/storage/upgrade/schema_v1.proto
+++ b/src/cobalt/storage/upgrade/schema_v1.proto
@@ -36,7 +36,7 @@
repeated LocalStorageEntry local_storage_entries = 2;
// A single cookie.
- mesage Cookie {
+ message Cookie {
// URL in canonical form, e.g. "https://www.example.com/". Must be provided
// or the cookie will be ignored.
string url = 1;
diff --git a/src/cobalt/storage/virtual_file.cc b/src/cobalt/storage/virtual_file.cc
deleted file mode 100644
index a38e31f..0000000
--- a/src/cobalt/storage/virtual_file.cc
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2015 Google Inc. 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 "cobalt/storage/virtual_file.h"
-
-#include <algorithm>
-
-#include "base/logging.h"
-
-namespace cobalt {
-namespace storage {
-namespace {
-
-// We must use forward declarations to be able to specify the
-// WARN_UNUSED_RESULT attribute.
-
-// Read size bytes from buffer into dest. Updates buffer and buffer_remaining
-// on success.
-// Returns true on success, false on buffer overrun
-bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
- size_t* buffer_remaining) WARN_UNUSED_RESULT;
-
-// Write size bytes from source into buffer. Return # of bytes written.
-size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
- bool dry_run) WARN_UNUSED_RESULT;
-
-bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
- size_t* buffer_remaining) {
- if (size > *buffer_remaining) {
- return false;
- }
- memcpy(dest, *buffer, size);
- *buffer_remaining -= size;
- *buffer += size;
- return true;
-}
-
-size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
- bool dry_run) {
- if (!dry_run) {
- memcpy(buffer, source, size);
- }
- return size;
-}
-
-} // namespace
-
-VirtualFile::VirtualFile(const std::string& name) : name_(name) {}
-VirtualFile::~VirtualFile() {}
-
-int VirtualFile::Read(void* dest, int bytes_in, int offset_in) const {
- DCHECK_GE(offset_in, 0);
- DCHECK_GE(bytes_in, 0);
- size_t offset = static_cast<size_t>(offset_in);
- size_t bytes = static_cast<size_t>(bytes_in);
- size_t size = buffer_.size();
- if (offset > size) {
- return 0;
- }
- size_t bytes_to_read = std::min(static_cast<size_t>(size - offset), bytes);
- if (bytes_to_read == 0) {
- return 0;
- }
- memcpy(dest, &buffer_[offset], bytes_to_read);
- return static_cast<int>(bytes_to_read);
-}
-
-int VirtualFile::Write(const void* source, int bytes_in, int offset_in) {
- DCHECK_GE(offset_in, 0);
- DCHECK_GE(bytes_in, 0);
- size_t bytes = static_cast<size_t>(bytes_in);
- size_t offset = static_cast<size_t>(offset_in);
- if (buffer_.size() < offset + bytes) {
- buffer_.resize(offset + bytes);
- }
-
- if (!buffer_.empty()) {
- // std::vector does not define access to underlying array when empty
- memcpy(&buffer_[offset], source, bytes);
- }
- return bytes_in;
-}
-
-int VirtualFile::Truncate(int size) {
- size_t newsize = std::min(buffer_.size(), static_cast<size_t>(size));
- buffer_.resize(newsize);
- return static_cast<int>(newsize);
-}
-
-int VirtualFile::Serialize(uint8* dest, bool dry_run) {
- uint8* cur_buffer = dest;
-
- // Write filename length
- uint64 name_length = name_.length();
- DCHECK_LT(name_length, kMaxVfsPathname);
- cur_buffer +=
- WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(&name_length),
- sizeof(name_length), dry_run);
-
- // Write the filename
- cur_buffer +=
- WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(name_.c_str()),
- static_cast<size_t>(name_length), dry_run);
-
- // NOTE: Ensure the file size is 64-bit for compatibility
- // with any existing serialized files.
- uint64 file_size = static_cast<uint64>(buffer_.size());
- // Write the file contents size
- cur_buffer +=
- WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(&file_size),
- sizeof(file_size), dry_run);
-
- // Write the file contents
- if (!buffer_.empty()) {
- // std::vector does not define access to underlying array when empty
- cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], buffer_.size(), dry_run);
- }
-
- // Return the number of bytes written
- return static_cast<int>(std::distance(dest, cur_buffer));
-}
-
-int VirtualFile::Deserialize(const uint8* source, size_t buffer_remaining) {
- // Read in filename length
- const uint8* cur_buffer = source;
- uint64 name_length;
- bool success = ReadBuffer(reinterpret_cast<uint8*>(&name_length), &cur_buffer,
- sizeof(name_length), &buffer_remaining);
- if (!success) {
- DLOG(ERROR) << "Buffer overrun";
- return -1;
- }
-
- if (name_length >= kMaxVfsPathname) {
- DLOG(ERROR) << "Filename was longer than the maximum allowed.";
- return -1;
- }
- // Read in filename
- char name[kMaxVfsPathname];
- success = ReadBuffer(reinterpret_cast<uint8*>(name), &cur_buffer,
- static_cast<size_t>(name_length), &buffer_remaining);
- if (!success) {
- DLOG(ERROR) << "Buffer overrun";
- return -1;
- }
- name_.assign(name, static_cast<size_t>(name_length));
-
- // Read in file contents size.
- uint64 file_size;
- success = ReadBuffer(reinterpret_cast<uint8*>(&file_size), &cur_buffer,
- sizeof(file_size), &buffer_remaining);
- if (!success) {
- DLOG(ERROR) << "Buffer overrun";
- return -1;
- }
- // Read in the file contents
- buffer_.resize(static_cast<size_t>(file_size));
-
- if (!buffer_.empty()) {
- // std::vector does not define access to underlying array when empty
- success =
- ReadBuffer(&buffer_[0], &cur_buffer, buffer_.size(), &buffer_remaining);
- if (!success) {
- DLOG(ERROR) << "Buffer overrun";
- return -1;
- }
- }
-
- // Return the number of bytes read
- return static_cast<int>(std::distance(source, cur_buffer));
-}
-
-} // namespace storage
-} // namespace cobalt
diff --git a/src/cobalt/storage/virtual_file.h b/src/cobalt/storage/virtual_file.h
deleted file mode 100644
index 2a88675..0000000
--- a/src/cobalt/storage/virtual_file.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2015 Google Inc. 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 COBALT_STORAGE_VIRTUAL_FILE_H_
-#define COBALT_STORAGE_VIRTUAL_FILE_H_
-
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-
-namespace cobalt {
-namespace storage {
-class VirtualFileSystem;
-
-// VirtualFile implements an in-memory file system. These
-// are managed by VirtualFileSystem for use by SQLite3.
-// The format of a serialized VirtualFile is as follows:
-// 8-byte name length
-// <name length> File name string
-// 8-byte file size
-// <file size> raw bytes.
-class VirtualFile {
- public:
- enum { kMaxVfsPathname = 512 };
-
- int Read(void* dest, int bytes, int offset) const;
- int Write(const void* source, int bytes, int offset);
- int Truncate(int size);
- int Size() const { return static_cast<int>(buffer_.size()); }
-
- private:
- explicit VirtualFile(const std::string& name);
- ~VirtualFile();
-
- // Returns the number of bytes written
- int Serialize(uint8* dest, const bool dry_run);
- // Deserializes a file, returning the size of the file or
- // < 0 on error.
- // |buffer_remaining| is the maximum size of |source|
- int Deserialize(const uint8* source, size_t buffer_remaining);
-
- std::vector<uint8> buffer_;
-
- std::string name_;
-
- friend class VirtualFileSystem;
- DISALLOW_COPY_AND_ASSIGN(VirtualFile);
-};
-
-} // namespace storage
-} // namespace cobalt
-
-#endif // COBALT_STORAGE_VIRTUAL_FILE_H_
diff --git a/src/cobalt/storage/virtual_file_system.cc b/src/cobalt/storage/virtual_file_system.cc
deleted file mode 100644
index 62c602e..0000000
--- a/src/cobalt/storage/virtual_file_system.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2015 Google Inc. 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 "cobalt/storage/virtual_file_system.h"
-
-#include "base/logging.h"
-#include "base/synchronization/lock.h"
-#include "cobalt/storage/virtual_file.h"
-
-#include "starboard/client_porting/poem/string_poem.h"
-
-namespace cobalt {
-namespace storage {
-
-namespace {
-// Update this any time the serialization format changes.
-const char kVersion[] = "SAV0";
-} // namespace
-
-// static
-int VirtualFileSystem::GetHeaderVersion(const SerializedHeader& header) {
- // Copy the version int to a char buffer to avoid endian issues.
- char version[4];
- memcpy(version, &header.version, sizeof(version));
-
- if (memcmp(version, kVersion, 3) != 0) {
- return -1;
- } else if (header.file_size < static_cast<int>(sizeof(SerializedHeader))) {
- return -1;
- } else {
- return version[3] - '0';
- }
-}
-
-// static
-int VirtualFileSystem::GetCurrentVersion() {
- COMPILE_ASSERT(sizeof(kVersion) - 1 == 4, Unexpected_version_size);
- int version;
- memcpy(&version, kVersion, sizeof(version));
- return version;
-}
-
-VirtualFileSystem::VirtualFileSystem() {}
-
-VirtualFileSystem::~VirtualFileSystem() {
- DCHECK(thread_checker_.CalledOnValidThread());
- base::AutoLock lock(file_table_lock_);
- ClearFileTable();
-}
-
-VirtualFile* VirtualFileSystem::Open(const std::string& filename) {
- base::AutoLock lock(file_table_lock_);
- VirtualFile* result = NULL;
- FileTable::iterator it = table_.find(filename);
- if (it != table_.end()) {
- result = it->second;
- } else {
- result = new VirtualFile(filename);
- table_.insert(it, FileTable::value_type(filename, result));
- }
- return result;
-}
-
-std::vector<std::string> VirtualFileSystem::ListFiles() {
- base::AutoLock lock(file_table_lock_);
- std::vector<std::string> files;
- for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
- files.push_back(it->first);
- }
- return files;
-}
-
-void VirtualFileSystem::Delete(const std::string& filename) {
- base::AutoLock lock(file_table_lock_);
- FileTable::iterator it = table_.find(filename);
- if (it != table_.end()) {
- delete it->second;
- table_.erase(it);
- }
-}
-
-int VirtualFileSystem::Serialize(uint8* buffer, bool dry_run) {
- DCHECK(thread_checker_.CalledOnValidThread());
- base::AutoLock lock(file_table_lock_);
- uint8* original = buffer;
-
- // We don't know the total size of the file yet, so defer writing the header.
- buffer += sizeof(SerializedHeader);
- int valid_file_count = 0;
-
- // Serialize each file
- for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
- if (it->second->Size() == 0) {
- continue;
- }
- int file_bytes = it->second->Serialize(buffer, dry_run);
- buffer += file_bytes;
- valid_file_count++;
- }
- const int bytes_written = static_cast<int>(buffer - original);
- if (!dry_run) {
- // Now we can write the header to the beginning of the buffer.
- SerializedHeader header;
- header.version = GetCurrentVersion();
- header.file_count = valid_file_count;
- header.file_size = bytes_written;
- memcpy(original, &header, sizeof(SerializedHeader));
- }
-
- return bytes_written;
-}
-
-bool VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size_in) {
- const uint8* caller_buffer = buffer;
-
- base::AutoLock lock(file_table_lock_);
- ClearFileTable();
-
- if (buffer_size_in < 0) {
- DLOG(ERROR) << "Buffer size must be positive: " << buffer_size_in
- << " < 0.";
- return false;
- }
-
- size_t buffer_size = static_cast<size_t>(buffer_size_in);
-
- // The size of the buffer must be checked before copying the beginning of it
- // into a SerializedHeader.
- if (buffer_size < sizeof(SerializedHeader)) {
- DLOG(ERROR) << "Buffer size " << buffer_size
- << " is too small to contain a SerializedHeader of size "
- << sizeof(SerializedHeader) << "; operation aborted.";
- return false;
- }
-
- // Read in expected number of files
- SerializedHeader header;
- memcpy(&header, buffer, sizeof(SerializedHeader));
- buffer += sizeof(SerializedHeader);
-
- int buffer_version = GetHeaderVersion(header);
- if (buffer_version != 0) {
- // Note: We would handle old versions here, if necessary.
- DLOG(ERROR) << "Attempted to load a different version; operation aborted.";
- return false;
- } else if (static_cast<size_t>(header.file_size) != buffer_size) {
- DLOG(ERROR) << "Buffer size mismatch: " << header.file_size
- << " != " << buffer_size;
- return false;
- }
-
- for (int i = 0; i < header.file_count; ++i) {
- size_t buffer_used = static_cast<size_t>(buffer - caller_buffer);
- if (buffer_size < buffer_used) {
- DLOG(ERROR) << "Buffer overrun deserializing files";
- ClearFileTable();
- return false;
- }
- size_t buffer_remaining = buffer_size - buffer_used;
-
- VirtualFile* file = new VirtualFile("");
-
- int bytes = file->Deserialize(buffer, buffer_remaining);
- if (bytes > 0) {
- buffer += bytes;
- table_[file->name_] = file;
- } else {
- DLOG(WARNING) << "Failed to deserialize virtual file system.";
- delete file;
- ClearFileTable();
- return false;
- }
- }
- return true;
-}
-
-void VirtualFileSystem::ClearFileTable() {
- DCHECK(thread_checker_.CalledOnValidThread());
- file_table_lock_.AssertAcquired();
- for (FileTable::iterator it = table_.begin(); it != table_.end(); ++it) {
- delete it->second;
- }
- table_.clear();
-}
-
-} // namespace storage
-} // namespace cobalt
diff --git a/src/cobalt/storage/virtual_file_system.h b/src/cobalt/storage/virtual_file_system.h
deleted file mode 100644
index f149576..0000000
--- a/src/cobalt/storage/virtual_file_system.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2015 Google Inc. 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 COBALT_STORAGE_VIRTUAL_FILE_SYSTEM_H_
-#define COBALT_STORAGE_VIRTUAL_FILE_SYSTEM_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
-
-namespace cobalt {
-namespace storage {
-
-class VirtualFile;
-
-// Implements a simple virtual filesystem, primarily intended to
-// be used for providing an in-memory filesystem that SQLite can write to,
-// and allowing that filesystem to be saved out into a single memory buffer.
-// Contains a mapping from paths to their corresponding memory blocks.
-// VirtualFileSystem must be serialized, deserialized, and destroyed on the
-// same thread it was created on.
-// Open/Delete are thread-safe so that SQL can run on any thread.
-class VirtualFileSystem {
- public:
- struct SerializedHeader {
- int32 version;
- int32 file_size;
- int32 file_count;
- };
- // Returns the version of the VirtualFileSystem header in buffer.
- // -1 if buffer is invalid.
- static int GetHeaderVersion(const SerializedHeader& header);
- static int GetCurrentVersion();
-
- VirtualFileSystem();
- ~VirtualFileSystem();
-
- // Serializes the file system out to a single contiguous buffer.
- // A dry run only calculates the number of bytes needed.
- // Returns the number of bytes written.
- int Serialize(uint8* buffer, bool dry_run);
-
- // Deserializes a file system from a memory buffer.
- // Returns false on failure.
- bool Deserialize(const uint8* buffer, int buffer_size);
-
- // Simple file open. Will create a file if it does not exist, and files are
- // always readable and writable.
- VirtualFile* Open(const std::string& filename);
-
- std::vector<std::string> ListFiles();
-
- void Delete(const std::string& filename);
-
- private:
- void ClearFileTable();
- void SQLRegister();
- void SQLUnregister();
-
- typedef std::map<std::string, VirtualFile*> FileTable;
- FileTable table_;
- // Certain operations (serialize, deserialize) must run on the same thread.
- base::ThreadChecker thread_checker_;
- // Lock to protect the file table. We want to support SQL queries on any
- // thread.
- base::Lock file_table_lock_;
-};
-
-} // namespace storage
-} // namespace cobalt
-
-#endif // COBALT_STORAGE_VIRTUAL_FILE_SYSTEM_H_
diff --git a/src/cobalt/storage/virtual_file_system_test.cc b/src/cobalt/storage/virtual_file_system_test.cc
deleted file mode 100644
index f325261..0000000
--- a/src/cobalt/storage/virtual_file_system_test.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2015 Google Inc. 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 "cobalt/storage/virtual_file.h"
-#include "cobalt/storage/virtual_file_system.h"
-
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include "starboard/client_porting/poem/string_poem.h"
-
-namespace cobalt {
-namespace storage {
-
-class VirtualFileSystemTest : public testing::Test {
- protected:
- VirtualFileSystem vfs_;
-};
-
-TEST_F(VirtualFileSystemTest, WriteAndRead) {
- VirtualFile* file = vfs_.Open("file1.tmp");
- ASSERT_TRUE(file);
- EXPECT_EQ(0, file->Size());
-
- int expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- file->Write(expected, sizeof(expected), 0);
-
- char out_data[128];
- int bytes = file->Read(out_data, sizeof(out_data), 0);
- EXPECT_EQ(bytes, sizeof(expected));
- EXPECT_EQ(0, memcmp(expected, out_data, sizeof(expected)));
-}
-
-TEST_F(VirtualFileSystemTest, ReadWriteOffsets) {
- VirtualFile* file = vfs_.Open("file1.tmp");
- ASSERT_TRUE(file);
- EXPECT_EQ(0, file->Size());
-
- char expected[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- file->Write(expected, sizeof(expected), 0);
-
- // Seek to position 4 and read bytes.
- char out_data[128];
- int bytes = file->Read(out_data, sizeof(out_data), 4);
-
- const int kDataSize = 6;
- EXPECT_EQ(kDataSize, bytes);
- EXPECT_EQ(0, memcmp(&expected[4], out_data, kDataSize));
-
- // Write some bytes into the middle
- file->Write(expected, 3, 3);
- char expected1[] = {0, 1, 2, 0, 1, 2, 6, 7, 8, 9};
- EXPECT_EQ(sizeof(expected1), file->Size());
-
- bytes = file->Read(out_data, sizeof(out_data), 0);
- EXPECT_EQ(0, memcmp(expected1, out_data, static_cast<size_t>(bytes)));
-
- // Try to read past the end.
- const int kOffsetPastEnd = 100000;
- bytes = file->Read(out_data, sizeof(out_data), kOffsetPastEnd);
- EXPECT_EQ(0, bytes);
-
- // Try to write past the end.
- file->Write(expected, sizeof(expected), kOffsetPastEnd);
- EXPECT_EQ(kOffsetPastEnd + sizeof(expected), file->Size());
-
- // Make sure the write past the end worked.
- bytes = file->Read(out_data, sizeof(out_data), kOffsetPastEnd);
- EXPECT_EQ(0, memcmp(expected, out_data, static_cast<size_t>(bytes)));
-}
-
-TEST_F(VirtualFileSystemTest, Open) {
- // Create a few files and write some data
- VirtualFile* file = vfs_.Open("file1.tmp");
- ASSERT_TRUE(file);
- EXPECT_EQ(0, file->Size());
-
- // Write a few bytes of random data.
- file->Write("test", 4, 0);
- EXPECT_EQ(4, file->Size());
-
- // Open a new file
- file = vfs_.Open("file2.tmp");
- ASSERT_TRUE(file);
- EXPECT_EQ(0, file->Size());
-
- // Try to reopen the existing file.
- file = vfs_.Open("file1.tmp");
- ASSERT_TRUE(file);
- EXPECT_EQ(4, file->Size());
-}
-
-TEST_F(VirtualFileSystemTest, Truncate) {
- VirtualFile* file = vfs_.Open("file1.tmp");
- ASSERT_TRUE(file);
- EXPECT_EQ(0, file->Size());
-
- const char* data = "test";
- char file_contents[4];
- // Write a few bytes of random data.
- file->Write(data, 4, 0);
- EXPECT_EQ(4, file->Size());
- int bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(4, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
-
- file->Truncate(3);
- EXPECT_EQ(3, file->Size());
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(3, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
-
- file->Truncate(0);
- EXPECT_EQ(0, file->Size());
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(0, bytes);
-}
-
-TEST_F(VirtualFileSystemTest, SerializeDeserialize) {
- // Create a few files and write some data
- VirtualFile* file = vfs_.Open("file1.tmp");
- EXPECT_TRUE(file != NULL);
- const char data1[] = "abc";
- int data1_size = 3;
- file->Write(data1, data1_size, 0);
-
- file = vfs_.Open("file2.tmp");
- EXPECT_TRUE(file != NULL);
- const char data2[] = "defg";
- int data2_size = 4;
- file->Write(data2, data2_size, 0);
-
- file = vfs_.Open("file3.tmp");
- EXPECT_TRUE(file != NULL);
- const char data3[] = "";
- int data3_size = 0;
- file->Write(data3, data3_size, 0);
-
- // First perform a dry run to figure out how much space we need.
- int bytes = vfs_.Serialize(NULL, true /*dry run*/);
- scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
-
- // Now serialize and deserialize
- vfs_.Serialize(buffer.get(), false /*dry run*/);
-
- // Deserialize the data into a new vfs
- VirtualFileSystem new_vfs;
- ASSERT_TRUE(new_vfs.Deserialize(buffer.get(), bytes));
-
- // Make sure the new vfs contains all the expected data.
- char file_contents[VirtualFile::kMaxVfsPathname];
- file = new_vfs.Open("file1.tmp");
- EXPECT_TRUE(file != NULL);
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(data1_size, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
-
- file = new_vfs.Open("file2.tmp");
- EXPECT_TRUE(file != NULL);
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(data2_size, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
-
- file = new_vfs.Open("file3.tmp");
- EXPECT_TRUE(file != NULL);
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(data3_size, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data3, static_cast<size_t>(bytes)));
-}
-
-TEST_F(VirtualFileSystemTest, DeserializeTruncated) {
- // Create a few files and write some data
- VirtualFile* file = vfs_.Open("file1.tmp");
- EXPECT_TRUE(file != NULL);
- const char data1[] = "abc";
- int data1_size = 3;
- file->Write(data1, data1_size, 0);
-
- file = vfs_.Open("file2.tmp");
- EXPECT_TRUE(file != NULL);
- const char data2[] = "defg";
- int data2_size = 4;
- file->Write(data2, data2_size, 0);
-
- // First perform a dry run to figure out how much space we need.
- int bytes = vfs_.Serialize(NULL, true /*dry run*/);
- scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
-
- // Now serialize and deserialize
- vfs_.Serialize(buffer.get(), false /*dry run*/);
-
- for (int i = 1; i < bytes; i++) {
- // Corrupt the header
- VirtualFileSystem::SerializedHeader header;
- memcpy(&header, buffer.get(), sizeof(header));
- header.file_size = header.file_size - i;
- memcpy(buffer.get(), &header, sizeof(header));
-
- // Deserialize the data into a new vfs
- VirtualFileSystem new_vfs;
- EXPECT_FALSE(new_vfs.Deserialize(buffer.get(), bytes - i));
- }
-}
-
-TEST_F(VirtualFileSystemTest, DeserializeBadData) {
- scoped_array<uint8> buffer(new uint8[0]);
- EXPECT_FALSE(vfs_.Deserialize(buffer.get(), 0));
- EXPECT_FALSE(vfs_.Deserialize(buffer.get(), -1));
- buffer.reset(new uint8[1]);
- EXPECT_FALSE(vfs_.Deserialize(buffer.get(), 1));
- buffer.reset(new uint8[sizeof(VirtualFileSystem::SerializedHeader)]);
- VirtualFileSystem::SerializedHeader header = {};
- header.version = -1;
- memcpy(buffer.get(), &header, sizeof(header));
- EXPECT_FALSE(vfs_.Deserialize(buffer.get(),
- sizeof(VirtualFileSystem::SerializedHeader)));
- memcpy(&(header.version), "SAV0", sizeof(header.version));
- header.file_size = sizeof(VirtualFileSystem::SerializedHeader);
- memcpy(buffer.get(), &header, sizeof(header));
- EXPECT_TRUE(vfs_.Deserialize(buffer.get(),
- sizeof(VirtualFileSystem::SerializedHeader)));
- ASSERT_EQ(0, vfs_.ListFiles().size());
-}
-
-TEST_F(VirtualFileSystemTest, GetHeaderVersion) {
- VirtualFileSystem::SerializedHeader header;
- memset(&header, 0, sizeof(header));
- header.file_size = sizeof(header);
-
- COMPILE_ASSERT(sizeof(header.version) == sizeof("SAV0") - 1,
- Invalid_header_version_size);
- memcpy(&header.version, "XYZW", sizeof(header.version));
- EXPECT_EQ(-1, VirtualFileSystem::GetHeaderVersion(header));
- memcpy(&header.version, "SAV0", sizeof(header.version));
- EXPECT_EQ(0, VirtualFileSystem::GetHeaderVersion(header));
- memcpy(&header.version, "SAV1", sizeof(header.version));
- EXPECT_EQ(1, VirtualFileSystem::GetHeaderVersion(header));
-}
-
-} // namespace storage
-} // namespace cobalt
diff --git a/src/cobalt/system_window/input_event.h b/src/cobalt/system_window/input_event.h
index ce7dd5f..42548b9 100644
--- a/src/cobalt/system_window/input_event.h
+++ b/src/cobalt/system_window/input_event.h
@@ -20,6 +20,7 @@
#include "cobalt/base/event.h"
#include "cobalt/math/point_f.h"
#include "starboard/event.h"
+#include "starboard/time.h"
namespace cobalt {
namespace system_window {
@@ -55,20 +56,22 @@
kForwardButton = 1 << 8,
};
- InputEvent(Type type, int device_id, int key_code, uint32 modifiers,
- bool is_repeat, const math::PointF& position = math::PointF(),
+ InputEvent(SbTimeMonotonic timestamp, Type type, int device_id,
+ int key_code, uint32 modifiers, bool is_repeat,
+ const math::PointF& position = math::PointF(),
const math::PointF& delta = math::PointF()
#if SB_API_VERSION >= 6
- ,
+ ,
float pressure = 0, const math::PointF& size = math::PointF(),
const math::PointF& tilt = math::PointF()
#endif
#if SB_HAS(ON_SCREEN_KEYBOARD)
- ,
+ ,
const std::string& input_text = ""
#endif // SB_HAS(ON_SCREEN_KEYBOARD)
)
- : type_(type),
+ : timestamp_(timestamp),
+ type_(type),
device_id_(device_id),
key_code_(key_code),
modifiers_(modifiers),
@@ -90,6 +93,7 @@
~InputEvent() {}
+ SbTimeMonotonic timestamp() const { return timestamp_; }
Type type() const { return type_; }
int key_code() const { return key_code_; }
int device_id() const { return device_id_; }
@@ -109,6 +113,7 @@
BASE_EVENT_SUBCLASS(InputEvent);
private:
+ SbTimeMonotonic timestamp_;
Type type_;
int device_id_;
int key_code_;
diff --git a/src/cobalt/system_window/system_window.cc b/src/cobalt/system_window/system_window.cc
index f9aab51..7c9de04 100644
--- a/src/cobalt/system_window/system_window.cc
+++ b/src/cobalt/system_window/system_window.cc
@@ -99,6 +99,21 @@
void SystemWindow::DispatchInputEvent(const SbInputData& data,
InputEvent::Type type, bool is_repeat) {
+ // Use the current time unless it was overridden.
+ SbTimeMonotonic timestamp = 0;
+
+#if SB_API_VERSION >= SB_INPUT_TIMESTAMP_API_VERSION
+ bool use_input_timestamp =
+ SbSystemHasCapability(kSbSystemCapabilitySetsInputTimestamp);
+ if (use_input_timestamp) {
+ timestamp = data.timestamp;
+ }
+#endif
+
+ if (timestamp == 0) {
+ timestamp = SbTimeGetMonotonicNow();
+ }
+
// Starboard handily uses the Microsoft key mapping, which is also what Cobalt
// uses.
int key_code = static_cast<int>(data.key);
@@ -134,7 +149,8 @@
#if SB_HAS(ON_SCREEN_KEYBOARD)
scoped_ptr<InputEvent> input_event(
- new InputEvent(type, data.device_id, key_code, modifiers, is_repeat,
+ new InputEvent(timestamp, type, data.device_id,
+ key_code, modifiers, is_repeat,
math::PointF(data.position.x, data.position.y),
math::PointF(data.delta.x, data.delta.y), pressure,
math::PointF(data.size.x, data.size.y),
@@ -142,7 +158,8 @@
data.input_text ? data.input_text : ""));
#else // SB_HAS(ON_SCREEN_KEYBOARD)
scoped_ptr<InputEvent> input_event(
- new InputEvent(type, data.device_id, key_code, modifiers, is_repeat,
+ new InputEvent(timestamp, type, data.device_id,
+ key_code, modifiers, is_repeat,
math::PointF(data.position.x, data.position.y),
math::PointF(data.delta.x, data.delta.y), pressure,
math::PointF(data.size.x, data.size.y),
@@ -150,8 +167,9 @@
#endif // SB_HAS(ON_SCREEN_KEYBOARD)
#else
scoped_ptr<InputEvent> input_event(
- new InputEvent(type, data.device_id, key_code, data.key_modifiers,
- is_repeat, math::PointF(data.position.x, data.position.y),
+ new InputEvent(timestamp, type, data.device_id,
+ key_code, data.key_modifiers, is_repeat,
+ math::PointF(data.position.x, data.position.y),
math::PointF(data.delta.x, data.delta.y)));
#endif
event_dispatcher()->DispatchEvent(input_event.PassAs<base::Event>());
diff --git a/src/cobalt/test/document_loader.h b/src/cobalt/test/document_loader.h
index 85d289a..f85693a 100644
--- a/src/cobalt/test/document_loader.h
+++ b/src/cobalt/test/document_loader.h
@@ -56,15 +56,16 @@
dom_stat_tracker_(new dom::DomStatTracker("IsDisplayedTest")),
resource_provider_(resource_provider_stub_.get()),
html_element_context_(
- &fetcher_factory_, css_parser_.get(), dom_parser_.get(),
- NULL /* can_play_type_handler */,
+ &fetcher_factory_, loader_factory_.get(), css_parser_.get(),
+ dom_parser_.get(), NULL /* can_play_type_handler */,
NULL /* web_media_player_factory */, &script_runner_,
NULL /* script_value_factory */, NULL /* media_source_registry */,
&resource_provider_, NULL /* animated_image_tracker */,
image_cache_.get(), NULL /* reduced_image_cache_capacity_manager */,
NULL /* remote_font_cache */, NULL /* mesh_cache */,
dom_stat_tracker_.get(), "" /* language */,
- base::kApplicationStateStarted) {}
+ base::kApplicationStateStarted,
+ NULL /* synchronous_loader_interrupt */) {}
void Load(const GURL& url) {
// Load the document in a nested message loop.
dom::Document::Options options(url);
diff --git a/src/cobalt/test/empty_document.h b/src/cobalt/test/empty_document.h
index bb36626..08817df 100644
--- a/src/cobalt/test/empty_document.h
+++ b/src/cobalt/test/empty_document.h
@@ -31,10 +31,10 @@
EmptyDocument()
: css_parser_(css_parser::Parser::Create()),
dom_stat_tracker_(new dom::DomStatTracker("EmptyDocument")),
- html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
+ html_element_context_(NULL, NULL, css_parser_.get(), NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted),
+ NULL, dom_stat_tracker_.get(), "",
+ base::kApplicationStateStarted, NULL),
document_(new dom::Document(&html_element_context_)) {}
dom::Document* document() { return document_.get(); }
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index 77ad2cd..47b9f2c 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -15,6 +15,6 @@
#define COBALT_VERSION_H_
// Cobalt release number.
-#define COBALT_VERSION "16"
+#define COBALT_VERSION "17"
#endif // COBALT_VERSION_H_
diff --git a/src/cobalt/webdriver/get_element_text_test.cc b/src/cobalt/webdriver/get_element_text_test.cc
index bb50bcb..88ca53b 100644
--- a/src/cobalt/webdriver/get_element_text_test.cc
+++ b/src/cobalt/webdriver/get_element_text_test.cc
@@ -40,10 +40,10 @@
GetElementTextTest()
: css_parser_(css_parser::Parser::Create()),
dom_stat_tracker_(new dom::DomStatTracker("GetElementTextTest")),
- html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
+ html_element_context_(NULL, NULL, css_parser_.get(), NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- dom_stat_tracker_.get(), "",
- base::kApplicationStateStarted) {}
+ NULL, dom_stat_tracker_.get(), "",
+ base::kApplicationStateStarted, NULL) {}
void SetUp() override {
dom::Document::Options options;
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc
index 74ef7d1..b2fe65f 100644
--- a/src/cobalt/webdriver/server.cc
+++ b/src/cobalt/webdriver/server.cc
@@ -18,6 +18,7 @@
#include <vector>
#include "base/bind.h"
+#include "base/debug/trace_event.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/string_util.h"
@@ -200,8 +201,9 @@
void WebDriverServer::OnHttpRequest(int connection_id,
const net::HttpServerRequestInfo& info) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ TRACE_EVENT0("cobalt::webdriver", "WebDriverServer::OnHttpRequest()");
+ DCHECK(thread_checker_.CalledOnValidThread());
std::string path = info.path;
size_t query_position = path.find("?");
// Discard any URL variables from the path
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc
index b4c4c60..55da6e9 100644
--- a/src/cobalt/webdriver/web_driver_module.cc
+++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -19,6 +19,7 @@
#include "base/base64.h"
#include "base/bind.h"
+#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/values.h"
#include "cobalt/webdriver/dispatcher.h"
@@ -150,6 +151,8 @@
void OnPNGEncodeComplete(ScreenshotResultContext* context,
const scoped_refptr<loader::image::EncodedStaticImage>&
compressed_image_data) {
+ TRACE_EVENT0("cobalt::WebDriver", "WebDriverServer::onPNGEncodeComplete()");
+
DCHECK(context);
DCHECK(compressed_image_data->GetImageFormat() ==
loader::image::EncodedStaticImage::ImageFormat::kPNG);
@@ -562,6 +565,7 @@
const base::Value* parameters,
const WebDriverDispatcher::PathVariableMap* path_variables,
scoped_ptr<WebDriverDispatcher::CommandResultHandler> result_handler) {
+ TRACE_EVENT0("cobalt::WebDriver", "WebDriverModule::RequestScreenshot()");
DCHECK(thread_checker_.CalledOnValidThread());
SessionDriver* session_driver = LookUpSessionDriverOrReturnInvalidResponse(
diff --git a/src/cobalt/webdriver/webdriver.gyp b/src/cobalt/webdriver/webdriver.gyp
index 5d35681..eb7165b 100644
--- a/src/cobalt/webdriver/webdriver.gyp
+++ b/src/cobalt/webdriver/webdriver.gyp
@@ -94,8 +94,6 @@
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/dom/dom.gyp:dom_testing',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/net/net.gyp:http_server',
'<(DEPTH)/third_party/icu/icu.gyp:icuuc',
],
diff --git a/src/cobalt/websocket/websocket.gyp b/src/cobalt/websocket/websocket.gyp
index dfc26e6..a5b1347 100644
--- a/src/cobalt/websocket/websocket.gyp
+++ b/src/cobalt/websocket/websocket.gyp
@@ -41,7 +41,6 @@
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
],
},
diff --git a/src/cobalt/xhr/xhr.gyp b/src/cobalt/xhr/xhr.gyp
index 7305802..3b66d98 100644
--- a/src/cobalt/xhr/xhr.gyp
+++ b/src/cobalt/xhr/xhr.gyp
@@ -30,7 +30,6 @@
],
'dependencies': [
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/cobalt/dom_parser/dom_parser.gyp:dom_parser',
],
'conditions': [
@@ -62,7 +61,6 @@
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
'<(DEPTH)/cobalt/test/test.gyp:run_all_unittests',
'<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h
index 80c391f..08a1aaf 100644
--- a/src/starboard/atomic.h
+++ b/src/starboard/atomic.h
@@ -30,6 +30,9 @@
extern "C" {
#endif
+#if SB_API_VERSION >= SB_INTRODUCE_ATOMIC8_VERSION
+typedef int8_t SbAtomic8;
+#endif
typedef int32_t SbAtomic32;
// Atomically execute:
@@ -85,6 +88,15 @@
static SbAtomic32 SbAtomicAcquire_Load(volatile const SbAtomic32* ptr);
static SbAtomic32 SbAtomicRelease_Load(volatile const SbAtomic32* ptr);
+#if SB_API_VERSION >= SB_INTRODUCE_ATOMIC8_VERSION
+// Overloaded functions for Atomic8.
+static SbAtomic8 SbAtomicRelease_CompareAndSwap8(volatile SbAtomic8* ptr,
+ SbAtomic8 old_value,
+ SbAtomic8 new_value);
+static void SbAtomicNoBarrier_Store8(volatile SbAtomic8* ptr, SbAtomic8 value);
+static SbAtomic8 SbAtomicNoBarrier_Load8(volatile const SbAtomic8* ptr);
+#endif
+
// 64-bit atomic operations (only available on 64-bit processors).
#if SB_HAS(64_BIT_ATOMICS)
typedef int64_t SbAtomic64;
@@ -249,6 +261,22 @@
namespace starboard {
namespace atomic {
+#if SB_API_VERSION >= SB_INTRODUCE_ATOMIC8_VERSION
+inline SbAtomic8 Release_CompareAndSwap(volatile SbAtomic8* ptr,
+ SbAtomic8 old_value,
+ SbAtomic8 new_value) {
+ return SbAtomicRelease_CompareAndSwap8(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile SbAtomic8* ptr, SbAtomic8 value) {
+ SbAtomicNoBarrier_Store8(ptr, value);
+}
+
+inline SbAtomic8 NoBarrier_Load(volatile const SbAtomic8* ptr) {
+ return SbAtomicNoBarrier_Load8(ptr);
+}
+#endif
+
inline SbAtomic32 NoBarrier_CompareAndSwap(volatile SbAtomic32* ptr,
SbAtomic32 old_value,
SbAtomic32 new_value) {
diff --git a/src/starboard/build/base_configuration.gypi b/src/starboard/build/base_configuration.gypi
index 89431f2..0a77009 100644
--- a/src/starboard/build/base_configuration.gypi
+++ b/src/starboard/build/base_configuration.gypi
@@ -109,6 +109,9 @@
# Used to pick a proper media platform.
'sb_media_platform%': 'starboard',
+ # Used to indicate that the player is filter based.
+ 'sb_filter_based_player%': 1,
+
# Compiler configuration.
# The following variables are used to specify compiler and linker
diff --git a/src/starboard/build/list_dmp_files.py b/src/starboard/build/list_dmp_files.py
new file mode 100644
index 0000000..ee7cde8
--- /dev/null
+++ b/src/starboard/build/list_dmp_files.py
@@ -0,0 +1,39 @@
+# Copyright 2018 Google Inc. 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.
+"""List dmp files corresponding to dmp.sha1 files found in a directory.
+
+Since the output of this script is intended to be use by GYP, all resulting
+paths are using Unix-style forward slashes.
+"""
+
+import os
+import sys
+
+import _env # pylint: disable=unused-import
+from starboard.tools import paths
+
+
+def main(argv):
+ dmp_files = []
+ directory_from_repo_root = argv[1]
+ dmp_sha1_dir = os.path.join(paths.REPOSITORY_ROOT, directory_from_repo_root)
+ for filename in os.listdir(dmp_sha1_dir):
+ if filename[-8:] == 'dmp.sha1':
+ dmp_files.append((os.path.join(dmp_sha1_dir, filename)[:-5]).replace(
+ '\\', '/'))
+ print ' '.join(dmp_files)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/src/starboard/client_porting/poem/eztime_poem.h b/src/starboard/client_porting/poem/eztime_poem.h
index 31d301a..c66a8fc 100644
--- a/src/starboard/client_porting/poem/eztime_poem.h
+++ b/src/starboard/client_porting/poem/eztime_poem.h
@@ -33,12 +33,19 @@
#undef timeval
#define timeval EzTimeValue
+#undef gettimeofday
#define gettimeofday(a, b) EzTimeValueGetNow(a, b)
+#undef gmtime_r
#define gmtime_r(a, b) EzTimeTExplodeUTC(a, b)
+#undef localtime_r
#define localtime_r(a, b) EzTimeTExplodeLocal(a, b)
+#undef mktime
#define mktime(x) EzTimeTImplodeLocal(x)
+#undef time
#define time(x) EzTimeTGetNow(x)
+#undef timegm
#define timegm(x) EzTimeTImplodeUTC(x)
+#undef timelocal
#define timelocal(x) EzTimeTImplodeLocal(x)
#endif // POEM_NO_EMULATION
diff --git a/src/starboard/client_porting/poem/math_poem.h b/src/starboard/client_porting/poem/math_poem.h
index 6a177bf..0dbf996 100644
--- a/src/starboard/client_porting/poem/math_poem.h
+++ b/src/starboard/client_porting/poem/math_poem.h
@@ -27,7 +27,7 @@
// Takes floor of a float |f|. Meant to be a drop-in replacement for |floorf|
static SB_C_INLINE float PoemSingleFloor(const float f) {
- double d(f);
+ double d = (double)f;
return SbDoubleFloor(d);
}
@@ -38,22 +38,38 @@
#if !defined(POEM_NO_EMULATION)
#include <math.h>
+#undef fabs
#define fabs(x) SbDoubleAbsolute(x)
+#undef floor
#define floor(x) SbDoubleFloor(x)
+#undef floorf
#define floorf(x) PoemSingleFloor(x)
+#undef pow
#define pow(x, y) SbDoubleExponent(x, y)
+#undef ceil
#define ceil(x) ceil(x)
+#undef fmod
#define fmod(x, y) fmod(x, y)
+#undef modf
#define modf(x, y) modf(x, y)
+#undef log
#define log(x) log(x)
+#undef sqrt
#define sqrt(x) sqrt(x)
+#undef sin
#define sin(x) sin(x)
+#undef cos
#define cos(x) cos(x)
+#undef tan
#define tan(x) tan(x)
+#undef atan
#define atan(x) atan(x)
+#undef atan2
#define atan2(x, y) atan2(x, y)
+#undef asin
#define asin(x) asin(x)
+#undef acos
#define acos(x) acos(x)
#endif // POEM_NO_EMULATION
diff --git a/src/starboard/client_porting/poem/stdio_poem.h b/src/starboard/client_porting/poem/stdio_poem.h
index eab3f05..3b3530f 100644
--- a/src/starboard/client_porting/poem/stdio_poem.h
+++ b/src/starboard/client_porting/poem/stdio_poem.h
@@ -29,14 +29,23 @@
// the following functions can have variable number of arguments
// and, out of compatibility concerns, we chose to not use
// __VA_ARGS__ functionality.
+#undef vsnprintf
#define vsnprintf SbStringFormat
+#undef snprintf
#define snprintf SbStringFormatF
+#undef sprintf
#define sprintf SbStringFormatUnsafeF
+#undef vsscanf
#define vsscanf SbStringScan
+#undef sscanf
#define sscanf SbStringScanF
+#undef malloc
#define malloc(sz) SbMemoryAllocate(sz)
+#undef calloc
#define calloc(c, s) SbMemoryCalloc(c, s)
+#undef free
#define free(a) SbMemoryDeallocate(a)
+#undef realloc
#define realloc(m, sz) SbMemoryReallocate(m, sz)
#endif // POEM_NO_EMULATION
diff --git a/src/starboard/client_porting/poem/stdlib_poem.h b/src/starboard/client_porting/poem/stdlib_poem.h
index d407d17..c096aa0 100644
--- a/src/starboard/client_porting/poem/stdlib_poem.h
+++ b/src/starboard/client_porting/poem/stdlib_poem.h
@@ -44,16 +44,23 @@
#endif
// number conversion functions
+#undef strtol
#define strtol(s, o, b) SbStringParseSignedInteger(s, o, b)
+#undef atoi
#define atoi(v) SbStringAToI(v)
+#undef atol
#define atol(v) SbStringAToL(v)
-#define strtol(s, o, b) SbStringParseSignedInteger(s, o, b)
+#undef strtoul
#define strtoul(s, o, b) SbStringParseUnsignedInteger(s, o, b)
+#undef strtoull
#define strtoull(s, o, b) SbStringParseUInt64(s, o, b)
+#undef strtod
#define strtod(s, o) SbStringParseDouble(s, o)
+#undef qsort
#define qsort(b, ec, ew, c) SbSystemSort(b, ec, ew, c);
+#undef abs
#define abs(x) PoemAbs(x)
#endif // POEM_NO_EMULATION
diff --git a/src/starboard/client_porting/poem/string_poem.h b/src/starboard/client_porting/poem/string_poem.h
index f5f3434..3eb07dc 100644
--- a/src/starboard/client_porting/poem/string_poem.h
+++ b/src/starboard/client_porting/poem/string_poem.h
@@ -160,22 +160,38 @@
#if !defined(POEM_NO_EMULATION)
+#undef strlen
#define strlen(s) SbStringGetLength(s)
+#undef strcpy
#define strcpy(o, s) SbStringCopyUnsafe(o, s)
+#undef strncpy
#define strncpy(o, s, ds) PoemStringCopyN(o, s, ds)
+#undef strcat
#define strcat(o, s) PoemStringConcatUnsafe(o, s)
+#undef strncat
#define strncat(o, s, ds) PoemStringConcat(o, s, ds)
+#undef strdup
#define strdup(s) SbStringDuplicate(s)
+#undef strchr
#define strchr(s, c) PoemFindCharacterInString(s, c)
+#undef strrchr
#define strrchr(s, c) PoemFindLastCharacterInString(s, c)
+#undef strstr
#define strstr(s, c) SbStringFindString(s, c)
+#undef strncmp
#define strncmp(s1, s2, c) SbStringCompare(s1, s2, c)
+#undef strcmp
#define strcmp(s1, s2) SbStringCompareAll(s1, s2)
+#undef memchr
#define memchr(s, c, n) SbMemoryFindByte(s, c, n)
+#undef memset
#define memset(s, c, n) SbMemorySet(s, c, n)
+#undef memcpy
#define memcpy(d, s, c) SbMemoryCopy(d, s, c)
+#undef memcmp
#define memcmp(s1, s2, n) SbMemoryCompare(s1, s2, n)
+#undef memmove
#define memmove(d, s, n) SbMemoryMove(d, s, n)
#endif // POEM_NO_EMULATION
diff --git a/src/starboard/client_porting/poem/strings_poem.h b/src/starboard/client_porting/poem/strings_poem.h
index 8f98a06..b3dcd8c 100644
--- a/src/starboard/client_porting/poem/strings_poem.h
+++ b/src/starboard/client_porting/poem/strings_poem.h
@@ -23,7 +23,9 @@
#include "starboard/string.h"
+#undef strcasecmp
#define strcasecmp(s1, s2) SbStringCompareNoCase(s1, s2)
+#undef strncasecmp
#define strncasecmp(s1, s2) SbStringCompareNoCaseN(s1, s2)
#endif // POEM_NO_EMULATION
diff --git a/src/starboard/client_porting/poem/strnlen_poem.h b/src/starboard/client_porting/poem/strnlen_poem.h
index fb2d674..d2413c3 100644
--- a/src/starboard/client_porting/poem/strnlen_poem.h
+++ b/src/starboard/client_porting/poem/strnlen_poem.h
@@ -28,6 +28,7 @@
return i;
}
+#undef strnlen
#define strnlen(s, maxlen) StringGetLengthFixed(s, maxlen)
#endif // STARBOARD_CLIENT_PORTING_POEM_STRNLEN_POEM_H_
diff --git a/src/starboard/client_porting/poem/wchar_poem.h b/src/starboard/client_porting/poem/wchar_poem.h
index 68af73c..18ec597 100644
--- a/src/starboard/client_porting/poem/wchar_poem.h
+++ b/src/starboard/client_porting/poem/wchar_poem.h
@@ -23,7 +23,9 @@
#include "starboard/string.h"
+#undef vswprintf
#define vswprintf SbStringFormatWide
+#undef wcsncmp
#define wcsncmp(s1, s2, c) SbStringCompareWide(s1, s2, c)
#endif // POEM_NO_EMULATION
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 6bd70f3..4dcb0ff 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -68,6 +68,9 @@
// // exposes functionality for my new feature.
// #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION
+// API version where compiling player_filter_tests is required.
+#define SB_PLAYER_FILTER_TESTS_REQUIRED_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
// API version where SbMediaTime is deprecated (for SbTime).
#define SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION SB_EXPERIMENTAL_API_VERSION
@@ -80,6 +83,9 @@
// Minimum API version where supporting audioless video playback is required.
#define SB_AUDIOLESS_VIDEO_API_VERSION SB_EXPERIMENTAL_API_VERSION
+// Minimum API version where supporting audio only video playback is required.
+#define SB_AUDIO_ONLY_VIDEO_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
// Minimum API version where calling SbPlayerCreate mutiple times (without
// calling SbPlayerDestroy in between) must not crash, and likewise calling
// SbAudioSinkCreate multiple times (without calling SbAudioSinkDestroy in
@@ -88,11 +94,21 @@
// kSbAudionSinkInvalid if additional audio sinks are not supported.
#define SB_MULTI_PLAYER_API_VERSION SB_EXPERIMENTAL_API_VERSION
-// API version where DRM session closed callback is required.
-// Add a callback to SbDrmCreateSystem that allows a DRM system to
-// signal that a DRM session has closed from the Starboard layer.
-// Previously, DRM sessions could only be closed from the application layer.
-#define SB_DRM_SESSION_CLOSED_API_VERSION SB_EXPERIMENTAL_API_VERSION
+// API version where passing NULL callbacks to SbPlayerCreate or
+// SbPlayerCreateWithUrl or SbDrmCreateSystem must result in invalid return
+// (|kSbPlayerInvalid| or |kSbDrmSystemInvalid| appropriately).
+#define SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// API version where the following DRM refinements are available.
+// 1. Add a callback to SbDrmCreateSystem that allows a DRM system to
+// signal that a DRM session has closed from the Starboard layer.
+// Previously, DRM sessions could only be closed from the application
+// layer.
+// 2. Allow calling |SbDrmSessionUpdateRequestFunc| and
+// |SbDrmSessionUpdatedFunc| with extra status and optional error message.
+// 3. Add request type parameter to |SbDrmSessionUpdateRequestFunc| to support
+// individualization, license renewal, and license release.
+#define SB_DRM_REFINEMENT_API_VERSION SB_EXPERIMENTAL_API_VERSION
// API version where kSbSystemPathSourceDirectory is removed.
// Test code looking for its static input files should instead use the `test`
@@ -134,6 +150,20 @@
// Please see the comment in audio_sink.h for more details.
#define SB_ASYNC_AUDIO_FRAMES_REPORTING_API_VERSION SB_EXPERIMENTAL_API_VERSION
+// API version where SbAtomic8 type and memory access functions for it are
+// introduced. They are required to be implemented if a platform wants to use
+// V8 as its JavaScript engine.
+#define SB_INTRODUCE_ATOMIC8_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// API version where SbMemoryProtect was introduced. Allows memory
+// permissions to be changed with `SbMemoryProtect` after they are mapped
+// with `SbMemoryMap`.
+#define SB_MEMORY_PROTECT_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// API version where SbInputData allows propagating a |timestamp| for the input
+// event to the dom Event.
+#define SB_INPUT_TIMESTAMP_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
// --- Release Candidate Feature Defines -------------------------------------
// --- Common Detected Features ----------------------------------------------
@@ -612,7 +642,7 @@
#endif // defined(SB_HAS_DRM_KEY_STATUSES)
#endif // SB_API_VERSION >= 6
-#if SB_API_VERSION >= SB_DRM_SESSION_CLOSED_API_VERSION
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
#if defined(SB_HAS_DRM_SESSION_CLOSED)
#if !SB_HAS(DRM_SESSION_CLOSED)
#error "SB_HAS_DRM_SESSION_CLOSED is required in this API version."
@@ -620,7 +650,7 @@
#else // defined(SB_HAS_DRM_SESSION_CLOSED)
#define SB_HAS_DRM_SESSION_CLOSED 1
#endif // defined(SB_HAS_DRM_SESSION_CLOSED)
-#endif // SB_API_VERSION >= SB_DRM_SESSION_CLOSED_API_VERSION
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
#if SB_API_VERSION >= 5
#if !defined(SB_HAS_SPEECH_RECOGNIZER)
@@ -650,6 +680,10 @@
#define SB_HAS_AUDIOLESS_VIDEO 1
#endif
+#if SB_API_VERSION >= SB_PLAYER_FILTER_TESTS_REQUIRED_API_VERSION
+#define SB_HAS_PLAYER_FILTER_TESTS 1
+#endif
+
#if SB_API_VERSION >= SB_PLAYER_ERROR_MESSAGE_API_VERSION
#define SB_HAS_PLAYER_ERROR_MESSAGE 1
#endif
diff --git a/src/starboard/contrib/creator/ci20x11/libraries.gypi b/src/starboard/contrib/creator/ci20x11/libraries.gypi
index 60a0f7e..7a37544 100644
--- a/src/starboard/contrib/creator/ci20x11/libraries.gypi
+++ b/src/starboard/contrib/creator/ci20x11/libraries.gypi
@@ -14,6 +14,17 @@
{
'variables': {
+ # This platform uses a compositor to present the rendering output, so
+ # set the swap interval to update the buffer immediately. That buffer
+ # will then be presented by the compositor on its own time.
+ 'cobalt_egl_swap_interval': 0,
+
+ # Hook into the swap buffers call to facilitate synchronization of the
+ # OpenGL output with the punch-through video layer.
+ 'linker_flags': [
+ '-Wl,--wrap=eglSwapBuffers',
+ ],
+
'platform_libraries': [
'-lEGL',
'-lGLESv2',
diff --git a/src/starboard/contrib/creator/ci20x11/starboard_platform.gypi b/src/starboard/contrib/creator/ci20x11/starboard_platform.gypi
index ab38135..ed8d810 100644
--- a/src/starboard/contrib/creator/ci20x11/starboard_platform.gypi
+++ b/src/starboard/contrib/creator/ci20x11/starboard_platform.gypi
@@ -23,6 +23,7 @@
'<(DEPTH)/starboard/contrib/creator/ci20x11/system_get_property.cc',
'<(DEPTH)/starboard/shared/starboard/link_receiver.cc',
'<(DEPTH)/starboard/shared/x11/application_x11.cc',
+ '<(DEPTH)/starboard/shared/x11/egl_swap_buffers.cc',
'<(DEPTH)/starboard/shared/x11/window_create.cc',
'<(DEPTH)/starboard/shared/x11/window_destroy.cc',
'<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc',
diff --git a/src/starboard/contrib/creator/shared/configuration_public.h b/src/starboard/contrib/creator/shared/configuration_public.h
index 1ea3bed..14631fd 100644
--- a/src/starboard/contrib/creator/shared/configuration_public.h
+++ b/src/starboard/contrib/creator/shared/configuration_public.h
@@ -90,11 +90,6 @@
// The API version implemented by this platform.
#define SB_API_VERSION 6
-// CI20 platform with 1.14 version of SGX libraries does not support
-// EGL_BIND_TO_TEXTURE_RGBA
-#define SB_HAS_QUIRK_NO_EGL_BIND_TO_TEXTURE 1
-
-
// --- System Header Configuration -------------------------------------------
// Any system headers listed here that are not provided by the platform will be
@@ -127,15 +122,6 @@
// Whether the current platform provides ssize_t.
#define SB_HAS_SSIZE_T 1
-// Whether the current platform has microphone supported.
-#define SB_HAS_MICROPHONE 0
-
-// Whether the current platform has speech recognizer.
-#define SB_HAS_SPEECH_RECOGNIZER 0
-
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
// Type detection for wchar_t.
#if defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -153,12 +139,6 @@
#define SB_IS_WCHAR_T_SIGNED 1
#endif
-// --- Architecture Configuration --------------------------------------------
-
-// On default Linux, you must be a superuser in order to set real time
-// scheduling on threads.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
-
// --- Compiler Configuration ------------------------------------------------
// The platform's annotation for forcing a C function to be inlined.
@@ -255,54 +235,6 @@
// The string form of SB_PATH_SEP_CHAR.
#define SB_PATH_SEP_STRING ":"
-// --- Memory Configuration --------------------------------------------------
-
-// The memory page size, which controls the size of chunks on memory that
-// allocators deal with, and the alignment of those chunks. This doesn't have to
-// be the hardware-defined physical page size, but it should be a multiple of
-// it.
-#define SB_MEMORY_PAGE_SIZE 4096
-
-// Whether this platform has and should use an MMAP function to map physical
-// memory to the virtual address space.
-#define SB_HAS_MMAP 1
-
-// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
-// required for platforms that want to JIT.
-#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
-
-// Whether this platform has and should use an growable heap (e.g. with sbrk())
-// to map physical memory to the virtual address space.
-#define SB_HAS_VIRTUAL_REGIONS 0
-
-// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
-// may require buffers to have a specific alignment, and this is the place to
-// specify that.
-#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
-
-// Determines the alignment that allocations should have on this platform.
-#define SB_MALLOC_ALIGNMENT ((size_t)16U)
-
-// Determines the threshhold of allocation size that should be done with mmap
-// (if available), rather than allocated within the core heap.
-#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
-
-// Defines the path where memory debugging logs should be written to.
-#define SB_MEMORY_LOG_PATH "/tmp/starboard"
-
-// --- Thread Configuration --------------------------------------------------
-
-// Defines the maximum number of simultaneous threads for this platform. Some
-// platforms require sharing thread handles with other kinds of system handles,
-// like mutexes, so we want to keep this managable.
-#define SB_MAX_THREADS 90
-
-// The maximum number of thread local storage keys supported by this platform.
-#define SB_MAX_THREAD_LOCAL_KEYS 512
-
-// The maximum length of the name for a thread, including the NULL-terminator.
-#define SB_MAX_THREAD_NAME_LENGTH 16;
-
// --- Graphics Configuration ------------------------------------------------
// Specifies whether this platform supports a performant accelerated blitter
@@ -329,6 +261,21 @@
// scene hasn't changed are enabled.
#define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER 0
+// CI20 platform with 1.14 version of SGX libraries does not support
+// EGL_BIND_TO_TEXTURE_RGBA
+#define SB_HAS_QUIRK_NO_EGL_BIND_TO_TEXTURE 1
+
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
// --- Media Configuration ---------------------------------------------------
// Specifies whether this platform has support for a possibly-decrypting
@@ -403,6 +350,41 @@
// value leads to more stable fps but also causes the app to use more memory.
#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
+// --- Memory Configuration --------------------------------------------------
+
+// The memory page size, which controls the size of chunks on memory that
+// allocators deal with, and the alignment of those chunks. This doesn't have to
+// be the hardware-defined physical page size, but it should be a multiple of
+// it.
+#define SB_MEMORY_PAGE_SIZE 4096
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#define SB_HAS_MMAP 1
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// Whether this platform has and should use an growable heap (e.g. with sbrk())
+// to map physical memory to the virtual address space.
+#define SB_HAS_VIRTUAL_REGIONS 0
+
+// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
+// may require buffers to have a specific alignment, and this is the place to
+// specify that.
+#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
+
+// Determines the alignment that allocations should have on this platform.
+#define SB_MALLOC_ALIGNMENT ((size_t)16U)
+
+// Determines the threshhold of allocation size that should be done with mmap
+// (if available), rather than allocated within the core heap.
+#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
+
+// Defines the path where memory debugging logs should be written to.
+#define SB_MEMORY_LOG_PATH "/tmp/starboard"
+
// --- Network Configuration -------------------------------------------------
// Specifies whether this platform supports IPV6.
@@ -411,6 +393,28 @@
// Specifies whether this platform supports pipe.
#define SB_HAS_PIPE 1
+// --- Thread Configuration --------------------------------------------------
+
+// Whether the current platform supports thread priorities.
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+
+// Defines the maximum number of simultaneous threads for this platform. Some
+// platforms require sharing thread handles with other kinds of system handles,
+// like mutexes, so we want to keep this managable.
+#define SB_MAX_THREADS 90
+
+// The maximum number of thread local storage keys supported by this platform.
+#define SB_MAX_THREAD_LOCAL_KEYS 512
+
+// The maximum length of the name for a thread, including the NULL-terminator.
+#define SB_MAX_THREAD_NAME_LENGTH 16
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
// --- Tuneable Parameters ---------------------------------------------------
// Specifies the network receive buffer size in bytes, set via
@@ -432,12 +436,6 @@
// The maximum number of users that can be signed in at the same time.
#define SB_USER_MAX_SIGNED_IN 1
-// --- Timing API ------------------------------------------------------------
-
-// Whether this platform has an API to retrieve how long the current thread
-// has spent in the executing state.
-#define SB_HAS_TIME_THREAD_NOW 1
-
// --- Platform Specific Audits ----------------------------------------------
#if !defined(__GNUC__)
diff --git a/src/starboard/contrib/linux/x64wl/atomic_public.h b/src/starboard/contrib/linux/x64wl/atomic_public.h
index 63bcc6a..4f58704 100644
--- a/src/starboard/contrib/linux/x64wl/atomic_public.h
+++ b/src/starboard/contrib/linux/x64wl/atomic_public.h
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/configuration_public.h b/src/starboard/contrib/linux/x64wl/configuration_public.h
index 9628a6e..496ac5c 100644
--- a/src/starboard/contrib/linux/x64wl/configuration_public.h
+++ b/src/starboard/contrib/linux/x64wl/configuration_public.h
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/gyp_configuration.gypi b/src/starboard/contrib/linux/x64wl/gyp_configuration.gypi
index 0683968..01a497c 100644
--- a/src/starboard/contrib/linux/x64wl/gyp_configuration.gypi
+++ b/src/starboard/contrib/linux/x64wl/gyp_configuration.gypi
@@ -1,4 +1,4 @@
-# Copyright 2017 Google Inc. All Rights Reserved.
+# Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/gyp_configuration.py b/src/starboard/contrib/linux/x64wl/gyp_configuration.py
index 6c2e7ed..d272522 100644
--- a/src/starboard/contrib/linux/x64wl/gyp_configuration.py
+++ b/src/starboard/contrib/linux/x64wl/gyp_configuration.py
@@ -1,4 +1,4 @@
-# Copyright 2017 Google Inc. All Rights Reserved.
+# Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/main.cc b/src/starboard/contrib/linux/x64wl/main.cc
index c835977..1653859 100644
--- a/src/starboard/contrib/linux/x64wl/main.cc
+++ b/src/starboard/contrib/linux/x64wl/main.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/player_output_mode_supported.cc b/src/starboard/contrib/linux/x64wl/player_output_mode_supported.cc
index 91978c0..166de03 100644
--- a/src/starboard/contrib/linux/x64wl/player_output_mode_supported.cc
+++ b/src/starboard/contrib/linux/x64wl/player_output_mode_supported.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/starboard_platform.gyp b/src/starboard/contrib/linux/x64wl/starboard_platform.gyp
index 3b085fa..62f355a 100644
--- a/src/starboard/contrib/linux/x64wl/starboard_platform.gyp
+++ b/src/starboard/contrib/linux/x64wl/starboard_platform.gyp
@@ -1,4 +1,4 @@
-# Copyright 2017 Google Inc. All Rights Reserved.
+# Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/starboard_platform.gypi b/src/starboard/contrib/linux/x64wl/starboard_platform.gypi
index 78a4930..0dbbdc6 100644
--- a/src/starboard/contrib/linux/x64wl/starboard_platform.gypi
+++ b/src/starboard/contrib/linux/x64wl/starboard_platform.gypi
@@ -1,4 +1,4 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/linux/x64wl/system_get_property.cc b/src/starboard/contrib/linux/x64wl/system_get_property.cc
index a9651c5..7e3291d 100644
--- a/src/starboard/contrib/linux/x64wl/system_get_property.cc
+++ b/src/starboard/contrib/linux/x64wl/system_get_property.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. 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.
@@ -14,8 +14,8 @@
#include "starboard/system.h"
-#include <netdb.h>
#include <linux/if.h> // NOLINT(build/include_alpha)
+#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -55,13 +55,12 @@
}
struct ifreq* cur_interface = config.ifc_req;
- const struct ifreq* const end = cur_interface +
- (config.ifc_len / sizeof(struct ifreq));
+ const struct ifreq* const end =
+ cur_interface + (config.ifc_len / sizeof(struct ifreq));
for (; cur_interface != end; ++cur_interface) {
- SbStringCopy(interface.ifr_name,
- cur_interface->ifr_name,
- sizeof(cur_interface->ifr_name));
+ SbStringCopy(interface.ifr_name, cur_interface->ifr_name,
+ sizeof(cur_interface->ifr_name));
if (ioctl(fd, SIOCGIFFLAGS, &interface) == -1) {
continue;
}
@@ -71,7 +70,8 @@
if (ioctl(fd, SIOCGIFHWADDR, &interface) == -1) {
continue;
}
- SbStringFormatF(out_value, value_length, "%x:%x:%x:%x:%x:%x",
+ SbStringFormatF(
+ out_value, value_length, "%x:%x:%x:%x:%x:%x",
interface.ifr_addr.sa_data[0], interface.ifr_addr.sa_data[1],
interface.ifr_addr.sa_data[2], interface.ifr_addr.sa_data[3],
interface.ifr_addr.sa_data[4], interface.ifr_addr.sa_data[5]);
@@ -80,7 +80,7 @@
return false;
}
-#endif // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION
+#endif // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION
} // namespace
diff --git a/src/starboard/contrib/linux/x64wl/thread_types_public.h b/src/starboard/contrib/linux/x64wl/thread_types_public.h
index f8df35f..ec6876b 100644
--- a/src/starboard/contrib/linux/x64wl/thread_types_public.h
+++ b/src/starboard/contrib/linux/x64wl/thread_types_public.h
@@ -1,4 +1,4 @@
-// Copyright 2017 Google Inc. All Rights Reserved.
+// Copyright 2018 Google Inc. 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.
diff --git a/src/starboard/contrib/tizen/armv7l/configuration_public.h b/src/starboard/contrib/tizen/armv7l/configuration_public.h
index 3b9e99b..e59b7ee 100644
--- a/src/starboard/contrib/tizen/armv7l/configuration_public.h
+++ b/src/starboard/contrib/tizen/armv7l/configuration_public.h
@@ -25,17 +25,6 @@
// The API version implemented by this platform.
#define SB_API_VERSION 4
-// --- System Header Configuration -------------------------------------------
-
-// Whether the current platform has microphone supported.
-#define SB_HAS_MICROPHONE 0
-
-// Whether the current platform has speech recognizer.
-#define SB_HAS_SPEECH_RECOGNIZER 0
-
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
// --- Architecture Configuration --------------------------------------------
// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
@@ -104,6 +93,17 @@
// on the specifically pinned core.
#define SB_HAS_CROSS_CORE_SCHEDULER 1
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
// --- Media Configuration ---------------------------------------------------
// The maximum audio bitrate the platform can decode. The following value
@@ -149,6 +149,12 @@
// value leads to more stable fps but also causes the app to use more memory.
#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
// --- Common Configuration ---------------------------------------------------
// Include the Tizen configuration that's common between all Tizen.
@@ -157,12 +163,7 @@
// --- User Configuration ----------------------------------------------------
// The maximum number of users that can be signed in at the same time.
+#undef SB_USER_MAX_SIGNED_IN
#define SB_USER_MAX_SIGNED_IN 1
-// --- Timing API ------------------------------------------------------------
-
-// Whether this platform has an API to retrieve how long the current thread
-// has spent in the executing state.
-#define SB_HAS_TIME_THREAD_NOW 1
-
#endif // STARBOARD_CONTRIB_TIZEN_ARMV7L_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/contrib/tizen/shared/configuration_public.h b/src/starboard/contrib/tizen/shared/configuration_public.h
index 9109655..8f1f40a 100644
--- a/src/starboard/contrib/tizen/shared/configuration_public.h
+++ b/src/starboard/contrib/tizen/shared/configuration_public.h
@@ -14,8 +14,8 @@
// The shared Starboard configuration for Raspberry Pi devices.
-#ifndef STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
+#ifndef STARBOARD_CONTRIB_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_CONTRIB_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
// --- System Header Configuration -------------------------------------------
@@ -65,9 +65,11 @@
// --- Architecture Configuration --------------------------------------------
-// On default Linux desktop, you must be a superuser in order to set real time
-// scheduling on threads.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+#define SB_IS_TIZEN_OS 1
+
+// Tizen uses system icu package.
+// undefined reference to 'icu_46::TimeZone::createDefault()'
+#define SB_HAS_QUIRK_NO_TIMEZONE_NAME_SUPPORT 1
// --- Attribute Configuration -----------------------------------------------
@@ -203,19 +205,6 @@
// Defines the path where memory debugging logs should be written to.
#define SB_MEMORY_LOG_PATH "/tmp/starboard"
-// --- Thread Configuration --------------------------------------------------
-
-// Defines the maximum number of simultaneous threads for this platform. Some
-// platforms require sharing thread handles with other kinds of system handles,
-// like mutexes, so we want to keep this managable.
-#define SB_MAX_THREADS 90
-
-// The maximum number of thread local storage keys supported by this platform.
-#define SB_MAX_THREAD_LOCAL_KEYS 512
-
-// The maximum length of the name for a thread, including the NULL-terminator.
-#define SB_MAX_THREAD_NAME_LENGTH 16;
-
// --- Graphics Configuration ------------------------------------------------
// Specifies whether this platform supports a performant accelerated blitter
@@ -242,6 +231,10 @@
// scene hasn't changed are enabled.
#define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER 0
+// Tizen TV product possible to use wayland display video window
+// instead of Evas_Object
+#define SB_CAN_USE_WAYLAND_VIDEO_WINDOW 0
+
// --- Network Configuration -------------------------------------------------
// Specifies whether this platform supports IPV6.
@@ -250,6 +243,23 @@
// Specifies whether this platform supports pipe.
#define SB_HAS_PIPE 1
+// --- Thread Configuration --------------------------------------------------
+
+// On default Linux desktop, you must be a superuser in order to set real time
+// scheduling on threads.
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+
+// Defines the maximum number of simultaneous threads for this platform. Some
+// platforms require sharing thread handles with other kinds of system handles,
+// like mutexes, so we want to keep this managable.
+#define SB_MAX_THREADS 90
+
+// The maximum number of thread local storage keys supported by this platform.
+#define SB_MAX_THREAD_LOCAL_KEYS 512
+
+// The maximum length of the name for a thread, including the NULL-terminator.
+#define SB_MAX_THREAD_NAME_LENGTH 16
+
// --- Tuneable Parameters ---------------------------------------------------
// Specifies the network receive buffer size in bytes, set via
@@ -272,18 +282,9 @@
#define SB_USER_MAX_SIGNED_IN 1
// --- Platform Specific Audits ----------------------------------------------
-#define SB_IS_TIZEN_OS 1
-
-// Tizen uses system icu package.
-// undefined reference to 'icu_46::TimeZone::createDefault()'
-#define SB_HAS_QUIRK_NO_TIMEZONE_NAME_SUPPORT 1
-
-// Tizen TV product possible to use wayland display video window
-// instead of Evas_Object
-#define SB_CAN_USE_WAYLAND_VIDEO_WINDOW 0
#if !defined(__GNUC__)
#error "Tizen builds need a GCC-like compiler (for the moment)."
#endif
-#endif // STARBOARD_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
+#endif // STARBOARD_CONTRIB_TIZEN_SHARED_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/contrib/tizen/shared/gyp_configuration.gypi b/src/starboard/contrib/tizen/shared/gyp_configuration.gypi
index 6e002ad..6d16a78 100644
--- a/src/starboard/contrib/tizen/shared/gyp_configuration.gypi
+++ b/src/starboard/contrib/tizen/shared/gyp_configuration.gypi
@@ -32,8 +32,6 @@
'cobalt_enable_jit': 0,
'linker_flags': [
- # We don't wrap these symbols, but this ensures that they aren't
- # linked in.
'-Wl,--wrap=eglGetDisplay',
],
'linker_flags_gold': [
diff --git a/src/starboard/contrib/tizen/shared/wayland/application_tizen.cc b/src/starboard/contrib/tizen/shared/wayland/application_tizen.cc
index 0dec35c..85a9480 100644
--- a/src/starboard/contrib/tizen/shared/wayland/application_tizen.cc
+++ b/src/starboard/contrib/tizen/shared/wayland/application_tizen.cc
@@ -49,6 +49,6 @@
return window;
}
-} // wayland
-} // shared
-} // starboard
+} // namespace wayland
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/contrib/tizen/shared/wayland/application_tizen.h b/src/starboard/contrib/tizen/shared/wayland/application_tizen.h
index e766a81..eee588c 100644
--- a/src/starboard/contrib/tizen/shared/wayland/application_tizen.h
+++ b/src/starboard/contrib/tizen/shared/wayland/application_tizen.h
@@ -43,8 +43,8 @@
tizen_policy* tz_policy_;
};
-} // wayland
-} // shared
-} // starboard
+} // namespace wayland
+} // namespace shared
+} // namespace starboard
#endif // STARBOARD_CONTRIB_TIZEN_SHARED_WAYLAND_APPLICATION_TIZEN_H_
diff --git a/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.cc b/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.cc
index 723050c..34fca35 100644
--- a/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.cc
+++ b/src/starboard/contrib/tizen/shared/wayland/window_internal_tizen.cc
@@ -34,8 +34,7 @@
}
static const struct tizen_visibility_listener tizen_visibility_listener = {
- WindowCbVisibilityChange
-};
+ WindowCbVisibilityChange};
SbWindowPrivateTizen::SbWindowPrivateTizen(wl_display* display,
tizen_policy* policy,
diff --git a/src/starboard/drm.h b/src/starboard/drm.h
index 4bdf4aa..bba6ec4 100644
--- a/src/starboard/drm.h
+++ b/src/starboard/drm.h
@@ -29,6 +29,30 @@
// --- Types -----------------------------------------------------------------
+// The type of the session request.
+// https://www.w3.org/TR/encrypted-media/#idl-def-mediakeymessagetype
+typedef enum SbDrmSessionRequestType {
+ kSbDrmSessionRequestTypeLicenseRequest,
+ kSbDrmSessionRequestTypeLicenseRenewal,
+ kSbDrmSessionRequestTypeLicenseRelease,
+ kSbDrmSessionRequestTypeIndividualizationRequest,
+} SbDrmSessionRequestType;
+
+// The status of session related operations.
+// Used by |SbDrmSessionUpdateRequestFunc|, |SbDrmSessionUpdatedFunc|, and
+// |SbDrmServerCertificateUpdatedFunc| to indicate the status of the operation.
+// https://w3c.github.io/encrypted-media/#error-names
+typedef enum SbDrmStatus {
+ kSbDrmStatusSuccess,
+ kSbDrmStatusTypeError,
+ kSbDrmStatusNotSupportedError,
+ kSbDrmStatusInvalidStateError,
+ kSbDrmStatusQuotaExceededError,
+ // The following error can be used when the error status cannot be mapped to
+ // one of the above errors.
+ kSbDrmStatusUnknownError,
+} SbDrmStatus;
+
// Status of a particular media key.
// https://w3c.github.io/encrypted-media/#idl-def-MediaKeyStatus
typedef enum SbDrmKeyStatus {
@@ -89,11 +113,34 @@
// SbDrmGenerateSessionUpdateRequest() was called on. |context| will be the same
// context that was passed into the call to SbDrmCreateSystem().
//
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+// |status| is the status of the session request.
+//
+// |type| is the status of the session request.
+//
+// |error_message| may contain an optional error message when |status| isn't
+// |kSbDrmStatusSuccess| to provide more details about the error. It may be
+// NULL if |status| is |kSbDrmStatusSuccess| or if no error message can be
+// provided.
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
// |ticket| will be the same ticket that was passed to
// SbDrmGenerateSessionUpdateRequest() or |kSbDrmTicketInvalid| if the update
// request was generated by the DRM system.
//
// |session_id| can be NULL if there was an error generating the request.
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+typedef void (*SbDrmSessionUpdateRequestFunc)(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);
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
typedef void (*SbDrmSessionUpdateRequestFunc)(SbDrmSystem drm_system,
void* context,
int ticket,
@@ -102,6 +149,7 @@
const void* content,
int content_size,
const char* url);
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
// A callback for notifications that a session has been added, and subsequent
// encrypted samples are actively ready to be decoded. |drm_system| will be the
@@ -110,13 +158,31 @@
//
// |ticket| will be the same ticket that was passed to SbDrmUpdateSession().
//
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+// |status| is the status of the session request.
+//
+// |error_message| may contain an optional error message when |status| isn't
+// |kSbDrmStatusSuccess| to provide more details about the error. It may be
+// NULL if |status| is |kSbDrmStatusSuccess| or if no error message can be
+// provided.
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
// |succeeded| is whether the session was successfully updated or not.
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+typedef void (*SbDrmSessionUpdatedFunc)(SbDrmSystem drm_system,
+ void* context,
+ int ticket,
+ SbDrmStatus status,
+ const char* error_message,
+ const void* session_id,
+ int session_id_size);
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
typedef void (*SbDrmSessionUpdatedFunc)(SbDrmSystem drm_system,
void* context,
int ticket,
const void* session_id,
int session_id_size,
bool succeeded);
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
// A callback for notifications that the status of one or more keys in a session
// has been changed. All keys of the session and their new status will be
@@ -141,6 +207,16 @@
int session_id_size);
#endif // SB_HAS(DRM_SESSION_CLOSED))
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+// A callback to notify the caller of SbDrmUpdateServerCertificate() whether the
+// update has been successfully updated or not.
+typedef void (*SbDrmServerCertificateUpdatedFunc)(SbDrmSystem drm_system,
+ void* context,
+ int ticket,
+ SbDrmStatus status,
+ const char* error_message);
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
// --- Constants -------------------------------------------------------------
// An invalid SbDrmSystem.
@@ -182,9 +258,31 @@
// SbDrmUpdateSession() is called.
// |key_statuses_changed_callback|: A function that can be called to indicate
// that key statuses have changed.
-// |session_closed_callback|: A function that can be called to indicate that
-// a session has closed.
-#if SB_HAS(DRM_SESSION_CLOSED)
+// |server_certificate_updated_callback|: A function that is called to report
+// whether the server certificate has been successfully updated. It is called
+// once and only once. It is possible that the callback is called before the
+// function returns.
+// |session_closed_callback|: A function that can be called to indicate that a
+// session has closed.
+#if SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+// If |NULL| is passed for any of the callbacks (|update_request_callback|,
+// |session_updated_callback|, |key_statuses_changed_callback|,
+// |server_certificate_updated_callback|, or |session_closed_callback|), then
+// |kSbDrmSystemInvalid| must be returned.
+#endif // SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+SB_EXPORT SbDrmSystem SbDrmCreateSystem(
+ const char* key_system,
+ void* context,
+ SbDrmSessionUpdateRequestFunc update_request_callback,
+ SbDrmSessionUpdatedFunc session_updated_callback,
+ SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
+ SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
+ SbDrmSessionClosedFunc session_closed_callback);
+
+#elif SB_HAS(DRM_SESSION_CLOSED)
#if !SB_HAS(DRM_KEY_STATUSES)
#error "Platforms with SB_HAS_DRM_SESSION_CLOSED must also set "
@@ -241,7 +339,7 @@
// |ticket|: The opaque ID that allows to distinguish callbacks from multiple
// concurrent calls to SbDrmGenerateSessionUpdateRequest(), which will be passed
// to |update_request_callback| as-is. It is the responsibility of the caller to
-// establish ticket uniqueness, issuing multiple request with the same ticket
+// establish ticket uniqueness, issuing multiple requests with the same ticket
// may result in undefined behavior. The value |kSbDrmTicketInvalid| must not be
// used.
//
@@ -307,6 +405,37 @@
// |drm_system|: The DRM system for which keys should be removed.
SB_EXPORT void SbDrmRemoveAllKeys(SbDrmSystem drm_system);
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+// Returns true if server certificate of |drm_system| can be updated via
+// SbDrmUpdateServerCertificate(). The return value should remain the same
+// during the life time of |drm_system|.
+//
+// |drm_system|: The DRM system to check if its server certificate is updatable.
+SB_EXPORT bool SbDrmIsServerCertificateUpdatable(SbDrmSystem drm_system);
+
+// Update the server certificate of |drm_system|. The function can be called
+// multiple times. It is possible that a call to it happens before the callback
+// of a previous call is called.
+// Note that this function should only be called after
+// |SbDrmIsServerCertificateUpdatable| is called first and returned true.
+//
+// |drm_system|: The DRM system whose server certificate is being updated.
+// |ticket|: The opaque ID that allows to distinguish callbacks from multiple
+// concurrent calls to SbDrmUpdateServerCertificate(), which will be passed to
+// |server_certificate_updated_callback| as-is. It is the responsibility of the
+// caller to establish ticket uniqueness, issuing multiple requests with the
+// same ticket may result in undefined behavior. The value |kSbDrmTicketInvalid|
+// must not be used.
+// |certificate|: Pointer to the server certificate data.
+// |certificate_size|: Size of the server certificate data.
+SB_EXPORT void SbDrmUpdateServerCertificate(SbDrmSystem drm_system,
+ int ticket,
+ const void* certificate,
+ int certificate_size);
+
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
// Destroys |drm_system|, which implicitly removes all keys installed in it and
// invalidates all outstanding session update requests. A DRM system cannot be
// destroyed unless any associated SbPlayer or SbDecoder has first been
diff --git a/src/starboard/input.h b/src/starboard/input.h
index ebc108b..e61d004 100644
--- a/src/starboard/input.h
+++ b/src/starboard/input.h
@@ -22,6 +22,7 @@
#include "starboard/configuration.h"
#include "starboard/export.h"
#include "starboard/key.h"
+#include "starboard/time.h"
#include "starboard/types.h"
#include "starboard/window.h"
@@ -147,6 +148,15 @@
// Event data for |kSbEventTypeInput| events.
typedef struct SbInputData {
+#if SB_API_VERSION >= SB_INPUT_TIMESTAMP_API_VERSION
+ // The time that should be reported for this event. This is intended to
+ // facilitate calculation of time-sensitive information (e.g. velocity for
+ // kSbInputEventTypeMove). This may be set to 0 to have the relevant systems
+ // automatically set the timestamp. However, this may happen at a later time,
+ // so the relative time between events may not be preserved.
+ SbTimeMonotonic timestamp;
+#endif
+
// The window in which the input was generated.
SbWindow window;
diff --git a/src/starboard/linux/shared/BUILD.gn b/src/starboard/linux/shared/BUILD.gn
index 73ce74e..011fb26 100644
--- a/src/starboard/linux/shared/BUILD.gn
+++ b/src/starboard/linux/shared/BUILD.gn
@@ -551,7 +551,9 @@
"//starboard/shared/stub/drm_create_system.cc",
"//starboard/shared/stub/drm_destroy_system.cc",
"//starboard/shared/stub/drm_generate_session_update_request.cc",
+ "//starboard/shared/stub/drm_is_server_certificate_updatable.cc",
"//starboard/shared/stub/drm_system_internal.h",
+ "//starboard/shared/stub/drm_update_server_certificate.cc",
"//starboard/shared/stub/drm_update_session.cc",
"//starboard/shared/stub/image_decode.cc",
"//starboard/shared/stub/image_is_decode_supported.cc",
diff --git a/src/starboard/linux/shared/compiler_flags.gypi b/src/starboard/linux/shared/compiler_flags.gypi
index 2223b30..c804d6a 100644
--- a/src/starboard/linux/shared/compiler_flags.gypi
+++ b/src/starboard/linux/shared/compiler_flags.gypi
@@ -107,6 +107,9 @@
'cflags_cc': [
'-std=gnu++11',
],
+ 'ldflags': [
+ '-Wl,-rpath=$ORIGIN/lib',
+ ],
'target_conditions': [
['sb_pedantic_warnings==1', {
'cflags': [
diff --git a/src/starboard/linux/shared/configuration.gni b/src/starboard/linux/shared/configuration.gni
index 99cb78e..c6cc8d0 100644
--- a/src/starboard/linux/shared/configuration.gni
+++ b/src/starboard/linux/shared/configuration.gni
@@ -33,7 +33,7 @@
gl_type = "system_gles3"
# Use media source extension implementation that is conformed to the
-# Candidate Recommandation of July 5th 2016.
+# Candidate Recommendation of July 5th 2016.
cobalt_use_media_source_2016 = true
# Use ASAN by default when building with Clang.
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h
index e0cbe8e..8f51a9f 100644
--- a/src/starboard/linux/shared/configuration_public.h
+++ b/src/starboard/linux/shared/configuration_public.h
@@ -24,7 +24,7 @@
#define STARBOARD_LINUX_SHARED_CONFIGURATION_PUBLIC_H_
#ifndef SB_API_VERSION
-#define SB_API_VERSION 6
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
#endif
// --- System Header Configuration -------------------------------------------
@@ -59,15 +59,6 @@
// Whether the current platform provides ssize_t.
#define SB_HAS_SSIZE_T 1
-// Whether the current platform has microphone supported.
-#define SB_HAS_MICROPHONE 0
-
-// Whether the current platform has speech recognizer.
-#define SB_HAS_SPEECH_RECOGNIZER 0
-
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
// Type detection for wchar_t.
#if defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -85,12 +76,6 @@
#define SB_IS_WCHAR_T_SIGNED 1
#endif
-// --- Architecture Configuration --------------------------------------------
-
-// On default Linux desktop, you must be a superuser in order to set real time
-// scheduling on threads.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
-
// --- Attribute Configuration -----------------------------------------------
// The platform's annotation for forcing a C function to be inlined.
@@ -187,54 +172,6 @@
// The string form of SB_PATH_SEP_CHAR.
#define SB_PATH_SEP_STRING ":"
-// --- Memory Configuration --------------------------------------------------
-
-// The memory page size, which controls the size of chunks on memory that
-// allocators deal with, and the alignment of those chunks. This doesn't have to
-// be the hardware-defined physical page size, but it should be a multiple of
-// it.
-#define SB_MEMORY_PAGE_SIZE 4096
-
-// Whether this platform has and should use an MMAP function to map physical
-// memory to the virtual address space.
-#define SB_HAS_MMAP 1
-
-// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
-// required for platforms that want to JIT.
-#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
-
-// Whether this platform has and should use an growable heap (e.g. with sbrk())
-// to map physical memory to the virtual address space.
-#define SB_HAS_VIRTUAL_REGIONS 0
-
-// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
-// may require buffers to have a specific alignment, and this is the place to
-// specify that.
-#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
-
-// Determines the alignment that allocations should have on this platform.
-#define SB_MALLOC_ALIGNMENT ((size_t)16U)
-
-// Determines the threshhold of allocation size that should be done with mmap
-// (if available), rather than allocated within the core heap.
-#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
-
-// Defines the path where memory debugging logs should be written to.
-#define SB_MEMORY_LOG_PATH "/tmp/starboard"
-
-// --- Thread Configuration --------------------------------------------------
-
-// Defines the maximum number of simultaneous threads for this platform. Some
-// platforms require sharing thread handles with other kinds of system handles,
-// like mutexes, so we want to keep this managable.
-#define SB_MAX_THREADS 90
-
-// The maximum number of thread local storage keys supported by this platform.
-#define SB_MAX_THREAD_LOCAL_KEYS 512
-
-// The maximum length of the name for a thread, including the NULL-terminator.
-#define SB_MAX_THREAD_NAME_LENGTH 16;
-
// --- Graphics Configuration ------------------------------------------------
// Specifies whether this platform supports a performant accelerated blitter
@@ -257,6 +194,17 @@
// scene hasn't changed are enabled.
#define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER 0
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
// --- Media Configuration ---------------------------------------------------
// The maximum audio bitrate the platform can decode. The following value
@@ -312,6 +260,41 @@
// value leads to more stable fps but also causes the app to use more memory.
#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
+// --- Memory Configuration --------------------------------------------------
+
+// The memory page size, which controls the size of chunks on memory that
+// allocators deal with, and the alignment of those chunks. This doesn't have to
+// be the hardware-defined physical page size, but it should be a multiple of
+// it.
+#define SB_MEMORY_PAGE_SIZE 4096
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#define SB_HAS_MMAP 1
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// Whether this platform has and should use an growable heap (e.g. with sbrk())
+// to map physical memory to the virtual address space.
+#define SB_HAS_VIRTUAL_REGIONS 0
+
+// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
+// may require buffers to have a specific alignment, and this is the place to
+// specify that.
+#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
+
+// Determines the alignment that allocations should have on this platform.
+#define SB_MALLOC_ALIGNMENT ((size_t)16U)
+
+// Determines the threshhold of allocation size that should be done with mmap
+// (if available), rather than allocated within the core heap.
+#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
+
+// Defines the path where memory debugging logs should be written to.
+#define SB_MEMORY_LOG_PATH "/tmp/starboard"
+
// --- Network Configuration -------------------------------------------------
// Specifies whether this platform supports IPV6.
@@ -320,6 +303,23 @@
// Specifies whether this platform supports pipe.
#define SB_HAS_PIPE 1
+// --- Thread Configuration --------------------------------------------------
+
+// On default Linux desktop, you must be a superuser in order to set real time
+// scheduling on threads.
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+
+// Defines the maximum number of simultaneous threads for this platform. Some
+// platforms require sharing thread handles with other kinds of system handles,
+// like mutexes, so we want to keep this managable.
+#define SB_MAX_THREADS 90
+
+// The maximum number of thread local storage keys supported by this platform.
+#define SB_MAX_THREAD_LOCAL_KEYS 512
+
+// The maximum length of the name for a thread, including the NULL-terminator.
+#define SB_MAX_THREAD_NAME_LENGTH 16
+
// --- Tuneable Parameters ---------------------------------------------------
// Specifies the network receive buffer size in bytes, set via
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index 33bf5c7..87d610b 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -78,7 +78,7 @@
return filters
_FILTERED_TESTS = {
- 'starboard_platform_tests': [test_filter.FILTER_ALL],
+ 'nplb': ['SbDrmTest.AnySupportedKeySystems',],
'player_filter_tests': [
# These tests have memory leaks related to av_malloc.
'AudioDecoderTests/AudioDecoderTest.*',
@@ -86,5 +86,6 @@
# These tests fail on buildbot.
'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/0',
'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/1',
- ]
+ ],
+ 'starboard_platform_tests': [test_filter.FILTER_ALL],
}
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 057d1d8..a7f6949 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -12,8 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
{
+ 'includes': [
+ '<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
+ ],
'variables': {
'starboard_platform_sources': [
+ '<@(filter_based_player_sources)',
'<(DEPTH)/starboard/linux/shared/atomic_public.h',
'<(DEPTH)/starboard/linux/shared/configuration_public.h',
'<(DEPTH)/starboard/linux/shared/decode_target_get_info.cc',
@@ -35,6 +39,7 @@
'<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc',
'<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
+ '<(DEPTH)/starboard/shared/dlmalloc/memory_protect.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
'<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h',
'<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc',
@@ -227,39 +232,6 @@
'<(DEPTH)/starboard/shared/starboard/new.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/cpu_video_frame.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/cpu_video_frame.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/player_components.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_frame_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_sink.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
@@ -309,7 +281,9 @@
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+ '<(DEPTH)/starboard/shared/stub/drm_is_server_certificate_updatable.cc',
'<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+ '<(DEPTH)/starboard/shared/stub/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
'<(DEPTH)/starboard/shared/stub/image_decode.cc',
'<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
diff --git a/src/starboard/linux/shared/starboard_platform_tests.gypi b/src/starboard/linux/shared/starboard_platform_tests.gypi
index 4eaacd1..e206d9f 100644
--- a/src/starboard/linux/shared/starboard_platform_tests.gypi
+++ b/src/starboard/linux/shared/starboard_platform_tests.gypi
@@ -12,9 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
{
- 'includes': [
- '<(DEPTH)/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi',
- ],
'targets': [
{
'target_name': 'starboard_platform_tests',
diff --git a/src/starboard/linux/shared/system_has_capability.cc b/src/starboard/linux/shared/system_has_capability.cc
index 8503ef4..5d5d9bf 100644
--- a/src/starboard/linux/shared/system_has_capability.cc
+++ b/src/starboard/linux/shared/system_has_capability.cc
@@ -22,6 +22,10 @@
return false;
case kSbSystemCapabilityCanQueryGPUMemoryStats:
return false;
+#if SB_API_VERSION >= SB_INPUT_TIMESTAMP_API_VERSION
+ case kSbSystemCapabilitySetsInputTimestamp:
+ return false;
+#endif
}
SB_DLOG(WARNING) << "Unrecognized capability: " << capability_id;
diff --git a/src/starboard/linux/x64directfb/future/atomic_public.h b/src/starboard/linux/x64directfb/future/atomic_public.h
deleted file mode 100644
index e4ef8fd..0000000
--- a/src/starboard/linux/x64directfb/future/atomic_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 Google Inc. 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_LINUX_X64DIRECTFB_FUTURE_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64DIRECTFB_FUTURE_ATOMIC_PUBLIC_H_
-
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_LINUX_X64DIRECTFB_FUTURE_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/future/configuration_public.h b/src/starboard/linux/x64directfb/future/configuration_public.h
deleted file mode 100644
index 59042fe..0000000
--- a/src/starboard/linux/x64directfb/future/configuration_public.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2016 Google Inc. 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.
-
-// The Starboard configuration for Desktop X64 Linux configured for the future
-// starboard API.
-
-#ifndef STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
-
-// The API version implemented by this platform.
-#undef SB_API_VERSION
-#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
-
-#undef SB_IS_PLAYER_PRODUCING_TEXTURE
-#undef SB_IS_PLAYER_COMPOSITED
-#undef SB_IS_PLAYER_PUNCHED_OUT
-
-// Whether the current platform implements the on screen keyboard interface.
-#define SB_HAS_ON_SCREEN_KEYBOARD 0
-
-// Whether the current platform uses a media player that relies on a URL.
-#define SB_HAS_PLAYER_WITH_URL 0
-
-// Include the X64DIRECTFB Linux configuration.
-#include "starboard/linux/x64directfb/configuration_public.h"
-
-#endif // STARBOARD_LINUX_X64DIRECTFB_FUTURE_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/future/gyp_configuration.gypi b/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
deleted file mode 100644
index 44b920d..0000000
--- a/src/starboard/linux/x64directfb/future/gyp_configuration.gypi
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-
-{
- 'target_defaults': {
- 'default_configuration': 'linux-x64directfb-future_debug',
- 'configurations': {
- 'linux-x64directfb-future_debug': {
- 'inherit_from': ['debug_base'],
- },
- 'linux-x64directfb-future_devel': {
- 'inherit_from': ['devel_base'],
- },
- 'linux-x64directfb-future_qa': {
- 'inherit_from': ['qa_base'],
- },
- 'linux-x64directfb-future_gold': {
- 'inherit_from': ['gold_base'],
- },
- }, # end of configurations
- },
-
- 'includes': [
- '../gyp_configuration.gypi',
- ],
-}
diff --git a/src/starboard/linux/x64directfb/future/gyp_configuration.py b/src/starboard/linux/x64directfb/future/gyp_configuration.py
deleted file mode 100644
index b19cab8..0000000
--- a/src/starboard/linux/x64directfb/future/gyp_configuration.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-"""Starboard Linux X64 DirectFB future platform configuration for gyp_cobalt."""
-
-from starboard.linux.x64directfb import gyp_configuration as linux_configuration
-
-
-def CreatePlatformConfig():
- return linux_configuration.CobaltLinuxX64DirectFbConfiguration(
- 'linux-x64directfb-future')
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform.gyp b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
deleted file mode 100644
index 6fe0639..0000000
--- a/src/starboard/linux/x64directfb/future/starboard_platform.gyp
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-{
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'product_name': 'starboard_platform_future',
- 'type': 'static_library',
- 'sources': [],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<(DEPTH)/starboard/common/common.gyp:common',
- '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp:starboard_platform',
- '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
- '<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
- ],
- },
- ],
-}
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform_tests.gyp b/src/starboard/linux/x64directfb/future/starboard_platform_tests.gyp
deleted file mode 100644
index 896e890..0000000
--- a/src/starboard/linux/x64directfb/future/starboard_platform_tests.gyp
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2018 Google Inc. 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.
-{
- 'includes': [
- '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
- ],
-}
diff --git a/src/starboard/linux/x64directfb/future/thread_types_public.h b/src/starboard/linux/x64directfb/future/thread_types_public.h
deleted file mode 100644
index 19803be..0000000
--- a/src/starboard/linux/x64directfb/future/thread_types_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 Google Inc. 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_LINUX_X64DIRECTFB_FUTURE_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_LINUX_X64DIRECTFB_FUTURE_THREAD_TYPES_PUBLIC_H_
-
-#include "starboard/linux/shared/thread_types_public.h"
-
-#endif // STARBOARD_LINUX_X64DIRECTFB_FUTURE_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/sbversion/4/atomic_public.h b/src/starboard/linux/x64directfb/sbversion/4/atomic_public.h
new file mode 100644
index 0000000..5ef653e
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/atomic_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/x64directfb/atomic_public.h"
+
+#endif // STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/sbversion/4/configuration_public.h b/src/starboard/linux/x64directfb/sbversion/4/configuration_public.h
new file mode 100644
index 0000000..b39baeb
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/configuration_public.h
@@ -0,0 +1,26 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_CONFIGURATION_PUBLIC_H_
+
+#undef SB_API_VERSION
+#define SB_API_VERSION 4
+
+#include "starboard/linux/x64directfb/configuration_public.h"
+
+#endif // STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/sbversion/4/gyp_configuration.gypi b/src/starboard/linux/x64directfb/sbversion/4/gyp_configuration.gypi
new file mode 100644
index 0000000..73d5c94
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/gyp_configuration.gypi
@@ -0,0 +1,40 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64directfb-sbversion-4_debug',
+ 'configurations': {
+ 'linux-x64directfb-sbversion-4_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64directfb-sbversion-4_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64directfb-sbversion-4_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64directfb-sbversion-4_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '<(DEPTH)/starboard/linux/x64directfb/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64directfb/sbversion/4/gyp_configuration.py b/src/starboard/linux/x64directfb/sbversion/4/gyp_configuration.py
new file mode 100644
index 0000000..45aaebf
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/gyp_configuration.py
@@ -0,0 +1,44 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+import os
+
+from starboard.linux.x64directfb import gyp_configuration as parent_configuration
+
+
+class LinuxX64DirectFbSbversion4Configuration(
+ parent_configuration.CobaltLinuxX64DirectFbConfiguration):
+
+ def GetVariables(self, config_name):
+ variables = super(LinuxX64DirectFbSbversion4Configuration,
+ self).GetVariables(config_name)
+ # V8 requires new Starboard features so we must use SpiderMonkey in older
+ # versions of Starboard.
+ variables.update({
+ 'javascript_engine': 'mozjs-45',
+ 'cobalt_enable_jit': 0,
+ })
+ return variables
+
+ def GetLauncherPath(self):
+ """Gets the path to the launcher module for this platform."""
+ return os.path.dirname(__file__)
+
+
+def CreatePlatformConfig():
+ return LinuxX64DirectFbSbversion4Configuration(
+ 'linux-x64directfb-sbversion-4')
diff --git a/src/starboard/linux/x64directfb/sbversion/4/launcher.py b/src/starboard/linux/x64directfb/sbversion/4/launcher.py
new file mode 100644
index 0000000..03c50b2
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/launcher.py
@@ -0,0 +1,25 @@
+# Copyright 2018 Google Inc. 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.
+"""Specialized Starboard version 4 launcher expressing no preload support."""
+
+import starboard.linux.shared.launcher as launcher
+
+
+class Launcher(launcher.Launcher):
+
+ def SupportsSuspendResume(self):
+ # While technically Starboard version 4 does support suspend/resume, it
+ # does not support preload and so we return false here to avoid running
+ # tests which assume "SuspendResume" support implies preload support.
+ return False
diff --git a/src/starboard/linux/x64directfb/sbversion/4/starboard_platform.gyp b/src/starboard/linux/x64directfb/sbversion/4/starboard_platform.gyp
new file mode 100644
index 0000000..18903fb
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/starboard_platform.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64directfb/sbversion/4/starboard_platform_tests.gyp b/src/starboard/linux/x64directfb/sbversion/4/starboard_platform_tests.gyp
new file mode 100644
index 0000000..dfe07b4
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/starboard_platform_tests.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64directfb/starboard_platform_tests.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64directfb/sbversion/4/thread_types_public.h b/src/starboard/linux/x64directfb/sbversion/4/thread_types_public.h
new file mode 100644
index 0000000..920700c
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/4/thread_types_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/x64directfb/thread_types_public.h"
+
+#endif // STARBOARD_LINUX_X64DIRECTFB_SBVERSION_4_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/sbversion/6/atomic_public.h b/src/starboard/linux/x64directfb/sbversion/6/atomic_public.h
new file mode 100644
index 0000000..32c1563
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/6/atomic_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/x64directfb/atomic_public.h"
+
+#endif // STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/sbversion/6/configuration_public.h b/src/starboard/linux/x64directfb/sbversion/6/configuration_public.h
new file mode 100644
index 0000000..152e646
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/6/configuration_public.h
@@ -0,0 +1,26 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_CONFIGURATION_PUBLIC_H_
+
+#undef SB_API_VERSION
+#define SB_API_VERSION 6
+
+#include "starboard/linux/x64directfb/configuration_public.h"
+
+#endif // STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/sbversion/6/gyp_configuration.gypi b/src/starboard/linux/x64directfb/sbversion/6/gyp_configuration.gypi
new file mode 100644
index 0000000..968028c
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/6/gyp_configuration.gypi
@@ -0,0 +1,40 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64directfb-sbversion-6_debug',
+ 'configurations': {
+ 'linux-x64directfb-sbversion-6_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64directfb-sbversion-6_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64directfb-sbversion-6_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64directfb-sbversion-6_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '<(DEPTH)/starboard/linux/x64directfb/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64directfb/sbversion/6/gyp_configuration.py b/src/starboard/linux/x64directfb/sbversion/6/gyp_configuration.py
new file mode 100644
index 0000000..4a0691e
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/6/gyp_configuration.py
@@ -0,0 +1,38 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+from starboard.linux.x64directfb import gyp_configuration as parent_configuration
+
+
+class LinuxX64DirectFbSbversion6Configuration(
+ parent_configuration.CobaltLinuxX64DirectFbConfiguration):
+
+ def GetVariables(self, config_name):
+ variables = super(LinuxX64DirectFbSbversion6Configuration,
+ self).GetVariables(config_name)
+ # V8 requires new Starboard features so we must use SpiderMonkey in older
+ # versions of Starboard.
+ variables.update({
+ 'javascript_engine': 'mozjs-45',
+ 'cobalt_enable_jit': 0,
+ })
+ return variables
+
+
+def CreatePlatformConfig():
+ return LinuxX64DirectFbSbversion6Configuration(
+ 'linux-x64directfb-sbversion-6')
diff --git a/src/starboard/linux/x64directfb/sbversion/6/starboard_platform.gyp b/src/starboard/linux/x64directfb/sbversion/6/starboard_platform.gyp
new file mode 100644
index 0000000..18903fb
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/6/starboard_platform.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64directfb/sbversion/6/starboard_platform_tests.gyp b/src/starboard/linux/x64directfb/sbversion/6/starboard_platform_tests.gyp
new file mode 100644
index 0000000..dfe07b4
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/6/starboard_platform_tests.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64directfb/starboard_platform_tests.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64directfb/sbversion/6/thread_types_public.h b/src/starboard/linux/x64directfb/sbversion/6/thread_types_public.h
new file mode 100644
index 0000000..effbbf9
--- /dev/null
+++ b/src/starboard/linux/x64directfb/sbversion/6/thread_types_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/x64directfb/thread_types_public.h"
+
+#endif // STARBOARD_LINUX_X64DIRECTFB_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/cobalt/configuration.gypi b/src/starboard/linux/x64x11/cobalt/configuration.gypi
index 9be77e9..2ba5387 100644
--- a/src/starboard/linux/x64x11/cobalt/configuration.gypi
+++ b/src/starboard/linux/x64x11/cobalt/configuration.gypi
@@ -15,6 +15,12 @@
{
'variables': {
'enable_map_to_mesh%': 1,
+ 'enable_key_statuses_callback%': '<!(test -e ../../third_party/cdm/cdm/include/content_decryption_module.h && grep -q GetKeys ../../third_party/cdm/cdm/include/content_decryption_module.h && echo 1 || echo 0)',
+ },
+ 'target_defaults': {
+ 'defines': [
+ 'ENABLE_KEY_STATUSES_CALLBACK=<(enable_key_statuses_callback)',
+ ],
},
'includes': [
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h
index caa1787..4aed560 100644
--- a/src/starboard/linux/x64x11/configuration_public.h
+++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -106,8 +106,7 @@
// textures. These textures typically originate from video decoders.
#define SB_HAS_NV12_TEXTURE_SUPPORT 1
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 0
+// --- Shared Configuration and Overrides ------------------------------------
// Include the Linux configuration that's common between all Desktop Linuxes.
#include "starboard/linux/shared/configuration_public.h"
@@ -116,6 +115,10 @@
#undef SB_HAS_MICROPHONE
#define SB_HAS_MICROPHONE 1
+// Whether the current platform has speech synthesis.
+#undef SB_HAS_SPEECH_SYNTHESIS
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
#if SB_API_VERSION >= 8
// Whether the current platform implements the on screen keyboard interface.
#define SB_HAS_ON_SCREEN_KEYBOARD 0
diff --git a/src/starboard/linux/x64x11/egl/starboard_platform.gyp b/src/starboard/linux/x64x11/egl/starboard_platform.gyp
index a57959f..1b15c93 100644
--- a/src/starboard/linux/x64x11/egl/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/egl/starboard_platform.gyp
@@ -13,23 +13,6 @@
# limitations under the License.
{
'includes': [
- '../starboard_platform.gypi'
- ],
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'type': 'static_library',
- 'sources': [
- '<@(starboard_platform_sources)',
- ],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<@(starboard_platform_dependencies)',
- ],
- },
+ '../starboard_platform.gyp'
],
}
diff --git a/src/starboard/linux/x64x11/enable_key_statuses.py b/src/starboard/linux/x64x11/enable_key_statuses.py
new file mode 100644
index 0000000..b714b57
--- /dev/null
+++ b/src/starboard/linux/x64x11/enable_key_statuses.py
@@ -0,0 +1,86 @@
+#
+# Copyright 2017 Google Inc. 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.
+#
+"""Utility to apply GetKeys() related changes to wv_cdm.
+
+Such changes are required for x64x11-wv to support key statuses.
+"""
+
+import os
+import subprocess
+
+
+def GetWideVinePath():
+ """Function to return the path to wv cdm."""
+ paths = ['starboard', 'linux', 'x64x11', 'wv']
+
+ current_path = os.path.dirname(os.path.realpath(__file__))
+ for path in reversed(paths):
+ assert os.path.basename(current_path) == path
+ current_path = os.path.dirname(current_path)
+ return os.path.join(current_path, 'third_party', 'cdm')
+
+
+def GetLastGitCommit():
+ """Function to return the hash of the last commit."""
+ return subprocess.check_output(['git', 'log', '--oneline', '-1'])[:7]
+
+
+def GetCurrentGitBranch():
+ """Function to return the current git branch."""
+ branches = subprocess.check_output(['git', 'branch']).split('\n')
+ current_branches = [branch for branch in branches if branch[:2] == '* ']
+ if len(current_branches) == 1:
+ return current_branches[0][2:].strip()
+ return ''
+
+
+def PatchWideVine():
+ """Function to patch wv cdm with the `wv_cdm_get_keys.diff` if not applied."""
+ os.chdir(GetWideVinePath())
+
+ with open('cdm/include/content_decryption_module.h') as f:
+ if f.read().find('GetKeys') != -1:
+ # We have already patched WideVine
+ print 'WideVine has already been been patched with GetKeys() support.'
+ return
+
+ if (GetCurrentGitBranch() != 'master' and
+ GetCurrentGitBranch() != '(HEAD detached at 5ae19b0)' and
+ GetLastGitCommit() != '5ae19b0'):
+ raise Exception('WideVine is not at master or `5ae19b0`'
+ ', not proceed to avoid overwriting changes.')
+
+ if (subprocess.call(['git', 'diff', '--exit-code']) == 1 or
+ subprocess.call(['git', 'diff', '--cached', '--exit-code']) == 1):
+ raise Exception('WideVine repository is not clean.')
+
+ subprocess.check_call(
+ ['git', 'checkout', '--quiet', '-b', 'wv2.2', 'tags/v2.2'])
+ assert GetLastGitCommit() == '5ae19b0'
+
+ subprocess.check_call(
+ ['git', 'apply', '../../starboard/linux/x64x11/wv_cdm_get_keys.diff'])
+ subprocess.check_call(
+ ['git', 'commit', '-a', '--quiet', '-m', '"Add GetKeys() support"'])
+
+ with open('cdm/include/content_decryption_module.h') as f:
+ assert f.read().find('GetKeys') != -1
+
+ print('WideVine has been patched with GetKeys() support, '
+ 'remember to re-run gyp_driver!')
+
+
+PatchWideVine()
diff --git a/src/starboard/linux/x64x11/future/atomic_public.h b/src/starboard/linux/x64x11/future/atomic_public.h
deleted file mode 100644
index e908f7d..0000000
--- a/src/starboard/linux/x64x11/future/atomic_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 Google Inc. 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_LINUX_X64X11_FUTURE_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_FUTURE_ATOMIC_PUBLIC_H_
-
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_LINUX_X64X11_FUTURE_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/configuration_public.h b/src/starboard/linux/x64x11/future/configuration_public.h
deleted file mode 100644
index 0a7e22d..0000000
--- a/src/starboard/linux/x64x11/future/configuration_public.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 Google Inc. 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.
-
-// The Starboard configuration for Desktop X86 Linux configured for the future
-// starboard API.
-
-#ifndef STARBOARD_LINUX_X64X11_FUTURE_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_FUTURE_CONFIGURATION_PUBLIC_H_
-
-// The Future configuration should explicitly always implement the experimental
-// API version.
-#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
-
-// Include the X64X11 Linux configuration.
-#include "starboard/linux/x64x11/configuration_public.h"
-
-#endif // STARBOARD_LINUX_X64X11_FUTURE_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.gypi b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
deleted file mode 100644
index 0cb12cf..0000000
--- a/src/starboard/linux/x64x11/future/gyp_configuration.gypi
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-
-{
- 'target_defaults': {
- 'default_configuration': 'linux-x64x11-future_debug',
- 'configurations': {
- 'linux-x64x11-future_debug': {
- 'inherit_from': ['debug_base'],
- },
- 'linux-x64x11-future_devel': {
- 'inherit_from': ['devel_base'],
- },
- 'linux-x64x11-future_qa': {
- 'inherit_from': ['qa_base'],
- },
- 'linux-x64x11-future_gold': {
- 'inherit_from': ['gold_base'],
- },
- }, # end of configurations
- },
-
- 'includes': [
- '../gyp_configuration.gypi',
- ],
-}
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.py b/src/starboard/linux/x64x11/future/gyp_configuration.py
deleted file mode 100644
index 6f63e92..0000000
--- a/src/starboard/linux/x64x11/future/gyp_configuration.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-"""Starboard Linux X64 X11 future platform configuration."""
-
-from starboard.linux.x64x11 import gyp_configuration as linux_configuration
-
-
-def CreatePlatformConfig():
- return linux_configuration.LinuxX64X11Configuration('linux-x64x11-future')
diff --git a/src/starboard/linux/x64x11/future/starboard_platform.gyp b/src/starboard/linux/x64x11/future/starboard_platform.gyp
deleted file mode 100644
index 9a450cc..0000000
--- a/src/starboard/linux/x64x11/future/starboard_platform.gyp
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-{
- 'includes': [
- '../starboard_platform.gypi'
- ],
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'product_name': 'starboard_platform_future',
- 'type': 'static_library',
- 'sources': [
- '<@(starboard_platform_sources)',
- ],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<@(starboard_platform_dependencies)',
- ],
- },
- ],
-}
diff --git a/src/starboard/linux/x64x11/future/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/future/starboard_platform_tests.gyp
deleted file mode 100644
index 896e890..0000000
--- a/src/starboard/linux/x64x11/future/starboard_platform_tests.gyp
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2018 Google Inc. 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.
-{
- 'includes': [
- '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
- ],
-}
diff --git a/src/starboard/linux/x64x11/future/thread_types_public.h b/src/starboard/linux/x64x11/future/thread_types_public.h
deleted file mode 100644
index 9ae5da8..0000000
--- a/src/starboard/linux/x64x11/future/thread_types_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2016 Google Inc. 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_LINUX_X64X11_FUTURE_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_FUTURE_THREAD_TYPES_PUBLIC_H_
-
-#include "starboard/linux/shared/thread_types_public.h"
-
-#endif // STARBOARD_LINUX_X64X11_FUTURE_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp b/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp
index 42b0bf5..1b15c93 100644
--- a/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/gczeal/starboard_platform.gyp
@@ -13,21 +13,6 @@
# limitations under the License.
{
'includes': [
- '../starboard_platform.gypi'
- ],
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'type': 'static_library',
- 'sources': ['<@(starboard_platform_sources)'],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<@(starboard_platform_dependencies)',
- ],
- },
+ '../starboard_platform.gyp'
],
}
diff --git a/src/starboard/linux/x64x11/gyp_configuration.gypi b/src/starboard/linux/x64x11/gyp_configuration.gypi
index b5f1ede..4e18620 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gyp_configuration.gypi
@@ -15,11 +15,6 @@
{
'variables': {
'enable_map_to_mesh%': 1,
-
- 'cobalt_platform_dependencies': [
- # GL Linux makes some GL calls within decode_target_internal.cc.
- '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
- ],
},
'target_defaults': {
'default_configuration': 'linux-x64x11_debug',
diff --git a/src/starboard/linux/x64x11/gyp_configuration.py b/src/starboard/linux/x64x11/gyp_configuration.py
index 50059e5..4e75ff9 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gyp_configuration.py
@@ -13,6 +13,8 @@
# limitations under the License.
"""Starboard Linux X64 X11 platform configuration."""
+import os.path
+
from starboard.linux.shared import gyp_configuration as shared_configuration
from starboard.tools.toolchain import ar
from starboard.tools.toolchain import bash
@@ -20,6 +22,7 @@
from starboard.tools.toolchain import clangxx
from starboard.tools.toolchain import cp
from starboard.tools.toolchain import touch
+from starboard.tools import paths
class LinuxX64X11Configuration(shared_configuration.LinuxConfiguration):
@@ -47,11 +50,31 @@
ar.StaticThinLinker(),
ar.StaticLinker(),
clangxx.ExecutableLinker(path=cxx_path),
+ clangxx.SharedLibraryLinker(path=cxx_path),
cp.Copy(),
touch.Stamp(),
bash.Shell(),
]
+ def GetTestFilters(self):
+ filters = super(LinuxX64X11Configuration, self).GetTestFilters()
+ # Remove the exclusion filter on SbDrmTest.AnySupportedKeySystems.
+ # Generally, children of linux/shared do not support widevine, but children
+ # of linux/x64x11 do, if the content decryption module is present.
+
+ has_cdm = os.path.isfile(
+ os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'cdm', 'cdm',
+ 'include', 'content_decryption_module.h'))
+
+ if not has_cdm:
+ return filters
+
+ for test_filter in filters:
+ if (test_filter.target_name == 'nplb' and
+ test_filter.test_name == 'SbDrmTest.AnySupportedKeySystems'):
+ filters.remove(test_filter)
+ return filters
+
def CreatePlatformConfig():
return LinuxX64X11Configuration()
diff --git a/src/starboard/linux/x64x11/libraries.gypi b/src/starboard/linux/x64x11/libraries.gypi
index 8024407..ac009ae 100644
--- a/src/starboard/linux/x64x11/libraries.gypi
+++ b/src/starboard/linux/x64x11/libraries.gypi
@@ -14,6 +14,17 @@
{
'variables': {
+ # This platform uses a compositor to present the rendering output, so
+ # set the swap interval to update the buffer immediately. That buffer
+ # will then be presented by the compositor on its own time.
+ 'cobalt_egl_swap_interval': 0,
+
+ # Hook into the swap buffers call to facilitate synchronization of the
+ # OpenGL output with the punch-through video layer.
+ 'linker_flags': [
+ '-Wl,--wrap=eglSwapBuffers',
+ ],
+
'cobalt_platform_dependencies': [
# GL Linux makes some GL calls within decode_target_internal.cc.
'<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
diff --git a/src/starboard/linux/x64x11/media_is_output_protected.cc b/src/starboard/linux/x64x11/media_is_output_protected.cc
new file mode 100644
index 0000000..debea8e
--- /dev/null
+++ b/src/starboard/linux/x64x11/media_is_output_protected.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 Google Inc. 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 "starboard/media.h"
+
+bool SbMediaIsOutputProtected() {
+ // Pretend that HDCP is always on because Linux is only able to play SD
+ // protected content.
+ return true;
+}
diff --git a/src/starboard/linux/x64x11/mock/configuration_public.h b/src/starboard/linux/x64x11/mock/configuration_public.h
index 2c07d9c..1a237f6 100644
--- a/src/starboard/linux/x64x11/mock/configuration_public.h
+++ b/src/starboard/linux/x64x11/mock/configuration_public.h
@@ -85,9 +85,6 @@
// Whether the current platform is expected to have exactly 6 cores.
#define SB_HAS_6_CORES 0
-// Whether the current platform supports thread priorities.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
-
// Whether the current platform's thread scheduler will automatically balance
// threads between cores, as opposed to systems where threads will only ever run
// on the specifically pinned core.
@@ -130,15 +127,6 @@
// Whether the current platform provides ssize_t.
#define SB_HAS_SSIZE_T 1
-// Whether the current platform has microphone supported.
-#define SB_HAS_MICROPHONE 1
-
-// Whether the current platform has speech recognizer.
-#define SB_HAS_SPEECH_RECOGNIZER 1
-
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 1
-
// Type detection for wchar_t.
#if defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -266,54 +254,6 @@
// access time is of 1 day precision.
#undef SB_HAS_QUIRK_FILESYSTEM_COARSE_ACCESS_TIME
-// --- Memory Configuration --------------------------------------------------
-
-// The memory page size, which controls the size of chunks on memory that
-// allocators deal with, and the alignment of those chunks. This doesn't have to
-// be the hardware-defined physical page size, but it should be a multiple of
-// it.
-#define SB_MEMORY_PAGE_SIZE 4096
-
-// Whether this platform has and should use an MMAP function to map physical
-// memory to the virtual address space.
-#define SB_HAS_MMAP 0
-
-// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
-// required for platforms that want to JIT.
-#define SB_CAN_MAP_EXECUTABLE_MEMORY 0
-
-// Whether this platform has and should use an growable heap (e.g. with sbrk())
-// to map physical memory to the virtual address space.
-#define SB_HAS_VIRTUAL_REGIONS 0
-
-// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
-// may require buffers to have a specific alignment, and this is the place to
-// specify that.
-#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
-
-// Determines the alignment that allocations should have on this platform.
-#define SB_MALLOC_ALIGNMENT ((size_t)16U)
-
-// Determines the threshhold of allocation size that should be done with mmap
-// (if available), rather than allocated within the core heap.
-#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
-
-// Defines the path where memory debugging logs should be written to.
-#define SB_MEMORY_LOG_PATH "/tmp/starboard"
-
-// --- Thread Configuration --------------------------------------------------
-
-// Defines the maximum number of simultaneous threads for this platform. Some
-// platforms require sharing thread handles with other kinds of system handles,
-// like mutexes, so we want to keep this managable.
-#define SB_MAX_THREADS 90
-
-// The maximum number of thread local storage keys supported by this platform.
-#define SB_MAX_THREAD_LOCAL_KEYS 512
-
-// The maximum length of the name for a thread, including the NULL-terminator.
-#define SB_MAX_THREAD_NAME_LENGTH 16;
-
// --- Graphics Configuration ------------------------------------------------
// Specifies whether this platform supports a performant accelerated blitter
@@ -342,6 +282,17 @@
#define SB_HAS_VIRTUAL_REALITY 0
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 1
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 1
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 1
+
// --- Media Configuration ---------------------------------------------------
// After a seek is triggerred, the default behavior is to append video frames
@@ -408,6 +359,41 @@
// value leads to more stable fps but also causes the app to use more memory.
#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
+// --- Memory Configuration --------------------------------------------------
+
+// The memory page size, which controls the size of chunks on memory that
+// allocators deal with, and the alignment of those chunks. This doesn't have to
+// be the hardware-defined physical page size, but it should be a multiple of
+// it.
+#define SB_MEMORY_PAGE_SIZE 4096
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#define SB_HAS_MMAP 0
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 0
+
+// Whether this platform has and should use an growable heap (e.g. with sbrk())
+// to map physical memory to the virtual address space.
+#define SB_HAS_VIRTUAL_REGIONS 0
+
+// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
+// may require buffers to have a specific alignment, and this is the place to
+// specify that.
+#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
+
+// Determines the alignment that allocations should have on this platform.
+#define SB_MALLOC_ALIGNMENT ((size_t)16U)
+
+// Determines the threshhold of allocation size that should be done with mmap
+// (if available), rather than allocated within the core heap.
+#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
+
+// Defines the path where memory debugging logs should be written to.
+#define SB_MEMORY_LOG_PATH "/tmp/starboard"
+
// --- Network Configuration -------------------------------------------------
// Specifies whether this platform supports IPV6.
@@ -416,6 +402,28 @@
// Specifies whether this platform supports pipe.
#define SB_HAS_PIPE 1
+// --- Thread Configuration --------------------------------------------------
+
+// Whether the current platform supports thread priorities.
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+
+// Defines the maximum number of simultaneous threads for this platform. Some
+// platforms require sharing thread handles with other kinds of system handles,
+// like mutexes, so we want to keep this managable.
+#define SB_MAX_THREADS 90
+
+// The maximum number of thread local storage keys supported by this platform.
+#define SB_MAX_THREAD_LOCAL_KEYS 512
+
+// The maximum length of the name for a thread, including the NULL-terminator.
+#define SB_MAX_THREAD_NAME_LENGTH 16
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
// --- Tuneable Parameters ---------------------------------------------------
// Specifies the network receive buffer size in bytes, set via
@@ -437,12 +445,6 @@
// The maximum number of users that can be signed in at the same time.
#define SB_USER_MAX_SIGNED_IN 1
-// --- Timing API ------------------------------------------------------------
-
-// Whether this platform has an API to retrieve how long the current thread
-// has spent in the executing state.
-#define SB_HAS_TIME_THREAD_NOW 1
-
// --- Platform Specific Audits ----------------------------------------------
#if !defined(__GNUC__)
diff --git a/src/starboard/linux/x64x11/mock/starboard_platform.gyp b/src/starboard/linux/x64x11/mock/starboard_platform.gyp
index 2e8b84a..620cc30 100644
--- a/src/starboard/linux/x64x11/mock/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/mock/starboard_platform.gyp
@@ -61,7 +61,9 @@
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+ '<(DEPTH)/starboard/shared/stub/drm_is_server_certificate_updatable.cc',
'<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+ '<(DEPTH)/starboard/shared/stub/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
'<(DEPTH)/starboard/shared/stub/file_can_open.cc',
'<(DEPTH)/starboard/shared/stub/file_close.cc',
diff --git a/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp b/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp
index db7c468..75991a5 100644
--- a/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/mozjs/starboard_platform.gyp
@@ -13,21 +13,6 @@
# limitations under the License.
{
'includes': [
- '../starboard_platform.gypi'
- ],
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'type': 'static_library',
- 'sources': ['<@(starboard_platform_sources)'],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<@(starboard_platform_dependencies)',
- ],
- },
+ '../starboard_platform.gyp'
],
}
diff --git a/src/starboard/linux/x64x11/sanitizer_options.cc b/src/starboard/linux/x64x11/sanitizer_options.cc
index e8a4aea..a3c3736 100644
--- a/src/starboard/linux/x64x11/sanitizer_options.cc
+++ b/src/starboard/linux/x64x11/sanitizer_options.cc
@@ -38,6 +38,7 @@
SANITIZER_HOOK_ATTRIBUTE const char* __lsan_default_suppressions() {
return
"leak:egl_gallium.so\n"
+ "leak:nvidia\n"
"leak:libspeechd.so\n";
}
diff --git a/src/starboard/linux/x64x11/sbversion/4/atomic_public.h b/src/starboard/linux/x64x11/sbversion/4/atomic_public.h
new file mode 100644
index 0000000..933830f
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/atomic_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64X11_SBVERSION_4_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SBVERSION_4_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/x64x11/atomic_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_SBVERSION_4_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/4/configuration_public.h b/src/starboard/linux/x64x11/sbversion/4/configuration_public.h
new file mode 100644
index 0000000..4e7efb2
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/configuration_public.h
@@ -0,0 +1,26 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64X11_SBVERSION_4_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SBVERSION_4_CONFIGURATION_PUBLIC_H_
+
+#undef SB_API_VERSION
+#define SB_API_VERSION 4
+
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_SBVERSION_4_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/4/gyp_configuration.gypi b/src/starboard/linux/x64x11/sbversion/4/gyp_configuration.gypi
new file mode 100644
index 0000000..8ff41cb
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/gyp_configuration.gypi
@@ -0,0 +1,40 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64x11-sbversion-4_debug',
+ 'configurations': {
+ 'linux-x64x11-sbversion-4_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64x11-sbversion-4_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64x11-sbversion-4_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64x11-sbversion-4_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '<(DEPTH)/starboard/linux/x64x11/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/sbversion/4/gyp_configuration.py b/src/starboard/linux/x64x11/sbversion/4/gyp_configuration.py
new file mode 100644
index 0000000..5059979
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/gyp_configuration.py
@@ -0,0 +1,43 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+import os
+
+from starboard.linux.x64x11 import gyp_configuration as parent_configuration
+
+
+class LinuxX64X11Sbversion4Configuration(
+ parent_configuration.LinuxX64X11Configuration):
+
+ def GetVariables(self, config_name):
+ variables = super(LinuxX64X11Sbversion4Configuration,
+ self).GetVariables(config_name)
+ # V8 requires new Starboard features so we must use SpiderMonkey in older
+ # versions of Starboard.
+ variables.update({
+ 'javascript_engine': 'mozjs-45',
+ 'cobalt_enable_jit': 0,
+ })
+ return variables
+
+ def GetLauncherPath(self):
+ """Gets the path to the launcher module for this platform."""
+ return os.path.dirname(__file__)
+
+
+def CreatePlatformConfig():
+ return LinuxX64X11Sbversion4Configuration('linux-x64x11-sbversion-4')
diff --git a/src/starboard/linux/x64x11/sbversion/4/launcher.py b/src/starboard/linux/x64x11/sbversion/4/launcher.py
new file mode 100644
index 0000000..03c50b2
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/launcher.py
@@ -0,0 +1,25 @@
+# Copyright 2018 Google Inc. 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.
+"""Specialized Starboard version 4 launcher expressing no preload support."""
+
+import starboard.linux.shared.launcher as launcher
+
+
+class Launcher(launcher.Launcher):
+
+ def SupportsSuspendResume(self):
+ # While technically Starboard version 4 does support suspend/resume, it
+ # does not support preload and so we return false here to avoid running
+ # tests which assume "SuspendResume" support implies preload support.
+ return False
diff --git a/src/starboard/linux/x64x11/sbversion/4/starboard_platform.gyp b/src/starboard/linux/x64x11/sbversion/4/starboard_platform.gyp
new file mode 100644
index 0000000..cb147aa
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/starboard_platform.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/sbversion/4/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/sbversion/4/starboard_platform_tests.gyp
new file mode 100644
index 0000000..d1a8fc2
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/starboard_platform_tests.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64x11/starboard_platform_tests.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/sbversion/4/thread_types_public.h b/src/starboard/linux/x64x11/sbversion/4/thread_types_public.h
new file mode 100644
index 0000000..4258c0e
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/4/thread_types_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64X11_SBVERSION_4_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SBVERSION_4_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/x64x11/thread_types_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_SBVERSION_4_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/atomic_public.h b/src/starboard/linux/x64x11/sbversion/6/atomic_public.h
new file mode 100644
index 0000000..82aac70
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/6/atomic_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64X11_SBVERSION_6_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SBVERSION_6_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/x64x11/atomic_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_SBVERSION_6_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/configuration_public.h b/src/starboard/linux/x64x11/sbversion/6/configuration_public.h
new file mode 100644
index 0000000..f770147
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/6/configuration_public.h
@@ -0,0 +1,26 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64X11_SBVERSION_6_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SBVERSION_6_CONFIGURATION_PUBLIC_H_
+
+#undef SB_API_VERSION
+#define SB_API_VERSION 6
+
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_SBVERSION_6_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi
new file mode 100644
index 0000000..968d646
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.gypi
@@ -0,0 +1,40 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64x11-sbversion-6_debug',
+ 'configurations': {
+ 'linux-x64x11-sbversion-6_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64x11-sbversion-6_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64x11-sbversion-6_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64x11-sbversion-6_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ '<(DEPTH)/starboard/linux/x64x11/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.py b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.py
new file mode 100644
index 0000000..7f8467c
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/6/gyp_configuration.py
@@ -0,0 +1,37 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+from starboard.linux.x64x11 import gyp_configuration as parent_configuration
+
+
+class LinuxX64X11Sbversion6Configuration(
+ parent_configuration.LinuxX64X11Configuration):
+
+ def GetVariables(self, config_name):
+ variables = super(LinuxX64X11Sbversion6Configuration,
+ self).GetVariables(config_name)
+ # V8 requires new Starboard features so we must use SpiderMonkey in older
+ # versions of Starboard.
+ variables.update({
+ 'javascript_engine': 'mozjs-45',
+ 'cobalt_enable_jit': 0,
+ })
+ return variables
+
+
+def CreatePlatformConfig():
+ return LinuxX64X11Sbversion6Configuration('linux-x64x11-sbversion-6')
diff --git a/src/starboard/linux/x64x11/sbversion/6/starboard_platform.gyp b/src/starboard/linux/x64x11/sbversion/6/starboard_platform.gyp
new file mode 100644
index 0000000..cb147aa
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/6/starboard_platform.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/sbversion/6/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/sbversion/6/starboard_platform_tests.gyp
new file mode 100644
index 0000000..d1a8fc2
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/6/starboard_platform_tests.gyp
@@ -0,0 +1,24 @@
+# Copyright 2018 Google Inc. 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.
+
+# This file was initially generated by create_derived_build.py,
+# though it may have been modified since its creation.
+
+{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/starboard/linux/x64x11/starboard_platform_tests.gyp',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/sbversion/6/thread_types_public.h b/src/starboard/linux/x64x11/sbversion/6/thread_types_public.h
new file mode 100644
index 0000000..3c957bc
--- /dev/null
+++ b/src/starboard/linux/x64x11/sbversion/6/thread_types_public.h
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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.
+
+// This file was initially generated by create_derived_build.py,
+// though it may have been modified since its creation.
+
+#ifndef STARBOARD_LINUX_X64X11_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/x64x11/thread_types_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_SBVERSION_6_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/skia/starboard_platform.gyp b/src/starboard/linux/x64x11/skia/starboard_platform.gyp
index 528d1cd..c33e4a8 100644
--- a/src/starboard/linux/x64x11/skia/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/skia/starboard_platform.gyp
@@ -13,21 +13,6 @@
# limitations under the License.
{
'includes': [
- '../starboard_platform.gypi'
- ],
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'type': 'static_library',
- 'sources': ['<@(starboard_platform_sources)'],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<@(starboard_platform_dependencies)',
- ],
- },
+ '../starboard_platform.gyp'
],
}
diff --git a/src/starboard/linux/x64x11/starboard_platform.gyp b/src/starboard/linux/x64x11/starboard_platform.gyp
index 18cf104..4733075 100644
--- a/src/starboard/linux/x64x11/starboard_platform.gyp
+++ b/src/starboard/linux/x64x11/starboard_platform.gyp
@@ -11,6 +11,10 @@
# 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.
+
+# Note, that despite the file extension ".gyp", this file is included by several
+# platform variants of linux-x64x11, like a ".gypi" file, since those platforms
+# have no need to modify this code.
{
'includes': [
'starboard_platform.gypi'
@@ -19,6 +23,7 @@
{
'target_name': 'starboard_platform',
'type': 'static_library',
+
'sources': [
'<@(starboard_platform_sources)',
'<(DEPTH)/starboard/shared/starboard/player/video_dmp_common.cc',
@@ -35,6 +40,20 @@
'dependencies': [
'<@(starboard_platform_dependencies)',
],
+ 'conditions': [
+ ['has_cdm==1', {
+ 'dependencies': [
+ '<(DEPTH)/starboard/linux/x64x11/widevine.gyp:wvcdm_static',
+ ],
+ 'sources!': [
+ '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc',
+ ],
+ 'sources/': [
+ ['exclude', 'shared/stub/drm_.*'],
+ ['exclude', 'shared/stub/media_is_supported\\.cc'],
+ ],
+ }],
+ ],
},
],
}
diff --git a/src/starboard/linux/x64x11/starboard_platform.gypi b/src/starboard/linux/x64x11/starboard_platform.gypi
index 6e54ebf..efdf8ea 100644
--- a/src/starboard/linux/x64x11/starboard_platform.gypi
+++ b/src/starboard/linux/x64x11/starboard_platform.gypi
@@ -15,17 +15,43 @@
'includes': ['../shared/starboard_platform.gypi'],
'variables': {
+ 'variables': {
+ 'has_cdm%': '<!(test -e <(DEPTH)/third_party/cdm/cdm/include/content_decryption_module.h && echo 1 || echo 0)',
+ },
+ # This has_cdm gets exported to gyp files that include this one.
+ 'has_cdm%': '<(has_cdm)',
'starboard_platform_sources': [
'<(DEPTH)/starboard/linux/x64x11/main.cc',
'<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc',
'<(DEPTH)/starboard/linux/x64x11/system_get_property.cc',
'<(DEPTH)/starboard/shared/starboard/link_receiver.cc',
'<(DEPTH)/starboard/shared/x11/application_x11.cc',
+ '<(DEPTH)/starboard/shared/x11/egl_swap_buffers.cc',
'<(DEPTH)/starboard/shared/x11/window_create.cc',
'<(DEPTH)/starboard/shared/x11/window_destroy.cc',
'<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc',
'<(DEPTH)/starboard/shared/x11/window_get_size.cc',
'<(DEPTH)/starboard/shared/x11/window_internal.cc',
],
+ 'conditions': [
+ ['has_cdm==1', {
+ 'starboard_platform_sources': [
+ '<(DEPTH)/starboard/linux/x64x11/media_is_output_protected.cc',
+
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_close_session.cc',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_destroy_system.cc',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_generate_session_update_request.cc',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_system_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_update_session.cc',
+
+ '<(DEPTH)/starboard/shared/widevine/drm_create_system.cc',
+ '<(DEPTH)/starboard/shared/widevine/drm_is_server_certificate_updatable.cc',
+ '<(DEPTH)/starboard/shared/widevine/drm_system_widevine.cc',
+ '<(DEPTH)/starboard/shared/widevine/drm_system_widevine.h',
+ '<(DEPTH)/starboard/shared/widevine/drm_update_server_certificate.cc',
+ '<(DEPTH)/starboard/shared/widevine/media_is_supported.cc',
+ ],
+ }],
+ ],
},
}
diff --git a/src/starboard/linux/x64x11/v8/atomic_public.h b/src/starboard/linux/x64x11/v8/atomic_public.h
deleted file mode 100644
index ac3dc77..0000000
--- a/src/starboard/linux/x64x11/v8/atomic_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 Google Inc. 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_LINUX_X64X11_V8_ATOMIC_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_V8_ATOMIC_PUBLIC_H_
-
-#include "starboard/linux/shared/atomic_public.h"
-
-#endif // STARBOARD_LINUX_X64X11_V8_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/v8/configuration_public.h b/src/starboard/linux/x64x11/v8/configuration_public.h
deleted file mode 100644
index d408b03..0000000
--- a/src/starboard/linux/x64x11/v8/configuration_public.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// The Starboard configuration for Desktop X86 Linux configured for the future
-// starboard API.
-
-#ifndef STARBOARD_LINUX_X64X11_V8_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_V8_CONFIGURATION_PUBLIC_H_
-
-// Include the X64X11 Linux configuration.
-#include "starboard/linux/x64x11/configuration_public.h"
-
-#endif // STARBOARD_LINUX_X64X11_V8_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/v8/gyp_configuration.gypi b/src/starboard/linux/x64x11/v8/gyp_configuration.gypi
deleted file mode 100644
index 28d6ee5..0000000
--- a/src/starboard/linux/x64x11/v8/gyp_configuration.gypi
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-
-{
- 'variables': {
- 'javascript_engine': 'v8',
- 'cobalt_enable_jit': 1,
- },
- 'target_defaults': {
- 'default_configuration': 'linux-x64x11-v8_debug',
- 'configurations': {
- 'linux-x64x11-v8_debug': {
- 'inherit_from': ['debug_base'],
- },
- 'linux-x64x11-v8_devel': {
- 'inherit_from': ['devel_base'],
- },
- 'linux-x64x11-v8_qa': {
- 'inherit_from': ['qa_base'],
- },
- 'linux-x64x11-v8_gold': {
- 'inherit_from': ['gold_base'],
- },
- }, # end of configurations
- },
-
- 'includes': [
- '../gyp_configuration.gypi',
- ],
-}
diff --git a/src/starboard/linux/x64x11/v8/gyp_configuration.py b/src/starboard/linux/x64x11/v8/gyp_configuration.py
deleted file mode 100644
index 338bc72..0000000
--- a/src/starboard/linux/x64x11/v8/gyp_configuration.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-"""Starboard Linux X64 X11 V8 platform configuration."""
-
-from starboard.linux.x64x11 import gyp_configuration as linux_configuration
-
-
-def CreatePlatformConfig():
- return linux_configuration.LinuxX64X11Configuration('linux-x64x11-v8')
diff --git a/src/starboard/linux/x64x11/v8/starboard_platform.gyp b/src/starboard/linux/x64x11/v8/starboard_platform.gyp
deleted file mode 100644
index 42b0bf5..0000000
--- a/src/starboard/linux/x64x11/v8/starboard_platform.gyp
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'includes': [
- '../starboard_platform.gypi'
- ],
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'type': 'static_library',
- 'sources': ['<@(starboard_platform_sources)'],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<@(starboard_platform_dependencies)',
- ],
- },
- ],
-}
diff --git a/src/starboard/linux/x64x11/v8/starboard_platform_tests.gyp b/src/starboard/linux/x64x11/v8/starboard_platform_tests.gyp
deleted file mode 100644
index 896e890..0000000
--- a/src/starboard/linux/x64x11/v8/starboard_platform_tests.gyp
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2018 Google Inc. 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.
-{
- 'includes': [
- '<(DEPTH)/starboard/linux/shared/starboard_platform_tests.gypi',
- ],
-}
diff --git a/src/starboard/linux/x64x11/v8/thread_types_public.h b/src/starboard/linux/x64x11/v8/thread_types_public.h
deleted file mode 100644
index dd50ad8..0000000
--- a/src/starboard/linux/x64x11/v8/thread_types_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 Google Inc. 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_LINUX_X64X11_V8_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_LINUX_X64X11_V8_THREAD_TYPES_PUBLIC_H_
-
-#include "starboard/linux/shared/thread_types_public.h"
-
-#endif // STARBOARD_LINUX_X64X11_V8_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/widevine.gyp b/src/starboard/linux/x64x11/widevine.gyp
new file mode 100644
index 0000000..1157121
--- /dev/null
+++ b/src/starboard/linux/x64x11/widevine.gyp
@@ -0,0 +1,30 @@
+# Copyright 2017 Google Inc. 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.
+{
+ 'variables': {
+ 'platform_oem_sources': [
+ '<(DEPTH)/starboard/keyboxes/linux/linux.h',
+ '<(DEPTH)/starboard/keyboxes/linux/linux_client.c',
+ 'wv_keybox.cc',
+ ],
+ },
+ 'includes': [
+ '<(DEPTH)/starboard/shared/widevine/widevine.gypi',
+ ],
+ 'target_defaults': {
+ 'defines': [
+ 'COBALT_WIDEVINE_KEYBOX_INCLUDE="starboard/keyboxes/widevine_settings_linux.h"',
+ ],
+ },
+}
diff --git a/src/starboard/linux/x64x11/wv_keybox.cc b/src/starboard/linux/x64x11/wv_keybox.cc
new file mode 100644
index 0000000..05054fc
--- /dev/null
+++ b/src/starboard/linux/x64x11/wv_keybox.cc
@@ -0,0 +1,47 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+
+#include <cstring>
+
+#include "starboard/keyboxes/linux/linux.h"
+#include "third_party/cdm/oemcrypto/mock/src/oemcrypto_keybox_mock.h"
+#include "third_party/cdm/oemcrypto/mock/src/wv_keybox.h"
+
+namespace wvoec_mock {
+
+namespace {
+
+#if defined(COBALT_WIDEVINE_KEYBOX_INCLUDE)
+#include COBALT_WIDEVINE_KEYBOX_INCLUDE
+#else // COBALT_WIDEVINE_KEYBOX_INCLUDE
+#error "COBALT_WIDEVINE_KEYBOX_INCLUDE is not defined."
+#endif // COBALT_WIDEVINE_KEYBOX_INCLUDE
+
+} // namespace
+
+bool WvKeybox::Prepare() {
+ // Make a local copy of the keybox.
+ WidevineKeybox keybox;
+ memcpy(&keybox, &kKeybox, sizeof(WidevineKeybox));
+
+ // Unobfuscate the key.
+ uint8_t clear[sizeof(kObfuscatedKey)];
+ // Despite the non-const type, linux_client does not modify the second
+ // argument.
+ linux_client(clear, const_cast<uint8_t*>(kObfuscatedKey));
+
+ // Move the clear key into the local copy of the keybox.
+ // NOTE: Clear device keys are half the size of obfuscated ones.
+ memcpy(&keybox.device_key_, clear, sizeof(keybox.device_key_));
+ memset(clear, 0, sizeof(clear));
+
+ // Install the local copy of the keybox.
+ uint8_t* bytes = reinterpret_cast<uint8_t*>(&keybox);
+ InstallKeybox(bytes, sizeof(keybox));
+
+ // Wipe the local copy of the keybox.
+ memset(&keybox, 0, sizeof(keybox));
+
+ return true;
+}
+
+} // namespace wvoec_mock
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 0dd1259..7c175ad 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -35,8 +35,14 @@
// Time represented in 90KHz ticks.
typedef int64_t SbMediaTime;
-#define SB_MEDIA_TIME_TO_SB_TIME(media) (media * 100 / 9)
-#define SB_TIME_TO_SB_MEDIA_TIME(time) (time * 9 / 100)
+#define SB_MEDIA_TIME_TO_SB_TIME(media) \
+ (((media == SB_PLAYER_NO_DURATION) \
+ ? SB_PLAYER_NO_DURATION \
+ : ((media == kSbInt64Max) ? kSbInt64Max : (media * 100 / 9))))
+#define SB_TIME_TO_SB_MEDIA_TIME(time) \
+ (((time == SB_PLAYER_NO_DURATION) \
+ ? SB_PLAYER_NO_DURATION \
+ : ((time == kSbInt64Max) ? kSbInt64Max : (time * 9 / 100))))
#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
// Types of media component streams.
diff --git a/src/starboard/memory.h b/src/starboard/memory.h
index c177ae3..18667be 100644
--- a/src/starboard/memory.h
+++ b/src/starboard/memory.h
@@ -195,7 +195,9 @@
//
// |size_bytes|: The amount of physical memory pages to be allocated.
// |flags|: The bitwise OR of the protection flags for the mapped memory
-// as specified in |SbMemoryMapFlags|.
+// as specified in |SbMemoryMapFlags|. Allocating executable memory is not
+// allowed and will fail. If executable memory is needed, map non-executable
+// memory first and then switch access to executable using SbMemoryProtect.
// |name|: A value that appears in the debugger on some platforms. The value
// can be up to 32 bytes.
SB_EXPORT void* SbMemoryMap(int64_t size_bytes, int flags, const char* name);
@@ -210,6 +212,14 @@
// |SbMemoryUnmap(0xA000, 0x2000)| should free both regions.
SB_EXPORT bool SbMemoryUnmap(void* virtual_address, int64_t size_bytes);
+#if SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+// Change the protection of |size_bytes| of physical pages, starting from
+// |virtual_address|, to |flags|, returning |true| on success.
+SB_EXPORT bool SbMemoryProtect(void* virtual_address,
+ int64_t size_bytes,
+ int flags);
+#endif
+
#if SB_CAN(MAP_EXECUTABLE_MEMORY)
// Flushes any data in the given virtual address range that is cached locally in
// the current processor core to physical memory, ensuring that data and
diff --git a/src/starboard/nplb/atomic_test.cc b/src/starboard/nplb/atomic_test.cc
index 9540c32..d9e9afa 100644
--- a/src/starboard/nplb/atomic_test.cc
+++ b/src/starboard/nplb/atomic_test.cc
@@ -26,21 +26,118 @@
namespace {
// Sets up an empty test fixture, required for typed tests.
+// BasicSbAtomicTest contains test cases all enabled atomic types should pass.
template <class SbAtomicType>
-class SbAtomicTest : public testing::Test {
+class BasicSbAtomicTest : public testing::Test {
public:
- SbAtomicTest() {}
- virtual ~SbAtomicTest() {}
+ BasicSbAtomicTest() {}
+ virtual ~BasicSbAtomicTest() {}
};
-#if SB_HAS(64_BIT_ATOMICS)
-typedef testing::Types<SbAtomic32, SbAtomic64, SbAtomicPtr> SbAtomicTestTypes;
-#else
-typedef testing::Types<SbAtomic32, SbAtomicPtr> SbAtomicTestTypes;
-#endif
-TYPED_TEST_CASE(SbAtomicTest, SbAtomicTestTypes);
+// AdvancedSbAtomicTest provides test coverage for all atomic functions.
+// Atomic8 is right now the only atomic type not required to pass
+// AdvancedSbAtomicTest, as it does not support implementations for the full
+// set of atomic functions like the other types do.
+template <class SbAtomicType>
+class AdvancedSbAtomicTest : public BasicSbAtomicTest<SbAtomicType> {
+ public:
+ AdvancedSbAtomicTest() {}
+ virtual ~AdvancedSbAtomicTest() {}
+};
-TYPED_TEST(SbAtomicTest, IncrementSingleThread) {
+typedef testing::Types<
+#if SB_API_VERSION >= SB_INTRODUCE_ATOMIC8_VERSION
+ SbAtomic8,
+#endif
+ SbAtomic32,
+#if SB_HAS(64_BIT_ATOMICS)
+ SbAtomic64,
+#endif
+ SbAtomicPtr> BasicSbAtomicTestTypes;
+typedef testing::Types<SbAtomic32,
+#if SB_HAS(64_BIT_ATOMICS)
+ SbAtomic64,
+#endif
+ SbAtomicPtr> AdvancedSbAtomicTestTypes;
+
+#if SB_API_VERSION >= SB_INTRODUCE_ATOMIC8_VERSION
+TYPED_TEST_CASE(BasicSbAtomicTest, BasicSbAtomicTestTypes);
+#else
+TYPED_TEST_CASE(BasicSbAtomicTest, AdvancedSbAtomicTestTypes);
+#endif
+
+TYPED_TEST_CASE(AdvancedSbAtomicTest, AdvancedSbAtomicTestTypes);
+
+// Return an SbAtomicType with the value 0xa5a5a5...
+template <class SbAtomicType>
+SbAtomicType TestFillValue() {
+ SbAtomicType val = 0;
+ SbMemorySet(&val, 0xa5, sizeof(SbAtomicType));
+ return val;
+}
+
+// Returns the number of bits in a type.
+template <class T>
+T Bits() {
+ return (sizeof(T) * 8);
+}
+
+// Returns a value with the high bit |bits| from the most significant bit set.
+template <class SbAtomicType>
+SbAtomicType GetHighBitAt(int bits) {
+ return (SbAtomicType(1) << (Bits<SbAtomicType>() - (bits + 1)));
+}
+
+// Produces a test value that has non-zero bits in both halves, for testing
+// 64-bit implementation on 32-bit platforms.
+template <class SbAtomicType>
+SbAtomicType GetTestValue() {
+ return GetHighBitAt<SbAtomicType>(1) + 11;
+}
+
+TYPED_TEST(BasicSbAtomicTest, ReleaseCompareAndSwapSingleThread) {
+ TypeParam value = 0;
+ TypeParam prev = atomic::Release_CompareAndSwap(&value, 0, 1);
+ EXPECT_EQ(1, value);
+ EXPECT_EQ(0, prev);
+
+ const TypeParam kTestValue = GetTestValue<TypeParam>();
+ value = kTestValue;
+ prev = atomic::Release_CompareAndSwap(&value, 0, 5);
+ EXPECT_EQ(kTestValue, value);
+ EXPECT_EQ(kTestValue, prev);
+
+ value = kTestValue;
+ prev = atomic::Release_CompareAndSwap(&value, kTestValue, 5);
+ EXPECT_EQ(5, value);
+ EXPECT_EQ(kTestValue, prev);
+}
+
+TYPED_TEST(BasicSbAtomicTest, NoBarrierStoreSingleThread) {
+ const TypeParam kVal1 = TestFillValue<TypeParam>();
+ const TypeParam kVal2 = static_cast<TypeParam>(-1);
+
+ TypeParam value;
+
+ atomic::NoBarrier_Store(&value, kVal1);
+ EXPECT_EQ(kVal1, value);
+ atomic::NoBarrier_Store(&value, kVal2);
+ EXPECT_EQ(kVal2, value);
+}
+
+TYPED_TEST(BasicSbAtomicTest, NoBarrierLoadSingleThread) {
+ const TypeParam kVal1 = TestFillValue<TypeParam>();
+ const TypeParam kVal2 = static_cast<TypeParam>(-1);
+
+ TypeParam value;
+
+ value = kVal1;
+ EXPECT_EQ(kVal1, atomic::NoBarrier_Load(&value));
+ value = kVal2;
+ EXPECT_EQ(kVal2, atomic::NoBarrier_Load(&value));
+}
+
+TYPED_TEST(AdvancedSbAtomicTest, IncrementSingleThread) {
// use a guard value to make sure the NoBarrier_AtomicIncrement doesn't go
// outside the expected address bounds. This is in particular to
// test that some future change to the asm code doesn't cause the
@@ -106,26 +203,7 @@
EXPECT_EQ(s.next_word, next_word_value);
}
-// Returns the number of bits in a type.
-template <class T>
-T Bits() {
- return (sizeof(T) * 8);
-}
-
-// Returns a value with the high bit |bits| from the most significant bit set.
-template <class SbAtomicType>
-SbAtomicType GetHighBitAt(int bits) {
- return (SbAtomicType(1) << (Bits<SbAtomicType>() - (bits + 1)));
-}
-
-// Produces a test value that has non-zero bits in both halves, for testing
-// 64-bit implementation on 32-bit platforms.
-template <class SbAtomicType>
-SbAtomicType GetTestValue() {
- return GetHighBitAt<SbAtomicType>(1) + 11;
-}
-
-TYPED_TEST(SbAtomicTest, CompareAndSwapSingleThread) {
+TYPED_TEST(AdvancedSbAtomicTest, NoBarrierCompareAndSwapSingleThread) {
TypeParam value = 0;
TypeParam prev = atomic::NoBarrier_CompareAndSwap(&value, 0, 1);
EXPECT_EQ(1, value);
@@ -143,7 +221,7 @@
EXPECT_EQ(kTestValue, prev);
}
-TYPED_TEST(SbAtomicTest, ExchangeSingleThread) {
+TYPED_TEST(AdvancedSbAtomicTest, ExchangeSingleThread) {
TypeParam value = 0;
TypeParam new_value = atomic::NoBarrier_Exchange(&value, 1);
EXPECT_EQ(1, value);
@@ -161,7 +239,7 @@
EXPECT_EQ(kTestValue, new_value);
}
-TYPED_TEST(SbAtomicTest, IncrementBoundsSingleThread) {
+TYPED_TEST(AdvancedSbAtomicTest, IncrementBoundsSingleThread) {
// Test at rollover boundary between int_max and int_min
TypeParam test_val = GetHighBitAt<TypeParam>(0);
TypeParam value = -1 ^ test_val;
@@ -183,25 +261,12 @@
EXPECT_EQ(test_val - 1, value);
}
-// Return an SbAtomicType with the value 0xa5a5a5...
-template <class SbAtomicType>
-SbAtomicType TestFillValue() {
- SbAtomicType val = 0;
- SbMemorySet(&val, 0xa5, sizeof(SbAtomicType));
- return val;
-}
-
-TYPED_TEST(SbAtomicTest, StoreSingleThread) {
+TYPED_TEST(AdvancedSbAtomicTest, BarrierStoreSingleThread) {
const TypeParam kVal1 = TestFillValue<TypeParam>();
const TypeParam kVal2 = static_cast<TypeParam>(-1);
TypeParam value;
- atomic::NoBarrier_Store(&value, kVal1);
- EXPECT_EQ(kVal1, value);
- atomic::NoBarrier_Store(&value, kVal2);
- EXPECT_EQ(kVal2, value);
-
atomic::Acquire_Store(&value, kVal1);
EXPECT_EQ(kVal1, value);
atomic::Acquire_Store(&value, kVal2);
@@ -213,18 +278,13 @@
EXPECT_EQ(kVal2, value);
}
-TYPED_TEST(SbAtomicTest, LoadSingleThread) {
+TYPED_TEST(AdvancedSbAtomicTest, BarrierLoadSingleThread) {
const TypeParam kVal1 = TestFillValue<TypeParam>();
const TypeParam kVal2 = static_cast<TypeParam>(-1);
TypeParam value;
value = kVal1;
- EXPECT_EQ(kVal1, atomic::NoBarrier_Load(&value));
- value = kVal2;
- EXPECT_EQ(kVal2, atomic::NoBarrier_Load(&value));
-
- value = kVal1;
EXPECT_EQ(kVal1, atomic::Acquire_Load(&value));
value = kVal2;
EXPECT_EQ(kVal2, atomic::Acquire_Load(&value));
@@ -295,7 +355,7 @@
// Tests a "once"-like functionality implemented with Atomics. This should
// effectively test multi-threaded CompareAndSwap, Release_Store and
// Acquire_Load.
-TYPED_TEST(SbAtomicTest, OnceMultipleThreads) {
+TYPED_TEST(AdvancedSbAtomicTest, OnceMultipleThreads) {
const int kNumTrials = 25;
const int kNumThreads = 12;
// Ensure each thread has slightly different data by offsetting it a bit as
@@ -372,7 +432,7 @@
return NULL;
}
-TYPED_TEST(SbAtomicTest, IncrementDecrementMultipleThreads) {
+TYPED_TEST(AdvancedSbAtomicTest, IncrementDecrementMultipleThreads) {
const int kNumTrials = 15;
const int kNumThreads = 12;
SB_COMPILE_ASSERT(kNumThreads % 2 == 0, kNumThreads_is_even);
diff --git a/src/starboard/nplb/condition_variable_wait_timed_test.cc b/src/starboard/nplb/condition_variable_wait_timed_test.cc
index 8698e37..e70c6b9 100644
--- a/src/starboard/nplb/condition_variable_wait_timed_test.cc
+++ b/src/starboard/nplb/condition_variable_wait_timed_test.cc
@@ -128,7 +128,9 @@
// We should have waited at least the delay_after_signal amount, but not the
// full delay.
- EXPECT_LT(context.delay_after_signal, SbTimeGetMonotonicNow() - start);
+ // Add some padding to tolerate slightly imprecise sleeps.
+ EXPECT_LT(context.delay_after_signal, SbTimeGetMonotonicNow() - start +
+ (context.delay_after_signal / 10));
EXPECT_GT(kDelay, SbTimeGetMonotonicNow() - start);
EXPECT_TRUE(SbMutexRelease(&context.mutex));
diff --git a/src/starboard/nplb/drm_create_system_test.cc b/src/starboard/nplb/drm_create_system_test.cc
new file mode 100644
index 0000000..8950a2d
--- /dev/null
+++ b/src/starboard/nplb/drm_create_system_test.cc
@@ -0,0 +1,193 @@
+// Copyright 2018 Google Inc. 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 <vector>
+
+#include "starboard/drm.h"
+#include "starboard/nplb/drm_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+TEST(SbDrmTest, AnySupportedKeySystems) {
+ bool any_supported_key_systems = false;
+ for (int i = 0; i < SB_ARRAY_SIZE_INT(kKeySystems); ++i) {
+ const char* key_system = kKeySystems[i];
+ SbDrmSystem drm_system = CreateDummyDrmSystem(key_system);
+ if (SbDrmSystemIsValid(drm_system)) {
+ SB_DLOG(INFO) << "Drm system with key system " << key_system
+ << " is valid.";
+ } else {
+ SB_DLOG(INFO) << "Drm system with key system " << key_system
+ << " is NOT valid.";
+ }
+ any_supported_key_systems |= SbDrmSystemIsValid(drm_system);
+ SbDrmDestroySystem(drm_system);
+ }
+ EXPECT_TRUE(any_supported_key_systems) << " no DRM key systems supported";
+}
+
+#if SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+TEST(SbDrmTest, NullCallbacks) {
+ for (int i = 0; i < SB_ARRAY_SIZE_INT(kKeySystems); ++i) {
+ const char* key_system = kKeySystems[i];
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */,
+ NULL /* session_update_request_func */, DummySessionUpdatedFunc,
+ DummySessionKeyStatusesChangedFunc, DummyServerCertificateUpdatedFunc,
+ DummySessionClosedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ NULL /*session_updated_func */, DummySessionKeyStatusesChangedFunc,
+ DummyServerCertificateUpdatedFunc, DummySessionClosedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, NULL /* session_key_statuses_changed_func */,
+ DummyServerCertificateUpdatedFunc, DummySessionClosedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
+ NULL /* server_certificatd_updated_func */, DummySessionClosedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
+ DummyServerCertificateUpdatedFunc, NULL /* session_closed_func */);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+#elif SB_HAS(DRM_SESSION_CLOSED)
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */,
+ NULL /* session_update_request_func */, DummySessionUpdatedFunc,
+ DummySessionKeyStatusesChangedFunc, DummySessionClosedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ NULL /*session_updated_func */, DummySessionKeyStatusesChangedFunc,
+ DummySessionClosedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, NULL /* session_key_statuses_changed_func */,
+ DummySessionClosedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
+ NULL /* session_closed_func */);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+#elif SB_HAS(DRM_KEY_STATUSES)
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */,
+ NULL /* session_update_request_func */, DummySessionUpdatedFunc,
+ DummySessionKeyStatusesChangedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ NULL /* session_updated_func */, DummySessionKeyStatusesChangedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc,
+ NULL /* session_key_statuses_changed_func */);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+#else
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */,
+ NULL /* session_update_request_func */, DummySessionUpdatedFunc);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ }
+ {
+ SbDrmSystem drm_system = SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ NULL /* session_updated_func */);
+ EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
+ SbDrmDestroySystem(drm_system);
+ }
+#endif // SB_HAS(DRM_KEY_STATUSES)
+ }
+}
+#endif // SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+
+#if SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+TEST(SbDrmTest, MultiDrm) {
+ const int kMaxPlayersPerKeySystem = 16;
+ std::vector<SbDrmSystem> created_drm_systems;
+ int number_of_drm_systems = 0;
+ for (int i = 0; i < kMaxPlayersPerKeySystem; ++i) {
+ for (int j = 0; j < SB_ARRAY_SIZE_INT(kKeySystems); ++j) {
+ const char* key_system = kKeySystems[j];
+ created_drm_systems.push_back(CreateDummyDrmSystem(key_system));
+ if (!SbDrmSystemIsValid(created_drm_systems.back())) {
+ created_drm_systems.pop_back();
+ }
+ }
+ if (created_drm_systems.size() == number_of_drm_systems) {
+ break;
+ }
+ number_of_drm_systems = created_drm_systems.size();
+ }
+ SB_DLOG(INFO) << "Created " << number_of_drm_systems
+ << " DRM systems in total.";
+ for (auto drm_system : created_drm_systems) {
+ SbDrmDestroySystem(drm_system);
+ }
+}
+#endif // SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/drm_helpers.cc b/src/starboard/nplb/drm_helpers.cc
new file mode 100644
index 0000000..81e1a59
--- /dev/null
+++ b/src/starboard/nplb/drm_helpers.cc
@@ -0,0 +1,108 @@
+// Copyright 2018 Google Inc. 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 "starboard/nplb/drm_helpers.h"
+
+#include "starboard/drm.h"
+
+namespace starboard {
+namespace nplb {
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+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) {}
+
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
+ void* context,
+ int ticket,
+ 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,
+ const void* session_id,
+ int session_id_size,
+ bool succeeded) {}
+
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+#if SB_HAS(DRM_KEY_STATUSES)
+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) {}
+#endif // SB_HAS(DRM_KEY_STATUSES)
+
+void DummySessionClosedFunc(SbDrmSystem drm_system,
+ void* context,
+ const void* session_id,
+ int session_id_size) {}
+
+SbDrmSystem CreateDummyDrmSystem(const char* key_system) {
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+ return SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
+ DummyServerCertificateUpdatedFunc, DummySessionClosedFunc);
+#elif SB_HAS(DRM_SESSION_CLOSED)
+ return SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
+ DummySessionClosedFunc);
+#elif SB_HAS(DRM_KEY_STATUSES)
+ return SbDrmCreateSystem(
+ key_system, NULL /* context */, DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc);
+#else // SB_HAS(DRM_KEY_STATUSES)
+ return SbDrmCreateSystem(key_system, NULL /* context */,
+ DummySessionUpdateRequestFunc,
+ DummySessionUpdatedFunc);
+#endif // SB_HAS(DRM_KEY_STATUSES)
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/drm_helpers.h b/src/starboard/nplb/drm_helpers.h
new file mode 100644
index 0000000..1335780
--- /dev/null
+++ b/src/starboard/nplb/drm_helpers.h
@@ -0,0 +1,95 @@
+// Copyright 2018 Google Inc. 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_NPLB_DRM_HELPERS_H_
+#define STARBOARD_NPLB_DRM_HELPERS_H_
+
+#include "starboard/drm.h"
+
+namespace starboard {
+namespace nplb {
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+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);
+
+#else // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+void DummySessionUpdateRequestFunc(SbDrmSystem drm_system,
+ void* context,
+ int ticket,
+ 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,
+ const void* session_id,
+ int session_id_size,
+ bool succeeded);
+
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+#if SB_HAS(DRM_KEY_STATUSES)
+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);
+#endif // SB_HAS(DRM_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", "fairplay",
+};
+
+} // namespace nplb
+} // namespace starboard
+
+#endif // STARBOARD_NPLB_DRM_HELPERS_H_
diff --git a/src/starboard/nplb/drm_is_server_certificate_updatable_test.cc b/src/starboard/nplb/drm_is_server_certificate_updatable_test.cc
new file mode 100644
index 0000000..9426769
--- /dev/null
+++ b/src/starboard/nplb/drm_is_server_certificate_updatable_test.cc
@@ -0,0 +1,57 @@
+// Copyright 2018 Google Inc. 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 "starboard/drm.h"
+
+#include "starboard/nplb/drm_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+TEST(SbDrmIsServerCertificateUpdatableTest, SunnyDay) {
+ // Ensure that |SbDrmIsServerCertificateUpdatable| can be called over all key
+ // systems.
+ for (auto key_system : kKeySystems) {
+ SbDrmSystem drm_system = CreateDummyDrmSystem(key_system);
+ if (!SbDrmSystemIsValid(drm_system)) {
+ continue;
+ }
+ SbDrmIsServerCertificateUpdatable(drm_system);
+ SbDrmDestroySystem(drm_system);
+ }
+}
+
+TEST(SbDrmIsServerCertificateUpdatableTest, Consistency) {
+ // Ensure that the function returns the same results for the same key system
+ // when being called for more than once.
+ for (auto key_system : kKeySystems) {
+ SbDrmSystem drm_system = CreateDummyDrmSystem(key_system);
+ if (!SbDrmSystemIsValid(drm_system)) {
+ continue;
+ }
+ bool updatable = SbDrmIsServerCertificateUpdatable(drm_system);
+ EXPECT_EQ(SbDrmIsServerCertificateUpdatable(drm_system), updatable);
+ SbDrmDestroySystem(drm_system);
+ }
+}
+
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/drm_update_server_certificate_test.cc b/src/starboard/nplb/drm_update_server_certificate_test.cc
new file mode 100644
index 0000000..12d0a94
--- /dev/null
+++ b/src/starboard/nplb/drm_update_server_certificate_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2018 Google Inc. 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 "starboard/drm.h"
+
+#include "starboard/nplb/drm_helpers.h"
+#include "starboard/string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+TEST(SbDrmUpdateServerCertificateTest, SunnyDay) {
+ // Ensure that |SbDrmUpdateServerCertificate| can be called over all key
+ // systems.
+ for (auto key_system : kKeySystems) {
+ SbDrmSystem drm_system = CreateDummyDrmSystem(key_system);
+ if (!SbDrmSystemIsValid(drm_system)) {
+ continue;
+ }
+ const char kInvalidCertificate[] = "*Cobalt*";
+ if (SbDrmIsServerCertificateUpdatable(drm_system)) {
+ SbDrmUpdateServerCertificate(drm_system, kSbDrmTicketInvalid + 1,
+ kInvalidCertificate,
+ SbStringGetLength(kInvalidCertificate));
+ }
+ SbDrmDestroySystem(drm_system);
+ }
+}
+
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/memory_map_test.cc b/src/starboard/nplb/memory_map_test.cc
index 2751e00..eb4c94f 100644
--- a/src/starboard/nplb/memory_map_test.cc
+++ b/src/starboard/nplb/memory_map_test.cc
@@ -107,22 +107,22 @@
}
#if SB_CAN(MAP_EXECUTABLE_MEMORY)
+
typedef int (*SumFunction)(int, int);
-static int sum(int x, int y) {
+static int Sum(int x, int y) {
return x + y;
}
-static int sum2(int x, int y) {
+static int Sum2(int x, int y) {
return x + y + x;
}
-// This test is known to run on x64, ARM, and MIPS32 with MIPS32 and MIPS16
-// instructions.
-TEST(SbMemoryMapTest, CanExecuteMappedMemoryWithExecFlag) {
- void* memory = SbMemoryMap(
- kSize, kSbMemoryMapProtectReadWrite | kSbMemoryMapProtectExec, "test");
- ASSERT_NE(kFailed, memory);
-
- // There's no reliable way to determine the size of the 'sum' function. If we
+// Copy the contents of |Sum| into |memory|, returning an assertion result
+// indicating whether the copy was successful, and a function pointer to the
+// possibly copied |Sum| function data. Clients are expected to immediately
+// call |ASSERT| on the assertion result.
+std::pair<::testing::AssertionResult, SumFunction> CopySumFunctionIntoMemory(
+ void* memory) {
+ // There's no reliable way to determine the size of the 'Sum' function. If we
// assume the function is at most a certain size, then we might try to read
// beyond mapped memory when copying it to the destination address. We can
// get a reasonable upper bound by assuming that the function's implementation
@@ -132,18 +132,18 @@
// However, since it's possible the function may cross the page boundary, we
// define two functions and use the one closest to the start of a page. There
// is no guarantee that the linker will place these definitions sequentially
- // (although it likely will), so we can't use the address of 'sum2' as the
- // end of 'sum'.
+ // (although it likely will), so we can't use the address of 'Sum2' as the
+ // end of 'Sum'.
//
// To avoid the possibility that COMDAT folding will combine these two
// definitions into one, make sure they are different.
// A function pointer can't be cast to void*, but uint8* seems to be okay. So
// cast to a uint* which will be implicitly casted to a void* below.
- SumFunction original_function = ∑
- if (reinterpret_cast<uintptr_t>(&sum2) % SB_MEMORY_PAGE_SIZE <
- reinterpret_cast<uintptr_t>(&sum) % SB_MEMORY_PAGE_SIZE) {
- original_function = &sum2;
+ SumFunction original_function = ∑
+ if (reinterpret_cast<uintptr_t>(&Sum2) % SB_MEMORY_PAGE_SIZE <
+ reinterpret_cast<uintptr_t>(&Sum) % SB_MEMORY_PAGE_SIZE) {
+ original_function = &Sum2;
}
uint8_t* sum_function_start = reinterpret_cast<uint8_t*>(original_function);
@@ -163,11 +163,29 @@
(reinterpret_cast<uintptr_t>(sum_function_start) / SB_MEMORY_PAGE_SIZE) *
SB_MEMORY_PAGE_SIZE +
SB_MEMORY_PAGE_SIZE);
- ASSERT_TRUE(SbMemoryIsAligned(sum_function_page_end, SB_MEMORY_PAGE_SIZE));
- ASSERT_LT(sum_function_start, sum_function_page_end);
+ if (!SbMemoryIsAligned(sum_function_page_end, SB_MEMORY_PAGE_SIZE)) {
+ return {::testing::AssertionFailure()
+ << "Expected |Sum| page end ("
+ << static_cast<void*>(sum_function_page_end)
+ << ") to be aligned to " << SB_MEMORY_PAGE_SIZE,
+ nullptr};
+ }
+ if (sum_function_start >= sum_function_page_end) {
+ return {::testing::AssertionFailure()
+ << "Expected |Sum| function page start ("
+ << static_cast<void*>(sum_function_start)
+ << ") to be less than |Sum| function page end ("
+ << static_cast<void*>(sum_function_page_end) << ")",
+ nullptr};
+ }
size_t bytes_to_copy = sum_function_page_end - sum_function_start;
- ASSERT_LE(bytes_to_copy, SB_MEMORY_PAGE_SIZE);
+ if (bytes_to_copy > SB_MEMORY_PAGE_SIZE) {
+ return {::testing::AssertionFailure()
+ << "Expected bytes required to copy |Sum| to be less than "
+ << SB_MEMORY_PAGE_SIZE << ", Actual: " << bytes_to_copy,
+ nullptr};
+ }
SbMemoryCopy(memory, sum_function_start, bytes_to_copy);
SbMemoryFlush(memory, bytes_to_copy);
@@ -175,12 +193,100 @@
SumFunction mapped_function = reinterpret_cast<SumFunction>(
reinterpret_cast<uint8_t*>(memory) + sum_function_offset);
- EXPECT_EQ((*original_function)(1, 3), (*mapped_function)(1, 3));
- EXPECT_EQ((*original_function)(10, -5), (*mapped_function)(10, -5));
-
- EXPECT_TRUE(SbMemoryUnmap(memory, kSize));
+ return {::testing::AssertionSuccess(), mapped_function};
}
+
+#if SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+// Cobalt can not map executable memory. If executable memory is needed, map
+// non-executable memory first and use SbMemoryProtect to change memory accesss
+// to executable.
+TEST(SbMemoryMapTest, CanNotDirectlyMapMemoryWithExecFlag) {
+ SbMemoryMapFlags exec_flags[] = {
+ SbMemoryMapFlags(kSbMemoryMapProtectExec),
+ SbMemoryMapFlags(kSbMemoryMapProtectRead | kSbMemoryMapProtectExec),
+ SbMemoryMapFlags(kSbMemoryMapProtectWrite | kSbMemoryMapProtectExec),
+ SbMemoryMapFlags(kSbMemoryMapProtectWrite | kSbMemoryMapProtectWrite |
+ kSbMemoryMapProtectExec),
+ };
+ for (auto exec_flag : exec_flags) {
+ void* memory = SbMemoryMap(kSize, exec_flag, "test");
+ ASSERT_EQ(kFailed, memory);
+ EXPECT_FALSE(SbMemoryUnmap(memory, 0));
+ }
+}
+#endif // SB_MEMORY_PROTECT_API_VERSION
#endif // SB_CAN(MAP_EXECUTABLE_MEMORY)
+
+#if SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+TEST(SbMemoryMapTest, CanChangeMemoryProtection) {
+ SbMemoryMapFlags all_from_flags[] = {
+ SbMemoryMapFlags(0),
+ SbMemoryMapFlags(kSbMemoryMapProtectRead),
+ SbMemoryMapFlags(kSbMemoryMapProtectWrite),
+ SbMemoryMapFlags(kSbMemoryMapProtectRead | kSbMemoryMapProtectWrite),
+ };
+ SbMemoryMapFlags all_to_flags[] = {
+ SbMemoryMapFlags(0),
+ SbMemoryMapFlags(kSbMemoryMapProtectRead),
+ SbMemoryMapFlags(kSbMemoryMapProtectWrite),
+ SbMemoryMapFlags(kSbMemoryMapProtectRead | kSbMemoryMapProtectWrite),
+#if SB_CAN(MAP_EXECUTABLE_MEMORY)
+ SbMemoryMapFlags(kSbMemoryMapProtectExec),
+ SbMemoryMapFlags(kSbMemoryMapProtectRead | kSbMemoryMapProtectExec),
+#endif
+ };
+
+ for (SbMemoryMapFlags from_flags : all_from_flags) {
+ for (SbMemoryMapFlags to_flags : all_to_flags) {
+ void* memory = SbMemoryMap(kSize, from_flags, "test");
+ // If the platform does not support a particular protection flags
+ // configuration in the first place, then just give them a pass, knowing
+ // that that feature will be tested elsewhere.
+ if (memory == SB_MEMORY_MAP_FAILED) {
+ continue;
+ }
+
+#if SB_CAN(MAP_EXECUTABLE_MEMORY)
+ // We can only test the ability to execute memory after changing
+ // protections if we have write permissions either now or then, because
+ // we have to actually put a valid function into the mapped memory.
+ SumFunction mapped_function = nullptr;
+ if (from_flags & kSbMemoryMapProtectWrite) {
+ auto copy_sum_function_result = CopySumFunctionIntoMemory(memory);
+ ASSERT_TRUE(copy_sum_function_result.first);
+ mapped_function = copy_sum_function_result.second;
+ }
+#endif
+
+ if (!SbMemoryProtect(memory, kSize, to_flags)) {
+ EXPECT_TRUE(SbMemoryUnmap(memory, kSize));
+ continue;
+ }
+
+#if SB_CAN(MAP_EXECUTABLE_MEMORY)
+ if ((to_flags & kSbMemoryMapProtectExec) && mapped_function != nullptr) {
+ EXPECT_EQ(Sum(0xc0ba178, 0xbadf00d),
+ mapped_function(0xc0ba178, 0xbadf00d));
+ }
+#endif
+
+ if (to_flags & kSbMemoryMapProtectRead) {
+ for (int i = 0; i < kSize; i++) {
+ volatile uint8_t force_read = static_cast<uint8_t*>(memory)[i];
+ }
+ }
+ if (to_flags & kSbMemoryMapProtectWrite) {
+ for (int i = 0; i < kSize; i++) {
+ static_cast<uint8_t*>(memory)[i] = 0xff;
+ }
+ }
+
+ EXPECT_TRUE(SbMemoryUnmap(memory, kSize));
+ }
+ }
+}
+#endif // SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+
#endif // SB_HAS(MMAP)
} // namespace
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index c951670..dad6813 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -99,6 +99,11 @@
'double_floor_test.cc',
'double_is_finite_test.cc',
'double_is_nan_test.cc',
+ 'drm_create_system_test.cc',
+ 'drm_helpers.cc',
+ 'drm_helpers.h',
+ 'drm_is_server_certificate_updatable_test.cc',
+ 'drm_update_server_certificate_test.cc',
'file_can_open_test.cc',
'file_close_test.cc',
'file_get_info_test.cc',
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index d9d09ae..54e7ffc 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -35,7 +35,29 @@
FakeGraphicsContextProvider fake_graphics_context_provider_;
};
-TEST_F(SbPlayerTest, SunnyDay) {
+void DummyDeallocateSampleFunc(SbPlayer player,
+ void* context,
+ const void* sample_buffer) {}
+
+void DummyDecoderStatusFunc(SbPlayer player,
+ void* context,
+ SbMediaType type,
+ SbPlayerDecoderState state,
+ int ticket) {}
+
+void DummyStatusFunc(SbPlayer player,
+ void* context,
+ SbPlayerState state,
+ int ticket) {}
+
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+void DummyErrorFunc(SbPlayer player,
+ void* context,
+ SbPlayerError error,
+ const char* message) {}
+#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+
+SbMediaAudioHeader GetDefaultAudioHeader() {
SbMediaAudioHeader audio_header;
audio_header.format_tag = 0xff;
@@ -48,6 +70,11 @@
audio_header.number_of_channels *
audio_header.bits_per_sample / 8;
+ return audio_header;
+}
+
+TEST_F(SbPlayerTest, SunnyDay) {
+ SbMediaAudioHeader audio_header = GetDefaultAudioHeader();
SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
@@ -66,11 +93,12 @@
#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
SB_PLAYER_NO_DURATION,
#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
- kSbDrmSystemInvalid, &audio_header, NULL, NULL, NULL,
+ kSbDrmSystemInvalid, &audio_header, DummyDeallocateSampleFunc,
+ DummyDecoderStatusFunc, DummyStatusFunc,
#if SB_HAS(PLAYER_ERROR_MESSAGE)
- NULL,
+ DummyErrorFunc,
#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- NULL, output_mode,
+ NULL /* context */, output_mode,
fake_graphics_context_provider_.decoder_target_provider());
EXPECT_TRUE(SbPlayerIsValid(player));
@@ -82,6 +110,99 @@
}
}
+#if SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+TEST_F(SbPlayerTest, NullCallbacks) {
+ SbMediaAudioHeader audio_header = GetDefaultAudioHeader();
+ SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
+ SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
+
+ SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
+ kSbPlayerOutputModePunchOut};
+
+ for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
+ SbPlayerOutputMode output_mode = output_modes[i];
+ if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
+ continue;
+ }
+
+ {
+ SbPlayer player = SbPlayerCreate(
+ fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
+ kSbMediaAudioCodecAac,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ SB_PLAYER_NO_DURATION,
+#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ kSbDrmSystemInvalid, &audio_header, NULL /* deallocate_sample_func */,
+ DummyDecoderStatusFunc, DummyStatusFunc,
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+ DummyErrorFunc,
+#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+ NULL /* context */, output_mode,
+ fake_graphics_context_provider_.decoder_target_provider());
+ EXPECT_FALSE(SbPlayerIsValid(player));
+
+ SbPlayerDestroy(player);
+ }
+
+ {
+ SbPlayer player = SbPlayerCreate(
+ fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
+ kSbMediaAudioCodecAac,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ SB_PLAYER_NO_DURATION,
+#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ kSbDrmSystemInvalid, &audio_header, DummyDeallocateSampleFunc,
+ NULL /* decoder_status_func */, DummyStatusFunc,
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+ DummyErrorFunc,
+#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+ NULL /* context */, output_mode,
+ fake_graphics_context_provider_.decoder_target_provider());
+ EXPECT_FALSE(SbPlayerIsValid(player));
+
+ SbPlayerDestroy(player);
+ }
+
+ {
+ SbPlayer player = SbPlayerCreate(
+ fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
+ kSbMediaAudioCodecAac,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ SB_PLAYER_NO_DURATION,
+#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ kSbDrmSystemInvalid, &audio_header, DummyDeallocateSampleFunc,
+ DummyDecoderStatusFunc, NULL /*status_func */,
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+ DummyErrorFunc,
+#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+ NULL /* context */, output_mode,
+ fake_graphics_context_provider_.decoder_target_provider());
+ EXPECT_FALSE(SbPlayerIsValid(player));
+
+ SbPlayerDestroy(player);
+ }
+
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+ {
+ SbPlayer player = SbPlayerCreate(
+ fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
+ kSbMediaAudioCodecAac,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ SB_PLAYER_NO_DURATION,
+#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ kSbDrmSystemInvalid, &audio_header, DummyDeallocateSampleFunc,
+ DummyDecoderStatusFunc, DummyStatusFunc, NULL /*error_func */,
+ NULL /* context */, output_mode,
+ fake_graphics_context_provider_.decoder_target_provider());
+ EXPECT_FALSE(SbPlayerIsValid(player));
+
+ SbPlayerDestroy(player);
+ }
+#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+ }
+}
+#endif // SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+
#if SB_HAS(AUDIOLESS_VIDEO)
TEST_F(SbPlayerTest, Audioless) {
SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
@@ -97,16 +218,17 @@
}
SbPlayer player = SbPlayerCreate(
- fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
+ fake_graphics_context_provider_.window(), kVideoCodec,
kSbMediaAudioCodecNone,
#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
SB_PLAYER_NO_DURATION,
#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
- kSbDrmSystemInvalid, NULL, NULL, NULL, NULL,
+ kSbDrmSystemInvalid, NULL /* audio_header */, DummyDeallocateSampleFunc,
+ DummyDecoderStatusFunc, DummyStatusFunc,
#if SB_HAS(PLAYER_ERROR_MESSAGE)
- NULL,
+ DummyErrorFunc,
#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- NULL, output_mode,
+ NULL /* context */, output_mode,
fake_graphics_context_provider_.decoder_target_provider());
EXPECT_TRUE(SbPlayerIsValid(player));
@@ -119,20 +241,49 @@
}
#endif // SB_HAS(AUDIOLESS_VIDEO)
+#if SB_API_VERSION >= SB_AUDIO_ONLY_VIDEO_API_VERSION
+TEST_F(SbPlayerTest, AudioOnly) {
+ SbMediaAudioHeader audio_header = GetDefaultAudioHeader();
+ SbMediaAudioCodec kAudioCodec = kSbMediaAudioCodecAac;
+ SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
+ SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
+
+ SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
+ kSbPlayerOutputModePunchOut};
+
+ for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
+ SbPlayerOutputMode output_mode = output_modes[i];
+ if (!SbPlayerOutputModeSupported(output_mode, kVideoCodec, kDrmSystem)) {
+ continue;
+ }
+
+ SbPlayer player = SbPlayerCreate(
+ fake_graphics_context_provider_.window(), kSbMediaVideoCodecNone,
+ kAudioCodec,
+#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ SB_PLAYER_NO_DURATION,
+#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ kSbDrmSystemInvalid, &audio_header, DummyDeallocateSampleFunc,
+ DummyDecoderStatusFunc, DummyStatusFunc,
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+ DummyErrorFunc,
+#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+ NULL /* context */, output_mode,
+ fake_graphics_context_provider_.decoder_target_provider());
+ EXPECT_TRUE(SbPlayerIsValid(player));
+
+ if (output_mode == kSbPlayerOutputModeDecodeToTexture) {
+ SbDecodeTarget current_frame = SbPlayerGetCurrentFrame(player);
+ }
+
+ SbPlayerDestroy(player);
+ }
+}
+#endif // SB_API_VERSION >= SB_AUDIO_ONLY_VIDEO_API_VERSION
+
#if SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
TEST_F(SbPlayerTest, MultiPlayer) {
- SbMediaAudioHeader audio_header;
-
- audio_header.format_tag = 0xff;
- audio_header.number_of_channels = 2;
- audio_header.samples_per_second = 22050;
- audio_header.block_alignment = 4;
- audio_header.bits_per_sample = 32;
- audio_header.audio_specific_config_size = 0;
- audio_header.average_bytes_per_second = audio_header.samples_per_second *
- audio_header.number_of_channels *
- audio_header.bits_per_sample / 8;
-
+ SbMediaAudioHeader audio_header = GetDefaultAudioHeader();
SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
constexpr SbPlayerOutputMode kOutputModes[] = {
@@ -192,8 +343,10 @@
for (int l = 0; l < SB_ARRAY_SIZE_INT(kVideoCodecs); ++l) {
created_players.push_back(SbPlayerCreate(
fake_graphics_context_provider_.window(), kVideoCodecs[l],
- kAudioCodecs[k], kSbDrmSystemInvalid, &audio_header, NULL, NULL,
- NULL, NULL, NULL, kOutputModes[j],
+ kAudioCodecs[k], kSbDrmSystemInvalid, &audio_header,
+ DummyDeallocateSampleFunc, DummyDecoderStatusFunc,
+ DummyStatusFunc, DummyErrorFunc, NULL /* context */,
+ kOutputModes[j],
fake_graphics_context_provider_.decoder_target_provider()));
if (!SbPlayerIsValid(created_players.back())) {
created_players.pop_back();
diff --git a/src/starboard/nplb/player_create_with_url_test.cc b/src/starboard/nplb/player_create_with_url_test.cc
index 3de57d0..d5c9e65 100644
--- a/src/starboard/nplb/player_create_with_url_test.cc
+++ b/src/starboard/nplb/player_create_with_url_test.cc
@@ -24,6 +24,25 @@
#if SB_HAS(PLAYER_WITH_URL)
+const char kPlayerUrl[] = "about:blank";
+
+void DummyPlayerStatusFunc(SbPlayer player,
+ void* context,
+ SbPlayerState state,
+ int ticket) {}
+
+void DummyEncryptedMediaInitaDataEncounteredFunc(
+ SbPlayer player,
+ void* context,
+ const char* init_data_type,
+ const unsigned char* init_data,
+ unsigned int init_data_length) {}
+
+void DummyPlayerErrorFunc(SbPlayer player,
+ void* context,
+ SbPlayerError error,
+ const char* message) {}
+
TEST(SbPlayerUrlTest, SunnyDay) {
SbWindowOptions window_options;
SbWindowSetDefaultOptions(&window_options);
@@ -39,12 +58,13 @@
if (!SbPlayerOutputModeSupportedWithUrl(output_mode)) {
continue;
}
- char url[] = "about:blank";
- SbPlayer player = SbPlayerCreateWithUrl(url, window,
+ SbPlayer player = SbPlayerCreateWithUrl(
+ kPlayerUrl, window,
#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
- SB_PLAYER_NO_DURATION,
+ SB_PLAYER_NO_DURATION,
#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
- NULL, NULL, NULL, NULL);
+ DummyPlayerStatusFunc, DummyEncryptedMediaInitaDataEncounteredFunc,
+ DummyPlayerErrorFunc, NULL);
EXPECT_TRUE(SbPlayerIsValid(player));
@@ -58,6 +78,54 @@
SbWindowDestroy(window);
}
+#if SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+TEST(SbPlayerUrlTest, NullCallbacks) {
+ SbWindowOptions window_options;
+ SbWindowSetDefaultOptions(&window_options);
+
+ SbWindow window = SbWindowCreate(&window_options);
+ EXPECT_TRUE(SbWindowIsValid(window));
+
+ SbMediaVideoCodec kVideoCodec = kSbMediaVideoCodecH264;
+ SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
+
+ SbPlayerOutputMode output_modes[] = {kSbPlayerOutputModeDecodeToTexture,
+ kSbPlayerOutputModePunchOut};
+
+ for (int i = 0; i < SB_ARRAY_SIZE_INT(output_modes); ++i) {
+ SbPlayerOutputMode output_mode = output_modes[i];
+ if (!SbPlayerOutputModeSupportedWithUrl(output_mode)) {
+ continue;
+ }
+ {
+ SbPlayer player =
+ SbPlayerCreateWithUrl(kPlayerUrl, window,
+ NULL /* player_status_func */,
+ DummyEncryptedMediaInitaDataEncounteredFunc,
+ DummyPlayerErrorFunc, NULL /* context */);
+ EXPECT_FALSE(SbPlayerIsValid(player));
+ SbPlayerDestroy(player);
+ }
+ {
+ SbPlayer player = SbPlayerCreateWithUrl(
+ kPlayerUrl, window, DummyPlayerStatusFunc,
+ NULL /* encrypted_media_inita_data_encountered_func */,
+ DummyPlayerErrorFunc, NULL /* context */);
+ EXPECT_FALSE(SbPlayerIsValid(player));
+ SbPlayerDestroy(player);
+ }
+ {
+ SbPlayer player = SbPlayerCreateWithUrl(
+ kPlayerUrl, window, DummyPlayerStatusFunc,
+ DummyEncryptedMediaInitaDataEncounteredFunc,
+ NULL /* player_error_func */, NULL /* context */);
+ EXPECT_FALSE(SbPlayerIsValid(player));
+ SbPlayerDestroy(player);
+ }
+ }
+}
+#endif // SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+
#if SB_API_VERSION >= SB_MULTI_PLAYER_API_VERSION
TEST(SbPlayerUrlTest, MultiPlayer) {
SbWindowOptions window_options;
@@ -76,10 +144,9 @@
}
const int kMaxPlayers = 16;
std::vector<SbPlayer> created_players;
- char url[] = "about:blank";
for (int j = 0; j < kMaxPlayers; ++j) {
created_players.push_back(
- SbPlayerCreateWithUrl(url, window, NULL, NULL, NULL, NULL));
+ SbPlayerCreateWithUrl(kPlayerUrl, window, NULL, NULL, NULL, NULL));
if (!SbPlayerIsValid(created_players[j])) {
created_players.pop_back();
break;
diff --git a/src/starboard/nplb/socket_send_to_test.cc b/src/starboard/nplb/socket_send_to_test.cc
index e4fae02..e84f0e7 100644
--- a/src/starboard/nplb/socket_send_to_test.cc
+++ b/src/starboard/nplb/socket_send_to_test.cc
@@ -179,7 +179,7 @@
int kNumRetries = 1000;
for (int i = 0; i < kNumRetries; ++i) {
char buff[kChunkSize] = {};
- SbThreadSleep(1);
+ SbThreadSleep(kSbTimeMillisecond);
int result = trio->client_socket->SendTo(buff, sizeof(buff), NULL);
if (result < 0) {
diff --git a/src/starboard/player.h b/src/starboard/player.h
index 47241dd..54dd83e 100644
--- a/src/starboard/player.h
+++ b/src/starboard/player.h
@@ -314,19 +314,22 @@
#if SB_HAS(PLAYER_WITH_URL)
-// Creates a URL-based SbPlayer that will be displayed on |window| for
-// the specified URL |url|, acquiring all resources needed to operate
-// it, and returning an opaque handle to it. The expectation is that a
-// new player will be created and destroyed for every playback.
+// Creates a URL-based SbPlayer that will be displayed on |window| for the
+// specified URL |url|, acquiring all resources needed to operate it, and
+// returning an opaque handle to it. The expectation is that a new player will
+// be created and destroyed for every playback.
//
-// In many ways this function is similar to SbPlayerCreate, but it is
-// missing the input arguments related to the configuration and format
-// of the audio and video stream, as well as the DRM system. The DRM
-// system for a player created with SbPlayerCreateWithUrl can be set
-// after creation using SbPlayerSetDrmSystem. Because the DRM system
-// is not available at the time of SbPlayerCreateWithUrl, it takes in
-// a callback, |encrypted_media_init_data_encountered_cb|, which is
-// run when encrypted media initial data is encountered.
+// In many ways this function is similar to SbPlayerCreate, but it is missing
+// the input arguments related to the configuration and format of the audio and
+// video stream, as well as the DRM system. The DRM system for a player created
+// with SbPlayerCreateWithUrl can be set after creation using
+// SbPlayerSetDrmSystem. Because the DRM system is not available at the time of
+// SbPlayerCreateWithUrl, it takes in a callback,
+// |encrypted_media_init_data_encountered_cb|, which is run when encrypted media
+// initial data is encountered.
+#if SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+// If the callback is |NULL|, then |kSbPlayerInvalid| must be returned.
+#endif // SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
SB_EXPORT SbPlayer
SbPlayerCreateWithUrl(const char* url,
SbWindow window,
@@ -378,6 +381,10 @@
// |video_codec|: The video codec used for the player. If |video_codec| is
// |kSbMediaVideoCodecNone|, the player is an audio-only player. If
// |video_codec| is any other value, the player is an audio/video decoder.
+#if SB_API_VERSION >= SB_AUDIO_ONLY_VIDEO_API_VERSION
+// This can be set to |kSbMediaVideoCodecNone| to play a video with only an
+// audio track.
+#endif // SB_API_VERSION >= SB_AUDIO_ONLY_VIDEO_API_VERSION
//
// |audio_codec|: The audio codec used for the player. The value should never
// be |kSbMediaAudioCodecNone|. In addition, the caller must provide a
@@ -445,6 +452,12 @@
// provider may not always be needed by the player, but if it is needed, and
// the provider is not given, the player will fail by returning
// |kSbPlayerInvalid|.
+//
+#if SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
+// If |NULL| is passed to any of the callbacks (|sample_deallocator_func|,
+// |decoder_status_func|, |player_status_func|, or |player_error_func| if it
+// applies), then |kSbPlayerInvalid| must be returned.
+#endif // SB_API_VERSION >= SB_NULL_CALLBACKS_INVALID_RETURN_API_VERSION
SB_EXPORT SbPlayer
SbPlayerCreate(SbWindow window,
SbMediaVideoCodec video_codec,
diff --git a/src/starboard/raspi/2/skia/gyp_configuration.py b/src/starboard/raspi/2/skia/gyp_configuration.py
index 0e2036b..660d967 100644
--- a/src/starboard/raspi/2/skia/gyp_configuration.py
+++ b/src/starboard/raspi/2/skia/gyp_configuration.py
@@ -13,8 +13,11 @@
# limitations under the License.
"""Starboard Raspberry Pi 2 platform configuration for gyp_cobalt."""
-from starboard.raspi.shared.gyp_configuration import RaspiPlatformConfig
+import importlib
+# pylint: disable=invalid-name
+Raspi2PlatformConfig = importlib.import_module(
+ 'starboard.raspi.2.gyp_configuration').Raspi2PlatformConfig
def CreatePlatformConfig():
- return RaspiPlatformConfig('raspi-2-skia')
+ return Raspi2PlatformConfig('raspi-2-skia')
diff --git a/src/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc b/src/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc
index f5026b8..8ee73d9 100644
--- a/src/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc
+++ b/src/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc
@@ -16,5 +16,5 @@
bool SbAudioSinkIsAudioSampleTypeSupported(
SbMediaAudioSampleType audio_sample_type) {
- return audio_sample_type == kSbMediaAudioSampleTypeInt16;
+ return audio_sample_type == kSbMediaAudioSampleTypeFloat32;
}
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index 4f07543..040b841 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -18,7 +18,7 @@
#define STARBOARD_RASPI_SHARED_CONFIGURATION_PUBLIC_H_
// The API version implemented by this platform.
-#define SB_API_VERSION 6
+#define SB_API_VERSION SB_EXPERIMENTAL_API_VERSION
// --- System Header Configuration -------------------------------------------
@@ -49,15 +49,6 @@
// Whether the current platform provides the standard header float.h.
#define SB_HAS_FLOAT_H 1
-// Whether the current platform has microphone supported.
-#define SB_HAS_MICROPHONE 0
-
-// Whether the current platform has speech recognizer.
-#define SB_HAS_SPEECH_RECOGNIZER 0
-
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
// Type detection for wchar_t.
#if defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -75,14 +66,6 @@
#define SB_IS_WCHAR_T_SIGNED 1
#endif
-// --- Architecture Configuration --------------------------------------------
-
-// On the current version of Raspbian, real time thread scheduling seems to be
-// broken in that higher priority threads do not always have priority over lower
-// priority threads. It looks like the thread created last will always have the
-// highest priority.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 1
-
// --- Attribute Configuration -----------------------------------------------
// The platform's annotation for forcing a C function to be inlined.
@@ -179,54 +162,6 @@
// The string form of SB_PATH_SEP_CHAR.
#define SB_PATH_SEP_STRING ":"
-// --- Memory Configuration --------------------------------------------------
-
-// The memory page size, which controls the size of chunks on memory that
-// allocators deal with, and the alignment of those chunks. This doesn't have to
-// be the hardware-defined physical page size, but it should be a multiple of
-// it.
-#define SB_MEMORY_PAGE_SIZE 4096
-
-// Whether this platform has and should use an MMAP function to map physical
-// memory to the virtual address space.
-#define SB_HAS_MMAP 1
-
-// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
-// required for platforms that want to JIT.
-#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
-
-// Whether this platform has and should use an growable heap (e.g. with sbrk())
-// to map physical memory to the virtual address space.
-#define SB_HAS_VIRTUAL_REGIONS 0
-
-// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
-// may require buffers to have a specific alignment, and this is the place to
-// specify that.
-#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
-
-// Determines the alignment that allocations should have on this platform.
-#define SB_MALLOC_ALIGNMENT ((size_t)16U)
-
-// Determines the threshhold of allocation size that should be done with mmap
-// (if available), rather than allocated within the core heap.
-#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
-
-// Defines the path where memory debugging logs should be written to.
-#define SB_MEMORY_LOG_PATH "/tmp/starboard"
-
-// --- Thread Configuration --------------------------------------------------
-
-// Defines the maximum number of simultaneous threads for this platform. Some
-// platforms require sharing thread handles with other kinds of system handles,
-// like mutexes, so we want to keep this managable.
-#define SB_MAX_THREADS 90
-
-// The maximum number of thread local storage keys supported by this platform.
-#define SB_MAX_THREAD_LOCAL_KEYS 512
-
-// The maximum length of the name for a thread, including the NULL-terminator.
-#define SB_MAX_THREAD_NAME_LENGTH 16;
-
// --- Graphics Configuration ------------------------------------------------
// Specifies whether this platform supports a performant accelerated blitter
@@ -253,8 +188,25 @@
// scene hasn't changed are enabled.
#define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER 0
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 0
+
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 0
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 0
+
// --- Media Configuration ---------------------------------------------------
+// Whether the current platform uses a media player that relies on a URL.
+#define SB_HAS_PLAYER_WITH_URL 0
+
// The maximum audio bitrate the platform can decode. The following value
// equals to 5M bytes per seconds which is more than enough for compressed
// audio.
@@ -305,6 +257,48 @@
// value leads to more stable fps but also causes the app to use more memory.
#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
+// --- Memory Configuration --------------------------------------------------
+
+// The memory page size, which controls the size of chunks on memory that
+// allocators deal with, and the alignment of those chunks. This doesn't have to
+// be the hardware-defined physical page size, but it should be a multiple of
+// it.
+#define SB_MEMORY_PAGE_SIZE 4096
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#define SB_HAS_MMAP 1
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// Whether this platform has and should use an growable heap (e.g. with sbrk())
+// to map physical memory to the virtual address space.
+#define SB_HAS_VIRTUAL_REGIONS 0
+
+// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
+// may require buffers to have a specific alignment, and this is the place to
+// specify that.
+#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
+
+// Determines the alignment that allocations should have on this platform.
+#define SB_MALLOC_ALIGNMENT ((size_t)16U)
+
+// Determines the threshhold of allocation size that should be done with mmap
+// (if available), rather than allocated within the core heap.
+#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
+
+// Defines the path where memory debugging logs should be written to.
+#define SB_MEMORY_LOG_PATH "/tmp/starboard"
+
+// The Raspberry Pi does not apparently align fields in a heap-allocated struct
+// by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
+
+// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
+#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
+
// --- Network Configuration -------------------------------------------------
// Specifies whether this platform supports IPV6.
@@ -313,6 +307,31 @@
// Specifies whether this platform supports pipe.
#define SB_HAS_PIPE 1
+// --- Thread Configuration --------------------------------------------------
+
+// On the current version of Raspbian, real time thread scheduling seems to be
+// broken in that higher priority threads do not always have priority over lower
+// priority threads. It looks like the thread created last will always have the
+// highest priority.
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 1
+
+// Defines the maximum number of simultaneous threads for this platform. Some
+// platforms require sharing thread handles with other kinds of system handles,
+// like mutexes, so we want to keep this managable.
+#define SB_MAX_THREADS 90
+
+// The maximum number of thread local storage keys supported by this platform.
+#define SB_MAX_THREAD_LOCAL_KEYS 512
+
+// The maximum length of the name for a thread, including the NULL-terminator.
+#define SB_MAX_THREAD_NAME_LENGTH 16
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
// --- Tuneable Parameters ---------------------------------------------------
// Specifies the network receive buffer size in bytes, set via
@@ -334,19 +353,6 @@
// The maximum number of users that can be signed in at the same time.
#define SB_USER_MAX_SIGNED_IN 1
-// The Raspberry Pi does not apparently align fields in a heap-allocated struct
-// by over 16 bytes.
-#define SB_HAS_QUIRK_DOES_NOT_ALIGN_FIELDS_IN_HEAP_OVER_16_BYTES 1
-
-// The Raspberry Pi does not apparently align stack variables by over 16 bytes.
-#define SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES 1
-
-// --- Timing API ------------------------------------------------------------
-
-// Whether this platform has an API to retrieve how long the current thread
-// has spent in the executing state.
-#define SB_HAS_TIME_THREAD_NOW 1
-
// --- Platform Specific Audits ----------------------------------------------
#if !defined(__GNUC__)
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index e7f27dc..bb9ae4c 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -95,9 +95,10 @@
return filters
_FILTERED_TESTS = {
- # The RasPi test devices don't have access to an IPV6 network, so
- # disable the related tests.
'nplb': [
+ 'SbDrmTest.AnySupportedKeySystems',
+ # The RasPi test devices don't have access to an IPV6 network, so
+ # disable the related tests.
'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest'
'.SunnyDayDestination/1',
'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest'
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.cc b/src/starboard/raspi/shared/open_max/video_decoder.cc
index ba743fd..3d48a1a 100644
--- a/src/starboard/raspi/shared/open_max/video_decoder.cc
+++ b/src/starboard/raspi/shared/open_max/video_decoder.cc
@@ -14,7 +14,6 @@
#include "starboard/raspi/shared/open_max/video_decoder.h"
-#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/time.h"
namespace starboard {
@@ -32,16 +31,14 @@
} // namespace
-VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec, JobQueue* job_queue)
+VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec)
: resource_pool_(new DispmanxResourcePool(kResourcePoolSize)),
eos_written_(false),
thread_(kSbThreadInvalid),
- request_thread_termination_(false),
- job_queue_(job_queue) {
+ request_thread_termination_(false) {
SB_DCHECK(video_codec == kSbMediaVideoCodecH264);
- SB_DCHECK(job_queue_ != NULL);
update_job_ = std::bind(&VideoDecoder::Update, this);
- update_job_token_ = job_queue_->Schedule(update_job_, kUpdateInterval);
+ update_job_token_ = Schedule(update_job_, kUpdateInterval);
}
VideoDecoder::~VideoDecoder() {
@@ -52,7 +49,7 @@
}
SbThreadJoin(thread_, NULL);
}
- job_queue_->RemoveJobByToken(update_job_token_);
+ RemoveJobByToken(update_job_token_);
}
void VideoDecoder::Initialize(const DecoderStatusCB& decoder_status_cb,
@@ -99,7 +96,7 @@
if (eos_written_) {
TryToDeliverOneFrame();
}
- update_job_token_ = job_queue_->Schedule(update_job_, kUpdateInterval);
+ update_job_token_ = Schedule(update_job_, kUpdateInterval);
}
bool VideoDecoder::TryToDeliverOneFrame() {
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.h b/src/starboard/raspi/shared/open_max/video_decoder.h
index 7b5386c..18cf9bd 100644
--- a/src/starboard/raspi/shared/open_max/video_decoder.h
+++ b/src/starboard/raspi/shared/open_max/video_decoder.h
@@ -37,11 +37,12 @@
namespace open_max {
class VideoDecoder
- : public starboard::shared::starboard::player::filter::VideoDecoder {
+ : public ::starboard::shared::starboard::player::filter::VideoDecoder,
+ private ::starboard::shared::starboard::player::JobQueue::JobOwner {
public:
typedef ::starboard::shared::starboard::player::JobQueue JobQueue;
- VideoDecoder(SbMediaVideoCodec video_codec, JobQueue* job_queue);
+ explicit VideoDecoder(SbMediaVideoCodec video_codec);
~VideoDecoder() override;
void Initialize(const DecoderStatusCB& decoder_status_cb,
@@ -91,7 +92,6 @@
std::queue<OMX_BUFFERHEADERTYPE*> filled_buffers_;
std::queue<OMX_BUFFERHEADERTYPE*> freed_buffers_;
- JobQueue* job_queue_;
JobQueue::JobToken update_job_token_;
std::function<void()> update_job_;
};
diff --git a/src/starboard/raspi/shared/player_components_impl.cc b/src/starboard/raspi/shared/player_components_impl.cc
index 60425a5..34124b6 100644
--- a/src/starboard/raspi/shared/player_components_impl.cc
+++ b/src/starboard/raspi/shared/player_components_impl.cc
@@ -67,11 +67,9 @@
SB_DCHECK(video_render_algorithm);
SB_DCHECK(video_renderer_sink);
- video_decoder->reset(new VideoDecoderImpl(video_parameters.video_codec,
- video_parameters.job_queue));
+ video_decoder->reset(new VideoDecoderImpl(video_parameters.video_codec));
video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
- *video_renderer_sink = new VideoRendererSinkImpl(
- video_parameters.player, video_parameters.job_queue);
+ *video_renderer_sink = new VideoRendererSinkImpl(video_parameters.player);
}
void GetAudioRendererParams(int* max_cached_frames,
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index d172430..361786c 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
{
+ 'includes': [
+ '<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
+ ],
'variables': {
'sb_pedantic_warnings': 1,
},
@@ -35,6 +38,7 @@
'target_name': 'starboard_platform',
'type': 'static_library',
'sources': [
+ '<@(filter_based_player_sources)',
'<(DEPTH)/starboard/linux/shared/atomic_public.h',
'<(DEPTH)/starboard/linux/shared/configuration_public.h',
'<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
@@ -89,6 +93,7 @@
'<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc',
+ '<(DEPTH)/starboard/shared/dlmalloc/memory_protect.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc',
'<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc',
'<(DEPTH)/starboard/shared/gcc/atomic_gcc.h',
@@ -281,35 +286,6 @@
'<(DEPTH)/starboard/shared/starboard/new.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/player_components.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_frame_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_sink.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
@@ -357,7 +333,9 @@
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+ '<(DEPTH)/starboard/shared/stub/drm_is_server_certificate_updatable.cc',
'<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+ '<(DEPTH)/starboard/shared/stub/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
'<(DEPTH)/starboard/shared/stub/media_is_supported.cc',
'<(DEPTH)/starboard/shared/stub/media_is_transfer_characteristics_supported.cc',
diff --git a/src/starboard/raspi/shared/system_get_property.cc b/src/starboard/raspi/shared/system_get_property.cc
index 94e5a53..8ebb2f9 100644
--- a/src/starboard/raspi/shared/system_get_property.cc
+++ b/src/starboard/raspi/shared/system_get_property.cc
@@ -75,10 +75,6 @@
return true;
}
- case kSbSystemPropertyPlatformUuid:
- SB_NOTIMPLEMENTED();
- return CopyStringAndTestIfSuccess(out_value, value_length, "N/A");
-
default:
SB_DLOG(WARNING) << __FUNCTION__
<< ": Unrecognized property: " << property_id;
diff --git a/src/starboard/raspi/shared/video_renderer_sink_impl.cc b/src/starboard/raspi/shared/video_renderer_sink_impl.cc
index 9cf45e5..71a4d7e 100644
--- a/src/starboard/raspi/shared/video_renderer_sink_impl.cc
+++ b/src/starboard/raspi/shared/video_renderer_sink_impl.cc
@@ -26,17 +26,9 @@
using std::placeholders::_1;
using std::placeholders::_2;
-VideoRendererSinkImpl::VideoRendererSinkImpl(SbPlayer player,
- JobQueue* job_queue)
- : JobOwner(job_queue),
- player_(player),
- z_index_(0),
- x_(0),
- y_(0),
- width_(0),
- height_(0) {
+VideoRendererSinkImpl::VideoRendererSinkImpl(SbPlayer player)
+ : player_(player), z_index_(0), x_(0), y_(0), width_(0), height_(0) {
SB_DCHECK(SbPlayerIsValid(player));
- SB_DCHECK(job_queue);
}
VideoRendererSinkImpl::~VideoRendererSinkImpl() {
diff --git a/src/starboard/raspi/shared/video_renderer_sink_impl.h b/src/starboard/raspi/shared/video_renderer_sink_impl.h
index ac650e8..80aa3a2 100644
--- a/src/starboard/raspi/shared/video_renderer_sink_impl.h
+++ b/src/starboard/raspi/shared/video_renderer_sink_impl.h
@@ -30,9 +30,7 @@
: public ::starboard::shared::starboard::player::filter::VideoRendererSink,
private ::starboard::shared::starboard::player::JobQueue::JobOwner {
public:
- typedef ::starboard::shared::starboard::player::JobQueue JobQueue;
-
- VideoRendererSinkImpl(SbPlayer player, JobQueue* job_queue);
+ explicit VideoRendererSinkImpl(SbPlayer player);
~VideoRendererSinkImpl() override;
private:
diff --git a/src/starboard/shared/dlmalloc/memory_protect.cc b/src/starboard/shared/dlmalloc/memory_protect.cc
new file mode 100644
index 0000000..270634d
--- /dev/null
+++ b/src/starboard/shared/dlmalloc/memory_protect.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. 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 "starboard/configuration.h"
+#include "starboard/memory.h"
+#include "starboard/shared/dlmalloc/page_internal.h"
+
+#if SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+bool SbMemoryProtect(void* virtual_address, int64_t size_bytes, int flags) {
+ return SbPageProtect(virtual_address, size_bytes, flags);
+}
+#endif
diff --git a/src/starboard/shared/dlmalloc/page_internal.h b/src/starboard/shared/dlmalloc/page_internal.h
index 4073a2e..c161ee7 100644
--- a/src/starboard/shared/dlmalloc/page_internal.h
+++ b/src/starboard/shared/dlmalloc/page_internal.h
@@ -129,7 +129,13 @@
// Same as SbUnmap(), but should be used only by dlmalloc to unmap pages
// allocated via MapUntracked().
bool SbPageUnmapUntracked(void* virtual_address, size_t size_bytes);
+
+#if SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+// Change the protection of |size_bytes| of physical pages, starting from
+// |virtual_address|, to |flags|, returning |true| on success.
+bool SbPageProtect(void* virtual_address, int64_t size_bytes, int flags);
#endif
+#endif // SB_HAS(MMAP)
// Returns the total amount, in bytes, of physical memory available. Should
// always be a multiple of SB_MEMORY_PAGE_SIZE.
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_common.h b/src/starboard/shared/ffmpeg/ffmpeg_common.h
index 86f0169..37cd734 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_common.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_common.h
@@ -19,7 +19,6 @@
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
-#include <libavresample/avresample.h>
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
diff --git a/src/starboard/shared/gcc/atomic_gcc_public.h b/src/starboard/shared/gcc/atomic_gcc_public.h
index cdf9fa8..1955a29 100644
--- a/src/starboard/shared/gcc/atomic_gcc_public.h
+++ b/src/starboard/shared/gcc/atomic_gcc_public.h
@@ -100,6 +100,29 @@
return __atomic_load_n(ptr, __ATOMIC_RELAXED);
}
+// 8-bit atomic operations.
+#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+static SB_C_FORCE_INLINE SbAtomic8
+SbAtomicRelease_CompareAndSwap8(volatile SbAtomic8* ptr,
+ SbAtomic8 old_value,
+ SbAtomic8 new_value) {
+ bool result = __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
+ __ATOMIC_RELEASE, __ATOMIC_RELAXED);
+ SB_UNREFERENCED_PARAMETER(result); // Make gcc compiler happy.
+ return old_value;
+}
+
+static SB_C_FORCE_INLINE void
+SbAtomicNoBarrier_Store8(volatile SbAtomic8* ptr, SbAtomic8 value) {
+ __atomic_store_n(ptr, value, __ATOMIC_RELAXED);
+}
+
+static SB_C_FORCE_INLINE SbAtomic8
+SbAtomicNoBarrier_Load8(volatile const SbAtomic8* ptr) {
+ return __atomic_load_n(ptr, __ATOMIC_RELAXED);
+}
+#endif
+
// 64-bit atomic operations (only available on 64-bit processors).
#if SB_HAS(64_BIT_ATOMICS)
static SB_C_FORCE_INLINE SbAtomic64
diff --git a/src/starboard/shared/linux/page_internal.cc b/src/starboard/shared/linux/page_internal.cc
index c06e0cd..1113da2 100644
--- a/src/starboard/shared/linux/page_internal.cc
+++ b/src/starboard/shared/linux/page_internal.cc
@@ -70,6 +70,13 @@
void* SbPageMapUntracked(size_t size_bytes,
int flags,
const char* /*unused_name*/) {
+#if SB_CAN(MAP_EXECUTABLE_MEMORY) \
+ && SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+ if (flags & kSbMemoryMapProtectExec) {
+ // Cobalt does not allow mapping executable memory directly.
+ return SB_MEMORY_MAP_FAILED;
+ }
+#endif
int mmap_protect = SbMemoryMapFlagsToMmapProtect(flags);
void* mem = mmap(0, size_bytes, mmap_protect, MAP_PRIVATE | MAP_ANON, -1, 0);
return mem;
@@ -84,6 +91,13 @@
return munmap(ptr, size_bytes) == 0;
}
+#if SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+bool SbPageProtect(void* virtual_address, int64_t size_bytes, int flags) {
+ int mmap_protect = SbMemoryMapFlagsToMmapProtect(flags);
+ return mprotect(virtual_address, size_bytes, mmap_protect) == 0;
+}
+#endif
+
size_t SbPageGetTotalPhysicalMemoryBytes() {
// Limit ourselves to remain similar to more constrained platforms.
return 1024U * 1024 * 1024;
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index 88fa79d..c5f53b6 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -250,6 +250,16 @@
int width,
int height);
+ // This is called immediately when SbPlayerSetBounds is called for punch-out
+ // video. Subclasses may override this to better synchronize the punch-out
+ // video bounds with the upcoming render output.
+ virtual void PlayerSetBounds(SbPlayer /* player */,
+ int /* z_index */,
+ int /* x */,
+ int /* y */,
+ int /* width */,
+ int /* height */) {}
+
// Registers a |callback| function that will be called when |Teardown| is
// called.
void RegisterTeardownCallback(TeardownCallback callback) {
diff --git a/src/starboard/shared/starboard/net_args.cc b/src/starboard/shared/starboard/net_args.cc
new file mode 100644
index 0000000..b200e39
--- /dev/null
+++ b/src/starboard/shared/starboard/net_args.cc
@@ -0,0 +1,150 @@
+// Copyright 2018 Google Inc. 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 "starboard/shared/starboard/net_args.h"
+
+#include <string>
+#include <vector>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/socket_waiter.h"
+#include "starboard/thread.h"
+
+#ifndef NET_ARGS_PORT
+#define NET_ARGS_PORT 49355
+#endif
+
+// Controls whether using IPv4 or IPv6.
+#ifndef NET_ARGS_IP_VERSION
+#define NET_ARGS_IP_VERSION kSbSocketAddressTypeIpv4
+#endif
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace {
+
+scoped_ptr<Socket> CreateListenSocket() {
+ scoped_ptr<Socket> socket(
+ new Socket(NET_ARGS_IP_VERSION, kSbSocketProtocolTcp));
+ socket->SetReuseAddress(true);
+ SbSocketAddress sock_addr;
+ // Ip address will be set to 0.0.0.0 so that it will bind to all sockets.
+ SbMemorySet(&sock_addr, 0, sizeof(SbSocketAddress));
+ sock_addr.type = NET_ARGS_IP_VERSION;
+ sock_addr.port = NET_ARGS_PORT;
+ SbSocketError sock_err = socket->Bind(&sock_addr);
+
+ const char kErrFmt[] = "Socket error while attempting to bind, error = %d\n";
+ if (sock_err != kSbSocketOk) {
+ SbLogRawFormatF(kErrFmt, sock_err);
+ }
+ sock_err = socket->Listen();
+ if (sock_err != kSbSocketOk) {
+ SbLogRawFormatF(kErrFmt, sock_err);
+ }
+ return socket.Pass();
+}
+
+void WaitUntilReadableOrConnectionReset(SbSocket sock) {
+ SbSocketWaiter waiter = SbSocketWaiterCreate();
+
+ struct F {
+ static void WakeUp(SbSocketWaiter waiter, SbSocket, void*, int) {
+ SbSocketWaiterWakeUp(waiter);
+ }
+ };
+
+ SbSocketWaiterAdd(waiter,
+ sock,
+ NULL,
+ &F::WakeUp,
+ kSbSocketWaiterInterestRead,
+ false); // false means one shot.
+
+ SbSocketWaiterWait(waiter);
+ SbSocketWaiterRemove(waiter, sock);
+ SbSocketWaiterDestroy(waiter);
+}
+
+scoped_ptr<Socket> WaitForClientConnection(Socket* listen_sock) {
+ while (true) {
+ scoped_ptr<Socket> client_connection(listen_sock->Accept());
+ if (client_connection) {
+ return client_connection.Pass();
+ }
+ SbThreadSleep(kSbTimeMillisecond);
+ }
+}
+
+std::vector<std::string> SplitStringByLines(const std::string& string_buff) {
+ std::vector<std::string> lines;
+ std::stringstream ss;
+ ss << string_buff;
+ for (std::string line; std::getline(ss, line);) {
+ if (!line.empty()) {
+ lines.push_back(line);
+ }
+ }
+ return lines;
+}
+
+} // namespace.
+
+// Command line switch useful for determining if NetArgsWaitForConnection()
+// should be called.
+const char kNetArgsCommandSwitchWait[] = "net_args_wait_for_connection";
+
+// Waits until.
+std::vector<std::string> NetArgsWaitForPayload() {
+ scoped_ptr<Socket> listen = CreateListenSocket();
+ scoped_ptr<Socket> client_connection = WaitForClientConnection(listen.get());
+
+ std::string str_buff;
+
+ while (true) {
+ char buff[128];
+ int result = client_connection->ReceiveFrom(buff, sizeof(buff), NULL);
+ if (result > 0) {
+ str_buff.append(buff, static_cast<size_t>(result));
+ continue;
+ } else if (result == 0) {
+ // Socket has closed.
+ break;
+ } else if (result < 0) { // Handle error condition.
+ SbSocketError err = client_connection->GetLastError();
+ client_connection->ClearLastError();
+
+ switch (err) {
+ case kSbSocketOk: {
+ SB_NOTREACHED() << "Expected error condition when return val "
+ << "is < 0.";
+ continue;
+ }
+ case kSbSocketPending: {
+ WaitUntilReadableOrConnectionReset(client_connection->socket());
+ continue;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+ return SplitStringByLines(str_buff);
+}
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/net_args.h b/src/starboard/shared/starboard/net_args.h
new file mode 100644
index 0000000..5db1552
--- /dev/null
+++ b/src/starboard/shared/starboard/net_args.h
@@ -0,0 +1,55 @@
+// Copyright 2018 Google Inc. 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.
+//
+// NetArgs is an optional component that allows socket access to tell an
+// executable what it's command args are.
+//
+// ****** //
+// TODO: Remove the following:
+// It's recommended to call NetLogWaitForClientConnected() to ensure that the
+// NetLog is running a predictable time from launch. It's also STRONGLY
+// recommended that NetLogFlushThenClose() is called during shutdown to
+// guarantee that any pending data is flushed out through the network layer.
+// Failure to do this will often result in the netlog being truncated as the
+// process shuts down.
+//
+// The netlog create a server that will bind to ip 0.0.0.0 (and similar in
+// ipv6) and will listen for client connections. See net_log.cc for which
+// port net_log will listen to.
+//
+// See also net_log.py for an example script that can connect to a running
+// netlog instance.
+
+#ifndef STARBOARD_SHARED_STARBOARD_NET_ARGS_H_
+#define STARBOARD_SHARED_STARBOARD_NET_ARGS_H_
+
+#include <string>
+#include <vector>
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+// Command line switch useful for determining if NetArgsWaitForConnection()
+// should be called.
+extern const char kNetArgsCommandSwitchWait[];
+
+// Waits until args stream in with a socket connection and data reception.
+std::vector<std::string> NetArgsWaitForPayload();
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_NET_ARGS_H_
diff --git a/src/starboard/shared/starboard/net_log.cc b/src/starboard/shared/starboard/net_log.cc
index a176fe6..715263d 100644
--- a/src/starboard/shared/starboard/net_log.cc
+++ b/src/starboard/shared/starboard/net_log.cc
@@ -166,7 +166,13 @@
}
log_mutex_.Release();
- SB_LOG_IF(ERROR, overflow) << "Net log dropped buffer data.";
+ if (overflow) {
+ // Can't use SB_LOG_IF() because SB_LOG_IF() might have triggered this
+ // call, and therefore would triggered reentrant behavior and then
+ // deadlock. SbLogRawFormat() is assumed to be safe to call. Note that
+ // NetLogWrite() ignores reentrant calls.
+ SbLogRaw("Net log dropped buffer data.");
+ }
}
void WaitUntilWritableOrConnectionReset(SbSocket sock) {
@@ -343,6 +349,7 @@
ScopedLock lock(socket_mutex_);
client_socket_ = client_socket.Pass();
client_socket_->SetSendBufferSize(NET_LOG_SOCKET_BUFFER_SIZE);
+ client_socket_->SetTcpKeepAlive(true, kSbTimeSecond);
}
// Has a client ever connected?
@@ -357,11 +364,12 @@
}
void Close() {
- writer_thread_->Join();
- writer_thread_.reset(nullptr);
+ if (writer_thread_) {
+ writer_thread_->Join();
+ writer_thread_.reset(nullptr);
+ Flush(); // One last flush to the socket.
+ }
socket_listener_.reset();
-
- Flush(); // One last flush to the socket.
ScopedLock lock(socket_mutex_);
client_socket_.reset();
listen_socket_.reset();
@@ -491,7 +499,9 @@
#if !SB_LOGGING_IS_OFFICIAL_BUILD
ScopeGuard guard;
if (guard.IsEnabled()) {
- NetLogServer::Instance()->Close();
+ NetLogServer* net_log = NetLogServer::Instance();
+ net_log->OnLog("Netlog is closing down\n");
+ net_log->Close();
}
#endif
}
diff --git a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
index 3824081..558cc7c 100644
--- a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
@@ -21,6 +21,7 @@
#include "starboard/media.h"
#include "starboard/shared/internal_only.h"
#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/callback.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
#include "starboard/types.h"
@@ -34,8 +35,8 @@
class AudioDecoder {
public:
typedef std::function<void()> ConsumedCB;
- typedef std::function<void()> ErrorCB;
typedef std::function<void()> OutputCB;
+ typedef ::starboard::shared::starboard::player::filter::ErrorCB ErrorCB;
typedef ::starboard::shared::starboard::player::DecodedAudio DecodedAudio;
typedef ::starboard::shared::starboard::player::InputBuffer InputBuffer;
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
index 5294d10..38ee614 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -49,9 +49,8 @@
bool eos_reached_;
};
-// AudioRenderer uses AudioTimeStretcher internally to adjust to playback rate
-// and AudioTimeStretcher can only process float32 samples. So we try to use
-// kSbMediaAudioSampleTypeFloat32 and only use
+// AudioRenderer uses AudioTimeStretcher internally to adjust to playback rate.
+// So we try to use kSbMediaAudioSampleTypeFloat32 and only use
// kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported. To use
// kSbMediaAudioSampleTypeFloat32 will cause an extra conversion from float32 to
// int16 before the samples are sent to the audio sink.
@@ -82,6 +81,7 @@
seeking_(false),
seeking_to_time_(0),
last_media_time_(0),
+ ended_cb_called_(false),
frame_buffer_(max_cached_frames_ * bytes_per_frame_),
frames_sent_to_sink_(0),
pending_decoder_outputs_(0),
@@ -115,11 +115,6 @@
SB_DCHECK(BelongsToCurrentThread());
}
-void AudioRenderer::Initialize(const AudioDecoder::ErrorCB& error_cb) {
- decoder_->Initialize(std::bind(&AudioRenderer::OnDecoderOutput, this),
- error_cb);
-}
-
void AudioRenderer::WriteSample(
const scoped_refptr<InputBuffer>& input_buffer) {
SB_DCHECK(BelongsToCurrentThread());
@@ -184,6 +179,21 @@
return seeking_;
}
+void AudioRenderer::Initialize(const ErrorCB& error_cb,
+ const PrerolledCB& prerolled_cb,
+ const EndedCB& ended_cb) {
+ SB_DCHECK(prerolled_cb);
+ SB_DCHECK(ended_cb);
+ SB_DCHECK(!prerolled_cb_);
+ SB_DCHECK(!ended_cb_);
+
+ prerolled_cb_ = prerolled_cb;
+ ended_cb_ = ended_cb;
+
+ decoder_->Initialize(std::bind(&AudioRenderer::OnDecoderOutput, this),
+ error_cb);
+}
+
void AudioRenderer::Play() {
SB_DCHECK(BelongsToCurrentThread());
@@ -237,6 +247,7 @@
eos_state_ = kEOSNotReceived;
seeking_to_time_ = std::max<SbTime>(seek_to_time, 0);
last_media_time_ = seek_to_time;
+ ended_cb_called_ = false;
seeking_ = true;
}
@@ -261,6 +272,7 @@
frames_in_buffer_on_sink_thread_ = 0;
offset_in_frames_on_sink_thread_ = 0;
frames_consumed_on_sink_thread_ = 0;
+ silence_frames_written_after_eos_on_sink_thread_ = 0;
if (first_input_written_) {
decoder_->Reset();
@@ -291,6 +303,10 @@
*is_playing = !paused_ && !seeking_;
*is_eos_played = IsEndOfStreamPlayed_Locked();
+ if (*is_eos_played && !ended_cb_called_) {
+ ended_cb_called_ = true;
+ Schedule(ended_cb_);
+ }
if (seeking_ || !decoder_sample_rate_) {
return seeking_to_time_;
@@ -337,13 +353,17 @@
}
if (system_and_media_time_offset_ > 0) {
SbTime offset = now - media_time;
- SbTime diff = std::abs(offset - system_and_media_time_offset_);
- max_offset_difference_ = std::max(diff, max_offset_difference_);
+ SbTime drift = offset - system_and_media_time_offset_;
+ min_drift_ = std::min(drift, min_drift_);
+ max_drift_ = std::max(drift, max_drift_);
SB_LOG(ERROR) << "Media time stats: (" << now << "-"
<< frames_consumed_set_at_ << "=" << elasped_since_last_set
<< ") + " << total_frames_consumed_ << " => " << frames_played
- << " => " << media_time << " diff: " << diff << "/"
- << max_offset_difference_;
+ << " => " << media_time << " drift: " << drift << "/ ("
+ << min_drift_ << ", " << max_drift_ << ") "
+ // How long the audio frames left in sink can be played.
+ << (frames_sent_to_sink_ - frames_consumed_by_sink_) *
+ kSbTimeSecond / samples_per_second;
}
#endif // SB_LOG_MEDIA_TIME_STATS
@@ -374,6 +394,20 @@
} else {
*frames_in_buffer = *offset_in_frames = 0;
}
+
+ if (*is_eos_reached && *frames_in_buffer < max_cached_frames_) {
+ // Fill silence frames on EOS to ensure keep the audio sink playing.
+ auto silence_frames_to_write = std::min(
+ max_cached_frames_ - *frames_in_buffer, max_frames_per_append_);
+ auto start_offset =
+ (*offset_in_frames + *frames_in_buffer) % max_cached_frames_;
+ silence_frames_to_write =
+ std::min(silence_frames_to_write, max_cached_frames_ - start_offset);
+ SbMemorySet(frame_buffer_.data() + start_offset * bytes_per_frame_, 0,
+ silence_frames_to_write * bytes_per_frame_);
+ silence_frames_written_after_eos_on_sink_thread_ += silence_frames_to_write;
+ *frames_in_buffer += silence_frames_to_write;
+ }
}
void AudioRenderer::ConsumeFrames(int frames_consumed
@@ -412,21 +446,30 @@
mutex_.DCheckAcquired();
if (frames_consumed_on_sink_thread_ > 0) {
- frames_consumed_by_sink_ += frames_consumed_on_sink_thread_;
- SB_DCHECK(frames_consumed_by_sink_ <= frames_sent_to_sink_);
+ SB_DCHECK(frames_consumed_by_sink_ + frames_consumed_on_sink_thread_ <=
+ frames_sent_to_sink_ +
+ silence_frames_written_after_eos_on_sink_thread_);
+ auto non_silence_frames_consumed =
+ std::min(frames_sent_to_sink_ - frames_consumed_by_sink_,
+ frames_consumed_on_sink_thread_);
+ frames_consumed_by_sink_ += non_silence_frames_consumed;
frames_consumed_by_sink_since_last_get_current_time_ +=
- frames_consumed_on_sink_thread_;
- frames_consumed_set_at_ = system_time_on_consume_frames;
+ non_silence_frames_consumed;
+ if (non_silence_frames_consumed != 0) {
+ frames_consumed_set_at_ = system_time_on_consume_frames;
+ }
consume_frames_called_ = true;
- frames_consumed_on_sink_thread_ = 0;
+ frames_consumed_on_sink_thread_ -= non_silence_frames_consumed;
}
is_eos_reached_on_sink_thread_ = eos_state_ >= kEOSSentToSink;
is_playing_on_sink_thread_ = !paused_ && !seeking_;
- frames_in_buffer_on_sink_thread_ =
- static_cast<int>(frames_sent_to_sink_ - frames_consumed_by_sink_);
+ frames_in_buffer_on_sink_thread_ = static_cast<int>(
+ frames_sent_to_sink_ + silence_frames_written_after_eos_on_sink_thread_ -
+ frames_consumed_by_sink_ - frames_consumed_on_sink_thread_);
offset_in_frames_on_sink_thread_ =
- frames_consumed_by_sink_ % max_cached_frames_;
+ (frames_consumed_by_sink_ + frames_consumed_on_sink_thread_) %
+ max_cached_frames_;
}
void AudioRenderer::OnFirstOutput() {
@@ -436,18 +479,18 @@
int destination_sample_rate =
audio_renderer_sink_->GetNearestSupportedSampleFrequency(
*decoder_sample_rate_);
- time_stretcher_.Initialize(channels_, destination_sample_rate);
+ time_stretcher_.Initialize(sink_sample_type_, channels_,
+ destination_sample_rate);
SbMediaAudioSampleType source_sample_type = decoder_->GetSampleType();
SbMediaAudioFrameStorageType source_storage_type = decoder_->GetStorageType();
- // AudioTimeStretcher only supports interleaved float32 samples.
if (*decoder_sample_rate_ != destination_sample_rate ||
- source_sample_type != kSbMediaAudioSampleTypeFloat32 ||
+ source_sample_type != sink_sample_type_ ||
source_storage_type != kSbMediaAudioFrameStorageTypeInterleaved) {
resampler_ = AudioResampler::Create(
decoder_->GetSampleType(), decoder_->GetStorageType(),
- *decoder_sample_rate_, kSbMediaAudioSampleTypeFloat32,
+ *decoder_sample_rate_, sink_sample_type_,
kSbMediaAudioFrameStorageTypeInterleaved, destination_sample_rate,
channels_);
SB_DCHECK(resampler_);
@@ -549,7 +592,10 @@
{
ScopedLock lock(mutex_);
eos_state_ = kEOSDecoded;
- seeking_ = false;
+ if (seeking_) {
+ seeking_ = false;
+ Schedule(prerolled_cb_);
+ }
}
resampled_audio = resampler_->WriteEndOfStream();
@@ -600,6 +646,7 @@
if (seeking_ && time_stretcher_.IsQueueFull()) {
ScopedLock lock(mutex_);
seeking_ = false;
+ Schedule(prerolled_cb_);
}
if (seeking_ || playback_rate_ == 0.0) {
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index 13a92e9..975cc42 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -66,18 +66,22 @@
int max_frames_per_append);
~AudioRenderer();
- void Initialize(const AudioDecoder::ErrorCB& error_cb);
void WriteSample(const scoped_refptr<InputBuffer>& input_buffer);
void WriteEndOfStream();
void SetVolume(double volume);
+ // TODO: Remove the eos state querying functions and their tests.
bool IsEndOfStreamWritten() const;
bool IsEndOfStreamPlayed() const;
bool CanAcceptMoreData() const;
+ // TODO: Remove the following function and its tests.
bool IsSeekingInProgress() const;
// MediaTimeProvider methods
+ void Initialize(const ErrorCB& error_cb,
+ const PrerolledCB& prerolled_cb,
+ const EndedCB& ended_cb) override;
void Play() override;
void Pause() override;
void SetPlaybackRate(double playback_rate) override;
@@ -95,6 +99,9 @@
const int max_cached_frames_;
const int max_frames_per_append_;
+ PrerolledCB prerolled_cb_;
+ EndedCB ended_cb_;
+
Mutex mutex_;
bool paused_;
@@ -103,6 +110,7 @@
SbTime seeking_to_time_;
SbTime last_media_time_;
AudioFrameTracker audio_frame_tracker_;
+ bool ended_cb_called_;
int64_t frames_sent_to_sink_;
int64_t frames_consumed_by_sink_;
@@ -169,10 +177,12 @@
int64_t offset_in_frames_on_sink_thread_ = 0;
int64_t frames_consumed_on_sink_thread_ = 0;
SbTime frames_consumed_set_at_on_sink_thread_ = 0;
+ int64_t silence_frames_written_after_eos_on_sink_thread_ = 0;
#if SB_LOG_MEDIA_TIME_STATS
SbTime system_and_media_time_offset_ = -1;
- SbTime max_offset_difference_ = 0;
+ SbTime min_drift_ = kSbTimeMax;
+ SbTime max_drift_ = 0;
int64_t total_frames_consumed_ = 0;
#endif // SB_LOG_MEDIA_TIME_STATS
};
diff --git a/src/starboard/shared/starboard/player/filter/audio_time_stretcher.cc b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
index 4f2cbf5..11dd1bb 100644
--- a/src/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
@@ -23,6 +23,7 @@
#include "starboard/log.h"
#include "starboard/memory.h"
+#include "starboard/shared/starboard/media/media_util.h"
#include "starboard/shared/starboard/player/filter/wsola_internal.h"
namespace starboard {
@@ -81,7 +82,8 @@
static const int kStartingCapacityInMs = 200;
AudioTimeStretcher::AudioTimeStretcher()
- : channels_(0),
+ : sample_type_(kSbMediaAudioSampleTypeFloat32),
+ channels_(0),
bytes_per_frame_(0),
samples_per_second_(0),
muted_partial_frame_(0),
@@ -99,9 +101,12 @@
AudioTimeStretcher::~AudioTimeStretcher() {}
-void AudioTimeStretcher::Initialize(int channels, int samples_per_second) {
+void AudioTimeStretcher::Initialize(SbMediaAudioSampleType sample_type,
+ int channels,
+ int samples_per_second) {
+ sample_type_ = sample_type;
channels_ = channels;
- bytes_per_frame_ = sizeof(float) * channels_;
+ bytes_per_frame_ = media::GetBytesPerSample(sample_type_) * channels_;
samples_per_second_ = samples_per_second;
initial_capacity_ = capacity_ =
ConvertMillisecondsToFrames(kStartingCapacityInMs);
@@ -144,22 +149,20 @@
internal::GetSymmetricHanningWindow(2 * ola_window_size_,
transition_window_.get());
- wsola_output_ =
- new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
- kSbMediaAudioFrameStorageTypeInterleaved, 0,
- (ola_window_size_ + ola_hop_size_) * bytes_per_frame_);
+ wsola_output_ = new DecodedAudio(
+ channels_, sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ (ola_window_size_ + ola_hop_size_) * bytes_per_frame_);
// Initialize for overlap-and-add of the first block.
SbMemorySet(wsola_output_->buffer(), 0, wsola_output_->size());
// Auxiliary containers.
- optimal_block_ = new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
+ optimal_block_ = new DecodedAudio(channels_, sample_type_,
kSbMediaAudioFrameStorageTypeInterleaved, 0,
ola_window_size_ * bytes_per_frame_);
search_block_ = new DecodedAudio(
- channels_, kSbMediaAudioSampleTypeFloat32,
- kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ channels_, sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, 0,
(num_candidate_blocks_ + (ola_window_size_ - 1)) * bytes_per_frame_);
- target_block_ = new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
+ target_block_ = new DecodedAudio(channels_, sample_type_,
kSbMediaAudioFrameStorageTypeInterleaved, 0,
ola_window_size_ * bytes_per_frame_);
}
@@ -169,10 +172,9 @@
SB_DCHECK(bytes_per_frame_ > 0);
SB_DCHECK(playback_rate >= 0);
- scoped_refptr<DecodedAudio> dest =
- new DecodedAudio(channels_, kSbMediaAudioSampleTypeFloat32,
- kSbMediaAudioFrameStorageTypeInterleaved, 0,
- requested_frames * bytes_per_frame_);
+ scoped_refptr<DecodedAudio> dest = new DecodedAudio(
+ channels_, sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, 0,
+ requested_frames * bytes_per_frame_);
if (playback_rate == 0) {
dest->ShrinkTo(0);
@@ -382,7 +384,6 @@
// |search_block_|.
optimal_index = internal::OptimalIndex(
search_block_.get(), target_block_.get(),
- kSbMediaAudioSampleTypeFloat32,
kSbMediaAudioFrameStorageTypeInterleaved, exclude_iterval);
// Translate |index| w.r.t. the beginning of |audio_buffer_| and extract the
diff --git a/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
index 8ff6033..c8b94f1 100644
--- a/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
+++ b/src/starboard/shared/starboard/player/filter/audio_time_stretcher.h
@@ -53,7 +53,9 @@
~AudioTimeStretcher();
// Initializes this object with information about the audio stream.
- void Initialize(int channels, int samples_per_second);
+ void Initialize(SbMediaAudioSampleType sample_type,
+ int channels,
+ int samples_per_second);
// Tries to fill |requested_frames| frames into |dest| with possibly scaled
// data from our |audio_buffer_|. Data is scaled based on |playback_rate|,
@@ -126,6 +128,9 @@
// Converts a time in milliseconds to frames using |samples_per_second_|.
int ConvertMillisecondsToFrames(int ms) const;
+ // Audio sink sample type.
+ SbMediaAudioSampleType sample_type_;
+
// Number of channels in audio stream.
int channels_;
diff --git a/src/starboard/shared/starboard/player/filter/callback.h b/src/starboard/shared/starboard/player/filter/callback.h
new file mode 100644
index 0000000..f0cc615
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/callback.h
@@ -0,0 +1,36 @@
+// Copyright 2018 Google Inc. 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_PLAYER_FILTER_CALLBACK_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_CALLBACK_H_
+
+#include <functional>
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+typedef std::function<void()> ErrorCB;
+typedef std::function<void()> PrerolledCB;
+typedef std::function<void()> EndedCB;
+
+} // namespace filter
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_CALLBACK_H_
diff --git a/src/starboard/shared/starboard/player/filter/decoded_audio_queue.cc b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
index 509453f..eb0f082 100644
--- a/src/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
+++ b/src/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
@@ -43,7 +43,6 @@
void DecodedAudioQueue::Append(
const scoped_refptr<DecodedAudio>& decoded_audio) {
- SB_DCHECK(decoded_audio->sample_type() == kSbMediaAudioSampleTypeFloat32);
SB_DCHECK(decoded_audio->storage_type() ==
kSbMediaAudioFrameStorageTypeInterleaved)
<< decoded_audio->storage_type();
@@ -116,11 +115,21 @@
// if |dest| is NULL, there's no need to copy.
if (dest) {
SB_DCHECK(buffer->channels() == dest->channels());
- const float* source = reinterpret_cast<const float*>(buffer->buffer()) +
- buffer->channels() * current_buffer_offset;
- float* destination = reinterpret_cast<float*>(dest->buffer()) +
- dest->channels() * (dest_frame_offset + taken);
- SbMemoryCopy(destination, source, copied * dest->channels() * 4);
+ if (dest->sample_type() == kSbMediaAudioSampleTypeFloat32) {
+ const float* source =
+ reinterpret_cast<const float*>(buffer->buffer()) +
+ buffer->channels() * current_buffer_offset;
+ float* destination = reinterpret_cast<float*>(dest->buffer()) +
+ dest->channels() * (dest_frame_offset + taken);
+ SbMemoryCopy(destination, source, copied * dest->channels() * 4);
+ } else {
+ const int16_t* source =
+ reinterpret_cast<const int16_t*>(buffer->buffer()) +
+ buffer->channels() * current_buffer_offset;
+ int16_t* destination = reinterpret_cast<int16_t*>(dest->buffer()) +
+ dest->channels() * (dest_frame_offset + taken);
+ SbMemoryCopy(destination, source, copied * dest->channels() * 2);
+ }
}
// Increase total number of frames copied, which regulates when to end
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index d03167c..5cc438f 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -36,7 +36,7 @@
MonotonicSystemTimeProvider;
// TODO: Make this configurable inside SbPlayerCreate().
-const SbTimeMonotonic kUpdateInterval = 5 * kSbTimeMillisecond;
+const SbTimeMonotonic kUpdateInterval = 200 * kSbTimeMillisecond;
class MonotonicSystemTimeProviderImpl : public MonotonicSystemTimeProvider {
SbTimeMonotonic GetMonotonicNow() const override {
@@ -53,7 +53,8 @@
const SbMediaAudioHeader* audio_header,
SbPlayerOutputMode output_mode,
SbDecodeTargetGraphicsContextProvider* provider)
- : video_codec_(video_codec),
+ : JobOwner(kDetached),
+ video_codec_(video_codec),
audio_codec_(audio_codec),
drm_system_(drm_system),
output_mode_(output_mode),
@@ -82,37 +83,30 @@
}
bool FilterBasedPlayerWorkerHandler::Init(
- PlayerWorker* player_worker,
- JobQueue* job_queue,
SbPlayer player,
- UpdateMediaTimeCB update_media_time_cb,
+ UpdateMediaInfoCB update_media_info_cb,
GetPlayerStateCB get_player_state_cb,
- UpdatePlayerStateCB update_player_state_cb
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
- ,
- UpdatePlayerErrorCB update_player_error_cb
-#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- ) {
+ UpdatePlayerStateCB update_player_state_cb,
+ UpdatePlayerErrorCB update_player_error_cb) {
// This function should only be called once.
- SB_DCHECK(player_worker_ == NULL);
+ SB_DCHECK(update_media_info_cb_ == NULL);
// All parameters have to be valid.
- SB_DCHECK(player_worker);
- SB_DCHECK(job_queue);
- SB_DCHECK(job_queue->BelongsToCurrentThread());
SB_DCHECK(SbPlayerIsValid(player));
- SB_DCHECK(update_media_time_cb);
+ SB_DCHECK(update_media_info_cb);
SB_DCHECK(get_player_state_cb);
SB_DCHECK(update_player_state_cb);
- player_worker_ = player_worker;
- job_queue_ = job_queue;
+ AttachToCurrentThread();
+
player_ = player;
- update_media_time_cb_ = update_media_time_cb;
+ update_media_info_cb_ = update_media_info_cb;
get_player_state_cb_ = get_player_state_cb;
update_player_state_cb_ = update_player_state_cb;
#if SB_HAS(PLAYER_ERROR_MESSAGE)
update_player_error_cb_ = update_player_error_cb;
+#else // SB_HAS(PLAYER_ERROR_MESSAGE)
+ SB_DCHECK(!update_player_error_cb);
#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
scoped_ptr<PlayerComponents> player_components = PlayerComponents::Create();
@@ -128,50 +122,68 @@
}
PlayerComponents::AudioParameters audio_parameters = {
- audio_codec_, audio_header_, drm_system_, job_queue_};
+ audio_codec_, audio_header_, drm_system_};
audio_renderer_ = player_components->CreateAudioRenderer(audio_parameters);
- SB_DCHECK(audio_renderer_);
- if (audio_renderer_) {
- audio_renderer_->SetPlaybackRate(playback_rate_);
- audio_renderer_->SetVolume(volume_);
- audio_renderer_->Initialize(
- std::bind(&FilterBasedPlayerWorkerHandler::OnError, this));
+ if (!audio_renderer_) {
+ SB_DLOG(ERROR) << "Failed to create audio renderer";
+ return false;
}
+ audio_renderer_->Initialize(
+ std::bind(&FilterBasedPlayerWorkerHandler::OnError, this),
+ std::bind(&FilterBasedPlayerWorkerHandler::OnPrerolled, this,
+ kSbMediaTypeAudio),
+ std::bind(&FilterBasedPlayerWorkerHandler::OnEnded, this,
+ kSbMediaTypeAudio));
+ audio_renderer_->SetPlaybackRate(playback_rate_);
+ audio_renderer_->SetVolume(volume_);
} else {
media_time_provider_impl_.reset(
new MediaTimeProviderImpl(scoped_ptr<MonotonicSystemTimeProvider>(
new MonotonicSystemTimeProviderImpl)));
+ media_time_provider_impl_->Initialize(
+ std::bind(&FilterBasedPlayerWorkerHandler::OnError, this),
+ std::bind(&FilterBasedPlayerWorkerHandler::OnPrerolled, this,
+ kSbMediaTypeAudio),
+ std::bind(&FilterBasedPlayerWorkerHandler::OnEnded, this,
+ kSbMediaTypeAudio));
media_time_provider_impl_->SetPlaybackRate(playback_rate_);
}
- PlayerComponents::VideoParameters video_parameters = {
- player_, video_codec_, drm_system_,
- job_queue_, output_mode_, decode_target_graphics_context_provider_};
+ if (video_codec_ != kSbMediaVideoCodecNone) {
+ PlayerComponents::VideoParameters video_parameters = {
+ player_, video_codec_, drm_system_, output_mode_,
+ decode_target_graphics_context_provider_};
- ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
+ ::starboard::ScopedLock lock(video_renderer_existence_mutex_);
- auto media_time_provider = GetMediaTimeProvider();
- SB_DCHECK(media_time_provider);
+ auto media_time_provider = GetMediaTimeProvider();
+ SB_DCHECK(media_time_provider);
- video_renderer_ = player_components->CreateVideoRenderer(video_parameters,
- media_time_provider);
- SB_DCHECK(video_renderer_);
- if (video_renderer_) {
+ video_renderer_ = player_components->CreateVideoRenderer(
+ video_parameters, media_time_provider);
+ if (!video_renderer_) {
+ SB_DLOG(ERROR) << "Failed to create video renderer";
+ return false;
+ }
video_renderer_->Initialize(
- std::bind(&FilterBasedPlayerWorkerHandler::OnError, this));
+ std::bind(&FilterBasedPlayerWorkerHandler::OnError, this),
+ std::bind(&FilterBasedPlayerWorkerHandler::OnPrerolled, this,
+ kSbMediaTypeVideo),
+ std::bind(&FilterBasedPlayerWorkerHandler::OnEnded, this,
+ kSbMediaTypeVideo));
}
- update_job_token_ = job_queue_->Schedule(update_job_, kUpdateInterval);
+ update_job_token_ = Schedule(update_job_, kUpdateInterval);
return true;
}
bool FilterBasedPlayerWorkerHandler::Seek(SbTime seek_to_time, int ticket) {
SB_UNREFERENCED_PARAMETER(ticket);
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
- if (!GetMediaTimeProvider() || !video_renderer_) {
+ if (!GetMediaTimeProvider()) {
return false;
}
@@ -181,8 +193,12 @@
}
GetMediaTimeProvider()->Pause();
- video_renderer_->Seek(seek_to_time);
+ if (video_renderer_) {
+ video_renderer_->Seek(seek_to_time);
+ }
GetMediaTimeProvider()->Seek(seek_to_time);
+ audio_prerolled_ = false;
+ video_prerolled_ = false;
return true;
}
@@ -190,7 +206,7 @@
const scoped_refptr<InputBuffer>& input_buffer,
bool* written) {
SB_DCHECK(input_buffer);
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(written != NULL);
if (input_buffer->sample_type() == kSbMediaTypeAudio) {
@@ -268,7 +284,7 @@
}
bool FilterBasedPlayerWorkerHandler::WriteEndOfStream(SbMediaType sample_type) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
if (sample_type == kSbMediaTypeAudio) {
if (!audio_renderer_) {
@@ -299,7 +315,7 @@
}
bool FilterBasedPlayerWorkerHandler::SetPause(bool pause) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
if (!GetMediaTimeProvider()) {
return false;
@@ -319,7 +335,7 @@
}
bool FilterBasedPlayerWorkerHandler::SetPlaybackRate(double playback_rate) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
playback_rate_ = playback_rate;
@@ -332,7 +348,7 @@
}
void FilterBasedPlayerWorkerHandler::SetVolume(double volume) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
volume_ = volume;
if (audio_renderer_) {
@@ -342,7 +358,7 @@
bool FilterBasedPlayerWorkerHandler::SetBounds(
const PlayerWorker::Bounds& bounds) {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
if (SbMemoryCompare(&bounds_, &bounds, sizeof(bounds_)) != 0) {
bounds_ = bounds;
@@ -357,67 +373,81 @@
}
void FilterBasedPlayerWorkerHandler::OnError() {
- if (!job_queue_->BelongsToCurrentThread()) {
- job_queue_->Schedule(
- std::bind(&FilterBasedPlayerWorkerHandler::OnError, this));
+ if (!BelongsToCurrentThread()) {
+ Schedule(std::bind(&FilterBasedPlayerWorkerHandler::OnError, this));
return;
}
#if SB_HAS(PLAYER_ERROR_MESSAGE)
if (update_player_error_cb_) {
- (*player_worker_.*
- update_player_error_cb_)("FilterBasedPlayerWorkerHandler error.");
+ update_player_error_cb_("FilterBasedPlayerWorkerHandler error.");
}
#else // SB_HAS(PLAYER_ERROR_MESSAGE)
- (*player_worker_.*update_player_state_cb_)(kSbPlayerStateError);
+ update_player_state_cb_(kSbPlayerStateError);
#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
}
-// TODO: This should be driven by callbacks instead polling.
-void FilterBasedPlayerWorkerHandler::Update() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
-
- if (!GetMediaTimeProvider() || !video_renderer_) {
+void FilterBasedPlayerWorkerHandler::OnPrerolled(SbMediaType media_type) {
+ if (!BelongsToCurrentThread()) {
+ Schedule(std::bind(&FilterBasedPlayerWorkerHandler::OnPrerolled, this,
+ media_type));
return;
}
- if ((*player_worker_.*get_player_state_cb_)() == kSbPlayerStatePrerolling) {
- bool audio_seek_in_progress =
- audio_renderer_ && audio_renderer_->IsSeekingInProgress();
- if (!audio_seek_in_progress &&
- !video_renderer_->UpdateAndRetrieveIsSeekingInProgress()) {
- (*player_worker_.*update_player_state_cb_)(kSbPlayerStatePresenting);
- if (!paused_) {
- GetMediaTimeProvider()->Play();
- }
+ SB_DCHECK(get_player_state_cb_() == kSbPlayerStatePrerolling)
+ << "Invalid player state " << get_player_state_cb_();
+
+ audio_prerolled_ |= media_type == kSbMediaTypeAudio;
+ video_prerolled_ |= media_type == kSbMediaTypeVideo;
+
+ if (audio_prerolled_ && (!video_renderer_ || video_prerolled_)) {
+ update_player_state_cb_(kSbPlayerStatePresenting);
+ if (!paused_) {
+ GetMediaTimeProvider()->Play();
}
}
+}
- if ((*player_worker_.*get_player_state_cb_)() == kSbPlayerStatePresenting) {
- bool is_audio_playing;
- bool is_audio_eos_played;
- GetMediaTimeProvider()->GetCurrentMediaTime(&is_audio_playing,
- &is_audio_eos_played);
- if (is_audio_eos_played && video_renderer_->IsEndOfStreamPlayed()) {
- (*player_worker_.*update_player_state_cb_)(kSbPlayerStateEndOfStream);
+void FilterBasedPlayerWorkerHandler::OnEnded(SbMediaType media_type) {
+ if (!BelongsToCurrentThread()) {
+ Schedule(
+ std::bind(&FilterBasedPlayerWorkerHandler::OnEnded, this, media_type));
+ return;
+ }
+ audio_ended_ |= media_type == kSbMediaTypeAudio;
+ video_ended_ |= media_type == kSbMediaTypeVideo;
+
+ if (audio_ended_ && (!video_renderer_ || video_ended_)) {
+ update_player_state_cb_(kSbPlayerStateEndOfStream);
+ }
+}
+
+void FilterBasedPlayerWorkerHandler::Update() {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ if (!GetMediaTimeProvider()) {
+ return;
+ }
+
+ if (get_player_state_cb_() == kSbPlayerStatePresenting) {
+ int dropped_frames = 0;
+ if (video_renderer_) {
+ dropped_frames = video_renderer_->GetDroppedFrames();
}
-
- player_worker_->UpdateDroppedVideoFrames(
- video_renderer_->GetDroppedFrames());
bool is_playing;
bool is_eos_played;
- (*player_worker_.*
- update_media_time_cb_)(GetMediaTimeProvider()->GetCurrentMediaTime(
- &is_playing, &is_eos_played));
+ auto media_time = GetMediaTimeProvider()->GetCurrentMediaTime(
+ &is_playing, &is_eos_played);
+ update_media_info_cb_(media_time, dropped_frames);
}
- update_job_token_ = job_queue_->Schedule(update_job_, kUpdateInterval);
+ update_job_token_ = Schedule(update_job_, kUpdateInterval);
}
void FilterBasedPlayerWorkerHandler::Stop() {
- SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(BelongsToCurrentThread());
- job_queue_->RemoveJobByToken(update_job_token_);
+ RemoveJobByToken(update_job_token_);
scoped_ptr<VideoRenderer> video_renderer;
{
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index a04000c..afecef9 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -37,7 +37,8 @@
namespace player {
namespace filter {
-class FilterBasedPlayerWorkerHandler : public PlayerWorker::Handler {
+class FilterBasedPlayerWorkerHandler : public PlayerWorker::Handler,
+ private JobQueue::JobOwner {
public:
FilterBasedPlayerWorkerHandler(
SbMediaVideoCodec video_codec,
@@ -49,17 +50,11 @@
private:
bool IsPunchoutMode() const;
- bool Init(PlayerWorker* player_worker,
- JobQueue* job_queue,
- SbPlayer player,
- UpdateMediaTimeCB update_media_time_cb,
+ bool Init(SbPlayer player,
+ UpdateMediaInfoCB update_media_info_cb,
GetPlayerStateCB get_player_state_cb,
- UpdatePlayerStateCB update_player_state_cb
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
- ,
- UpdatePlayerErrorCB update_player_error_cb
-#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- ) override;
+ UpdatePlayerStateCB update_player_state_cb,
+ UpdatePlayerErrorCB update_player_error_cb) override;
bool Seek(SbTime seek_to_time, int ticket) override;
bool WriteSample(const scoped_refptr<InputBuffer>& input_buffer,
bool* written) override;
@@ -72,19 +67,17 @@
void Update();
void OnError();
+ void OnPrerolled(SbMediaType media_type);
+ void OnEnded(SbMediaType media_type);
SbDecodeTarget GetCurrentDecodeTarget() override;
MediaTimeProvider* GetMediaTimeProvider() const;
- PlayerWorker* player_worker_ = NULL;
- JobQueue* job_queue_ = NULL;
SbPlayer player_ = kSbPlayerInvalid;
- UpdateMediaTimeCB update_media_time_cb_ = NULL;
- GetPlayerStateCB get_player_state_cb_ = NULL;
- UpdatePlayerStateCB update_player_state_cb_ = NULL;
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
- UpdatePlayerErrorCB update_player_error_cb_ = NULL;
-#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+ UpdateMediaInfoCB update_media_info_cb_;
+ GetPlayerStateCB get_player_state_cb_;
+ UpdatePlayerStateCB update_player_state_cb_;
+ UpdatePlayerErrorCB update_player_error_cb_;
SbMediaVideoCodec video_codec_;
SbMediaAudioCodec audio_codec_;
@@ -110,6 +103,11 @@
JobQueue::JobToken update_job_token_;
std::function<void()> update_job_;
+ bool audio_prerolled_ = false;
+ bool video_prerolled_ = false;
+ bool audio_ended_ = false;
+ bool video_ended_ = false;
+
// A mutex guarding changes to the existence (e.g. creation/destruction)
// of the |video_renderer_| object. This is necessary because calls to
// GetCurrentDecodeTarget() need to be supported on arbitrary threads, and
diff --git a/src/starboard/shared/starboard/player/filter/media_time_provider.h b/src/starboard/shared/starboard/player/filter/media_time_provider.h
index 211495e..bfdaca5 100644
--- a/src/starboard/shared/starboard/player/filter/media_time_provider.h
+++ b/src/starboard/shared/starboard/player/filter/media_time_provider.h
@@ -16,6 +16,7 @@
#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_MEDIA_TIME_PROVIDER_H_
#include "starboard/media.h"
+#include "starboard/shared/starboard/player/filter/callback.h"
namespace starboard {
namespace shared {
@@ -25,6 +26,9 @@
class MediaTimeProvider {
public:
+ virtual void Initialize(const ErrorCB& error_cb,
+ const PrerolledCB& prerolled_cb,
+ const EndedCB& ended_cb) = 0;
virtual void Play() = 0;
virtual void Pause() = 0;
virtual void SetPlaybackRate(double playback_rate) = 0;
diff --git a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc
index e525073..a6a85c0 100644
--- a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc
+++ b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.cc
@@ -28,20 +28,22 @@
SB_DCHECK(system_time_provider_);
}
-void MediaTimeProviderImpl::SetPlaybackRate(double playback_rate) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
+void MediaTimeProviderImpl::Initialize(const ErrorCB& error_cb,
+ const PrerolledCB& prerolled_cb,
+ const EndedCB& ended_cb) {
+ SB_UNREFERENCED_PARAMETER(error_cb);
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(prerolled_cb);
+ SB_DCHECK(ended_cb);
+ SB_DCHECK(!prerolled_cb_);
+ SB_DCHECK(!ended_cb_);
- if (playback_rate_ == playback_rate) {
- return;
- }
-
- ScopedLock scoped_lock(mutex_);
- seek_to_time_ = GetCurrentMediaTime_Locked(&seek_to_time_set_at_);
- playback_rate_ = playback_rate;
+ prerolled_cb_ = prerolled_cb;
+ ended_cb_ = ended_cb;
}
void MediaTimeProviderImpl::Play() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(BelongsToCurrentThread());
if (is_playing_) {
return;
@@ -53,7 +55,7 @@
}
void MediaTimeProviderImpl::Pause() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(BelongsToCurrentThread());
if (!is_playing_) {
return;
@@ -64,8 +66,21 @@
is_playing_ = false;
}
+void MediaTimeProviderImpl::SetPlaybackRate(double playback_rate) {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ if (playback_rate_ == playback_rate) {
+ return;
+ }
+
+ ScopedLock scoped_lock(mutex_);
+ seek_to_time_ = GetCurrentMediaTime_Locked(&seek_to_time_set_at_);
+ playback_rate_ = playback_rate;
+}
+
void MediaTimeProviderImpl::Seek(SbTime seek_to_time) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(prerolled_cb_);
ScopedLock scoped_lock(mutex_);
@@ -73,10 +88,15 @@
seek_to_time_set_at_ = system_time_provider_->GetMonotonicNow();
video_duration_ = nullopt;
is_video_end_of_stream_reached_ = false;
+
+ CancelPendingJobs();
+ Schedule(prerolled_cb_);
}
SbTime MediaTimeProviderImpl::GetCurrentMediaTime(bool* is_playing,
bool* is_eos_played) {
+ SB_DCHECK(ended_cb_);
+
ScopedLock scoped_lock(mutex_);
SbTime current = GetCurrentMediaTime_Locked();
@@ -86,6 +106,7 @@
is_video_end_of_stream_reached_ &&
(!video_duration_.has_engaged() || current >= video_duration_.value());
+ Schedule(ended_cb_);
return current;
}
diff --git a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h
index 7ba310c..d88dd1e 100644
--- a/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h
+++ b/src/starboard/shared/starboard/player/filter/media_time_provider_impl.h
@@ -20,7 +20,7 @@
#include "starboard/media.h"
#include "starboard/mutex.h"
#include "starboard/shared/starboard/player/filter/media_time_provider.h"
-#include "starboard/shared/starboard/thread_checker.h"
+#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/time.h"
namespace starboard {
@@ -30,7 +30,8 @@
namespace filter {
// This class provides the media playback time when there isn't an audio track.
-class MediaTimeProviderImpl : public MediaTimeProvider {
+class MediaTimeProviderImpl : public MediaTimeProvider,
+ private JobQueue::JobOwner {
public:
class MonotonicSystemTimeProvider {
public:
@@ -41,9 +42,12 @@
explicit MediaTimeProviderImpl(
scoped_ptr<MonotonicSystemTimeProvider> system_time_provider);
- void SetPlaybackRate(double playback_rate) override;
+ void Initialize(const ErrorCB& error_cb,
+ const PrerolledCB& prerolled_cb,
+ const EndedCB& ended_cb) override;
void Play() override;
void Pause() override;
+ void SetPlaybackRate(double playback_rate) override;
void Seek(SbTime seek_to_time) override;
SbTime GetCurrentMediaTime(bool* is_playing, bool* is_eos_played) override;
@@ -61,10 +65,11 @@
// should handle this properly.
SbTime GetCurrentMediaTime_Locked(SbTimeMonotonic* current_time = NULL);
- ThreadChecker thread_checker_;
-
scoped_ptr<MonotonicSystemTimeProvider> system_time_provider_;
+ PrerolledCB prerolled_cb_;
+ EndedCB ended_cb_;
+
Mutex mutex_;
double playback_rate_ = 1.0;
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index ef8dec3..bb9d096 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -46,14 +46,12 @@
SbMediaAudioCodec audio_codec;
const SbMediaAudioHeader& audio_header;
SbDrmSystem drm_system;
- JobQueue* job_queue;
};
struct VideoParameters {
SbPlayer player;
SbMediaVideoCodec video_codec;
SbDrmSystem drm_system;
- JobQueue* job_queue;
SbPlayerOutputMode output_mode;
SbDecodeTargetGraphicsContextProvider*
decode_target_graphics_context_provider;
diff --git a/src/starboard/shared/starboard/player/filter/player_filter.gypi b/src/starboard/shared/starboard/player/filter/player_filter.gypi
new file mode 100644
index 0000000..475fb7b
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/player_filter.gypi
@@ -0,0 +1,56 @@
+# Copyright 2018 Google Inc. 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.
+{
+ 'variables': {
+ 'filter_based_player_sources': [
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/callback.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/cpu_video_frame.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/cpu_video_frame.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/mock_audio_decoder.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/mock_audio_renderer_sink.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/player_components.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_frame_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_sink.h',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.h',
+ ],
+ },
+}
\ No newline at end of file
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
index f0030a1..66f778e 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
@@ -29,6 +29,7 @@
#include "starboard/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
// TODO: Write test for HE-AAC
namespace starboard {
@@ -74,7 +75,7 @@
PlayerComponents::AudioParameters audio_parameters = {
dmp_reader_.audio_codec(), dmp_reader_.audio_header(),
- kSbDrmSystemInvalid, &job_queue_};
+ kSbDrmSystemInvalid};
scoped_ptr<PlayerComponents> components = PlayerComponents::Create();
components->CreateAudioComponents(audio_parameters, &audio_decoder_,
@@ -240,7 +241,7 @@
PlayerComponents::AudioParameters audio_parameters = {
dmp_reader_.audio_codec(), dmp_reader_.audio_header(),
- kSbDrmSystemInvalid, &job_queue_};
+ kSbDrmSystemInvalid};
scoped_ptr<PlayerComponents> components = PlayerComponents::Create();
scoped_ptr<AudioDecoder> audio_decoders[kDecodersToCreate];
@@ -350,3 +351,4 @@
} // namespace starboard
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
index 215dad1..3d41bb0 100644
--- a/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -27,6 +27,7 @@
#include "starboard/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -44,6 +45,7 @@
using ::testing::Return;
using ::testing::SaveArg;
+// TODO: Write tests to cover callbacks.
class AudioRendererTest : public ::testing::Test {
protected:
static const int kDefaultNumberOfChannels = 2;
@@ -109,7 +111,10 @@
make_scoped_ptr<AudioDecoder>(audio_decoder_),
make_scoped_ptr<AudioRendererSink>(audio_renderer_sink_),
GetDefaultAudioHeader(), kMaxCachedFrames, kMaxFramesPerAppend));
- audio_renderer_->Initialize(std::bind(&AudioRendererTest::OnError, this));
+ audio_renderer_->Initialize(
+ std::bind(&AudioRendererTest::OnError, this),
+ std::bind(&AudioRendererTest::OnPrerolled, this),
+ std::bind(&AudioRendererTest::OnEnded, this));
}
// Creates audio buffers, decodes them, and passes them onto the renderer,
@@ -194,6 +199,8 @@
}
void OnError() {}
+ void OnPrerolled() {}
+ void OnEnded() {}
SbMediaAudioSampleType sample_type_;
SbMediaAudioFrameStorageType storage_type_;
@@ -254,6 +261,8 @@
EXPECT_FALSE(is_eos_played);
}
+// TODO: adapt these tests for async audio frames reporting.
+#if !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
TEST_F(AudioRendererTest, SunnyDay) {
{
InSequence seq;
@@ -775,6 +784,7 @@
EXPECT_TRUE(audio_renderer_->IsEndOfStreamPlayed());
}
+#endif // !SB_HAS(ASYNC_AUDIO_FRAMES_REPORTING)
// TODO: Add more Seek tests.
@@ -785,3 +795,4 @@
} // namespace starboard
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc b/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
index 0156a3e..fbe49f5 100644
--- a/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc
@@ -14,11 +14,13 @@
#include "starboard/shared/starboard/player/filter/media_time_provider_impl.h"
+#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/thread.h"
#include "starboard/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -73,6 +75,7 @@
SbTimeMonotonic current_time_;
};
+// TODO: Write tests to cover callbacks.
class MediaTimeProviderImplTest : public ::testing::Test {
protected:
MediaTimeProviderImplTest()
@@ -82,8 +85,18 @@
// can adjust expectation on it. This is safe in the context of the
// tests.
media_time_provider_impl_(make_scoped_ptr<MonotonicSystemTimeProvider>(
- system_time_provider_)) {}
+ system_time_provider_)) {
+ media_time_provider_impl_.Initialize(
+ std::bind(&MediaTimeProviderImplTest::OnError, this),
+ std::bind(&MediaTimeProviderImplTest::OnPrerolled, this),
+ std::bind(&MediaTimeProviderImplTest::OnEnded, this));
+ }
+ void OnError() {}
+ void OnPrerolled() {}
+ void OnEnded() {}
+
+ JobQueue job_queue_;
StrictMock<MockMonotonicSystemTimeProvider>* system_time_provider_;
MediaTimeProviderImpl media_time_provider_impl_;
};
@@ -274,3 +287,4 @@
} // namespace starboard
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp b/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp
new file mode 100644
index 0000000..0a5ba07
--- /dev/null
+++ b/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp
@@ -0,0 +1,81 @@
+# Copyright 2017 Google Inc. 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'player_filter_tests',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ '<(DEPTH)/starboard/common/test_main.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/video_dmp_common.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/video_dmp_common.h',
+ '<(DEPTH)/starboard/shared/starboard/player/video_dmp_reader.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/video_dmp_reader.h',
+ '<(DEPTH)/starboard/testing/fake_graphics_context_provider.cc',
+ '<(DEPTH)/starboard/testing/fake_graphics_context_provider.h',
+ ],
+ 'defines': [
+ # This allows the tests to include internal only header files.
+ 'STARBOARD_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '<@(cobalt_platform_dependencies)',
+ '<(DEPTH)/starboard/starboard.gyp:starboard',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ 'player_filter_tests_copy_test_data',
+ ],
+ },
+ {
+ 'target_name': 'player_filter_tests_copy_test_data',
+ 'type': 'none',
+ 'variables': {
+ 'content_test_input_files': ['<!@(python <(DEPTH)/starboard/build/list_dmp_files.py "starboard/shared/starboard/player/testdata")'],
+ 'content_test_output_subdir': 'starboard/shared/starboard/player/testdata',
+ 'download_at_gyp_time': ['<!@(download_from_google_storage --no_resume --no_auth --num_threads 8 --bucket cobalt-static-storage -d <(DEPTH)/starboard/shared/starboard/player/testdata -v | grep "Success")'],
+ },
+ 'actions' : [
+ {
+ 'action_name': 'player_filter_tests_download_test_data',
+ 'action': [ 'download_from_google_storage',
+ '--no_resume',
+ '--no_auth',
+ '--num_threads', '8',
+ '--bucket', 'cobalt-static-storage',
+ '-d', '<(DEPTH)/starboard/shared/starboard/player/testdata',
+ ],
+ 'inputs': [],
+ 'outputs': ['<!@(python <(DEPTH)/starboard/build/list_dmp_files.py "starboard/shared/starboard/player/testdata")'],
+ },
+ ],
+ 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'],
+ },
+ {
+ 'target_name': 'player_filter_tests_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'player_filter_tests',
+ ],
+ 'variables': {
+ 'executable_name': 'player_filter_tests',
+ },
+ 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi b/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi
deleted file mode 100644
index af39e56..0000000
--- a/src/starboard/shared/starboard/player/filter/testing/player_filter_tests.gypi
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-
-{
- 'targets': [
- {
- 'target_name': 'player_filter_tests',
- 'type': '<(gtest_target_type)',
- 'sources': [
- '<(DEPTH)/starboard/common/test_main.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/testing/media_time_provider_impl_test.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc',
- '<(DEPTH)/starboard/shared/starboard/player/video_dmp_common.cc',
- '<(DEPTH)/starboard/shared/starboard/player/video_dmp_common.h',
- '<(DEPTH)/starboard/shared/starboard/player/video_dmp_reader.cc',
- '<(DEPTH)/starboard/shared/starboard/player/video_dmp_reader.h',
- '<(DEPTH)/starboard/testing/fake_graphics_context_provider.cc',
- '<(DEPTH)/starboard/testing/fake_graphics_context_provider.h',
- ],
- 'defines': [
- # This allows the tests to include internal only header files.
- 'STARBOARD_IMPLEMENTATION',
- ],
- 'dependencies': [
- '<@(cobalt_platform_dependencies)',
- '<(DEPTH)/starboard/starboard.gyp:starboard',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- 'player_filter_tests_copy_test_data',
- ],
- },
- {
- 'target_name': 'player_filter_tests_copy_test_data',
- 'type': 'none',
- 'variables': {
- 'content_test_input_files': [
- '<(DEPTH)/starboard/shared/starboard/player/testdata',
- ],
- 'content_test_output_subdir': 'starboard/shared/starboard/player',
- },
- 'includes': ['<(DEPTH)/starboard/build/copy_test_data.gypi'],
- },
- {
- 'target_name': 'player_filter_tests_deploy',
- 'type': 'none',
- 'dependencies': [
- 'player_filter_tests',
- ],
- 'variables': {
- 'executable_name': 'player_filter_tests',
- },
- 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
- },
- ],
-}
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
index 17ce97a..3df8d8c 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
@@ -36,6 +36,7 @@
#include "starboard/time.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
// This has to be defined in the global namespace as its instance will be used
// as SbPlayer.
struct SbPlayerPrivate {};
@@ -112,7 +113,6 @@
&player_,
dmp_reader_.video_codec(),
kSbDrmSystemInvalid,
- &job_queue_,
output_mode,
fake_graphics_context_provider_.decoder_target_provider()};
@@ -427,7 +427,6 @@
&players[i],
dmp_reader_.video_codec(),
kSbDrmSystemInvalid,
- &job_queue_,
output_mode,
fake_graphics_context_provider_.decoder_target_provider()};
@@ -668,3 +667,4 @@
} // namespace starboard
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
index 5749658..f50e48d 100644
--- a/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_decoder_internal.h
@@ -21,6 +21,7 @@
#include "starboard/configuration.h"
#include "starboard/player.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/filter/callback.h"
#include "starboard/shared/starboard/player/filter/video_frame_internal.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
@@ -54,7 +55,7 @@
typedef std::function<void(Status status,
const scoped_refptr<VideoFrame>& frame)>
DecoderStatusCB;
- typedef std::function<void()> ErrorCB;
+ typedef ::starboard::shared::starboard::player::filter::ErrorCB ErrorCB;
virtual ~VideoDecoder() {}
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
index dfdf8d6..1fb7421 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
@@ -23,9 +23,15 @@
namespace player {
namespace filter {
+namespace {
+
using std::placeholders::_1;
using std::placeholders::_2;
+const SbTime kSeekTimeoutRetryInterval = 25 * kSbTimeMillisecond;
+
+} // namespace
+
VideoRenderer::VideoRenderer(scoped_ptr<VideoDecoder> decoder,
MediaTimeProvider* media_time_provider,
scoped_ptr<VideoRenderAlgorithm> algorithm,
@@ -33,18 +39,20 @@
: media_time_provider_(media_time_provider),
algorithm_(algorithm.Pass()),
sink_(sink),
- seeking_(false),
- seeking_to_time_(0),
- end_of_stream_written_(false),
- need_more_input_(true),
decoder_(decoder.Pass()),
- first_input_written_(false) {
+ end_of_stream_written_(false),
+ ended_cb_called_(false),
+ need_more_input_(true),
+ seeking_(false),
+ number_of_frames_(0) {
SB_DCHECK(decoder_ != NULL);
SB_DCHECK(algorithm_ != NULL);
SB_DCHECK(sink_ != NULL);
}
VideoRenderer::~VideoRenderer() {
+ SB_DCHECK(BelongsToCurrentThread());
+
sink_ = NULL;
// Be sure to release anything created by the decoder_ before releasing the
@@ -52,11 +60,27 @@
if (first_input_written_) {
decoder_->Reset();
}
- frames_.clear();
+
+ // Now both the decoder thread and the sink thread should have been shutdown.
+ decoder_frames_.clear();
+ sink_frames_.clear();
+ number_of_frames_.store(0);
+
decoder_.reset();
}
-void VideoRenderer::Initialize(const ErrorCB& error_cb) {
+void VideoRenderer::Initialize(const ErrorCB& error_cb,
+ const PrerolledCB& prerolled_cb,
+ const EndedCB& ended_cb) {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(prerolled_cb);
+ SB_DCHECK(ended_cb);
+ SB_DCHECK(!prerolled_cb_);
+ SB_DCHECK(!ended_cb_);
+
+ prerolled_cb_ = prerolled_cb;
+ ended_cb_ = ended_cb;
+
decoder_->Initialize(std::bind(&VideoRenderer::OnDecoderStatus, this, _1, _2),
error_cb);
if (sink_) {
@@ -64,20 +88,12 @@
}
}
-void VideoRenderer::SetBounds(int z_index,
- int x,
- int y,
- int width,
- int height) {
- sink_->SetBounds(z_index, x, y, width, height);
-}
-
void VideoRenderer::WriteSample(
const scoped_refptr<InputBuffer>& input_buffer) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(input_buffer);
- if (end_of_stream_written_) {
+ if (end_of_stream_written_.load()) {
SB_LOG(ERROR) << "Appending video sample at " << input_buffer->timestamp()
<< " after EOS reached.";
return;
@@ -88,29 +104,31 @@
absolute_time_of_first_input_ = SbTimeGetMonotonicNow();
}
- {
- ScopedLock lock(mutex_);
- need_more_input_ = false;
- }
+ SB_DCHECK(need_more_input_.load());
+ need_more_input_.store(false);
decoder_->WriteInputBuffer(input_buffer);
}
void VideoRenderer::WriteEndOfStream() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(BelongsToCurrentThread());
- SB_LOG_IF(WARNING, end_of_stream_written_)
+ SB_LOG_IF(WARNING, end_of_stream_written_.load())
<< "Try to write EOS after EOS is reached";
- if (end_of_stream_written_) {
+ if (end_of_stream_written_.load()) {
return;
}
- end_of_stream_written_ = true;
+ end_of_stream_written_.store(true);
+ if (!first_input_written_ && !ended_cb_called_.load()) {
+ ended_cb_called_.store(true);
+ Schedule(ended_cb_);
+ }
first_input_written_ = true;
decoder_->WriteEndOfStream();
}
void VideoRenderer::Seek(SbTime seek_to_time) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
+ SB_DCHECK(BelongsToCurrentThread());
SB_DCHECK(seek_to_time >= 0);
if (first_input_written_) {
@@ -118,74 +136,41 @@
first_input_written_ = false;
}
- ScopedLock lock(mutex_);
-
+ // After decoder_->Reset(), OnDecoderStatus() won't be called before another
+ // WriteSample(). So it is safe to modify |seeking_to_time_| here.
seeking_to_time_ = std::max<SbTime>(seek_to_time, 0);
- seeking_ = true;
- end_of_stream_written_ = false;
- need_more_input_ = true;
+ seeking_.store(true);
+ end_of_stream_written_.store(false);
+ ended_cb_called_.store(false);
+ need_more_input_.store(true);
- frames_.clear();
-}
+ CancelPendingJobs();
-bool VideoRenderer::IsEndOfStreamPlayed() const {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- ScopedLock lock(mutex_);
- return end_of_stream_written_ && frames_.size() <= 1;
+ auto preroll_timeout = decoder_->GetPrerollTimeout();
+ if (preroll_timeout != kSbTimeMax) {
+ Schedule(std::bind(&VideoRenderer::OnSeekTimeout, this), preroll_timeout);
+ }
+
+ ScopedLock scoped_lock_decoder_frames(decoder_frames_mutex_);
+ ScopedLock scoped_lock_sink_frames(sink_frames_mutex_);
+ decoder_frames_.clear();
+ sink_frames_.clear();
+ number_of_frames_.store(0);
}
bool VideoRenderer::CanAcceptMoreData() const {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- ScopedLock lock(mutex_);
- return frames_.size() < decoder_->GetMaxNumberOfCachedFrames() &&
- !end_of_stream_written_ && need_more_input_;
+ SB_DCHECK(BelongsToCurrentThread());
+ return number_of_frames_.load() <
+ static_cast<int32_t>(decoder_->GetMaxNumberOfCachedFrames()) &&
+ !end_of_stream_written_.load() && need_more_input_.load();
}
-bool VideoRenderer::UpdateAndRetrieveIsSeekingInProgress() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- ScopedLock lock(mutex_);
- if (seeking_ && !frames_.empty()) {
- auto elapsed = SbTimeGetMonotonicNow() - absolute_time_of_first_input_;
- seeking_ = elapsed < decoder_->GetPrerollTimeout();
- }
- return seeking_;
-}
-
-void VideoRenderer::OnDecoderStatus(VideoDecoder::Status status,
- const scoped_refptr<VideoFrame>& frame) {
- ScopedLock lock(mutex_);
-
- if (status == VideoDecoder::kReleaseAllFrames) {
- frames_.clear();
- return;
- }
-
- if (frame) {
- SB_DCHECK(first_input_written_);
-
- bool frame_too_early = false;
- if (seeking_) {
- if (frame->is_end_of_stream()) {
- seeking_ = false;
- } else if (frame->timestamp() < seeking_to_time_) {
- frame_too_early = true;
- }
- }
- if (!frame_too_early) {
- frames_.push_back(frame);
- }
-
- if (seeking_ && frames_.size() >= decoder_->GetPrerollFrameCount()) {
- seeking_ = false;
- }
- }
-
- need_more_input_ = (status == VideoDecoder::kNeedMoreInput);
-}
-
-void VideoRenderer::Render(VideoRendererSink::DrawFrameCB draw_frame_cb) {
- ScopedLock lock(mutex_);
- algorithm_->Render(media_time_provider_, &frames_, draw_frame_cb);
+void VideoRenderer::SetBounds(int z_index,
+ int x,
+ int y,
+ int width,
+ int height) {
+ sink_->SetBounds(z_index, x, y, width, height);
}
SbDecodeTarget VideoRenderer::GetCurrentDecodeTarget() {
@@ -197,6 +182,79 @@
return decoder_->GetCurrentDecodeTarget();
}
+void VideoRenderer::OnDecoderStatus(VideoDecoder::Status status,
+ const scoped_refptr<VideoFrame>& frame) {
+ if (status == VideoDecoder::kReleaseAllFrames) {
+ ScopedLock scoped_lock_decoder_frames(decoder_frames_mutex_);
+ ScopedLock scoped_lock_sink_frames(sink_frames_mutex_);
+ decoder_frames_.clear();
+ sink_frames_.clear();
+ number_of_frames_.store(0);
+ return;
+ }
+
+ if (frame) {
+ SB_DCHECK(first_input_written_);
+
+ bool frame_too_early = false;
+ if (seeking_.load()) {
+ if (frame->is_end_of_stream()) {
+ seeking_.store(false);
+ Schedule(prerolled_cb_);
+ } else if (frame->timestamp() < seeking_to_time_) {
+ frame_too_early = true;
+ }
+ }
+ if (!frame_too_early) {
+ ScopedLock scoped_lock(decoder_frames_mutex_);
+ decoder_frames_.push_back(frame);
+ number_of_frames_.increment();
+ }
+
+ if (seeking_.load() &&
+ number_of_frames_.load() >=
+ static_cast<int32_t>(decoder_->GetPrerollFrameCount())) {
+ seeking_.store(false);
+ Schedule(prerolled_cb_);
+ }
+ }
+
+ need_more_input_.store(status == VideoDecoder::kNeedMoreInput);
+}
+
+void VideoRenderer::Render(VideoRendererSink::DrawFrameCB draw_frame_cb) {
+ {
+ ScopedLock scoped_lock_decoder_frames(decoder_frames_mutex_);
+ sink_frames_mutex_.Acquire();
+ sink_frames_.insert(sink_frames_.end(), decoder_frames_.begin(),
+ decoder_frames_.end());
+ decoder_frames_.clear();
+ }
+ size_t number_of_sink_frames = sink_frames_.size();
+ algorithm_->Render(media_time_provider_, &sink_frames_, draw_frame_cb);
+ number_of_frames_.fetch_sub(
+ static_cast<int32_t>(number_of_sink_frames - sink_frames_.size()));
+ if (number_of_frames_.load() <= 1 && end_of_stream_written_.load() &&
+ !ended_cb_called_.load()) {
+ ended_cb_called_.store(true);
+ Schedule(ended_cb_);
+ }
+ sink_frames_mutex_.Release();
+}
+
+void VideoRenderer::OnSeekTimeout() {
+ SB_DCHECK(BelongsToCurrentThread());
+ if (seeking_.load()) {
+ if (number_of_frames_.load() > 0) {
+ seeking_.store(false);
+ Schedule(prerolled_cb_);
+ } else {
+ Schedule(std::bind(&VideoRenderer::OnSeekTimeout, this),
+ kSeekTimeoutRetryInterval);
+ }
+ }
+}
+
} // namespace filter
} // namespace player
} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
index e982ab5..13e927d 100644
--- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
@@ -17,12 +17,14 @@
#include <list>
+#include "starboard/atomic.h"
#include "starboard/common/ref_counted.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/log.h"
#include "starboard/media.h"
#include "starboard/mutex.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/filter/callback.h"
#include "starboard/shared/starboard/player/filter/media_time_provider.h"
#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
#include "starboard/shared/starboard/player/filter/video_frame_internal.h"
@@ -30,7 +32,7 @@
#include "starboard/shared/starboard/player/filter/video_renderer_internal.h"
#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
-#include "starboard/shared/starboard/thread_checker.h"
+#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/time.h"
namespace starboard {
@@ -41,18 +43,19 @@
// A class that sits in between the video decoder, the video sink and the
// pipeline to coordinate data transfer between these parties.
-class VideoRenderer {
+class VideoRenderer : JobQueue::JobOwner {
public:
- typedef VideoDecoder::ErrorCB ErrorCB;
-
+ // All of the functions are called on the PlayerWorker thread unless marked
+ // otherwise.
VideoRenderer(scoped_ptr<VideoDecoder> decoder,
MediaTimeProvider* media_time_provider,
scoped_ptr<VideoRenderAlgorithm> algorithm,
scoped_refptr<VideoRendererSink> sink);
~VideoRenderer();
- void Initialize(const ErrorCB& error_cb);
- void SetBounds(int z_index, int x, int y, int width, int height);
+ void Initialize(const ErrorCB& error_cb,
+ const PrerolledCB& prerolled_cb,
+ const EndedCB& ended_cb);
int GetDroppedFrames() const { return algorithm_->GetDroppedFrames(); }
void WriteSample(const scoped_refptr<InputBuffer>& input_buffer);
@@ -60,44 +63,57 @@
void Seek(SbTime seek_to_time);
- bool IsEndOfStreamWritten() const { return end_of_stream_written_; }
- bool IsEndOfStreamPlayed() const;
+ bool IsEndOfStreamWritten() const { return end_of_stream_written_.load(); }
bool CanAcceptMoreData() const;
- bool UpdateAndRetrieveIsSeekingInProgress();
+ // Both of the following two functions can be called on any threads.
+ void SetBounds(int z_index, int x, int y, int width, int height);
SbDecodeTarget GetCurrentDecodeTarget();
private:
typedef std::list<scoped_refptr<VideoFrame>> Frames;
+ // Both of the following two functions can be called on any threads.
void OnDecoderStatus(VideoDecoder::Status status,
const scoped_refptr<VideoFrame>& frame);
void Render(VideoRendererSink::DrawFrameCB draw_frame_cb);
-
- ThreadChecker thread_checker_;
- Mutex mutex_;
+ void OnSeekTimeout();
MediaTimeProvider* const media_time_provider_;
scoped_ptr<VideoRenderAlgorithm> algorithm_;
scoped_refptr<VideoRendererSink> sink_;
-
- bool seeking_;
- SbTimeMonotonic absolute_time_of_first_input_;
-
- Frames frames_;
-
- SbTime seeking_to_time_;
- bool end_of_stream_written_;
- bool need_more_input_;
-
scoped_ptr<VideoDecoder> decoder_;
+ PrerolledCB prerolled_cb_;
+ EndedCB ended_cb_;
+
+ SbTimeMonotonic absolute_time_of_first_input_ = 0;
// Our owner will attempt to seek to time 0 when playback begins. In
// general, seeking could require a full reset of the underlying decoder on
// some platforms, so we make an effort to improve playback startup
// performance by keeping track of whether we already have a fresh decoder,
// and can thus avoid doing a full reset.
- bool first_input_written_;
+ bool first_input_written_ = false;
+ atomic_bool end_of_stream_written_;
+ atomic_bool ended_cb_called_;
+
+ atomic_bool need_more_input_;
+ atomic_bool seeking_;
+ SbTime seeking_to_time_ = 0;
+
+ // |number_of_frames_| = decoder_frames_.size() + sink_frames_.size()
+ atomic_int32_t number_of_frames_;
+ // |sink_frames_| is locked inside VideoRenderer::Render() when calling
+ // algorithm_->Render(). So OnDecoderStatus() won't try to lock and append
+ // the decoded frames to |sink_frames_| directly to avoid being blocked. It
+ // will append newly decoded frames to |decoder_frames_| instead. Note that
+ // both |decoder_frames_| and |sink_frames_| can be used on multiple threads.
+ // When they are being modified at the same time, |decoder_frames_mutex_|
+ // should always be locked before |sink_frames_mutex_| to avoid deadlock.
+ Mutex decoder_frames_mutex_;
+ Frames decoder_frames_;
+ Mutex sink_frames_mutex_;
+ Frames sink_frames_;
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/wsola_internal.cc b/src/starboard/shared/starboard/player/filter/wsola_internal.cc
index 3742d0a..9ef8d22 100644
--- a/src/starboard/shared/starboard/player/filter/wsola_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/wsola_internal.cc
@@ -298,12 +298,10 @@
int OptimalIndex(const scoped_refptr<DecodedAudio>& search_block,
const scoped_refptr<DecodedAudio>& target_block,
- SbMediaAudioSampleType sample_type,
SbMediaAudioFrameStorageType storage_type,
Interval exclude_interval) {
int channels = search_block->channels();
SB_DCHECK(channels == target_block->channels());
- SB_DCHECK(sample_type == kSbMediaAudioSampleTypeFloat32);
SB_DCHECK(storage_type == kSbMediaAudioFrameStorageTypeInterleaved);
int target_size = target_block->frames();
diff --git a/src/starboard/shared/starboard/player/filter/wsola_internal.h b/src/starboard/shared/starboard/player/filter/wsola_internal.h
index c49dc0c..145aff1 100644
--- a/src/starboard/shared/starboard/player/filter/wsola_internal.h
+++ b/src/starboard/shared/starboard/player/filter/wsola_internal.h
@@ -43,7 +43,6 @@
// |exclude_interval| is an interval that is excluded from the search.
int OptimalIndex(const scoped_refptr<DecodedAudio>& search_block,
const scoped_refptr<DecodedAudio>& target_block,
- SbMediaAudioSampleType sample_type,
SbMediaAudioFrameStorageType storage_type,
Interval exclude_interval);
diff --git a/src/starboard/shared/starboard/player/job_queue.cc b/src/starboard/shared/starboard/player/job_queue.cc
index 077b885..24fcb40 100644
--- a/src/starboard/shared/starboard/player/job_queue.cc
+++ b/src/starboard/shared/starboard/player/job_queue.cc
@@ -18,6 +18,7 @@
#include "starboard/log.h"
#include "starboard/once.h"
+#include "starboard/system.h"
#include "starboard/thread.h"
namespace starboard {
@@ -62,11 +63,7 @@
} // namespace
-JobQueue::JobQueue()
- : thread_id_(SbThreadGetId()),
- condition_(mutex_),
- current_job_token_(JobToken::kInvalidToken + 1),
- stopped_(false) {
+JobQueue::JobQueue() : thread_id_(SbThreadGetId()), condition_(mutex_) {
SB_DCHECK(SbThreadIsValidId(thread_id_));
SetCurrentThreadJobQueue(this);
}
@@ -89,10 +86,10 @@
}
ScopedLock scoped_lock(mutex_);
- for (TimeToJobMap::iterator iter = time_to_job_map_.begin();
- iter != time_to_job_map_.end(); ++iter) {
+ for (TimeToJobRecordMap::iterator iter = time_to_job_record_map_.begin();
+ iter != time_to_job_record_map_.end(); ++iter) {
if (iter->second.job_token == job_token) {
- time_to_job_map_.erase(iter);
+ time_to_job_record_map_.erase(iter);
return;
}
}
@@ -102,7 +99,7 @@
{
ScopedLock scoped_lock(mutex_);
stopped_ = true;
- time_to_job_map_.clear();
+ time_to_job_record_map_.clear();
condition_.Signal();
}
}
@@ -150,16 +147,19 @@
JobToken job_token(current_job_token_);
JobRecord job_record = {job_token, job, owner};
-
+#if ENABLE_JOB_QUEUE_PROFILING
+ job_record.stack_size =
+ SbSystemGetStack(job_record.stack, kProfileStackDepth);
+#endif // ENABLE_JOB_QUEUE_PROFILING
ScopedLock scoped_lock(mutex_);
if (stopped_) {
return JobToken();
}
SbTimeMonotonic time_to_run_job = SbTimeGetMonotonicNow() + delay;
- bool is_first_job = time_to_job_map_.empty() ||
- time_to_run_job < time_to_job_map_.begin()->first;
+ bool is_first_job = time_to_job_record_map_.empty() ||
+ time_to_run_job < time_to_job_record_map_.begin()->first;
- time_to_job_map_.insert(std::make_pair(time_to_run_job, job_record));
+ time_to_job_record_map_.insert(std::make_pair(time_to_run_job, job_record));
if (is_first_job) {
condition_.Signal();
}
@@ -176,10 +176,10 @@
bool should_keep_running = true;
while (should_keep_running) {
should_keep_running = false;
- for (TimeToJobMap::iterator iter = time_to_job_map_.begin();
- iter != time_to_job_map_.end(); ++iter) {
+ for (TimeToJobRecordMap::iterator iter = time_to_job_record_map_.begin();
+ iter != time_to_job_record_map_.end(); ++iter) {
if (iter->second.owner == owner) {
- time_to_job_map_.erase(iter);
+ time_to_job_record_map_.erase(iter);
should_keep_running = true;
break;
}
@@ -190,26 +190,27 @@
bool JobQueue::TryToRunOneJob(bool wait_for_next_job) {
SB_DCHECK(BelongsToCurrentThread());
- Job job;
+ JobRecord job_record;
{
ScopedLock scoped_lock(mutex_);
if (stopped_) {
return false;
}
- if (time_to_job_map_.empty() && wait_for_next_job) {
+ if (time_to_job_record_map_.empty() && wait_for_next_job) {
// |kSbTimeMax| makes more sense here, but |kSbTimeDay| is much safer.
condition_.WaitTimed(kSbTimeDay);
}
- if (time_to_job_map_.empty()) {
+ if (time_to_job_record_map_.empty()) {
return false;
}
- TimeToJobMap::iterator first_delayed_job = time_to_job_map_.begin();
+ TimeToJobRecordMap::iterator first_delayed_job =
+ time_to_job_record_map_.begin();
SbTimeMonotonic delay = first_delayed_job->first - SbTimeGetMonotonicNow();
if (delay > 0) {
if (wait_for_next_job) {
condition_.WaitTimed(delay);
- if (time_to_job_map_.empty()) {
+ if (time_to_job_record_map_.empty()) {
return false;
}
} else {
@@ -218,17 +219,45 @@
}
// Try to retrieve the job again as the job map can be altered during the
// wait.
- first_delayed_job = time_to_job_map_.begin();
+ first_delayed_job = time_to_job_record_map_.begin();
delay = first_delayed_job->first - SbTimeGetMonotonicNow();
if (delay > 0) {
return false;
}
- job = first_delayed_job->second.job;
- time_to_job_map_.erase(first_delayed_job);
+ job_record = first_delayed_job->second;
+ time_to_job_record_map_.erase(first_delayed_job);
}
- SB_DCHECK(job);
- job();
+ SB_DCHECK(job_record.job);
+
+#if ENABLE_JOB_QUEUE_PROFILING
+ SbTimeMonotonic start = SbTimeGetMonotonicNow();
+#endif // ENABLE_JOB_QUEUE_PROFILING
+
+ job_record.job();
+
+#if ENABLE_JOB_QUEUE_PROFILING
+ auto now = SbTimeGetMonotonicNow();
+ auto elapsed = now - start;
+ if (elapsed > max_job_interval_) {
+ job_record_with_max_interval_ = job_record;
+ max_job_interval_ = elapsed;
+ }
+ if (now - last_reset_time_ > kProfileResetInterval) {
+ SB_LOG(INFO) << "================ Max job takes " << max_job_interval_;
+ for (int i = 0; i < job_record.stack_size; ++i) {
+ char function_name[1024];
+ if (SbSystemSymbolize(job_record.stack[i], function_name,
+ SB_ARRAY_SIZE_INT(function_name))) {
+ SB_LOG(INFO) << " " << function_name;
+ } else {
+ SB_LOG(INFO) << " " << job_record.stack[i];
+ }
+ }
+ last_reset_time_ = now;
+ max_job_interval_ = 0;
+ }
+#endif // ENABLE_JOB_QUEUE_PROFILING
return true;
}
diff --git a/src/starboard/shared/starboard/player/job_queue.h b/src/starboard/shared/starboard/player/job_queue.h
index ab736ef..55e1567 100644
--- a/src/starboard/shared/starboard/player/job_queue.h
+++ b/src/starboard/shared/starboard/player/job_queue.h
@@ -27,6 +27,11 @@
#error "Only C++ files can include this header."
#endif
+// Uncomment the following statement to enable JobQueue profiling, which will
+// log the stack trace of the job that takes the longest time to execute every a
+// while.
+// #define ENABLE_JOB_QUEUE_PROFILING 1
+
namespace starboard {
namespace shared {
namespace starboard {
@@ -57,12 +62,26 @@
class JobOwner {
protected:
+ enum DetachedState { kDetached };
+
+ explicit JobOwner(DetachedState detached_state) : job_queue_(NULL) {
+ SB_DCHECK(detached_state == kDetached);
+ }
explicit JobOwner(JobQueue* job_queue = JobQueue::current())
: job_queue_(job_queue) {
SB_DCHECK(job_queue);
}
~JobOwner() { CancelPendingJobs(); }
+ // Allow |JobOwner| created on another thread to run on the current thread
+ // if it is created with |kDetached|.
+ // Note that this operation is not thread safe. It is the caller's
+ // responsilibity to ensure that concurrency hasn't happened yet.
+ void AttachToCurrentThread() {
+ SB_DCHECK(job_queue_ == NULL);
+ job_queue_ = JobQueue::current();
+ }
+
bool BelongsToCurrentThread() const {
return job_queue_->BelongsToCurrentThread();
}
@@ -98,12 +117,22 @@
static JobQueue* current();
private:
+#if ENABLE_JOB_QUEUE_PROFILING
+ // Reset the max value periodically to catch all local peaks.
+ static const SbTime kProfileResetInterval = kSbTimeSecond;
+ static const int kProfileStackDepth = 10;
+#endif // ENABLE_JOB_QUEUE_PROFILING
+
struct JobRecord {
JobToken job_token;
Job job;
JobOwner* owner;
+#if ENABLE_JOB_QUEUE_PROFILING
+ void* stack[kProfileStackDepth];
+ int stack_size;
+#endif // ENABLE_JOB_QUEUE_PROFILING
};
- typedef std::multimap<SbTimeMonotonic, JobRecord> TimeToJobMap;
+ typedef std::multimap<SbTimeMonotonic, JobRecord> TimeToJobRecordMap;
JobToken Schedule(Job job, JobOwner* owner, SbTimeMonotonic delay);
void RemoveJobsByOwner(JobOwner* owner);
@@ -117,9 +146,15 @@
SbThreadId thread_id_;
Mutex mutex_;
ConditionVariable condition_;
- int64_t current_job_token_;
- TimeToJobMap time_to_job_map_;
- bool stopped_;
+ int64_t current_job_token_ = JobToken::kInvalidToken + 1;
+ TimeToJobRecordMap time_to_job_record_map_;
+ bool stopped_ = false;
+
+#if ENABLE_JOB_QUEUE_PROFILING
+ SbTimeMonotonic last_reset_time_ = SbTimeGetMonotonicNow();
+ JobRecord job_record_with_max_interval_;
+ SbTimeMonotonic max_job_interval_ = 0;
+#endif // ENABLE_JOB_QUEUE_PROFILING
};
} // namespace player
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc
index c6cedd9..39cfe3a 100644
--- a/src/starboard/shared/starboard/player/player_create.cc
+++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -22,9 +22,9 @@
#include "starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h"
#include "starboard/shared/starboard/player/player_internal.h"
#include "starboard/shared/starboard/player/player_worker.h"
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
#include "starboard/shared/starboard/player/video_dmp_writer.h"
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
using starboard::shared::media_session::
UpdateActiveSessionPlatformPlaybackState;
@@ -59,6 +59,14 @@
SB_UNREFERENCED_PARAMETER(duration_pts);
#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
+ if (!sample_deallocate_func || !decoder_status_func || !player_status_func
+#if SB_HAS(PLAYER_ERROR_MESSAGE)
+ || !player_error_func
+#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
+ ) {
+ return kSbPlayerInvalid;
+ }
+
const int64_t kDefaultBitRate = 0;
if (audio_codec != kSbMediaAudioCodecNone &&
!SbMediaIsAudioSupported(audio_codec, kDefaultBitRate)) {
@@ -69,7 +77,8 @@
const int kDefaultFrameWidth = 0;
const int kDefaultFrameHeight = 0;
const int kDefaultFrameRate = 0;
- if (!SbMediaIsVideoSupported(video_codec, kDefaultFrameWidth,
+ if (video_codec != kSbMediaVideoCodecNone &&
+ !SbMediaIsVideoSupported(video_codec, kDefaultFrameWidth,
kDefaultFrameHeight, kDefaultBitRate,
kDefaultFrameRate)) {
SB_LOG(ERROR) << "Unsupported video codec " << video_codec;
@@ -82,6 +91,13 @@
return kSbPlayerInvalid;
}
+ if (audio_codec == kSbMediaAudioCodecNone &&
+ video_codec == kSbMediaVideoCodecNone) {
+ SB_LOG(ERROR) << "SbPlayerCreate() requires at least one audio track or"
+ << " one video track.";
+ return kSbPlayerInvalid;
+ }
+
if (!SbPlayerOutputModeSupported(output_mode, video_codec, drm_system)) {
SB_LOG(ERROR) << "Unsupported player output mode " << output_mode;
return kSbPlayerInvalid;
@@ -93,18 +109,19 @@
new FilterBasedPlayerWorkerHandler(video_codec, audio_codec, drm_system,
audio_header, output_mode, provider));
- SbPlayer player = new SbPlayerPrivate(audio_codec, sample_deallocate_func,
- decoder_status_func, player_status_func,
+ SbPlayer player =
+ new SbPlayerPrivate(audio_codec, video_codec, sample_deallocate_func,
+ decoder_status_func, player_status_func,
#if SB_HAS(PLAYER_ERROR_MESSAGE)
- player_error_func,
+ player_error_func,
#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- context, handler.Pass());
+ context, handler.Pass());
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
VideoDmpWriter::OnPlayerCreate(player, video_codec, audio_codec, drm_system,
audio_header);
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
return player;
}
diff --git a/src/starboard/shared/starboard/player/player_destroy.cc b/src/starboard/shared/starboard/player/player_destroy.cc
index b383546..937dda7 100644
--- a/src/starboard/shared/starboard/player/player_destroy.cc
+++ b/src/starboard/shared/starboard/player/player_destroy.cc
@@ -16,9 +16,9 @@
#include "starboard/shared/media_session/playback_state.h"
#include "starboard/shared/starboard/player/player_internal.h"
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
#include "starboard/shared/starboard/player/video_dmp_writer.h"
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
using starboard::shared::media_session::kNone;
using starboard::shared::media_session::
@@ -30,10 +30,10 @@
}
UpdateActiveSessionPlatformPlaybackState(kNone);
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
VideoDmpWriter::OnPlayerDestroy(player);
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
delete player;
}
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index a1d778a..4932222 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -14,21 +14,31 @@
#include "starboard/shared/starboard/player/player_internal.h"
-#include "starboard/log.h"
+#include <functional>
-using starboard::shared::starboard::player::InputBuffer;
+#include "starboard/log.h"
namespace {
-SbTime GetMediaTime(SbTime media_time, SbTimeMonotonic media_time_update_time) {
+using starboard::shared::starboard::player::InputBuffer;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+
+SbTime GetMediaTime(SbTime media_time,
+ SbTimeMonotonic media_time_update_time,
+ double playback_rate) {
SbTimeMonotonic elapsed = SbTimeGetMonotonicNow() - media_time_update_time;
- return media_time + elapsed;
+ return media_time + static_cast<SbTime>(elapsed * playback_rate);
}
} // namespace
+int SbPlayerPrivate::number_of_players_ = 0;
+
SbPlayerPrivate::SbPlayerPrivate(
SbMediaAudioCodec audio_codec,
+ SbMediaVideoCodec video_codec,
SbPlayerDeallocateSampleFunc sample_deallocate_func,
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
@@ -41,24 +51,27 @@
context_(context),
ticket_(SB_PLAYER_INITIAL_TICKET),
media_time_(0),
- media_time_update_time_(SbTimeGetMonotonicNow()),
+ media_time_updated_at_(SbTimeGetMonotonicNow()),
frame_width_(0),
frame_height_(0),
- is_paused_(true),
+ is_paused_(false),
playback_rate_(1.0),
volume_(1.0),
total_video_frames_(0),
dropped_video_frames_(0),
- worker_(new PlayerWorker(this,
- audio_codec,
- player_worker_handler.Pass(),
- decoder_status_func,
- player_status_func,
+ worker_(new PlayerWorker(
+ audio_codec,
+ video_codec,
+ player_worker_handler.Pass(),
+ std::bind(&SbPlayerPrivate::UpdateMediaInfo, this, _1, _2, _3),
+ decoder_status_func,
+ player_status_func,
#if SB_HAS(PLAYER_ERROR_MESSAGE)
- player_error_func,
+ player_error_func,
#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- this,
- context)) {
+ this,
+ context)) {
+ ++number_of_players_;
}
void SbPlayerPrivate::Seek(SbTime seek_to_time, int ticket) {
@@ -66,7 +79,7 @@
starboard::ScopedLock lock(mutex_);
SB_DCHECK(ticket_ != ticket);
media_time_ = seek_to_time;
- media_time_update_time_ = SbTimeGetMonotonicNow();
+ media_time_updated_at_ = SbTimeGetMonotonicNow();
ticket_ = ticket;
}
@@ -119,7 +132,7 @@
out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME(media_time_);
} else {
out_player_info->current_media_pts = SB_TIME_TO_SB_MEDIA_TIME(
- GetMediaTime(media_time_, media_time_update_time_));
+ GetMediaTime(media_time_, media_time_updated_at_, playback_rate_));
}
#else // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
out_player_info->duration = SB_PLAYER_NO_DURATION;
@@ -127,7 +140,7 @@
out_player_info->current_media_timestamp = media_time_;
} else {
out_player_info->current_media_timestamp =
- GetMediaTime(media_time_, media_time_update_time_);
+ GetMediaTime(media_time_, media_time_updated_at_, playback_rate_);
}
#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
@@ -142,6 +155,7 @@
}
void SbPlayerPrivate::SetPause(bool pause) {
+ is_paused_ = pause;
worker_->SetPause(pause);
}
@@ -155,17 +169,15 @@
worker_->SetVolume(volume_);
}
-void SbPlayerPrivate::UpdateMediaTime(SbTime media_time, int ticket) {
+void SbPlayerPrivate::UpdateMediaInfo(SbTime media_time,
+ int dropped_video_frames,
+ int ticket) {
starboard::ScopedLock lock(mutex_);
if (ticket_ != ticket) {
return;
}
media_time_ = media_time;
- media_time_update_time_ = SbTimeGetMonotonicNow();
-}
-
-void SbPlayerPrivate::UpdateDroppedVideoFrames(int dropped_video_frames) {
- starboard::ScopedLock lock(mutex_);
+ media_time_updated_at_ = SbTimeGetMonotonicNow();
dropped_video_frames_ = dropped_video_frames;
}
diff --git a/src/starboard/shared/starboard/player/player_internal.h b/src/starboard/shared/starboard/player/player_internal.h
index c26ced5..29c041c 100644
--- a/src/starboard/shared/starboard/player/player_internal.h
+++ b/src/starboard/shared/starboard/player/player_internal.h
@@ -24,13 +24,13 @@
#include "starboard/time.h"
#include "starboard/window.h"
-struct SbPlayerPrivate
- : starboard::shared::starboard::player::PlayerWorker::Host {
+struct SbPlayerPrivate {
public:
typedef starboard::shared::starboard::player::PlayerWorker PlayerWorker;
SbPlayerPrivate(
SbMediaAudioCodec audio_codec,
+ SbMediaVideoCodec video_codec,
SbPlayerDeallocateSampleFunc sample_deallocate_func,
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
@@ -62,10 +62,12 @@
SbDecodeTarget GetCurrentDecodeTarget();
+ ~SbPlayerPrivate() { --number_of_players_; }
+
+ static int number_of_players() { return number_of_players_; }
+
private:
- // PlayerWorker::Host methods.
- void UpdateMediaTime(SbTime media_time, int ticket) override;
- void UpdateDroppedVideoFrames(int dropped_video_frames) override;
+ void UpdateMediaInfo(SbTime media_time, int dropped_video_frames, int ticket);
SbPlayerDeallocateSampleFunc sample_deallocate_func_;
void* context_;
@@ -73,7 +75,7 @@
starboard::Mutex mutex_;
int ticket_;
SbTime media_time_;
- SbTimeMonotonic media_time_update_time_;
+ SbTimeMonotonic media_time_updated_at_;
int frame_width_;
int frame_height_;
bool is_paused_;
@@ -83,6 +85,8 @@
int dropped_video_frames_;
starboard::scoped_ptr<PlayerWorker> worker_;
+
+ static int number_of_players_;
};
#endif // STARBOARD_SHARED_STARBOARD_PLAYER_PLAYER_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/player/player_set_bounds.cc b/src/starboard/shared/starboard/player/player_set_bounds.cc
index 0c0921c..27367bd 100644
--- a/src/starboard/shared/starboard/player/player_set_bounds.cc
+++ b/src/starboard/shared/starboard/player/player_set_bounds.cc
@@ -15,6 +15,7 @@
#include "starboard/player.h"
#include "starboard/log.h"
+#include "starboard/shared/starboard/application.h"
#include "starboard/shared/starboard/player/player_internal.h"
void SbPlayerSetBounds(SbPlayer player,
@@ -28,4 +29,6 @@
return;
}
player->SetBounds(z_index, x, y, width, height);
+ starboard::shared::starboard::Application::Get()->PlayerSetBounds(
+ player, z_index, x, y, width, height);
}
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index 7c050f2..0be4ba6 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -28,6 +28,9 @@
namespace {
+using std::placeholders::_1;
+using std::placeholders::_2;
+
// 8 ms is enough to ensure that DoWritePendingSamples() is called twice for
// every frame in HFR.
// TODO: Reduce this as there should be enough frames caches in the renderers.
@@ -45,9 +48,10 @@
} // namespace
-PlayerWorker::PlayerWorker(Host* host,
- SbMediaAudioCodec audio_codec,
+PlayerWorker::PlayerWorker(SbMediaAudioCodec audio_codec,
+ SbMediaVideoCodec video_codec,
scoped_ptr<Handler> handler,
+ UpdateMediaInfoCB update_media_info_cb,
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
#if SB_HAS(PLAYER_ERROR_MESSAGE)
@@ -56,9 +60,10 @@
SbPlayer player,
void* context)
: thread_(kSbThreadInvalid),
- host_(host),
audio_codec_(audio_codec),
+ video_codec_(video_codec),
handler_(handler.Pass()),
+ update_media_info_cb_(update_media_info_cb),
decoder_status_func_(decoder_status_func),
player_status_func_(player_status_func),
#if SB_HAS(PLAYER_ERROR_MESSAGE)
@@ -68,14 +73,17 @@
context_(context),
ticket_(SB_PLAYER_INITIAL_TICKET),
player_state_(kSbPlayerStateInitialized) {
- SB_DCHECK(host_ != NULL);
SB_DCHECK(handler_ != NULL);
+ SB_DCHECK(update_media_info_cb_);
ThreadParam thread_param(this);
thread_ = SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
"player_worker", &PlayerWorker::ThreadEntryPoint,
&thread_param);
- SB_DCHECK(SbThreadIsValid(thread_));
+ if (!SbThreadIsValid(thread_)) {
+ SB_DLOG(ERROR) << "Failed to create thread in PlayerWorker constructor.";
+ return;
+ }
ScopedLock scoped_lock(thread_param.mutex);
while (!job_queue_) {
thread_param.condition_variable.Wait();
@@ -93,9 +101,10 @@
// effects are gone.
}
-void PlayerWorker::UpdateMediaTime(SbTime time) {
- host_->UpdateMediaTime(time, ticket_);
+void PlayerWorker::UpdateMediaInfo(SbTime time, int dropped_video_frames) {
+ update_media_info_cb_(time, dropped_video_frames, ticket_);
}
+
void PlayerWorker::UpdatePlayerState(SbPlayerState player_state) {
#if SB_HAS(PLAYER_ERROR_MESSAGE)
SB_DCHECK(!error_occurred_) << "Player state should not update after error.";
@@ -157,14 +166,16 @@
void PlayerWorker::DoInit() {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
- if (handler_->Init(
- this, job_queue_.get(), player_, &PlayerWorker::UpdateMediaTime,
- &PlayerWorker::player_state, &PlayerWorker::UpdatePlayerState
+ Handler::UpdatePlayerErrorCB update_player_error_cb;
#if SB_HAS(PLAYER_ERROR_MESSAGE)
- ,
- &PlayerWorker::UpdatePlayerError
+ update_player_error_cb =
+ std::bind(&PlayerWorker::UpdatePlayerError, this, _1);
#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- )) {
+ if (handler_->Init(player_,
+ std::bind(&PlayerWorker::UpdateMediaInfo, this, _1, _2),
+ std::bind(&PlayerWorker::player_state, this),
+ std::bind(&PlayerWorker::UpdatePlayerState, this, _1),
+ update_player_error_cb)) {
UpdatePlayerState(kSbPlayerStateInitialized);
} else {
UpdatePlayerError("Failed to initialize PlayerWorker.");
@@ -198,7 +209,9 @@
if (audio_codec_ != kSbMediaAudioCodecNone) {
UpdateDecoderState(kSbMediaTypeAudio, kSbPlayerDecoderStateNeedsData);
}
- UpdateDecoderState(kSbMediaTypeVideo, kSbPlayerDecoderStateNeedsData);
+ if (video_codec_ != kSbMediaVideoCodecNone) {
+ UpdateDecoderState(kSbMediaTypeVideo, kSbPlayerDecoderStateNeedsData);
+ }
}
void PlayerWorker::DoWriteSample(
@@ -222,6 +235,7 @@
SB_DCHECK(audio_codec_ != kSbMediaAudioCodecNone);
SB_DCHECK(!pending_audio_buffer_);
} else {
+ SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
SB_DCHECK(!pending_video_buffer_);
}
bool written;
@@ -257,6 +271,7 @@
DoWriteSample(common::ResetAndReturn(&pending_audio_buffer_));
}
if (pending_video_buffer_) {
+ SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
DoWriteSample(common::ResetAndReturn(&pending_video_buffer_));
}
}
@@ -281,6 +296,7 @@
SB_DCHECK(audio_codec_ != kSbMediaAudioCodecNone);
SB_DCHECK(!pending_audio_buffer_);
} else {
+ SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
SB_DCHECK(!pending_video_buffer_);
}
@@ -321,7 +337,11 @@
SB_DCHECK(job_queue_->BelongsToCurrentThread());
handler_->Stop();
- UpdatePlayerState(kSbPlayerStateDestroyed);
+ handler_.reset();
+
+ if (!error_occurred_) {
+ UpdatePlayerState(kSbPlayerStateDestroyed);
+ }
job_queue_->StopSoon();
}
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index 14f1936..c2b3b7f 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -43,14 +43,9 @@
// they needn't maintain the thread and queue internally.
class PlayerWorker {
public:
- class Host {
- public:
- virtual void UpdateMediaTime(SbTime media_time, int ticket) = 0;
- virtual void UpdateDroppedVideoFrames(int dropped_video_frames) = 0;
-
- protected:
- virtual ~Host() {}
- };
+ typedef std::function<
+ void(SbTime media_time, int dropped_video_frames, int ticket)>
+ UpdateMediaInfoCB;
struct Bounds {
int z_index;
@@ -63,27 +58,20 @@
// All functions of this class will be called from the JobQueue thread.
class Handler {
public:
- typedef void (PlayerWorker::*UpdateMediaTimeCB)(SbTime media_time);
- typedef SbPlayerState (PlayerWorker::*GetPlayerStateCB)() const;
- typedef void (PlayerWorker::*UpdatePlayerStateCB)(
- SbPlayerState player_state);
- typedef void (PlayerWorker::*UpdatePlayerErrorCB)(
- const std::string& message);
+ typedef std::function<void(SbTime media_time, int dropped_video_frames)>
+ UpdateMediaInfoCB;
+ typedef std::function<SbPlayerState()> GetPlayerStateCB;
+ typedef std::function<void(SbPlayerState player_state)> UpdatePlayerStateCB;
+ typedef std::function<void(const std::string& message)> UpdatePlayerErrorCB;
virtual ~Handler() {}
// All the following functions return false to signal a fatal error. The
// event processing loop in PlayerWorker will termimate in this case.
- virtual bool Init(PlayerWorker* player_worker,
- JobQueue* job_queue,
- SbPlayer player,
- UpdateMediaTimeCB update_media_time_cb,
+ virtual bool Init(SbPlayer player,
+ UpdateMediaInfoCB update_media_info_cb,
GetPlayerStateCB get_player_state_cb,
- UpdatePlayerStateCB update_player_state_cb
-#if SB_HAS(PLAYER_ERROR_MESSAGE)
- ,
- UpdatePlayerErrorCB update_player_error_cb
-#endif // SB_HAS(PLAYER_ERROR_MESSAGE)
- ) = 0;
+ UpdatePlayerStateCB update_player_state_cb,
+ UpdatePlayerErrorCB update_player_error_cb) = 0;
virtual bool Seek(SbTime seek_to_time, int ticket) = 0;
virtual bool WriteSample(const scoped_refptr<InputBuffer>& input_buffer,
bool* written) = 0;
@@ -102,9 +90,10 @@
virtual SbDecodeTarget GetCurrentDecodeTarget() = 0;
};
- PlayerWorker(Host* host,
- SbMediaAudioCodec audio_codec,
+ PlayerWorker(SbMediaAudioCodec audio_codec,
+ SbMediaVideoCodec video_codec,
scoped_ptr<Handler> handler,
+ UpdateMediaInfoCB update_media_info_cb,
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
#if SB_HAS(PLAYER_ERROR_MESSAGE)
@@ -158,16 +147,12 @@
job_queue_->Schedule(std::bind(&PlayerWorker::DoSetVolume, this, volume));
}
- void UpdateDroppedVideoFrames(int dropped_video_frames) {
- host_->UpdateDroppedVideoFrames(dropped_video_frames);
- }
-
SbDecodeTarget GetCurrentDecodeTarget() {
return handler_->GetCurrentDecodeTarget();
}
private:
- void UpdateMediaTime(SbTime time);
+ void UpdateMediaInfo(SbTime time, int dropped_video_frames);
SbPlayerState player_state() const { return player_state_; }
void UpdatePlayerState(SbPlayerState player_state);
@@ -191,9 +176,10 @@
SbThread thread_;
scoped_ptr<JobQueue> job_queue_;
- Host* host_;
SbMediaAudioCodec audio_codec_;
+ SbMediaVideoCodec video_codec_;
scoped_ptr<Handler> handler_;
+ UpdateMediaInfoCB update_media_info_cb_;
SbPlayerDecoderStatusFunc decoder_status_func_;
SbPlayerStatusFunc player_status_func_;
diff --git a/src/starboard/shared/starboard/player/player_write_sample.cc b/src/starboard/shared/starboard/player/player_write_sample.cc
index 3fa2129..8176f47 100644
--- a/src/starboard/shared/starboard/player/player_write_sample.cc
+++ b/src/starboard/shared/starboard/player/player_write_sample.cc
@@ -16,9 +16,9 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
#include "starboard/shared/starboard/player/video_dmp_writer.h"
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
void SbPlayerWriteSample(SbPlayer player,
@@ -57,13 +57,13 @@
SbTime sample_timestamp = SB_MEDIA_TIME_TO_SB_TIME(sample_pts);
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
VideoDmpWriter::OnPlayerWriteSample(
player, sample_type, sample_buffers, sample_buffer_sizes,
number_of_sample_buffers, sample_timestamp, video_sample_info,
sample_drm_info);
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
player->WriteSample(sample_type, sample_buffers, sample_buffer_sizes,
number_of_sample_buffers, sample_timestamp,
diff --git a/src/starboard/shared/starboard/player/player_write_sample2.cc b/src/starboard/shared/starboard/player/player_write_sample2.cc
index 17dab34..4b95825 100644
--- a/src/starboard/shared/starboard/player/player_write_sample2.cc
+++ b/src/starboard/shared/starboard/player/player_write_sample2.cc
@@ -16,9 +16,9 @@
#include "starboard/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
#include "starboard/shared/starboard/player/video_dmp_writer.h"
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
#if SB_API_VERSION >= SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
void SbPlayerWriteSample2(SbPlayer player,
@@ -50,13 +50,13 @@
return;
}
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
VideoDmpWriter::OnPlayerWriteSample(
player, sample_type, sample_buffers, sample_buffer_sizes,
number_of_sample_buffers, sample_timestamp, video_sample_info,
sample_drm_info);
-#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER
+#endif // SB_PLAYER_ENABLE_VIDEO_DUMPER && SB_HAS(PLAYER_FILTER_TESTS)
player->WriteSample(sample_type, sample_buffers, sample_buffer_sizes,
number_of_sample_buffers, sample_timestamp,
diff --git a/src/starboard/shared/starboard/player/video_dmp_common.cc b/src/starboard/shared/starboard/player/video_dmp_common.cc
index c729bd1..212bb50 100644
--- a/src/starboard/shared/starboard/player/video_dmp_common.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_common.cc
@@ -16,6 +16,7 @@
#include <limits>
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -99,8 +100,15 @@
Read(read_cb, reverse_byte_order, &audio_header->audio_specific_config_size);
audio_header->stored_audio_specific_config.resize(
audio_header->audio_specific_config_size);
+#if SB_API_VERSION >= 6
audio_header->audio_specific_config =
audio_header->stored_audio_specific_config.data();
+#else
+ SB_DCHECK(8 >= audio_header->stored_audio_specific_config.size());
+ SbMemoryCopy(audio_header->audio_specific_config,
+ audio_header->stored_audio_specific_config.data(),
+ audio_header->stored_audio_specific_config.size());
+#endif
Read(read_cb, audio_header->stored_audio_specific_config.data(),
audio_header->audio_specific_config_size);
}
@@ -261,3 +269,4 @@
} // namespace starboard
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/video_dmp_common.h b/src/starboard/shared/starboard/player/video_dmp_common.h
index 20a10a3..0b4b97e 100644
--- a/src/starboard/shared/starboard/player/video_dmp_common.h
+++ b/src/starboard/shared/starboard/player/video_dmp_common.h
@@ -21,7 +21,9 @@
#include "starboard/log.h"
#include "starboard/media.h"
+#include "starboard/memory.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -70,7 +72,13 @@
SbMediaAudioHeaderWithConfig(const SbMediaAudioHeaderWithConfig& that)
: SbMediaAudioHeader(that),
stored_audio_specific_config(that.stored_audio_specific_config) {
+#if SB_API_VERSION >= 6
audio_specific_config = stored_audio_specific_config.data();
+#else
+ SB_DCHECK(8 >= stored_audio_specific_config.size());
+ SbMemoryCopy(audio_specific_config, stored_audio_specific_config.data(),
+ stored_audio_specific_config.size());
+#endif
}
void operator=(const SbMediaAudioHeaderWithConfig& that) = delete;
@@ -161,4 +169,5 @@
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
#endif // STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_DMP_COMMON_H_
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.cc b/src/starboard/shared/starboard/player/video_dmp_reader.cc
index a73425c..88e8b49 100644
--- a/src/starboard/shared/starboard/player/video_dmp_reader.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_reader.cc
@@ -16,6 +16,7 @@
#include <functional>
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -213,3 +214,4 @@
} // namespace starboard
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.h b/src/starboard/shared/starboard/player/video_dmp_reader.h
index e3674aa..ad09d05 100644
--- a/src/starboard/shared/starboard/player/video_dmp_reader.h
+++ b/src/starboard/shared/starboard/player/video_dmp_reader.h
@@ -25,6 +25,7 @@
#include "starboard/shared/starboard/player/input_buffer_internal.h"
#include "starboard/shared/starboard/player/video_dmp_common.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -122,4 +123,5 @@
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
#endif // STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_DMP_READER_H_
diff --git a/src/starboard/shared/starboard/player/video_dmp_writer.cc b/src/starboard/shared/starboard/player/video_dmp_writer.cc
index 66338b4..520be83 100644
--- a/src/starboard/shared/starboard/player/video_dmp_writer.cc
+++ b/src/starboard/shared/starboard/player/video_dmp_writer.cc
@@ -24,6 +24,7 @@
#include "starboard/shared/starboard/application.h"
#include "starboard/string.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -201,3 +202,4 @@
} // namespace starboard
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
diff --git a/src/starboard/shared/starboard/player/video_dmp_writer.h b/src/starboard/shared/starboard/player/video_dmp_writer.h
index 23d5628..05463cd 100644
--- a/src/starboard/shared/starboard/player/video_dmp_writer.h
+++ b/src/starboard/shared/starboard/player/video_dmp_writer.h
@@ -20,6 +20,7 @@
#include "starboard/player.h"
#include "starboard/shared/starboard/player/video_dmp_common.h"
+#if SB_HAS(PLAYER_FILTER_TESTS)
namespace starboard {
namespace shared {
namespace starboard {
@@ -72,4 +73,5 @@
} // namespace shared
} // namespace starboard
+#endif // SB_HAS(PLAYER_FILTER_TESTS)
#endif // STARBOARD_SHARED_STARBOARD_PLAYER_VIDEO_DMP_WRITER_H_
diff --git a/src/starboard/shared/stub/atomic_public.h b/src/starboard/shared/stub/atomic_public.h
index fcafcdd..b500fbd 100644
--- a/src/starboard/shared/stub/atomic_public.h
+++ b/src/starboard/shared/stub/atomic_public.h
@@ -110,6 +110,30 @@
return 0;
}
+#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+SB_C_FORCE_INLINE SbAtomic8
+SbAtomicRelease_CompareAndSwap8(volatile SbAtomic8* ptr,
+ SbAtomic8 old_value,
+ SbAtomic8 new_value) {
+ SB_UNREFERENCED_PARAMETER(ptr);
+ SB_UNREFERENCED_PARAMETER(old_value);
+ SB_UNREFERENCED_PARAMETER(new_value);
+ return 0;
+}
+
+SB_C_FORCE_INLINE void
+SbAtomicNoBarrier_Store8(volatile SbAtomic8* ptr, SbAtomic8 value) {
+ SB_UNREFERENCED_PARAMETER(ptr);
+ SB_UNREFERENCED_PARAMETER(value);
+}
+
+SB_C_FORCE_INLINE SbAtomic8
+SbAtomicNoBarrier_Load8(volatile const SbAtomic8* ptr) {
+ SB_UNREFERENCED_PARAMETER(ptr);
+ return 0;
+}
+#endif
+
// 64-bit atomic operations (only available on 64-bit processors).
#if SB_HAS(64_BIT_ATOMICS)
SB_C_FORCE_INLINE SbAtomic64
diff --git a/src/starboard/shared/stub/drm_create_system.cc b/src/starboard/shared/stub/drm_create_system.cc
index 1bfb425..38cf4c1 100644
--- a/src/starboard/shared/stub/drm_create_system.cc
+++ b/src/starboard/shared/stub/drm_create_system.cc
@@ -14,7 +14,27 @@
#include "starboard/drm.h"
-#if SB_HAS(DRM_SESSION_CLOSED)
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+SbDrmSystem SbDrmCreateSystem(
+ const char* key_system,
+ void* context,
+ SbDrmSessionUpdateRequestFunc update_request_callback,
+ SbDrmSessionUpdatedFunc session_updated_callback,
+ SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
+ SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
+ SbDrmSessionClosedFunc session_closed_callback) {
+ SB_UNREFERENCED_PARAMETER(context);
+ SB_UNREFERENCED_PARAMETER(key_system);
+ SB_UNREFERENCED_PARAMETER(update_request_callback);
+ SB_UNREFERENCED_PARAMETER(session_updated_callback);
+ SB_UNREFERENCED_PARAMETER(key_statuses_changed_callback);
+ SB_UNREFERENCED_PARAMETER(server_certificate_updated_callback);
+ SB_UNREFERENCED_PARAMETER(session_closed_callback);
+ return kSbDrmSystemInvalid;
+}
+
+#elif SB_HAS(DRM_SESSION_CLOSED)
SbDrmSystem SbDrmCreateSystem(
const char* key_system,
diff --git a/src/starboard/shared/stub/drm_is_server_certificate_updatable.cc b/src/starboard/shared/stub/drm_is_server_certificate_updatable.cc
new file mode 100644
index 0000000..cd9cff5
--- /dev/null
+++ b/src/starboard/shared/stub/drm_is_server_certificate_updatable.cc
@@ -0,0 +1,25 @@
+// Copyright 2018 Google Inc. 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 "starboard/drm.h"
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+bool SbDrmIsServerCertificateUpdatable(SbDrmSystem drm_system) {
+ SB_UNREFERENCED_PARAMETER(drm_system);
+
+ return false;
+}
+
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
diff --git a/src/starboard/shared/stub/drm_update_server_certificate.cc b/src/starboard/shared/stub/drm_update_server_certificate.cc
new file mode 100644
index 0000000..802340b
--- /dev/null
+++ b/src/starboard/shared/stub/drm_update_server_certificate.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 Google Inc. 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 "starboard/drm.h"
+
+#if SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
+
+void SbDrmUpdateServerCertificate(SbDrmSystem drm_system,
+ int ticket,
+ const void* certificate,
+ int certificate_size) {
+ SB_UNREFERENCED_PARAMETER(drm_system);
+ SB_UNREFERENCED_PARAMETER(ticket);
+ SB_UNREFERENCED_PARAMETER(certificate);
+ SB_UNREFERENCED_PARAMETER(certificate_size);
+}
+
+#endif // SB_API_VERSION >= SB_DRM_REFINEMENT_API_VERSION
diff --git a/src/starboard/shared/stub/memory_protect.cc b/src/starboard/shared/stub/memory_protect.cc
new file mode 100644
index 0000000..5f38701
--- /dev/null
+++ b/src/starboard/shared/stub/memory_protect.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 Google Inc. 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 "starboard/configuration.h"
+#include "starboard/memory.h"
+
+#if SB_API_VERSION >= SB_MEMORY_PROTECT_API_VERSION
+bool SbMemoryProtect(void* /*virtual_address*/,
+ int64_t /*size_bytes*/,
+ int /*flags*/) {
+ return false;
+}
+#endif
diff --git a/src/starboard/shared/uwp/analog_thumbstick_input.cc b/src/starboard/shared/uwp/analog_thumbstick_input.cc
deleted file mode 100644
index 202a12d..0000000
--- a/src/starboard/shared/uwp/analog_thumbstick_input.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2017 Google Inc. 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 "starboard/shared/uwp/analog_thumbstick_input.h"
-
-#include <Windows.Gaming.Input.h>
-#include <algorithm>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/types.h"
-
-#pragma comment(lib, "xinput9_1_0.lib")
-
-using Windows::Foundation::Collections::IVectorView;
-using Windows::Gaming::Input::Gamepad;
-using Windows::Gaming::Input::GamepadReading;
-using Windows::Gaming::Input::RawGameController;
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-namespace {
-
-const int kMaxPlayerCounter = 4;
-const float kDeadZoneThreshold = .24f;
-
-float ApplyLinearDeadZone(float value, float maxValue, float deadZoneSize) {
- if (value < -deadZoneSize) {
- // Increase negative values to remove the deadzone discontinuity.
- value += deadZoneSize;
- } else if (value > deadZoneSize) {
- // Decrease positive values to remove the deadzone discontinuity.
- value -= deadZoneSize;
- } else {
- // Values inside the deadzone come out zero.
- return 0;
- }
-
- // Scale into 0-1 range.
- float scaledValue = value / (maxValue - deadZoneSize);
- return std::max(-1.f, std::min(scaledValue, 1.f));
-}
-
-void ApplyStickDeadZone(float x,
- float y,
- float max_value,
- float dead_zone_size,
- float* result_x,
- float* result_y) {
- *result_x = ApplyLinearDeadZone(x, max_value, dead_zone_size);
- *result_y = ApplyLinearDeadZone(y, max_value, dead_zone_size);
-}
-
-ThumbSticks ReadThumbStick(Gamepad ^ controller) {
- ThumbSticks output;
- GamepadReading reading = controller->GetCurrentReading();
-
- ApplyStickDeadZone(static_cast<float>(reading.LeftThumbstickX),
- static_cast<float>(reading.LeftThumbstickY), 1.f,
- kDeadZoneThreshold, &output.left_x, &output.left_y);
-
- ApplyStickDeadZone(static_cast<float>(reading.RightThumbstickX),
- static_cast<float>(reading.RightThumbstickY), 1.f,
- kDeadZoneThreshold, &output.right_x, &output.right_y);
- return output;
-}
-} // namespace
-
-void GetGamepadThumbSticks(std::vector<ThumbSticks>* destination) {
- destination->erase(destination->begin(), destination->end());
-
- // This profiled to an average time of 33us to execute.
- IVectorView<Gamepad ^> ^ gamepads = Gamepad::Gamepads;
-
- // This profiled to take on average 9us of time to read controller state.
- const uint32_t n = gamepads->Size;
- for (uint32_t i = 0; i < n; ++i) {
- Gamepad ^ gamepad = gamepads->GetAt(i);
- ThumbSticks thumb_stick = ReadThumbStick(gamepad);
- destination->push_back(thumb_stick);
- }
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/analog_thumbstick_input.h b/src/starboard/shared/uwp/analog_thumbstick_input.h
deleted file mode 100644
index 3af0d42..0000000
--- a/src/starboard/shared/uwp/analog_thumbstick_input.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2017 Google Inc. 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_UWP_ANALOG_THUMBSTICK_INPUT_H_
-#define STARBOARD_SHARED_UWP_ANALOG_THUMBSTICK_INPUT_H_
-
-#include <vector>
-
-#include "starboard/types.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-struct ThumbSticks {
- float left_x = 0.0f;
- float left_y = 0.0f;
- float right_x = 0.0f;
- float right_y = 0.0f;
-};
-
-// Reads all connected game pads and stores the joystick states in the
-// destination vector. Note that the destination vector is unconditionally
-// cleared before being populated.
-void GetGamepadThumbSticks(std::vector<ThumbSticks>* destination);
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_ANALOG_THUMBSTICK_INPUT_H_
diff --git a/src/starboard/shared/uwp/analog_thumbstick_input_thread.cc b/src/starboard/shared/uwp/analog_thumbstick_input_thread.cc
deleted file mode 100644
index 6405e6a..0000000
--- a/src/starboard/shared/uwp/analog_thumbstick_input_thread.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2017 Google Inc. 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 "starboard/shared/uwp/analog_thumbstick_input_thread.h"
-
-#include <algorithm>
-#include <map>
-#include <vector>
-
-#include "starboard/common/thread.h"
-#include "starboard/double.h"
-#include "starboard/shared/uwp/analog_thumbstick_input.h"
-#include "starboard/thread.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-class AnalogThumbstickThread::Impl : public Thread {
- public:
- explicit Impl(Callback* cb) : Thread("AnalogGamepad"), callback_(cb) {
- stick_is_centered_[kSbKeyGamepadLeftStickLeft] = true;
- stick_is_centered_[kSbKeyGamepadRightStickLeft] = true;
- stick_is_centered_[kSbKeyGamepadLeftStickUp] = true;
- stick_is_centered_[kSbKeyGamepadRightStickUp] = true;
-
- Thread::Start();
- }
- ~Impl() { Thread::Join(); }
-
- void Run() override {
- while (!join_called()) {
- Update();
- // 120hz to provide smooth 60fps playback.
- SbThreadSleep(kSbTimeSecond / kPollingFrequency);
- }
- }
-
- void Update() {
- ThumbSticks thumb_state = GetCombinedThumbStickState();
-
- FireEventIfNecessary(kSbKeyGamepadLeftStickLeft, thumb_state.left_x);
- FireEventIfNecessary(kSbKeyGamepadLeftStickUp, thumb_state.left_y);
- FireEventIfNecessary(kSbKeyGamepadRightStickLeft, thumb_state.right_x);
- FireEventIfNecessary(kSbKeyGamepadRightStickUp, thumb_state.right_y);
- }
-
- void FireEventIfNecessary(SbKey sb_key, float value) {
- if (value == 0.0f) {
- if (stick_is_centered_[sb_key]) {
- // The previous stick input is in center position, so it is not
- // necessary to inject another center input event.
- return;
- }
- stick_is_centered_[sb_key] = true;
- } else {
- stick_is_centered_[sb_key] = false;
- }
-
- SbInputVector input_vector = {0, 0};
-
- switch (sb_key) {
- case kSbKeyGamepadRightStickLeft:
- case kSbKeyGamepadLeftStickLeft: {
- input_vector.x = value;
- break;
- }
- case kSbKeyGamepadRightStickUp:
- case kSbKeyGamepadLeftStickUp: {
- input_vector.y = -value;
- break;
- }
- default: {
- SB_NOTREACHED();
- break;
- }
- }
- callback_->OnJoystickUpdate(sb_key, input_vector);
- }
-
- ThumbSticks GetCombinedThumbStickState() {
- ThumbSticks all_thumb_state;
- GetGamepadThumbSticks(&thumb_sticks_);
-
- for (int i = 0; i < thumb_sticks_.size(); ++i) {
- ThumbSticks thumb_state = thumb_sticks_[i];
-
- all_thumb_state.left_x += thumb_state.left_x;
- all_thumb_state.left_y += thumb_state.left_y;
- all_thumb_state.right_x += thumb_state.right_x;
- all_thumb_state.right_y += thumb_state.right_y;
- }
-
- all_thumb_state.left_x = ClampToZeroOne(all_thumb_state.left_x);
- all_thumb_state.left_y = ClampToZeroOne(all_thumb_state.left_y);
- all_thumb_state.right_x = ClampToZeroOne(all_thumb_state.right_x);
- all_thumb_state.right_y = ClampToZeroOne(all_thumb_state.right_y);
-
- return all_thumb_state;
- }
-
- static float ClampToZeroOne(float in) {
- return std::max(-1.0f, std::min(1.0f, in));
- }
- Callback* callback_;
- std::map<SbKey, bool> stick_is_centered_;
- std::vector<ThumbSticks> thumb_sticks_;
-};
-
-AnalogThumbstickThread::AnalogThumbstickThread(Callback* cb) {
- impl_.reset(new Impl(cb));
-}
-
-AnalogThumbstickThread::~AnalogThumbstickThread() {
- impl_.reset(nullptr);
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/analog_thumbstick_input_thread.h b/src/starboard/shared/uwp/analog_thumbstick_input_thread.h
deleted file mode 100644
index e5bbefa..0000000
--- a/src/starboard/shared/uwp/analog_thumbstick_input_thread.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2017 Google Inc. 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_UWP_ANALOG_THUMBSTICK_INPUT_THREAD_H_
-#define STARBOARD_SHARED_UWP_ANALOG_THUMBSTICK_INPUT_THREAD_H_
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/configuration.h"
-#include "starboard/input.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-// This class represents a thread that will poll all gamepads for
-// the analog sticks. The sticks are summed together and then
-// the callback will be invoked with the thumbstick values.
-class AnalogThumbstickThread {
- public:
- enum { kPollingFrequency = 120 }; // Smooth playback for 60fps.
-
- class Callback {
- public:
- virtual ~Callback() {}
- virtual void OnJoystickUpdate(SbKey key, SbInputVector position) = 0;
- };
-
- explicit AnalogThumbstickThread(Callback* cb);
- ~AnalogThumbstickThread();
-
- private:
- SB_DISALLOW_COPY_AND_ASSIGN(AnalogThumbstickThread);
- class Impl;
- scoped_ptr<Impl> impl_;
-};
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_ANALOG_THUMBSTICK_INPUT_THREAD_H_
diff --git a/src/starboard/shared/uwp/app_accessors.h b/src/starboard/shared/uwp/app_accessors.h
deleted file mode 100644
index 8ba623a..0000000
--- a/src/starboard/shared/uwp/app_accessors.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2017 Google Inc. 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_UWP_APP_ACCESSORS_H_
-#define STARBOARD_SHARED_UWP_APP_ACCESSORS_H_
-
-// A set of application and main-thread accessors
-// so as to avoid including application_uwp.h
-
-#include <agile.h>
-#include <ppltasks.h>
-
-#include <string>
-
-#include "starboard/key.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-// Returns the main window's CoreDispatcher.
-Platform::Agile<Windows::UI::Core::CoreDispatcher> GetDispatcher();
-
-// Returns the main window's SystemMediaTransportControls.
-Platform::Agile<Windows::Media::SystemMediaTransportControls>
- GetTransportControls();
-
-// Asks the screen to remain active via
-// Windows::System::DisplayRequest->RequestActive()
-void DisplayRequestActive();
-
-// Releases previous screen active request via
-// Windows::System::DisplayRequest->RequestRelease()
-void DisplayRequestRelease();
-
-// Schedules a lambda to run on the main thread and returns immediately.
-template<typename T>
-void RunInMainThreadAsync(const T& lambda) {
- GetDispatcher()->RunAsync(
- Windows::UI::Core::CoreDispatcherPriority::Normal,
- ref new Windows::UI::Core::DispatchedHandler(lambda));
-}
-
-// Tries to fetch an SSO token, returning nullptr or exception on failure
-concurrency::task<
- Windows::Security::Authentication::Web::Core::WebTokenRequestResult^>
- TryToFetchSsoToken(const std::string& url);
-
-void InjectKeypress(SbKey key);
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-#endif // STARBOARD_SHARED_UWP_APP_ACCESSORS_H_
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc
deleted file mode 100644
index 76da8a2..0000000
--- a/src/starboard/shared/uwp/application_uwp.cc
+++ /dev/null
@@ -1,912 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/uwp/application_uwp.h"
-
-#include <D3D11.h>
-#include <WinSock2.h>
-#include <mfapi.h>
-#include <ppltasks.h>
-#include <windows.h>
-#include <windows.system.display.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "starboard/event.h"
-#include "starboard/file.h"
-#include "starboard/input.h"
-#include "starboard/log.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/starboard/application.h"
-#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
-#include "starboard/shared/starboard/net_log.h"
-#include "starboard/shared/uwp/analog_thumbstick_input_thread.h"
-#include "starboard/shared/uwp/app_accessors.h"
-#include "starboard/shared/uwp/async_utils.h"
-#include "starboard/shared/uwp/log_file_impl.h"
-#include "starboard/shared/uwp/watchdog_log.h"
-#include "starboard/shared/uwp/window_internal.h"
-#include "starboard/shared/win32/thread_private.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-#include "starboard/system.h"
-
-namespace sbuwp = starboard::shared::uwp;
-namespace sbwin32 = starboard::shared::win32;
-
-using Microsoft::WRL::ComPtr;
-using starboard::shared::starboard::Application;
-using starboard::shared::starboard::CommandLine;
-using starboard::shared::starboard::kNetLogCommandSwitchWait;
-using starboard::shared::starboard::NetLogFlushThenClose;
-using starboard::shared::starboard::NetLogWaitForClientConnected;
-using starboard::shared::uwp::ApplicationUwp;
-using starboard::shared::uwp::RunInMainThreadAsync;
-using starboard::shared::uwp::WaitForResult;
-using starboard::shared::win32::platformStringToString;
-using starboard::shared::win32::stringToPlatformString;
-using starboard::shared::win32::wchar_tToUTF8;
-using Windows::ApplicationModel::Activation::ActivationKind;
-using Windows::ApplicationModel::Activation::DialReceiverActivatedEventArgs;
-using Windows::ApplicationModel::Activation::IActivatedEventArgs;
-using Windows::ApplicationModel::Activation::IProtocolActivatedEventArgs;
-using Windows::ApplicationModel::Core::CoreApplication;
-using Windows::ApplicationModel::Core::CoreApplicationView;
-using Windows::ApplicationModel::Core::IFrameworkView;
-using Windows::ApplicationModel::Core::IFrameworkViewSource;
-using Windows::ApplicationModel::SuspendingEventArgs;
-using Windows::Foundation::Collections::IVectorView;
-using Windows::Foundation::EventHandler;
-using Windows::Foundation::IAsyncOperation;
-using Windows::Foundation::Metadata::ApiInformation;
-using Windows::Foundation::TimeSpan;
-using Windows::Foundation::TypedEventHandler;
-using Windows::Foundation::Uri;
-using Windows::Graphics::Display::Core::HdmiDisplayInformation;
-using Windows::Graphics::Display::DisplayInformation;
-using Windows::Globalization::Calendar;
-using Windows::Media::Protection::HdcpProtection;
-using Windows::Media::Protection::HdcpSession;
-using Windows::Media::Protection::HdcpSetProtectionResult;
-using Windows::Security::Authentication::Web::Core::WebTokenRequestResult;
-using Windows::Security::Authentication::Web::Core::WebTokenRequestStatus;
-using Windows::Security::Credentials::WebAccountProvider;
-using Windows::Storage::FileAttributes;
-using Windows::Storage::KnownFolders;
-using Windows::Storage::StorageFolder;
-using Windows::Storage::StorageFile;
-using Windows::System::Threading::ThreadPoolTimer;
-using Windows::System::Threading::TimerElapsedHandler;
-using Windows::System::UserAuthenticationStatus;
-using Windows::UI::Core::CoreDispatcherPriority;
-using Windows::UI::Core::CoreProcessEventsOption;
-using Windows::UI::Core::CoreWindow;
-using Windows::UI::Core::DispatchedHandler;
-using Windows::UI::Core::KeyEventArgs;
-using Windows::UI::ViewManagement::ApplicationView;
-
-namespace {
-
-// Per Microsoft, HdcpProtection::On means HDCP 1.x required.
-const HdcpProtection kHDCPProtectionMode = HdcpProtection::On;
-
-const int kWinSockVersionMajor = 2;
-const int kWinSockVersionMinor = 2;
-
-const char kDialParamPrefix[] = "cobalt-dial:?";
-const char kLogPathSwitch[] = "xb1_log_file";
-// A special log that the app will periodically write to. This allows
-// tests to determine if the app is still alive.
-const char kWatchDogLog[] = "xb1_watchdog_log";
-const char kStarboardArgumentsPath[] = "arguments\\starboard_arguments.txt";
-const int64_t kMaxArgumentFileSizeBytes = 4 * 1024 * 1024;
-
-int main_return_value = 0;
-
-// IDisplayRequest is both "non-agile" and apparently
-// incompatible with Platform::Agile (it doesn't fully implement
-// a thread marshaller). We must neither use ComPtr or Platform::Agile
-// here. We manually create, access release on the main app thread only.
-ABI::Windows::System::Display::IDisplayRequest* display_request = nullptr;
-
-// If an argv[0] is required, fill it in with the result of
-// GetModuleFileName()
-std::string GetArgvZero() {
- const size_t kMaxModuleNameSize = SB_FILE_MAX_NAME;
- wchar_t buffer[kMaxModuleNameSize];
- DWORD result = GetModuleFileName(NULL, buffer, kMaxModuleNameSize);
- std::string arg;
- if (result == 0) {
- arg = "unknown";
- } else {
- arg = wchar_tToUTF8(buffer, result).c_str();
- }
- return arg;
-}
-
-int MakeDeviceId() {
- // TODO: Devices MIGHT have colliding hashcodes. Some other unique int
- // ID generation tool would be better.
- using Windows::Security::ExchangeActiveSyncProvisioning::
- EasClientDeviceInformation;
- auto device_information = ref new EasClientDeviceInformation();
- Platform::String ^ device_id_string = device_information->Id.ToString();
- return device_id_string->GetHashCode();
-}
-
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-
-void SplitArgumentsIntoVector(std::string* args,
- std::vector<std::string> *result) {
- SB_DCHECK(args);
- SB_DCHECK(result);
- while (!args->empty()) {
- size_t next = args->find(';');
- result->push_back(args->substr(0, next));
- if (next == std::string::npos) {
- return;
- }
- *args = args->substr(next + 1);
- }
-}
-
-// Parses a starboard: URI scheme by splitting args at ';' boundaries.
-std::vector<std::string> ParseStarboardUri(const std::string& uri) {
- std::vector<std::string> result;
- result.push_back(GetArgvZero());
-
- size_t index = uri.find(':');
- if (index == std::string::npos) {
- return result;
- }
-
- std::string args = uri.substr(index + 1);
- SplitArgumentsIntoVector(&args, &result);
-
- return result;
-}
-
-void AddArgumentsFromFile(const char* path,
- std::vector<std::string>* args) {
- starboard::ScopedFile file(path, kSbFileOpenOnly | kSbFileRead);
- if (!file.IsValid()) {
- SB_LOG(INFO) << path << " is not valid for arguments.";
- return;
- }
-
- int64_t file_size = file.GetSize();
- if (file_size > kMaxArgumentFileSizeBytes) {
- SB_DLOG(ERROR) << "The arguments file is too big.";
- return;
- }
-
- if (file_size <= 0) {
- SB_DLOG(INFO) << "Arguments file is empty.";
- return;
- }
-
- std::string argument_string(file_size, '\0');
- int return_value = file.ReadAll(&argument_string[0], file_size);
- if (return_value < 0) {
- SB_DLOG(ERROR) << "Error while reading arguments from file.";
- return;
- }
- argument_string.resize(return_value);
-
- SplitArgumentsIntoVector(&argument_string, args);
-}
-
-#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-
-std::unique_ptr<Application::Event> MakeDeepLinkEvent(
- const std::string& uri_string) {
- SB_LOG(INFO) << "Navigate to: [" << uri_string << "]";
- const size_t kMaxDeepLinkSize = 128 * 1024;
- const std::size_t uri_size = uri_string.size();
- if (uri_size > kMaxDeepLinkSize) {
- SB_NOTREACHED() << "App launch data too big: " << uri_size;
- return nullptr;
- }
-
- const int kBufferSize = static_cast<int>(uri_string.size()) + 1;
- char* deep_link = new char[kBufferSize];
- SB_DCHECK(deep_link);
- SbStringCopy(deep_link, uri_string.c_str(), kBufferSize);
-
- return std::unique_ptr<Application::Event>(
- new Application::Event(kSbEventTypeLink, deep_link,
- Application::DeleteArrayDestructor<const char*>));
-}
-
-// Returns if |full_string| ends with |substring|.
-bool ends_with(const std::string& full_string, const std::string& substring) {
- if (substring.length() > full_string.length()) {
- return false;
- }
- return std::equal(substring.rbegin(), substring.rend(), full_string.rbegin());
-}
-
-std::string GetBinaryName() {
- std::string full_binary_path = GetArgvZero();
- std::string::size_type index = full_binary_path.rfind(SB_FILE_SEP_CHAR);
- if (index == std::string::npos) {
- return full_binary_path;
- }
-
- return full_binary_path.substr(index + 1);
-}
-} // namespace
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-// Called into drm_system_playready.cc
-extern void DrmSystemOnUwpResume();
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-ref class App sealed : public IFrameworkView {
- public:
- App() : previously_activated_(false) {}
-
- // IFrameworkView methods.
- virtual void Initialize(CoreApplicationView^ application_view) {
- // The following incantation creates a DisplayRequest and obtains
- // it's underlying COM interface.
- ComPtr<IInspectable> inspectable = reinterpret_cast<IInspectable*>(
- ref new Windows::System::Display::DisplayRequest());
- ComPtr<ABI::Windows::System::Display::IDisplayRequest> dr;
- inspectable.As(&dr);
- display_request = dr.Detach();
-
- SbAudioSinkPrivate::Initialize();
- CoreApplication::Suspending +=
- ref new EventHandler<SuspendingEventArgs^>(this, &App::OnSuspending);
- CoreApplication::Resuming +=
- ref new EventHandler<Object^>(this, &App::OnResuming);
- application_view->Activated +=
- ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(
- this, &App::OnActivated);
- }
-
- virtual void SetWindow(CoreWindow^ window) {
- ApplicationUwp::Get()->SetCoreWindow(window);
- window->KeyUp += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(
- this, &App::OnKeyUp);
- window->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(
- this, &App::OnKeyDown);
- }
-
- virtual void Load(Platform::String^ entry_point) {
- entry_point_ = wchar_tToUTF8(entry_point->Data());
- }
-
- virtual void Run() {
- main_return_value = application_.Run(
- static_cast<int>(argv_.size()), const_cast<char**>(argv_.data()));
- }
- virtual void Uninitialize() {
- SbAudioSinkPrivate::TearDown();
- display_request->Release();
- display_request = nullptr;
- }
-
- void OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args) {
- SB_DLOG(INFO) << "Suspending application.";
- // Note if we dispatch "suspend" here before pause, application.cc
- // will inject the "pause" which will cause us to go async which
- // will cause us to not have completed the suspend operation before
- // returning, which UWP requires.
- ApplicationUwp::Get()->DispatchAndDelete(
- new ApplicationUwp::Event(kSbEventTypePause, NULL, NULL));
- ApplicationUwp::Get()->DispatchAndDelete(
- new ApplicationUwp::Event(kSbEventTypeSuspend, NULL, NULL));
- }
-
- void OnResuming(Platform::Object^ sender, Platform::Object^ args) {
- SB_DLOG(INFO) << "Resuming";
- ApplicationUwp::Get()->DispatchAndDelete(
- new ApplicationUwp::Event(kSbEventTypeResume, NULL, NULL));
- ApplicationUwp::Get()->DispatchAndDelete(
- new ApplicationUwp::Event(kSbEventTypeUnpause, NULL, NULL));
- sbwin32::DrmSystemOnUwpResume();
- }
-
- void OnKeyUp(CoreWindow^ sender, KeyEventArgs^ args) {
- ApplicationUwp::Get()->OnKeyEvent(sender, args, true);
- }
-
- void OnKeyDown(CoreWindow^ sender, KeyEventArgs^ args) {
- ApplicationUwp::Get()->OnKeyEvent(sender, args, false);
- }
-
- void OnActivated(
- CoreApplicationView^ application_view, IActivatedEventArgs^ args) {
- SB_LOG(INFO) << "OnActivated";
- ApplicationUwp* application_uwp = ApplicationUwp::Get();
- Microsoft::WRL::ComPtr<ID3D12Device> device;
- HRESULT hr =
- D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device));
- SB_DCHECK(SUCCEEDED(hr));
- application_uwp->SetD3D12Device(device);
- std::string start_url = entry_point_;
- bool command_line_set = false;
-
- // Please see application lifecyle description:
- // https://docs.microsoft.com/en-us/windows/uwp/launch-resume/app-lifecycle
- // Note that this document was written for Xaml apps not core apps,
- // so for us the precise API is a little different.
- // The substance is that, while OnActiviated is definitely called the
- // first time the application is started, it may additionally called
- // in other cases while the process is already running. Starboard
- // applications cannot fully restart in a process lifecycle,
- // so we interpret the first activation and the subsequent ones differently.
- if (args->Kind == ActivationKind::Protocol) {
- Uri^ uri = dynamic_cast<IProtocolActivatedEventArgs^>(args)->Uri;
-
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- // The starboard: scheme provides commandline arguments, but that's
- // only allowed during a process's first activation.
- std::string scheme = sbwin32::platformStringToString(uri->SchemeName);
- if (!previously_activated_ && ends_with(scheme, "-starboard")) {
- std::string uri_string = wchar_tToUTF8(uri->RawUri->Data());
- // args_ is a vector of std::string, but argv_ is a vector of
- // char* into args_ so as to compose a char**.
- args_ = ParseStarboardUri(uri_string);
- for (const std::string& arg : args_) {
- argv_.push_back(arg.c_str());
- }
-
- ApplicationUwp::Get()->SetCommandLine(
- static_cast<int>(argv_.size()), argv_.data());
- command_line_set = true;
- }
-#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- if (uri->SchemeName->Equals("youtube") ||
- uri->SchemeName->Equals("ms-xbl-07459769")) {
- std::string uri_string = sbwin32::platformStringToString(uri->RawUri);
-
- // Strip the protocol from the uri.
- size_t index = uri_string.find(':');
- if (index != std::string::npos) {
- uri_string = uri_string.substr(index + 1);
- }
-
- ProcessDeepLinkUri(&uri_string);
- }
- } else if (args->Kind == ActivationKind::DialReceiver) {
- DialReceiverActivatedEventArgs^ dial_args =
- dynamic_cast<DialReceiverActivatedEventArgs^>(args);
- SB_CHECK(dial_args);
- Platform::String^ arguments = dial_args->Arguments;
- if (previously_activated_) {
- std::string uri_string =
- kDialParamPrefix + sbwin32::platformStringToString(arguments);
- ProcessDeepLinkUri(&uri_string);
- } else {
- std::string activation_args = "--url=";
- activation_args.append(start_url);
- activation_args.append("?");
- activation_args.append(sbwin32::platformStringToString(arguments));
- SB_DLOG(INFO) << "Dial Activation url: " << activation_args;
- args_.push_back(GetArgvZero());
- args_.push_back(activation_args);
- // Set partition URL in case start_url is the main app first run
- // special case.
- std::string partition_arg = "--local_storage_partition_url=";
- partition_arg.append(entry_point_);
- args_.push_back(partition_arg);
- for (const std::string& arg : args_) {
- argv_.push_back(arg.c_str());
- }
- ApplicationUwp::Get()->SetCommandLine(static_cast<int>(argv_.size()),
- argv_.data());
- command_line_set = true;
- }
- }
- previous_activation_kind_ = args->Kind;
-
- if (!previously_activated_) {
- if (!command_line_set) {
- args_.push_back(GetArgvZero());
- std::string start_url_arg = "--url=";
- start_url_arg.append(start_url);
- args_.push_back(start_url_arg);
- std::string partition_arg = "--local_storage_partition_url=";
- partition_arg.append(entry_point_);
- args_.push_back(partition_arg);
-
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- char content_directory[SB_FILE_MAX_NAME];
- content_directory[0] = '\0';
- if (SbSystemGetPath(kSbSystemPathContentDirectory, content_directory,
- SB_ARRAY_SIZE_INT(content_directory))) {
- std::string arguments_file_path = content_directory;
- arguments_file_path += SB_FILE_SEP_STRING;
- arguments_file_path += kStarboardArgumentsPath;
- AddArgumentsFromFile(arguments_file_path.c_str(), &args_);
- }
-#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-
- for (auto& arg : args_) {
- argv_.push_back(arg.c_str());
- }
-
- ApplicationUwp::Get()->SetCommandLine(
- static_cast<int>(argv_.size()), argv_.data());
- }
-
- const CommandLine* command_line =
- ::starboard::shared::uwp::GetCommandLinePointer(application_uwp);
-
-#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- if (command_line->HasSwitch(kWatchDogLog)) {
- // Launch a thread.
- std::string switch_val = command_line->GetSwitchValue(kWatchDogLog);
- auto uwp_dir =
- Windows::Storage::ApplicationData::Current->LocalCacheFolder;
- std::stringstream ss;
- ss << sbwin32::platformStringToString(uwp_dir->Path) << "/"
- << switch_val;
- sbuwp::StartWatchdogLog(ss.str());
- }
-
- if (command_line->HasSwitch(kLogPathSwitch)) {
- std::stringstream ss;
- ss << sbwin32::platformStringToString(
- Windows::Storage::ApplicationData::Current->LocalCacheFolder->Path);
- ss << "\\" << "" << command_line->GetSwitchValue(kLogPathSwitch);
- std::string full_path_log_file = ss.str();
- sbuwp::OpenLogFileWin32(full_path_log_file.c_str());
- } else {
-#if !defined(COBALT_BUILD_TYPE_GOLD)
- // Log to a file on the last removable device available (probably the
- // most recently added removable device).
- if (KnownFolders::RemovableDevices != nullptr) {
- concurrency::create_task(
- KnownFolders::RemovableDevices->GetFoldersAsync()).then(
- [](concurrency::task<IVectorView<StorageFolder^>^> result) {
- IVectorView<StorageFolder^>^ results;
- try {
- results = result.get();
- } catch(Platform::Exception^) {
- SB_LOG(ERROR) <<
- "Unable to open log file in RemovableDevices";
- return;
- }
-
- if (results->Size == 0) {
- return;
- }
-
- StorageFolder^ folder = results->GetAt(results->Size - 1);
- Calendar^ now = ref new Calendar();
- char filename[128];
- SbStringFormatF(filename, sizeof(filename),
- "cobalt_log_%04d%02d%02d_%02d%02d%02d.txt",
- now->Year, now->Month, now->Day,
- now->Hour + now->FirstHourInThisPeriod,
- now->Minute, now->Second);
- sbuwp::OpenLogFileUWP(folder, filename);
- });
- }
-#endif // !defined(COBALT_BUILD_TYPE_GOLD)
- }
-#endif // defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- SB_LOG(INFO) << "Starting " << GetBinaryName();
-
- CoreWindow::GetForCurrentThread()->Activate();
- // Call DispatchStart async so the UWP system thinks we're activated.
- // Some tools seem to want the application to be activated before
- // interacting with them, some things are disallowed during activation
- // (such as exiting), and DispatchStart (for example) runs
- // automated tests synchronously.
-
- RunInMainThreadAsync([this]() {
- ApplicationUwp::Get()->DispatchStart();
- });
- }
- previously_activated_ = true;
- }
-
- private:
- void ProcessDeepLinkUri(std::string *uri_string) {
- SB_DCHECK(uri_string);
- if (previously_activated_) {
- std::unique_ptr<Application::Event> event =
- MakeDeepLinkEvent(*uri_string);
- SB_DCHECK(event);
- ApplicationUwp::Get()->Inject(event.release());
- } else {
- ApplicationUwp::Get()->SetStartLink(uri_string->c_str());
- }
- }
-
- std::string entry_point_;
- bool previously_activated_;
- // Only valid if previously_activated_ is true
- ActivationKind previous_activation_kind_;
- std::vector<std::string> args_;
- std::vector<const char *> argv_;
-
- starboard::shared::uwp::ApplicationUwp application_;
-};
-
-ref class Direct3DApplicationSource sealed : IFrameworkViewSource {
- public:
- Direct3DApplicationSource() {}
- virtual IFrameworkView^ CreateView() {
- return ref new App();
- }
-};
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-ApplicationUwp::ApplicationUwp()
- : window_(kSbWindowInvalid),
- localized_strings_(SbSystemGetLocaleId()),
- device_id_(MakeDeviceId()) {
- analog_thumbstick_thread_.reset(new AnalogThumbstickThread(this));
-}
-
-ApplicationUwp::~ApplicationUwp() {
- analog_thumbstick_thread_.reset(nullptr);
-}
-
-void ApplicationUwp::Initialize() {}
-
-void ApplicationUwp::Teardown() {
- CloseWatchdogLog();
-}
-
-Application::Event* ApplicationUwp::GetNextEvent() {
- SB_NOTREACHED();
- return nullptr;
-}
-
-SbWindow ApplicationUwp::CreateWindowForUWP(const SbWindowOptions*) {
- // TODO: Determine why SB_DCHECK(IsCurrentThread()) fails in nplb, fix it,
- // and add back this check.
-
- if (SbWindowIsValid(window_)) {
- return kSbWindowInvalid;
- }
-
- // Get the logical resolution in pixels. See section "Bounds" in
- // https://docs.microsoft.com/en-us/uwp/api/windows.ui.core.corewindow.
- Windows::Foundation::Rect bounds_in_dips =
- CoreWindow::GetForCurrentThread()->Bounds;
- float dpi = DisplayInformation::GetForCurrentView()->LogicalDpi;
- int width = static_cast<int>(bounds_in_dips.Width * dpi / 96.0f);
- int height = static_cast<int>(bounds_in_dips.Height * dpi / 96.0f);
-
- // For UWP on XB1, the logical resolution is always 1080p, regardless of the
- // actual output resolution -- section "Scale factor and adaptive layout" in
- // "https://docs.microsoft.com/en-us/windows/uwp/input-and-devices/
- // designing-for-tv". However, if the swap chain uses a special surface
- // format (R10G10B10A2), it can be passed to the output without scaling.
- bool supports_hdmi_api = ApiInformation::IsApiContractPresent(
- "Windows.Foundation.UniversalApiContract", 4);
- bool is_fullscreen = ApplicationView::GetForCurrentView()->IsFullScreenMode;
- if (supports_hdmi_api && is_fullscreen) {
- // This reports the actual output resolution.
- auto display_info = HdmiDisplayInformation::GetForCurrentView();
- auto current_mode = display_info->GetCurrentDisplayMode();
- width = static_cast<int>(current_mode->ResolutionWidthInRawPixels);
- height = static_cast<int>(current_mode->ResolutionHeightInRawPixels);
- }
-
- SB_LOG(INFO) << "Window resolution is " << width << " x " << height;
-
- window_ = new SbWindowPrivate(width, height);
- return window_;
-}
-
-bool ApplicationUwp::DestroyWindow(SbWindow window) {
- // TODO: Determine why SB_DCHECK(IsCurrentThread()) fails in nplb, fix it,
- // and add back this check.
-
- if (!SbWindowIsValid(window)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid context.";
- return false;
- }
-
- SB_DCHECK(window_ == window);
- delete window;
- window_ = kSbWindowInvalid;
-
- return true;
-}
-
-bool ApplicationUwp::DispatchNextEvent() {
- core_window_->Activate();
- core_window_->Dispatcher->ProcessEvents(
- CoreProcessEventsOption::ProcessUntilQuit);
- return false;
-}
-
-Windows::Media::Protection::HdcpSession^ ApplicationUwp::GetHdcpSession() {
- if (!hdcp_session_) {
- hdcp_session_ = ref new HdcpSession();
- }
- return hdcp_session_;
-}
-
-void ApplicationUwp::ResetHdcpSession() {
- // delete will call the destructor, but not free memory.
- // The destructor is called explicitly so that HDCP session can be
- // torn down immediately.
- if (hdcp_session_) {
- delete hdcp_session_;
- hdcp_session_ = nullptr;
- }
-}
-
-void ApplicationUwp::Inject(Application::Event* event) {
- RunInMainThreadAsync([this, event]() {
- bool result = DispatchAndDelete(event);
- if (!result) {
- NetLogFlushThenClose();
- CoreApplication::Exit();
- }
- });
-}
-
-void ApplicationUwp::InjectKeypress(SbKey key) {
- std::unique_ptr<SbInputData> press_data(new SbInputData());
- std::unique_ptr<SbInputData> unpress_data(new SbInputData());
-
- SbMemorySet(press_data.get(), 0, sizeof(*press_data));
- press_data->window = window_;
- press_data->device_type = kSbInputDeviceTypeKeyboard;
- press_data->device_id = device_id();
- press_data->key = key;
- press_data->type = kSbInputEventTypePress;
-
- *unpress_data = *press_data;
- unpress_data->type = kSbInputEventTypeUnpress;
-
- Inject(new Event(kSbEventTypeInput, press_data.release(),
- &Application::DeleteDestructor<SbInputData>));
- Inject(new Event(kSbEventTypeInput, unpress_data.release(),
- &Application::DeleteDestructor<SbInputData>));
-}
-
-void ApplicationUwp::InjectTimedEvent(Application::TimedEvent* timed_event) {
- SbTimeMonotonic delay_usec =
- timed_event->target_time - SbTimeGetMonotonicNow();
- if (delay_usec < 0) {
- delay_usec = 0;
- }
-
- // TimeSpan ticks are, like FILETIME, 100ns
- const SbTimeMonotonic kTicksPerUsec = 10;
-
- TimeSpan timespan;
- timespan.Duration = delay_usec * kTicksPerUsec;
-
- ScopedLock lock(mutex_);
- ThreadPoolTimer^ timer = ThreadPoolTimer::CreateTimer(
- ref new TimerElapsedHandler([this, timed_event](ThreadPoolTimer^ timer) {
- RunInMainThreadAsync([this, timed_event]() {
- // Even if the event is canceled, the callback can still fire.
- // Thus, the existence of event in timer_event_map_ is used
- // as a source of truth.
- std::size_t number_erased = 0;
- {
- ScopedLock lock(mutex_);
- number_erased = timer_event_map_.erase(timed_event->id);
- }
- if (number_erased > 0) {
- timed_event->callback(timed_event->context);
- }
- });
- }), timespan);
- timer_event_map_.emplace(timed_event->id, timer);
-}
-
-void ApplicationUwp::CancelTimedEvent(SbEventId event_id) {
- ScopedLock lock(mutex_);
- auto it = timer_event_map_.find(event_id);
- if (it == timer_event_map_.end()) {
- return;
- }
- it->second->Cancel();
- timer_event_map_.erase(it);
-}
-
-Application::TimedEvent* ApplicationUwp::GetNextDueTimedEvent() {
- SB_NOTIMPLEMENTED();
- return nullptr;
-}
-
-SbTimeMonotonic ApplicationUwp::GetNextTimedEventTargetTime() {
- SB_NOTIMPLEMENTED();
- return 0;
-}
-
-void ApplicationUwp::OnJoystickUpdate(SbKey key, SbInputVector input_vector) {
- scoped_ptr<SbInputData> data(new SbInputData());
- SbMemorySet(data.get(), 0, sizeof(*data));
- data->window = window_;
- data->type = kSbInputEventTypeMove;
- data->device_type = kSbInputDeviceTypeGamepad;
- data->device_id = device_id();
- data->key = key;
- data->character = 0;
-
- data->key_modifiers = kSbKeyModifiersNone;
- data->position = input_vector;
-
- SbKeyLocation key_location = kSbKeyLocationUnspecified;
- switch (key) {
- case kSbKeyGamepadLeftStickLeft:
- case kSbKeyGamepadLeftStickUp: {
- key_location = kSbKeyLocationLeft;
- break;
- }
- case kSbKeyGamepadRightStickLeft:
- case kSbKeyGamepadRightStickUp: {
- key_location = kSbKeyLocationRight;
- break;
- }
- default: {
- SB_NOTREACHED();
- break;
- }
- }
-
- data->key_location = key_location;
- Inject(new Event(kSbEventTypeInput, data.release(),
- &Application::DeleteDestructor<SbInputData>));
-}
-
-Platform::String^ ApplicationUwp::GetString(
- const char* id, const char* fallback) const {
- return stringToPlatformString(localized_strings_.GetString(id, fallback));
-}
-
-bool ApplicationUwp::IsHdcpOn() {
- ::starboard::ScopedLock lock(hdcp_session_mutex_);
-
- return GetHdcpSession()->IsEffectiveProtectionAtLeast(kHDCPProtectionMode);
-}
-
-bool ApplicationUwp::TurnOnHdcp() {
- HdcpSetProtectionResult protection_result;
- {
- ::starboard::ScopedLock lock(hdcp_session_mutex_);
-
- protection_result = WaitForResult(
- GetHdcpSession()->SetDesiredMinProtectionAsync(kHDCPProtectionMode));
- }
-
- if (IsHdcpOn()) {
- return true;
- }
-
- // If the operation did not have intended result, log something.
- switch (protection_result) {
- case HdcpSetProtectionResult::Success:
- SB_LOG(INFO) << "Successfully set HDCP.";
- break;
- case HdcpSetProtectionResult::NotSupported:
- SB_LOG(INFO) << "HDCP is not supported.";
- break;
- case HdcpSetProtectionResult::TimedOut:
- SB_LOG(INFO) << "Setting HDCP timed out.";
- break;
- case HdcpSetProtectionResult::UnknownFailure:
- SB_LOG(INFO) << "Unknown failure returned while setting HDCP.";
- break;
- }
-
- return false;
-}
-
-bool ApplicationUwp::TurnOffHdcp() {
- {
- ::starboard::ScopedLock lock(hdcp_session_mutex_);
- ResetHdcpSession();
- }
- bool success = !SbMediaIsOutputProtected();
- return success;
-}
-
-Platform::Agile<Windows::UI::Core::CoreDispatcher> GetDispatcher() {
- return ApplicationUwp::Get()->GetDispatcher();
-}
-
-Platform::Agile<Windows::Media::SystemMediaTransportControls>
- GetTransportControls() {
- return ApplicationUwp::Get()->GetTransportControls();
-}
-
-void DisplayRequestActive() {
- RunInMainThreadAsync([]() {
- if (display_request != nullptr) {
- display_request->RequestActive();
- }
- });
-}
-
-void DisplayRequestRelease() {
- RunInMainThreadAsync([]() {
- if (display_request != nullptr) {
- display_request->RequestRelease();
- }
- });
-}
-
-void InjectKeypress(SbKey key) {
- ApplicationUwp::Get()->InjectKeypress(key);
-}
-
-bool HasNetLogSwitch(Platform::Array<Platform::String^>^ args) {
- CommandLine::StringVector arg_v;
- for (Platform::String^ arg : args) {
- arg_v.push_back(platformStringToString(arg));
- }
- CommandLine cmd_line(arg_v);
- return cmd_line.HasSwitch(kNetLogCommandSwitchWait);
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-[Platform::MTAThread]
-int main(Platform::Array<Platform::String^>^ args) {
- if (!IsDebuggerPresent()) {
- // By default, a Windows application will display a dialog box
- // when it crashes. This is extremely undesirable when run offline.
- // The following configures messages to be print to the console instead.
- _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
- _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
- _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
- _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
- }
-
- WSAData wsaData;
- int init_result = WSAStartup(
- MAKEWORD(kWinSockVersionMajor, kWinSockVersionMajor), &wsaData);
-
- SB_CHECK(init_result == 0);
- // WSAStartup returns the highest version that is supported up to the version
- // we request.
- SB_CHECK(LOBYTE(wsaData.wVersion) == kWinSockVersionMajor &&
- HIBYTE(wsaData.wVersion) == kWinSockVersionMinor);
-
- HRESULT hr = MFStartup(MF_VERSION);
- SB_DCHECK(SUCCEEDED(hr));
-
- starboard::shared::win32::RegisterMainThread();
-
- if (starboard::shared::uwp::HasNetLogSwitch(args)) {
- NetLogWaitForClientConnected();
- }
- auto direct3DApplicationSource = ref new Direct3DApplicationSource();
- CoreApplication::Run(direct3DApplicationSource);
- NetLogFlushThenClose();
-
- MFShutdown();
- WSACleanup();
-
- return main_return_value;
-}
diff --git a/src/starboard/shared/uwp/application_uwp.h b/src/starboard/shared/uwp/application_uwp.h
deleted file mode 100644
index 5e5de59..0000000
--- a/src/starboard/shared/uwp/application_uwp.h
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2017 Google Inc. 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_UWP_APPLICATION_UWP_H_
-#define STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
-
-#include <D3D12.h>
-#include <agile.h>
-
-#include <string>
-#include <unordered_map>
-
-#include "starboard/configuration.h"
-#include "starboard/input.h"
-#include "starboard/key.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/application.h"
-#include "starboard/shared/starboard/command_line.h"
-#include "starboard/shared/starboard/localized_strings.h"
-#include "starboard/shared/uwp/analog_thumbstick_input_thread.h"
-#include "starboard/types.h"
-#include "starboard/window.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-// Including <agile.h>, will eventually include <windows.h>, which includes
-// C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\um\processenv.h,
-// line 164 in processenv.h redefines GetCommandLine to GetCommandLineW if
-// UNICODE is defined.
-// This function was added so that it could be used as a work around when
-// GetCommandLine() needed to be called.
-const starboard::CommandLine*
-GetCommandLinePointer(starboard::Application* app);
-
-class ApplicationUwp : public shared::starboard::Application,
- private AnalogThumbstickThread::Callback {
- public:
- ApplicationUwp();
- ~ApplicationUwp() override;
-
- static ApplicationUwp* Get() {
- return static_cast<ApplicationUwp*>(shared::starboard::Application::Get());
- }
-
- SbWindow CreateWindowForUWP(const SbWindowOptions* options);
-
- bool DestroyWindow(SbWindow window);
-
- void DispatchStart() { shared::starboard::Application::DispatchStart(); }
-
- // public for IFrameworkView subclass
- void SetCommandLine(int argc, const char** argv) {
- shared::starboard::Application::SetCommandLine(argc, argv);
- }
-
- // public for IFrameworkView subclass
- bool DispatchAndDelete(Application::Event* event) {
- return shared::starboard::Application::DispatchAndDelete(event);
- }
-
- Platform::Agile<Windows::UI::Core::CoreWindow> GetCoreWindow() const {
- return core_window_;
- }
-
- Platform::Agile<Windows::UI::Core::CoreDispatcher> GetDispatcher() const {
- return dispatcher_;
- }
-
- Platform::Agile<Windows::Media::SystemMediaTransportControls>
- GetTransportControls() const {
- return transport_controls_;
- }
-
- // public for IFrameworkView subclass
- void SetCoreWindow(Windows::UI::Core::CoreWindow^ window) {
- core_window_ = window;
-
- dispatcher_ = window->Dispatcher;
- transport_controls_ =
- Windows::Media::SystemMediaTransportControls::GetForCurrentView();
- }
-
- void OnKeyEvent(Windows::UI::Core::CoreWindow^ sender,
- Windows::UI::Core::KeyEventArgs^ args,
- bool up);
-
- void Inject(Event* event) override;
-
- void InjectKeypress(SbKey key);
-
- void SetStartLink(const char* link) {
- shared::starboard::Application::SetStartLink(link);
- }
-
- Platform::String^ GetString(const char* id, const char* fallback) const;
-
- bool IsHdcpOn();
- // Returns true on success.
- bool TurnOnHdcp();
- // Returns true on success.
- bool TurnOffHdcp();
-
- Microsoft::WRL::ComPtr<ID3D12Device> GetD3D12Device() { return d3d12device_; }
-
- void SetD3D12Device(const Microsoft::WRL::ComPtr<ID3D12Device>& device) {
- d3d12device_ = device;
- }
-
- private:
- // --- Application overrides ---
- bool IsStartImmediate() override { return false; }
- void Initialize() override;
- void Teardown() override;
- Event* GetNextEvent() override;
- bool DispatchNextEvent() override;
- void InjectTimedEvent(TimedEvent* timed_event) override;
- void CancelTimedEvent(SbEventId event_id) override;
- TimedEvent* GetNextDueTimedEvent() override;
- SbTimeMonotonic GetNextTimedEventTargetTime() override;
-
- int device_id() const { return device_id_; }
- void OnJoystickUpdate(SbKey key, SbInputVector value) override;
-
- // These two functions should only be called while holding
- // |hdcp_session_mutex_|.
- Windows::Media::Protection::HdcpSession^ GetHdcpSession();
- void ResetHdcpSession();
-
- // The single open window, if any.
- SbWindow window_;
- Platform::Agile<Windows::UI::Core::CoreWindow> core_window_;
-
- Platform::Agile<Windows::UI::Core::CoreDispatcher> dispatcher_;
- Platform::Agile<Windows::Media::SystemMediaTransportControls>
- transport_controls_;
-
- shared::starboard::LocalizedStrings localized_strings_;
-
- Mutex mutex_;
- // |timer_event_map_| is locked by |mutex_|.
- std::unordered_map<SbEventId, Windows::System::Threading::ThreadPoolTimer^>
- timer_event_map_;
-
- Microsoft::WRL::ComPtr<ID3D12Device> d3d12device_;
- int device_id_;
-
- // |hdcp_session_| is locked by |hdcp_session_mutex_|.
- Mutex hdcp_session_mutex_;
- Windows::Media::Protection::HdcpSession^ hdcp_session_;
-
- scoped_ptr<AnalogThumbstickThread> analog_thumbstick_thread_;
-};
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_APPLICATION_UWP_H_
diff --git a/src/starboard/shared/uwp/application_uwp_get_commandline_pointer.cc b/src/starboard/shared/uwp/application_uwp_get_commandline_pointer.cc
deleted file mode 100644
index 37fb157..0000000
--- a/src/starboard/shared/uwp/application_uwp_get_commandline_pointer.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/starboard/application.h"
-#include "starboard/shared/starboard/command_line.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-const starboard::CommandLine*
-GetCommandLinePointer(starboard::Application* app) {
- return app->GetCommandLine();
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/application_uwp_key_event.cc b/src/starboard/shared/uwp/application_uwp_key_event.cc
deleted file mode 100644
index cfe7c16..0000000
--- a/src/starboard/shared/uwp/application_uwp_key_event.cc
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/uwp/application_uwp.h"
-
-#include "starboard/event.h"
-#include "starboard/input.h"
-#include "starboard/key.h"
-
-using Windows::UI::Core::CoreWindow;
-using Windows::UI::Core::KeyEventArgs;
-using Windows::UI::Core::CoreVirtualKeyStates;
-using Windows::System::VirtualKey;
-
-namespace {
-
-SbKey VirtualKeyToSbKey(VirtualKey key) {
-// Disable warning for Invalid valid values from switch of enum.
-#pragma warning(push)
-#pragma warning(disable : 4063)
- switch (key) {
- case VirtualKey::None:
- case VirtualKey::NavigationView:
- case VirtualKey::NavigationMenu:
- case VirtualKey::NavigationUp:
- case VirtualKey::NavigationDown:
- case VirtualKey::NavigationLeft:
- case VirtualKey::NavigationRight:
- case VirtualKey::NavigationAccept:
- case VirtualKey::NavigationCancel:
- return kSbKeyUnknown;
- case VirtualKey::Cancel: return kSbKeyCancel;
- case VirtualKey::Back: return kSbKeyBack;
- case VirtualKey::Tab: return kSbKeyTab;
- case VirtualKey::Clear: return kSbKeyClear;
- case VirtualKey::Enter: return kSbKeyReturn;
- case VirtualKey::Shift: return kSbKeyShift;
- case VirtualKey::Control: return kSbKeyControl;
- case VirtualKey::Menu: return kSbKeyMenu;
- case VirtualKey::Pause: return kSbKeyPause;
- case VirtualKey::CapitalLock: return kSbKeyCapital;
- // Hangul and Kana have the same VirtualKey constant
- case VirtualKey::Kana: return kSbKeyKana;
- case VirtualKey::Junja: return kSbKeyJunja;
- case VirtualKey::Final: return kSbKeyFinal;
- // Hanja and Kanji have the same VirtualKey constant
- case VirtualKey::Hanja: return kSbKeyHanja;
- case VirtualKey::Escape: return kSbKeyEscape;
- case VirtualKey::Convert: return kSbKeyConvert;
- case VirtualKey::NonConvert: return kSbKeyNonconvert;
- case VirtualKey::Accept: return kSbKeyAccept;
- case VirtualKey::ModeChange: return kSbKeyModechange;
- case VirtualKey::Space: return kSbKeySpace;
- case VirtualKey::PageUp: return kSbKeyPrior;
- case VirtualKey::PageDown: return kSbKeyNext;
- case VirtualKey::End: return kSbKeyEnd;
- case VirtualKey::Home: return kSbKeyHome;
- case VirtualKey::Left: return kSbKeyLeft;
- case VirtualKey::Up: return kSbKeyUp;
- case VirtualKey::Right: return kSbKeyRight;
- case VirtualKey::Down: return kSbKeyDown;
- case VirtualKey::Select: return kSbKeySelect;
- case VirtualKey::Print: return kSbKeyPrint;
- case VirtualKey::Execute: return kSbKeyExecute;
- case VirtualKey::Snapshot: return kSbKeySnapshot;
- case VirtualKey::Insert: return kSbKeyInsert;
- case VirtualKey::Delete: return kSbKeyDelete;
- case VirtualKey::Number0: return kSbKey0;
- case VirtualKey::Number1: return kSbKey1;
- case VirtualKey::Number2: return kSbKey2;
- case VirtualKey::Number3: return kSbKey3;
- case VirtualKey::Number4: return kSbKey4;
- case VirtualKey::Number5: return kSbKey5;
- case VirtualKey::Number6: return kSbKey6;
- case VirtualKey::Number7: return kSbKey7;
- case VirtualKey::Number8: return kSbKey8;
- case VirtualKey::Number9: return kSbKey9;
- case VirtualKey::A: return kSbKeyA;
- case VirtualKey::B: return kSbKeyB;
- case VirtualKey::C: return kSbKeyC;
- case VirtualKey::D: return kSbKeyD;
- case VirtualKey::E: return kSbKeyE;
- case VirtualKey::F: return kSbKeyF;
- case VirtualKey::G: return kSbKeyG;
- case VirtualKey::H: return kSbKeyH;
- case VirtualKey::I: return kSbKeyI;
- case VirtualKey::J: return kSbKeyJ;
- case VirtualKey::K: return kSbKeyK;
- case VirtualKey::L: return kSbKeyL;
- case VirtualKey::M: return kSbKeyM;
- case VirtualKey::N: return kSbKeyN;
- case VirtualKey::O: return kSbKeyO;
- case VirtualKey::P: return kSbKeyP;
- case VirtualKey::Q: return kSbKeyQ;
- case VirtualKey::R: return kSbKeyR;
- case VirtualKey::S: return kSbKeyS;
- case VirtualKey::T: return kSbKeyT;
- case VirtualKey::U: return kSbKeyU;
- case VirtualKey::V: return kSbKeyV;
- case VirtualKey::W: return kSbKeyW;
- case VirtualKey::X: return kSbKeyX;
- case VirtualKey::Y: return kSbKeyY;
- case VirtualKey::Z: return kSbKeyZ;
- case VirtualKey::LeftWindows: return kSbKeyLwin;
- case VirtualKey::RightWindows: return kSbKeyRwin;
- case VirtualKey::Application: return kSbKeyApps;
- case VirtualKey::Sleep: return kSbKeySleep;
- case VirtualKey::NumberPad0: return kSbKeyNumpad0;
- case VirtualKey::NumberPad1: return kSbKeyNumpad1;
- case VirtualKey::NumberPad2: return kSbKeyNumpad2;
- case VirtualKey::NumberPad3: return kSbKeyNumpad3;
- case VirtualKey::NumberPad4: return kSbKeyNumpad4;
- case VirtualKey::NumberPad5: return kSbKeyNumpad5;
- case VirtualKey::NumberPad6: return kSbKeyNumpad6;
- case VirtualKey::NumberPad7: return kSbKeyNumpad7;
- case VirtualKey::NumberPad8: return kSbKeyNumpad8;
- case VirtualKey::NumberPad9: return kSbKeyNumpad9;
- case VirtualKey::Multiply: return kSbKeyMultiply;
- case VirtualKey::Add: return kSbKeyAdd;
- case VirtualKey::Separator: return kSbKeySeparator;
- case VirtualKey::Subtract: return kSbKeySubtract;
- case VirtualKey::Decimal: return kSbKeyDecimal;
- case VirtualKey::Divide: return kSbKeyDivide;
- case VirtualKey::F1: return kSbKeyF1;
- case VirtualKey::F2: return kSbKeyF2;
- case VirtualKey::F3: return kSbKeyF3;
- case VirtualKey::F4: return kSbKeyF4;
- case VirtualKey::F5: return kSbKeyF5;
- case VirtualKey::F6: return kSbKeyF6;
- case VirtualKey::F7: return kSbKeyF7;
- case VirtualKey::F8: return kSbKeyF8;
- case VirtualKey::F9: return kSbKeyF9;
- case VirtualKey::F10: return kSbKeyF10;
- case VirtualKey::F11: return kSbKeyF11;
- case VirtualKey::F12: return kSbKeyF12;
- case VirtualKey::F13: return kSbKeyF13;
- case VirtualKey::F14: return kSbKeyF14;
- case VirtualKey::F15: return kSbKeyF15;
- case VirtualKey::F16: return kSbKeyF16;
- case VirtualKey::F17: return kSbKeyF17;
- case VirtualKey::F18: return kSbKeyF18;
- case VirtualKey::F19: return kSbKeyF19;
- case VirtualKey::F20: return kSbKeyF20;
- case VirtualKey::F21: return kSbKeyF21;
- case VirtualKey::F22: return kSbKeyF22;
- case VirtualKey::F23: return kSbKeyF23;
- case VirtualKey::F24: return kSbKeyF24;
- // SbKeys were originally modeled after the windows virtual key mappings [1].
- // UWP VirtualKey uses a very similar mapping, but the UWP enum does not
- // contain all of the Virtual-Key Codes.
- // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
- case 0xBA: return kSbKeyOem1; // Used for ";:" key with US keyboards.
- case 0xBB: return kSbKeyOemPlus;
- case 0xBC: return kSbKeyOemComma;
- case 0xBD: return kSbKeyOemMinus;
- case 0xBE: return kSbKeyOemPeriod;
- case 0xBF: return kSbKeyOem2; // Used for "/?" key with US keyboards.
- case 0xC0: return kSbKeyOem3; // Used for "~" with US keyboards.
- case 0xDB: return kSbKeyOem4; // Used for "[{" with US keyboards.
- case 0xDC: return kSbKeyOem5; // Used for "\|" with US keyboards.
- case 0xDD: return kSbKeyOem6; // Used for "]}" with US keyboards.
- case 0xDE: return kSbKeyOem7; // Used for quotes with US keyboards.
- case 0xDF: return kSbKeyOem8; // Used for misc. chars with US keyboards.
- case 0xE2: return kSbKeyOem102; // Used for "/" or angle bracket keys.
- case VirtualKey::NumberKeyLock: return kSbKeyNumlock;
- case VirtualKey::Scroll: return kSbKeyScroll;
- case VirtualKey::LeftShift: return kSbKeyLshift;
- case VirtualKey::RightShift: return kSbKeyRshift;
- case VirtualKey::LeftControl: return kSbKeyLcontrol;
- case VirtualKey::RightControl: return kSbKeyRcontrol;
- case VirtualKey::LeftMenu: return kSbKeyLmenu;
- case VirtualKey::RightMenu: return kSbKeyRmenu;
- case VirtualKey::GoBack: return kSbKeyBrowserBack;
- case VirtualKey::GoForward: return kSbKeyBrowserForward;
- case VirtualKey::Refresh: return kSbKeyBrowserRefresh;
- case VirtualKey::Stop: return kSbKeyBrowserStop;
- case VirtualKey::Search: return kSbKeyBrowserSearch;
- case VirtualKey::Favorites: return kSbKeyBrowserFavorites;
- case VirtualKey::GoHome: return kSbKeyBrowserHome;
- case VirtualKey::LeftButton: return kSbKeyMouse1;
- case VirtualKey::RightButton: return kSbKeyMouse2;
- case VirtualKey::MiddleButton: return kSbKeyMouse3;
- case VirtualKey::XButton1: return kSbKeyMouse4;
- case VirtualKey::XButton2: return kSbKeyMouse5;
- case VirtualKey::GamepadA: return kSbKeyGamepad1;
- case VirtualKey::GamepadB: return kSbKeyGamepad2;
- case VirtualKey::GamepadX: return kSbKeyGamepad3;
- case VirtualKey::GamepadY: return kSbKeyGamepad4;
- case VirtualKey::GamepadRightShoulder: return kSbKeyGamepadRightBumper;
- case VirtualKey::GamepadLeftShoulder: return kSbKeyGamepadLeftBumper;
- case VirtualKey::GamepadLeftTrigger: return kSbKeyGamepadLeftTrigger;
- case VirtualKey::GamepadRightTrigger: return kSbKeyGamepadRightTrigger;
- case VirtualKey::GamepadDPadUp: return kSbKeyGamepadDPadUp;
- case VirtualKey::GamepadDPadDown: return kSbKeyGamepadDPadDown;
- case VirtualKey::GamepadDPadLeft: return kSbKeyGamepadDPadLeft;
- case VirtualKey::GamepadDPadRight: return kSbKeyGamepadDPadRight;
- case VirtualKey::GamepadMenu: return kSbKeyGamepad6;
- case VirtualKey::GamepadView: return kSbKeyGamepad5;
- case VirtualKey::GamepadLeftThumbstickButton:
- return kSbKeyGamepadLeftStick;
- case VirtualKey::GamepadRightThumbstickButton:
- return kSbKeyGamepadRightStick;
- case VirtualKey::GamepadLeftThumbstickUp:
- return kSbKeyGamepadLeftStickUp;
- case VirtualKey::GamepadLeftThumbstickDown:
- return kSbKeyGamepadLeftStickDown;
- case VirtualKey::GamepadLeftThumbstickRight:
- return kSbKeyGamepadLeftStickRight;
- case VirtualKey::GamepadLeftThumbstickLeft:
- return kSbKeyGamepadLeftStickLeft;
- case VirtualKey::GamepadRightThumbstickUp:
- return kSbKeyGamepadRightStickUp;
- case VirtualKey::GamepadRightThumbstickDown:
- return kSbKeyGamepadRightStickDown;
- case VirtualKey::GamepadRightThumbstickRight:
- return kSbKeyGamepadRightStickRight;
- case VirtualKey::GamepadRightThumbstickLeft:
- return kSbKeyGamepadRightStickLeft;
- default:
- return kSbKeyUnknown;
- }
-#pragma warning(pop) // Warning 4093 (Invalid valid values from switch of enum)
-}
-
-// Returns true if a given VirtualKey is currently being held down.
-bool IsDown(CoreWindow^ sender, VirtualKey key) {
- return ((sender->GetKeyState(key) & CoreVirtualKeyStates::Down) ==
- CoreVirtualKeyStates::Down);
-}
-
-} // namespace
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-void ApplicationUwp::OnKeyEvent(
- CoreWindow^ sender, KeyEventArgs^ args, bool up) {
- args->Handled = true;
- SbInputData* data = new SbInputData();
- SbMemorySet(data, 0, sizeof(*data));
-
- data->window = window_;
- data->device_type = kSbInputDeviceTypeKeyboard;
- data->device_id = device_id();
- data->key = VirtualKeyToSbKey(args->VirtualKey);
-
- if (up) {
- data->type = kSbInputEventTypeUnpress;
- } else {
- data->type = kSbInputEventTypePress;
- }
-
- // Build up key_modifiers
- if (IsDown(sender, VirtualKey::Menu) ||
- IsDown(sender, VirtualKey::LeftMenu) ||
- IsDown(sender, VirtualKey::RightMenu)) {
- data->key_modifiers |= kSbKeyModifiersAlt;
- }
- if (IsDown(sender, VirtualKey::Control) ||
- IsDown(sender, VirtualKey::LeftControl) ||
- IsDown(sender, VirtualKey::RightControl)) {
- data->key_modifiers |= kSbKeyModifiersCtrl;
- }
- if (IsDown(sender, VirtualKey::LeftWindows) ||
- IsDown(sender, VirtualKey::RightWindows)) {
- data->key_modifiers |= kSbKeyModifiersMeta;
- }
- if (IsDown(sender, VirtualKey::Shift) ||
- IsDown(sender, VirtualKey::LeftShift) ||
- IsDown(sender, VirtualKey::RightShift)) {
- data->key_modifiers |= kSbKeyModifiersShift;
- }
-
- // Set key_location
- switch (args->VirtualKey) {
- case VirtualKey::LeftMenu:
- case VirtualKey::LeftControl:
- case VirtualKey::LeftWindows:
- case VirtualKey::LeftShift:
- data->key_location = kSbKeyLocationLeft;
- break;
- case VirtualKey::RightMenu:
- case VirtualKey::RightControl:
- case VirtualKey::RightWindows:
- case VirtualKey::RightShift:
- data->key_location = kSbKeyLocationRight;
- break;
- default:
- break;
- }
-
- DispatchAndDelete(new Event(kSbEventTypeInput, data,
- &Application::DeleteDestructor<SbInputData>));
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/async_utils.h b/src/starboard/shared/uwp/async_utils.h
deleted file mode 100644
index ad4d4e7..0000000
--- a/src/starboard/shared/uwp/async_utils.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 Google Inc. 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_UWP_ASYNC_UTILS_H_
-#define STARBOARD_SHARED_UWP_ASYNC_UTILS_H_
-
-#include <windows.h>
-
-#include <ppltasks.h>
-
-#include "starboard/log.h"
-
-using Windows::Foundation::IAsyncOperation;
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-template <typename TResult>
-TResult WaitForResult(IAsyncOperation<TResult>^ operation) {
- using concurrency::task_continuation_context;
- HANDLE event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
- concurrency::create_task(operation,
- task_continuation_context::use_arbitrary())
- .then([&event](TResult result) {
- SB_UNREFERENCED_PARAMETER(result);
- BOOL success = SetEvent(event);
- SB_DCHECK(success);
- }, task_continuation_context::use_arbitrary());
- DWORD return_value = WaitForSingleObject(event, INFINITE);
- SB_DCHECK(return_value == WAIT_OBJECT_0);
- CloseHandle(event);
- return operation->GetResults();
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_ASYNC_UTILS_H_
diff --git a/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp b/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
deleted file mode 100644
index 4e56f13..0000000
--- a/src/starboard/shared/uwp/cobalt/cobalt_platform.gyp
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-
-{
- 'variables': {
- 'sb_pedantic_warnings': 1,
- },
- 'targets': [
- {
- 'target_name': 'cobalt_platform',
- 'type': 'static_library',
- 'sources': [
- 'xb1_media_session_client.cc',
- 'xb1_media_session_client.h',
- 'xb1_media_session_updates.cc',
- 'xhr_modify_headers.cc',
- ],
- 'dependencies': [
- '<(DEPTH)/cobalt/media_session/media_session.gyp:media_session',
- ],
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'AdditionalOptions': [
- '/ZW', # Windows Runtime
- '/ZW:nostdlib', # Windows Runtime, no default #using
- '/EHsc', # C++ exceptions (required with /ZW)
- '/FU"<(visual_studio_install_path)/lib/x86/store/references/platform.winmd"',
- '/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.FoundationContract/3.0.0.0/Windows.Foundation.FoundationContract.winmd"',
- '/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.UniversalApiContract/4.0.0.0/Windows.Foundation.UniversalApiContract.winmd"',
- ],
- },
- },
- 'defines': [
- # VS2017 always defines this for UWP apps
- 'WINAPI_FAMILY=WINAPI_FAMILY_APP',
- # VS2017 always defines this for UWP apps
- '__WRL_NO_DEFAULT_LIB__',
- ],
- },
- ],
-}
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
deleted file mode 100644
index 75bb089..0000000
--- a/src/starboard/shared/uwp/cobalt/xhr_modify_headers.cc
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2017 Google Inc. 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 "cobalt/xhr/xhr_modify_headers.h"
-
-#include "base/logging.h"
-#include "base/synchronization/waitable_event.h"
-
-#include "starboard/mutex.h"
-#include "starboard/once.h"
-#include "starboard/shared/uwp/app_accessors.h"
-#include "starboard/shared/uwp/async_utils.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-
-using Windows::Security::Authentication::Web::Core::WebTokenResponse;
-using Windows::Security::Authentication::Web::Core::WebTokenRequestResult;
-using Windows::Security::Authentication::Web::Core::WebTokenRequestStatus;
-using Windows::System::UserAuthenticationStatus;
-
-namespace sbwin32 = starboard::shared::win32;
-namespace sbuwp = starboard::shared::uwp;
-
-namespace {
-
-const char kXboxLiveAccountProviderId[] = "https://xsts.auth.xboxlive.com";
-
-// The name of the header to send the STS token out on.
-const char kXauthHeaderName[] = "Authorization";
-
-// The prefix for the value of the Authorization header when there is an XAuth
-// token to attach to the request. The token itself should directly follow this
-// prefix.
-const char kXauthHeaderPrefix[] = "XBL3.0 x=";
-
-// The name of the header to detect on requests as a trigger to add an STS token
-// for the given RelyingPartyId (the value of the header). The header itself
-// will be removed from the outgoing request, and replaced with an Authorization
-// header with a valid STS token for the current primary user.
-const base::StringPiece kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
-
-class LastTokenCache {
- bool has_last_token_ = false;
- std::string last_token_;
- starboard::Mutex mutex_;
- public:
- bool MaybeGetLastToken(std::string* out) {
- starboard::ScopedLock lock(mutex_);
- out->assign(last_token_);
- return has_last_token_;
- }
-
- void SetLastToken(const std::string& in) {
- starboard::ScopedLock lock(mutex_);
- has_last_token_ = true;
- last_token_ = in;
- }
-};
-
-SB_ONCE_INITIALIZE_FUNCTION(LastTokenCache, GetLastTokenCache);
-
-bool PopulateToken(const std::string& relying_party, std::string* out) {
- out->clear();
- DCHECK(!sbuwp::GetDispatcher()->HasThreadAccess)
- << "Must not be called from the UWP main thread";
-
- WebTokenRequestResult^ token_result;
- try {
- token_result = sbuwp::TryToFetchSsoToken(relying_party).get();
- } catch (Platform::Exception^) {
- token_result = nullptr;
- }
- if (!token_result ||
- token_result->ResponseStatus != WebTokenRequestStatus::Success ||
- token_result->ResponseData->Size != 1) {
- // The token is valid for 16 hours, however there appears to be
- // no easy way for us to check on the client side when it expires.
- // We must use it hourly.
- // However, it appears that sometimes asking for a new token will
- // fail for unknown reasons.
- // Therefore, simply use the last token we had in case it's still good.
- // Note that while "relying_party" may change slightly, it
- // never changes any of the token's attributes.
- return GetLastTokenCache()->MaybeGetLastToken(out);
- }
-
- WebTokenResponse^ token_response = token_result->ResponseData->GetAt(0);
- *out = sbwin32::platformStringToString(token_response->Token);
- GetLastTokenCache()->SetLastToken(
- sbwin32::platformStringToString(token_response->Token));
- // Always get the token through the cache so the exceptional case
- // is less exceptional.
- return GetLastTokenCache()->MaybeGetLastToken(out);
-}
-
-void AppendUrlPath(const std::string& path, std::string* url_parameter) {
- DCHECK(url_parameter);
-
- if (path.empty()) {
- return;
- }
-
- std::string& url(*url_parameter);
-
- // if path starts with a '/' and url ends with a '/', remove trailing slash
- // from url before appending the path
- if (!url.empty() && *(url.rbegin()) == '/' && path[0] == '/') {
- url.resize(url.size() - 1);
- }
- url.append(path);
-}
-
-bool StringStartsWith(const std::string& str, const char* starts_with) {
- size_t starts_with_length = SbStringGetLength(starts_with);
- if (str.size() < starts_with_length) {
- return false;
- }
-
- std::string sub_str = str.substr(0, starts_with_length);
- return sub_str == starts_with;
-}
-
-} // namespace
-
-namespace cobalt {
-namespace xhr {
-
-void CobaltXhrModifyHeader(const GURL& request_url,
- net::HttpRequestHeaders* headers) {
- DCHECK(headers);
-
- std::string relying_party;
- bool trigger_header_found = headers->GetHeader(kXauthTriggerHeaderName,
- &relying_party);
-
- if (!trigger_header_found) {
- return;
- }
-
- if (request_url.has_path()) {
- std::string request_url_path = request_url.path();
- AppendUrlPath(request_url_path, &relying_party);
- }
-
- std::string out_string;
- if (!PopulateToken(relying_party, &out_string)) {
- return;
- }
- headers->RemoveHeader(kXauthTriggerHeaderName);
- headers->SetHeader(kXauthHeaderName, out_string);
-}
-
-} // namespace xhr
-} // namespace cobalt
diff --git a/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc b/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc
deleted file mode 100644
index 88aa32c..0000000
--- a/src/starboard/shared/uwp/cobalt/xhr_modify_headers_test.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017 Google Inc. 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 "cobalt/xhr/xhr_modify_headers.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-namespace cobalt {
-
-using ::cobalt::xhr::CobaltXhrModifyHeader;
-using net::HttpRequestHeaders;
-const std::string kUrlString = "https://example.com/abc/xyz";
-
-TEST(XHRModificationTest, EmptyHeaders) {
- HttpRequestHeaders headers;
- GURL url(kUrlString);
- CobaltXhrModifyHeader(url, &headers);
- EXPECT_TRUE(headers.IsEmpty());
-}
-
-TEST(XHRModificationTest, HeaderNotFound) {
- HttpRequestHeaders headers;
- headers.SetHeader("Authorization", "ABC");
- GURL url(kUrlString);
- CobaltXhrModifyHeader(url, &headers);
- EXPECT_FALSE(!headers.IsEmpty());
- std::string headers_serialized = headers.ToString();
- EXPECT_STREQ(headers_serialized.c_str(), "Authorization: ABC\r\n\r\n");
-}
-
-TEST(XHRModificationTest, HeaderReplaced) {
- HttpRequestHeaders headers;
- static const char* kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
- headers.SetHeader(kXauthTriggerHeaderName, "ABC");
- EXPECT_TRUE(headers.HasHeader(kXauthTriggerHeaderName));
- GURL url(kUrlString);
- CobaltXhrModifyHeader(url, &headers);
- EXPECT_FALSE(headers.HasHeader(kXauthTriggerHeaderName));
- std::string headers_serialized = headers.ToString();
- EXPECT_TRUE(headers_serialized.find("Authorization: XBL3.0 x=") !=
- std::string::npos);
-}
-
-TEST(XHRModificationTest, MultipleHeaders) {
- HttpRequestHeaders headers;
- static const char* kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
- headers.SetHeader("H1", "h1");
- headers.SetHeader("H2", "h2");
- headers.SetHeader("H3", "h3");
- EXPECT_TRUE(headers.HasHeader(kXauthTriggerHeaderName));
- GURL url(kUrlString);
- CobaltXhrModifyHeader(url, &headers);
- EXPECT_TRUE(headers.HasHeader("H1"));
- EXPECT_TRUE(headers.HasHeader("H2"));
- EXPECT_TRUE(headers.HasHeader("H3"));
- EXPECT_FALSE(headers.HasHeader(kXauthTriggerHeaderName));
- std::string headers_serialized = headers.ToString();
- EXPECT_TRUE(headers_serialized.find("Authorization: XBL3.0 x=") !=
- std::string::npos);
-}
-
-} // namespace cobalt
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/decode_target_internal.cc b/src/starboard/shared/uwp/decode_target_internal.cc
deleted file mode 100644
index 3ed4863..0000000
--- a/src/starboard/shared/uwp/decode_target_internal.cc
+++ /dev/null
@@ -1,383 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/uwp/decode_target_internal.h"
-
-#include "starboard/configuration.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "third_party/angle/include/EGL/egl.h"
-#include "third_party/angle/include/EGL/eglext.h"
-#include "third_party/angle/include/GLES2/gl2.h"
-#include "third_party/angle/include/GLES2/gl2ext.h"
-
-namespace {
-
-using Microsoft::WRL::ComPtr;
-using starboard::shared::win32::CheckResult;
-
-// {3C3A43AB-C69B-46C9-AA8D-B0CFFCD4596D}
-const GUID kCobaltNv12BindChroma = {
- 0x3c3a43ab,
- 0xc69b,
- 0x46c9,
- {0xaa, 0x8d, 0xb0, 0xcf, 0xfc, 0xd4, 0x59, 0x6d}};
-
-// {C62BF18D-B5EE-46B1-9C31-F61BD8AE3B0D}
-const GUID kCobaltDxgiBuffer = {
- 0Xc62bf18d,
- 0Xb5ee,
- 0X46b1,
- {0X9c, 0X31, 0Xf6, 0X1b, 0Xd8, 0Xae, 0X3b, 0X0d}};
-
-ComPtr<ID3D11Texture2D> CreateEmptyTexture(
- const ComPtr<ID3D11Device>& d3d_device,
- int width,
- int height) {
- ComPtr<ID3D11Texture2D> texture;
- D3D11_TEXTURE2D_DESC texture_desc = {};
- texture_desc.Width = width;
- texture_desc.Height = height;
- texture_desc.MipLevels = 1;
- texture_desc.ArraySize = 1;
- texture_desc.Format = DXGI_FORMAT_NV12;
- texture_desc.SampleDesc.Count = 1;
- texture_desc.SampleDesc.Quality = 0;
- texture_desc.Usage = D3D11_USAGE_DEFAULT;
- texture_desc.BindFlags =
- D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
- CheckResult(d3d_device->CreateTexture2D(&texture_desc, nullptr,
- texture.GetAddressOf()));
- return texture;
-}
-
-void UpdateTexture(
- const ComPtr<ID3D11Texture2D>& texture,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area) {
- ComPtr<IMFMediaBuffer> media_buffer;
- CheckResult(video_sample->GetBufferByIndex(0, media_buffer.GetAddressOf()));
-
- ComPtr<IMFDXGIBuffer> dxgi_buffer;
- CheckResult(media_buffer.As(&dxgi_buffer));
-
- ComPtr<ID3D11Texture2D> input_texture;
- CheckResult(dxgi_buffer->GetResource(IID_PPV_ARGS(&input_texture)));
-
- // The VideoProcessor needs to know what subset of the decoded
- // frame contains active pixels that should be displayed to the user.
- video_context->VideoProcessorSetStreamSourceRect(video_processor.Get(), 0,
- TRUE, &video_area);
-
- D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_desc = {};
- input_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
- input_desc.Texture2D.MipSlice = 0;
- dxgi_buffer->GetSubresourceIndex(&input_desc.Texture2D.ArraySlice);
-
- ComPtr<ID3D11VideoProcessorInputView> input_view;
- CheckResult(video_device->CreateVideoProcessorInputView(
- input_texture.Get(), video_enumerator.Get(), &input_desc,
- input_view.GetAddressOf()));
-
- D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC output_desc = {};
- output_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
- output_desc.Texture2D.MipSlice = 0;
-
- ComPtr<ID3D11VideoProcessorOutputView> output_view;
- CheckResult(video_device->CreateVideoProcessorOutputView(
- texture.Get(), video_enumerator.Get(), &output_desc,
- output_view.GetAddressOf()));
-
- // We have a single video stream, which is enabled for display.
- D3D11_VIDEO_PROCESSOR_STREAM stream_info = {};
- stream_info.Enable = TRUE;
- stream_info.pInputSurface = input_view.Get();
- CheckResult(video_context->VideoProcessorBlt(
- video_processor.Get(), output_view.Get(), 0, 1, &stream_info));
-}
-
-ComPtr<ID3D11Texture2D> CreateVPXTexture(const ComPtr<ID3D11Device>& d3d_device,
- const vpx_image_t* img,
- BYTE* frame_buf_nv12) {
- ComPtr<ID3D11Texture2D> texture;
- D3D11_TEXTURE2D_DESC texture_desc = {};
- texture_desc.Width = img->w;
- texture_desc.Height = img->h;
- texture_desc.MipLevels = 1;
- texture_desc.ArraySize = 1;
- texture_desc.Format = DXGI_FORMAT_NV12;
- texture_desc.SampleDesc.Count = 1;
- texture_desc.SampleDesc.Quality = 0;
- texture_desc.Usage = D3D11_USAGE_DEFAULT;
- texture_desc.BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE;
-
- BYTE* pData = frame_buf_nv12;
- SbMemoryCopy(pData, img->planes[VPX_PLANE_Y], img->w * img->h);
- pData += texture_desc.Width * texture_desc.Height;
-
- unsigned int j = 0;
- for (unsigned int i = 0; i < (img->w * img->h / 4); i++) {
- pData[j++] = img->planes[VPX_PLANE_U][i];
- pData[j++] = img->planes[VPX_PLANE_V][i];
- }
-
- D3D11_SUBRESOURCE_DATA tSData;
- tSData.pSysMem = frame_buf_nv12;
- tSData.SysMemPitch = img->w;
- tSData.SysMemSlicePitch = img->w * img->h + (img->w * img->h / 2);
- CheckResult(d3d_device->CreateTexture2D(&texture_desc, &tSData,
- texture.GetAddressOf()));
- return texture;
-}
-
-} // namespace
-void SbDecodeTargetPrivate::CreateGLSurfacesNV12(
- const ComPtr<ID3D11Texture2D>& texture,
- int display_width,
- int display_height) {
- SbDecodeTargetInfoPlane* planeY = &(info.planes[kSbDecodeTargetPlaneY]);
- SbDecodeTargetInfoPlane* planeUV = &(info.planes[kSbDecodeTargetPlaneUV]);
-
- planeY->width = info.width;
- planeY->height = info.height;
- planeY->content_region.left = 0;
- planeY->content_region.top = display_height;
- planeY->content_region.right = display_width;
- planeY->content_region.bottom = 0;
-
- planeUV->width = info.width / 2;
- planeUV->height = info.height / 2;
- planeUV->content_region.left = planeY->content_region.left / 2;
- planeUV->content_region.top = planeY->content_region.top / 2;
- planeUV->content_region.right = planeY->content_region.right / 2;
- planeUV->content_region.bottom = planeY->content_region.bottom / 2;
-
- EGLint luma_texture_attributes[] = {EGL_WIDTH,
- static_cast<EGLint>(info.width),
- EGL_HEIGHT,
- static_cast<EGLint>(info.height),
- EGL_TEXTURE_TARGET,
- EGL_TEXTURE_2D,
- EGL_TEXTURE_FORMAT,
- EGL_TEXTURE_RGBA,
- EGL_NONE};
-
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
- EGLConfig config;
- EGLint attribute_list[] = {EGL_SURFACE_TYPE, // this must be first
- EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
- EGL_RED_SIZE,
- 8,
- EGL_GREEN_SIZE,
- 8,
- EGL_BLUE_SIZE,
- 8,
- EGL_ALPHA_SIZE,
- 8,
- EGL_BIND_TO_TEXTURE_RGBA,
- EGL_TRUE,
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES2_BIT,
- EGL_NONE};
-
- EGLint num_configs;
- bool ok = eglChooseConfig(display, attribute_list, &config, 1, &num_configs);
- SB_DCHECK(ok);
- SB_DCHECK(num_configs == 1);
-
- GLuint gl_textures[2] = {0};
- glGenTextures(2, gl_textures);
- SB_DCHECK(glGetError() == GL_NO_ERROR);
-
- // This tells ANGLE that the texture it creates should draw
- // the luma channel on R8.
- HRESULT hr = texture->SetPrivateData(kCobaltNv12BindChroma, 0, nullptr);
- SB_DCHECK(SUCCEEDED(hr));
-
- surface[0] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
- texture.Get(), config,
- luma_texture_attributes);
-
- SB_DCHECK(surface[0] != EGL_NO_SURFACE);
-
- glBindTexture(GL_TEXTURE_2D, gl_textures[0]);
- SB_DCHECK(glGetError() == GL_NO_ERROR);
-
- ok = eglBindTexImage(display, surface[0], EGL_BACK_BUFFER);
- SB_DCHECK(ok);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- planeY->texture = gl_textures[0];
- planeY->gl_texture_target = GL_TEXTURE_2D;
- planeY->gl_texture_format = GL_RED_EXT;
-
- // This tells ANGLE that the texture it creates should draw
- // the chroma channel on R8G8.
- bool bind_chroma = true;
- hr = texture->SetPrivateData(kCobaltNv12BindChroma, 1, &bind_chroma);
- SB_DCHECK(SUCCEEDED(hr));
-
- EGLint chroma_texture_attributes[] = {EGL_WIDTH,
- static_cast<EGLint>(info.width) / 2,
- EGL_HEIGHT,
- static_cast<EGLint>(info.height) / 2,
- EGL_TEXTURE_TARGET,
- EGL_TEXTURE_2D,
- EGL_TEXTURE_FORMAT,
- EGL_TEXTURE_RGBA,
- EGL_NONE};
- surface[1] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
- texture.Get(), config,
- chroma_texture_attributes);
-
- SB_DCHECK(surface[1] != EGL_NO_SURFACE);
-
- glBindTexture(GL_TEXTURE_2D, gl_textures[1]);
- SB_DCHECK(glGetError() == GL_NO_ERROR);
-
- ok = eglBindTexImage(display, surface[1], EGL_BACK_BUFFER);
- SB_DCHECK(ok);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- planeUV->texture = gl_textures[1];
- planeUV->gl_texture_target = GL_TEXTURE_2D;
- planeUV->gl_texture_format = GL_RG_EXT;
-
- hr = texture->SetPrivateData(kCobaltDxgiBuffer, 0, nullptr);
- SB_DCHECK(SUCCEEDED(hr));
-}
-
-SbDecodeTargetPrivate::SbDecodeTargetPrivate(
- const ComPtr<ID3D11Device>& d3d_device,
- const vpx_image_t* img,
- BYTE* frame_buf_nv12)
- : refcount(1), d3d_device_(d3d_device) {
- SbMemorySet(&info, 0, sizeof(info));
- info.format = kSbDecodeTargetFormat2PlaneYUVNV12;
- info.is_opaque = true;
- info.width = img->w;
- info.height = img->h;
-
- d3d_texture = CreateVPXTexture(d3d_device, img, frame_buf_nv12);
- CreateGLSurfacesNV12(d3d_texture, img->d_w, img->d_h);
-}
-
-SbDecodeTargetPrivate::SbDecodeTargetPrivate(
- const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area)
- : refcount(1) {
- SbMemorySet(&info, 0, sizeof(info));
- info.format = kSbDecodeTargetFormat2PlaneYUVNV12;
- info.is_opaque = true;
- info.width = video_area.right;
- info.height = video_area.bottom;
-
- d3d_texture = CreateEmptyTexture(d3d_device, info.width, info.height);
- if (video_sample) {
- UpdateTexture(d3d_texture, video_device, video_context, video_enumerator,
- video_processor, video_sample, video_area);
- }
- CreateGLSurfacesNV12(d3d_texture, info.width, info.height);
-}
-
-SbDecodeTargetPrivate::~SbDecodeTargetPrivate() {
- glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneY].texture));
- glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneUV].texture));
-
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
- eglReleaseTexImage(display, surface[0], EGL_BACK_BUFFER);
- eglDestroySurface(display, surface[0]);
-
- eglReleaseTexImage(display, surface[1], EGL_BACK_BUFFER);
- eglDestroySurface(display, surface[1]);
-}
-
-bool SbDecodeTargetPrivate::Update(
- const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area) {
- // Only allow updating if this is the only reference. Otherwise the update
- // may change something that's currently being used.
- if (SbAtomicNoBarrier_Load(&refcount) > 1) {
- return false;
- }
-
- // The decode target info must be compatible. The resolution should match
- // exactly, otherwise the shader may sample invalid texels along the
- // texture border.
- if (info.format != kSbDecodeTargetFormat2PlaneYUVNV12 ||
- info.is_opaque != true || info.width != video_area.right ||
- info.height != video_area.bottom) {
- return false;
- }
-
- SB_UNREFERENCED_PARAMETER(d3d_device);
- UpdateTexture(d3d_texture, video_device, video_context, video_enumerator,
- video_processor, video_sample, video_area);
- return true;
-}
-
-void SbDecodeTargetPrivate::AddRef() {
- SbAtomicBarrier_Increment(&refcount, 1);
-}
-
-void SbDecodeTargetPrivate::Release() {
- int new_count = SbAtomicBarrier_Increment(&refcount, -1);
- SB_DCHECK(new_count >= 0);
- if (new_count == 0) {
- delete this;
- }
-}
-
-void SbDecodeTargetRelease(SbDecodeTarget decode_target) {
- if (SbDecodeTargetIsValid(decode_target)) {
- decode_target->Release();
- }
-}
-
-SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget decode_target) {
- // Note that kSbDecodeTargetFormat2PlaneYUVNV12 represents DXGI_FORMAT_NV12.
- SB_DCHECK(kSbDecodeTargetFormat2PlaneYUVNV12 == decode_target->info.format);
- return decode_target->info.format;
-}
-
-bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
- SbDecodeTargetInfo* out_info) {
- if (!out_info || !SbMemoryIsZero(out_info, sizeof(*out_info))) {
- SB_DCHECK(false) << "out_info must be zeroed out.";
- return false;
- }
- SbMemoryCopy(out_info, &decode_target->info, sizeof(*out_info));
- return true;
-}
\ No newline at end of file
diff --git a/src/starboard/shared/uwp/decode_target_internal.h b/src/starboard/shared/uwp/decode_target_internal.h
deleted file mode 100644
index e3725b2..0000000
--- a/src/starboard/shared/uwp/decode_target_internal.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2018 Google Inc. 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_UWP_DECODE_TARGET_INTERNAL_H_
-#define STARBOARD_SHARED_UWP_DECODE_TARGET_INTERNAL_H_
-
-#include <D3d11_1.h>
-#include <mfidl.h>
-#include <wrl/client.h>
-
-#include "starboard/atomic.h"
-#include "starboard/decode_target.h"
-#include "starboard/shared/libvpx_xb1/vpx_xb1_video_decoder.h"
-
-struct SbDecodeTargetPrivate {
- template <typename T>
- using ComPtr = Microsoft::WRL::ComPtr<T>;
-
- SbAtomic32 refcount;
-
- // Publicly accessible information about the decode target.
- SbDecodeTargetInfo info;
-
- ComPtr<ID3D11Texture2D> d3d_texture;
-
- // EGLSurface is defined as void* in "third_party/angle/include/EGL/egl.h".
- // Use void* directly here to avoid `egl.h` being included broadly.
- void* surface[2];
-
- ComPtr<ID3D11Texture2D> d3d_textures_[3];
- void* surface_[3] = {};
- ComPtr<ID3D11Device> d3d_device_ = nullptr;
-
- SbDecodeTargetPrivate(
- const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area);
-
- // This constructor works properly for Xbox One platform only
- SbDecodeTargetPrivate(const ComPtr<ID3D11Device>& d3d_device,
- const vpx_image_t* img,
- BYTE* frame_buf_nv12);
-
- ~SbDecodeTargetPrivate();
-
- // Update the existing texture with the given video_sample's data.
- // If the current object is not compatible with the new video_sample, then
- // this will return false, and the caller should just create a new
- // decode target for the sample.
- bool Update(const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area);
- void CreateGLSurfacesNV12(const ComPtr<ID3D11Texture2D>& texture,
- int display_width,
- int display_height);
- void AddRef();
- void Release();
-};
-
-#endif // STARBOARD_SHARED_UWP_DECODE_TARGET_INTERNAL_H_
diff --git a/src/starboard/shared/uwp/get_home_directory.cc b/src/starboard/shared/uwp/get_home_directory.cc
deleted file mode 100644
index 6c08a3b..0000000
--- a/src/starboard/shared/uwp/get_home_directory.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 Google Inc. 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/log.h"
-#include "starboard/shared/nouser/user_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-#include "starboard/system.h"
-
-using Windows::Storage::ApplicationData;
-
-namespace sbwin32 = starboard::shared::win32;
-
-namespace starboard {
-namespace shared {
-namespace nouser {
-
-bool GetHomeDirectory(SbUser user, char* out_path, int path_size) {
- if (user != SbUserGetCurrent()) {
- return false;
- }
- std::string home_directory = sbwin32::platformStringToString(
- ApplicationData::Current->LocalFolder->Path);
- return SbStringCopy(out_path, home_directory.c_str(), path_size);
-}
-
-} // namespace nouser
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/log_file_impl.cc b/src/starboard/shared/uwp/log_file_impl.cc
deleted file mode 100644
index bc1f6e0..0000000
--- a/src/starboard/shared/uwp/log_file_impl.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/uwp/log_file_impl.h"
-
-#include <ppltasks.h>
-#include <string>
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/mutex.h"
-#include "starboard/once.h"
-#include "starboard/shared/uwp/log_writer_uwp.h"
-#include "starboard/shared/uwp/log_writer_win32.h"
-#include "starboard/string.h"
-
-using Windows::Storage::StorageFolder;
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-namespace {
-
-class LogFileImpl {
- public:
- static LogFileImpl* GetInstance();
-
- void OpenUWP(StorageFolder^ folder, const char* filename) {
- ScopedLock lock(mutex_);
- impl_.reset();
- impl_ = CreateLogWriterUWP(folder, filename);
- }
-
- void OpenWin32(const char* path) {
- ScopedLock lock(mutex_);
- impl_.reset();
- impl_ = CreateLogWriterWin32(path);
- }
-
- void Close() {
- ScopedLock lock(mutex_);
- impl_.reset();
- }
-
- void Write(const char* text, int text_length) {
- ScopedLock lock(mutex_);
- if (impl_) {
- impl_->Write(text, text_length);
- }
- }
-
- private:
- LogFileImpl() {}
- starboard::Mutex mutex_;
- starboard::scoped_ptr<ILogWriter> impl_;
-};
-
-SB_ONCE_INITIALIZE_FUNCTION(LogFileImpl, LogFileImpl::GetInstance);
-
-} // namespace
-
-void CloseLogFile() {
- LogFileImpl::GetInstance()->Close();
-}
-
-void OpenLogFileUWP(StorageFolder^ folder, const char* filename) {
- LogFileImpl::GetInstance()->OpenUWP(folder, filename);
-}
-
-void OpenLogFileWin32(const char* path) {
- LogFileImpl::GetInstance()->OpenWin32(path);
-}
-
-void WriteToLogFile(const char* text, int text_length) {
- if (text_length <= 0) {
- return;
- }
- LogFileImpl::GetInstance()->Write(text, text_length);
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/log_file_impl.h b/src/starboard/shared/uwp/log_file_impl.h
deleted file mode 100644
index cfd1c17..0000000
--- a/src/starboard/shared/uwp/log_file_impl.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// This header provides a mechanism for multiple Android logging
-// formats to share a single log file handle.
-
-#ifndef STARBOARD_SHARED_UWP_LOG_FILE_IMPL_H_
-#define STARBOARD_SHARED_UWP_LOG_FILE_IMPL_H_
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-void OpenLogFileUWP(Windows::Storage::StorageFolder^ folder,
- const char* filename);
-
-void OpenLogFileWin32(const char* path);
-
-void CloseLogFile();
-void WriteToLogFile(const char* text, int text_length);
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_LOG_FILE_IMPL_H_
diff --git a/src/starboard/shared/uwp/log_raw.cc b/src/starboard/shared/uwp/log_raw.cc
deleted file mode 100644
index e7a73c7..0000000
--- a/src/starboard/shared/uwp/log_raw.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-#include <stdio.h>
-#include <windows.h>
-
-#include "starboard/shared/starboard/net_log.h"
-#include "starboard/shared/uwp/log_file_impl.h"
-#include "starboard/string.h"
-
-namespace sbuwp = starboard::shared::uwp;
-
-namespace {
-
-void OutputToDebugConsole(const char* message) {
- // OutputDebugStringA may stall for multiple seconds if the output string is
- // too long. Split |message| into shorter strings for output.
- char buffer[512];
- for (;;) {
- errno_t result = strncpy_s(buffer, message, _TRUNCATE);
- if (result == 0) {
- OutputDebugStringA(buffer);
- break;
- } else if (result == STRUNCATE) {
- OutputDebugStringA(buffer);
- message += sizeof(buffer) - 1;
- } else {
- break;
- }
- }
-}
-
-} // namespace.
-
-void SbLogRaw(const char* message) {
- fprintf(stderr, "%s", message);
- sbuwp::WriteToLogFile(
- message, static_cast<int>(SbStringGetLength(message)));
-
- OutputToDebugConsole(message);
- starboard::shared::starboard::NetLogWrite(message);
-}
diff --git a/src/starboard/shared/uwp/log_raw_format.cc b/src/starboard/shared/uwp/log_raw_format.cc
deleted file mode 100644
index 4664fbe..0000000
--- a/src/starboard/shared/uwp/log_raw_format.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-#include <stdio.h>
-
-void SbLogRawFormat(const char* format, va_list arguments) {
- char log_buffer[16 * 1024];
- vsnprintf_s(log_buffer, _TRUNCATE, format, arguments);
- SbLogRaw(log_buffer);
-}
diff --git a/src/starboard/shared/uwp/log_writer_interface.h b/src/starboard/shared/uwp/log_writer_interface.h
deleted file mode 100644
index 865583b..0000000
--- a/src/starboard/shared/uwp/log_writer_interface.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2018 Google Inc. 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_UWP_LOG_WRITER_INTERFACE_H_
-#define STARBOARD_SHARED_UWP_LOG_WRITER_INTERFACE_H_
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-class ILogWriter {
- public:
- ILogWriter() {}
- virtual ~ILogWriter() {}
- virtual void Write(const char* content, int size) = 0;
-};
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_LOG_WRITER_INTERFACE_H_
diff --git a/src/starboard/shared/uwp/log_writer_uwp.cc b/src/starboard/shared/uwp/log_writer_uwp.cc
deleted file mode 100644
index dc4959c..0000000
--- a/src/starboard/shared/uwp/log_writer_uwp.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/uwp/log_writer_uwp.h"
-
-#include <string>
-
-#include "starboard/common/semaphore.h"
-#include "starboard/log.h"
-#include "starboard/mutex.h"
-#include "starboard/once.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-
-using Windows::Foundation::AsyncOperationCompletedHandler;
-using Windows::Foundation::AsyncStatus;
-using Windows::Foundation::IAsyncOperation;
-using Windows::Storage::FileAccessMode;
-using Windows::Storage::StorageFile;
-using Windows::Storage::StorageFolder;
-using Windows::Storage::Streams::DataWriter;
-using Windows::Storage::Streams::IRandomAccessStream;
-using Windows::Storage::Streams::IOutputStream;
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-namespace {
-
-class SharedMutex {
- public:
- SharedMutex() : sema_(1) {}
- void Acquire() { sema_.Take(); }
- void Release() { sema_.Put(); }
- bool AcquireTry() { return sema_.TakeTry(); }
- private:
- Semaphore sema_;
-};
-
-class LogWriterUWP : public ILogWriter {
- public:
- LogWriterUWP(StorageFolder^ folder, const char* filename) {
- OpenLogFile(folder, filename);
- }
-
- ~LogWriterUWP() {
- CloseLogFile();
- }
-
- void Write(const char* content, int size) override {
- WriteToLogFile(content, size);
- }
-
- private:
- // SbMutex is not reentrant, so factor out close log file functionality for
- // use by other functions.
- void CloseLogFile_Locked() {
- log_writer_ = nullptr;
- }
-
- void CloseLogFile() {
- log_mutex_.Acquire();
- CloseLogFile_Locked();
- log_mutex_.Release();
- }
-
- void OpenLogFile(StorageFolder^ folder, const char* filename) {
- std::wstring wfilename = win32::CStringToWString(filename);
-
- log_mutex_.Acquire();
- CloseLogFile_Locked();
-
- // Manually set the completion callback function instead of using
- // concurrency::create_task() since those tasks may not execute before the
- // UI thread wants the log_mutex_ to output another log.
- auto task = folder->CreateFileAsync(
- ref new Platform::String(wfilename.c_str()),
- Windows::Storage::CreationCollisionOption::ReplaceExisting);
- task->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>(
- [folder, this](IAsyncOperation<StorageFile^>^ op, AsyncStatus) {
- if (op->Status != AsyncStatus::Completed) {
- this->log_mutex_.Release();
- SB_LOG(ERROR) << "Unable to open log file in folder "
- << win32::platformStringToString(folder->Name);
- return;
- }
-
- try {
- auto task = op->GetResults()->OpenAsync(FileAccessMode::ReadWrite);
- task->Completed = ref new
- AsyncOperationCompletedHandler<IRandomAccessStream^>(
- [this](IAsyncOperation<IRandomAccessStream^>^ op, AsyncStatus) {
- this->log_writer_ = ref new DataWriter(
- op->GetResults()->GetOutputStreamAt(0));
- this->log_mutex_.Release();
- });
- } catch(Platform::Exception^) {
- this->log_mutex_.Release();
- SB_LOG(ERROR) << "Unable to open log file in folder "
- << win32::platformStringToString(folder->Name);
- }
- });
- }
-
- void WriteToLogFile(const char* text, int text_length) {
- if (text_length <= 0) {
- return;
- }
- log_mutex_.Acquire();
- if (log_writer_) {
- log_writer_->WriteBytes(ref new Platform::Array<unsigned char>(
- (unsigned char*) text, text_length));
-
- // Manually set the completion callback function instead of using
- // concurrency::create_task() since those tasks may not execute before the
- // UI thread wants the log_mutex_ to output another log.
- auto task = log_writer_->StoreAsync();
- task->Completed = ref new AsyncOperationCompletedHandler<unsigned int>(
- [this](IAsyncOperation<unsigned int>^, AsyncStatus) {
- auto task = this->log_writer_->FlushAsync();
- task->Completed = ref new AsyncOperationCompletedHandler<bool>(
- [this](IAsyncOperation<bool>^, AsyncStatus) {
- this->log_mutex_.Release();
- });
- });
- } else {
- log_mutex_.Release();
- }
- }
- SharedMutex log_mutex_;
- // The Windows Storage API must be used in order to access files in
- // privileged areas (e.g. KnownFolders::RemovableDevices). The win32
- // file API used by SbFile returns access denied errors in these situations.
- DataWriter^ log_writer_ = nullptr;
-};
-} // namespace.
-
-scoped_ptr<ILogWriter> CreateLogWriterUWP(
- Windows::Storage::StorageFolder^ folder, const char* filename) {
- scoped_ptr<ILogWriter> output(new LogWriterUWP(folder, filename));
- return output.Pass();
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/log_writer_uwp.h b/src/starboard/shared/uwp/log_writer_uwp.h
deleted file mode 100644
index be0978b..0000000
--- a/src/starboard/shared/uwp/log_writer_uwp.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2018 Google Inc. 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_UWP_LOG_WRITER_UWP_H_
-#define STARBOARD_SHARED_UWP_LOG_WRITER_UWP_H_
-
-#include <ppltasks.h>
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/shared/uwp/log_writer_interface.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-starboard::scoped_ptr<ILogWriter> CreateLogWriterUWP(
- Windows::Storage::StorageFolder^ folder, const char* filename);
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_LOG_WRITER_UWP_H_
diff --git a/src/starboard/shared/uwp/log_writer_win32.cc b/src/starboard/shared/uwp/log_writer_win32.cc
deleted file mode 100644
index 990e11a..0000000
--- a/src/starboard/shared/uwp/log_writer_win32.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/uwp/log_writer_win32.h"
-
-#include <string>
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/common/semaphore.h"
-#include "starboard/file.h"
-#include "starboard/log.h"
-#include "starboard/string.h"
-
-using starboard::scoped_ptr;
-using starboard::ScopedFile;
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-namespace {
-
-class LogWriterWin32 : public ILogWriter {
- public:
- explicit LogWriterWin32(const std::string& file_path) {
- SbFileError out_error = kSbFileOk;
- bool created_ok = false;
- file_.reset(
- new ScopedFile(file_path.c_str(),
- kSbFileCreateAlways | kSbFileWrite,
- &created_ok,
- &out_error));
- if (!created_ok || out_error != kSbFileOk) {
- SB_LOG(ERROR) << "Could not create watchdog file " << file_path;
- file_.reset();
- }
- }
-
- ~LogWriterWin32() {
- FlushToDisk();
- }
-
- void Write(const char* content, int size) override {
- starboard::ScopedLock lock(mutex_);
- if (IsValid_Locked()) {
- file_->Write(content, size);
- }
- return;
- }
-
- private:
- bool IsValid_Locked() const {
- return file_ && file_->IsValid();
- }
-
- void FlushToDisk() {
- starboard::ScopedLock lock(mutex_);
- if (IsValid_Locked()) {
- file_->Flush();
- }
- }
- std::string file_path_;
- starboard::Mutex mutex_;
- scoped_ptr<ScopedFile> file_;
-};
-
-} // namespace.
-
-scoped_ptr<ILogWriter> CreateLogWriterWin32(const char* path) {
- scoped_ptr<ILogWriter> output(new LogWriterWin32(path));
- return output.Pass();
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/log_writer_win32.h b/src/starboard/shared/uwp/log_writer_win32.h
deleted file mode 100644
index 6cd233e..0000000
--- a/src/starboard/shared/uwp/log_writer_win32.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2018 Google Inc. 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_UWP_LOG_WRITER_WIN32_H_
-#define STARBOARD_SHARED_UWP_LOG_WRITER_WIN32_H_
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/shared/uwp/log_writer_interface.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-starboard::scoped_ptr<ILogWriter> CreateLogWriterWin32(const char* path);
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_LOG_WRITER_WIN32_H_
diff --git a/src/starboard/shared/uwp/media_get_audio_configuration.cc b/src/starboard/shared/uwp/media_get_audio_configuration.cc
deleted file mode 100644
index ee7c923..0000000
--- a/src/starboard/shared/uwp/media_get_audio_configuration.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/media.h"
-#include "starboard/shared/uwp/wasapi_audio.h"
-
-using starboard::shared::uwp::WASAPIAudioDevice;
-
-SB_EXPORT bool SbMediaGetAudioConfiguration(
- int output_index,
- SbMediaAudioConfiguration* out_configuration) {
- if (output_index != 0 || out_configuration == NULL) {
- return false;
- }
-
- out_configuration->index = 0;
- out_configuration->connector = kSbMediaAudioConnectorNone;
- out_configuration->latency = 0;
- out_configuration->coding_type = kSbMediaAudioCodingTypePcm;
-
- Microsoft::WRL::ComPtr<WASAPIAudioDevice> audioDevice =
- Microsoft::WRL::Make<WASAPIAudioDevice>();
-
- int channels = audioDevice->GetNumChannels();
-
- if (channels < 2) {
- out_configuration->number_of_channels = 2;
- } else {
- out_configuration->number_of_channels = channels;
- }
-
- return true;
-}
diff --git a/src/starboard/shared/uwp/media_is_audio_supported.cc b/src/starboard/shared/uwp/media_is_audio_supported.cc
deleted file mode 100644
index ea5e26f..0000000
--- a/src/starboard/shared/uwp/media_is_audio_supported.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/starboard/media/media_support_internal.h"
-
-#include "starboard/configuration.h"
-#include "starboard/media.h"
-#include "starboard/shared/uwp/wasapi_audio.h"
-
-using starboard::shared::uwp::WASAPIAudioDevice;
-
-SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- int64_t bitrate) {
- // TODO: Add Opus.
- if (audio_codec != kSbMediaAudioCodecAac) {
- return false;
- }
-
- Microsoft::WRL::ComPtr<WASAPIAudioDevice> audioDevice =
- Microsoft::WRL::Make<WASAPIAudioDevice>();
-
- int64_t local_bitrate = audioDevice->GetBitrate();
-
- if (local_bitrate <= 0) {
- local_bitrate = SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND;
- }
-
- // Here we say : "The app cannot play any encoded audio whose encoded bitrate
- // exceeds the output bitrate"
- return bitrate <= local_bitrate;
-}
diff --git a/src/starboard/shared/uwp/media_is_output_protected.cc b/src/starboard/shared/uwp/media_is_output_protected.cc
deleted file mode 100644
index cb08cb0..0000000
--- a/src/starboard/shared/uwp/media_is_output_protected.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/media.h"
-
-#include "starboard/shared/uwp/application_uwp.h"
-
-bool SbMediaIsOutputProtected() {
- return starboard::shared::uwp::ApplicationUwp::Get()->IsHdcpOn();
-}
diff --git a/src/starboard/shared/uwp/media_is_video_supported.cc b/src/starboard/shared/uwp/media_is_video_supported.cc
deleted file mode 100644
index 3be4eab..0000000
--- a/src/starboard/shared/uwp/media_is_video_supported.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/starboard/media/media_support_internal.h"
-
-#include <d3d11.h>
-#include <d3d12.h>
-#include <mfapi.h>
-#include <mfidl.h>
-#include <wrl/client.h>
-
-#include "starboard/shared/uwp/application_uwp.h"
-#include "third_party/libvpx_xb1/libvpx/vpx/vp8dx.h"
-#include "third_party/libvpx_xb1/libvpx/vpx/vpx_decoder.h"
-
-namespace {
-
-using ::starboard::shared::uwp::ApplicationUwp;
-
-const int kMaxVpxGpuDecodeTargetWidth = 3840;
-const int kMaxVpxGpuDecodeTargetHeight = 2160;
-const int kMaxVpxGpuDecoderCpuCoreUse = 4;
-
-} // namespace
-
-#if SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
-
-namespace {
-// Cache the VP9 support status since the check may be expensive.
-enum Vp9Support { kVp9SupportUnknown, kVp9SupportYes, kVp9SupportNo };
-Vp9Support s_vp9_hw_support = kVp9SupportUnknown;
-Vp9Support s_vp9_gpu_support = kVp9SupportUnknown;
-} // namespace
-
-// Check for VP9 support. Since this is used by a starboard function, it
-// cannot depend on other modules (e.g. ANGLE).
-SB_EXPORT bool IsVp9HwDecoderSupported() {
- if (s_vp9_hw_support == kVp9SupportUnknown) {
- // Try initializing the VP9 decoder to determine if it is supported.
- HRESULT hr;
-
- Microsoft::WRL::ComPtr<ID3D11Device> d3d_device;
- hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0,
- nullptr, 0, D3D11_SDK_VERSION,
- d3d_device.GetAddressOf(), nullptr, nullptr);
-
- UINT reset_token = 0;
- Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> device_manager;
- if (SUCCEEDED(hr)) {
- hr = MFCreateDXGIDeviceManager(&reset_token,
- device_manager.GetAddressOf());
- }
- if (SUCCEEDED(hr)) {
- hr = device_manager->ResetDevice(d3d_device.Get(), reset_token);
- }
-
- Microsoft::WRL::ComPtr<IMFTransform> transform;
- if (SUCCEEDED(hr)) {
- hr = CoCreateInstance(CLSID_MSVPxDecoder, nullptr, CLSCTX_INPROC_SERVER,
- IID_PPV_ARGS(transform.GetAddressOf()));
- }
-
- if (SUCCEEDED(hr)) {
- hr = transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
- ULONG_PTR(device_manager.Get()));
- }
-
- s_vp9_hw_support = SUCCEEDED(hr) ? kVp9SupportYes : kVp9SupportNo;
- }
- return s_vp9_hw_support == kVp9SupportYes;
-}
-
-SB_EXPORT bool IsVp9GPUDecoderSupported() {
- if (s_vp9_gpu_support == kVp9SupportUnknown) {
- D3D12_COMMAND_QUEUE_DESC desc = {};
- desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
- desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
- Microsoft::WRL::ComPtr<ID3D12CommandQueue> d3d12queue;
- ApplicationUwp* application_uwp = ApplicationUwp::Get();
- SB_CHECK(application_uwp->GetD3D12Device());
- HRESULT hr = application_uwp->GetD3D12Device()->CreateCommandQueue(
- &desc, IID_PPV_ARGS(&d3d12queue));
- if (FAILED(hr)) {
- SB_LOG(WARNING)
- << "Could not create DX12 command queue: cannot use GPU VP9";
- return false;
- }
- vpx_codec_ctx vpx_context = {};
- vpx_codec_dec_cfg_t vpx_config = {};
- vpx_config.w = kMaxVpxGpuDecodeTargetWidth;
- vpx_config.h = kMaxVpxGpuDecodeTargetHeight;
- vpx_config.threads = kMaxVpxGpuDecoderCpuCoreUse;
-
- vpx_config.hw_device = application_uwp->GetD3D12Device().Get();
- vpx_config.hw_command_queue = d3d12queue.Get();
- vpx_codec_err_t status =
- vpx_codec_dec_init(&vpx_context, vpx_codec_vp9_dx(), &vpx_config,
- VPX_CODEC_USE_FRAME_THREADING);
- s_vp9_gpu_support =
- (status == VPX_CODEC_OK) ? kVp9SupportYes : kVp9SupportNo;
- vpx_codec_destroy(&vpx_context);
- }
- return s_vp9_gpu_support == kVp9SupportYes;
-}
-
-#else // SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
-
-bool IsVp9HwDecoderSupported() {
- return false;
-}
-
-bool IsVp9GPUDecoderSupported() {
- return false;
-}
-
-#endif
-
-SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- int frame_width,
- int frame_height,
- int64_t bitrate,
- int fps) {
- int max_width = 1920;
- int max_height = 1080;
-
- if (video_codec == kSbMediaVideoCodecVp9) {
-// Vp9 supports 8k only in whitelisted platforms, up to 4k in the others.
-#ifdef ENABLE_VP9_8K_SUPPORT
- max_width = 7680;
- max_height = 4320;
-#else // ENABLE_VP9_8K_SUPPORT
- max_width = 3840;
- max_height = 2160;
-#endif // ENABLE_VP9_8K_SUPPORT
- } else if (video_codec == kSbMediaVideoCodecH264) {
-// Not all devices can support 4k H264; some (e.g. xb1) may crash in
-// the decoder if provided too high of a resolution. Therefore
-// platforms must explicitly opt-in to support 4k H264.
-#ifdef ENABLE_H264_4K_SUPPORT
- max_width = 3840;
- max_height = 2160;
-#endif // ENABLE_H264_4K_SUPPORT
- }
-
- if (frame_width > max_width || frame_height > max_height) {
- return false;
- }
-
- // Is bitrate in range?
- if (bitrate > SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND) {
- return false;
- }
- if (fps > 60) {
- return false;
- }
- if (video_codec == kSbMediaVideoCodecH264) {
- return true;
- }
- if (video_codec == kSbMediaVideoCodecVp9) {
- if (IsVp9HwDecoderSupported())
- return true;
- if (IsVp9GPUDecoderSupported())
- return true;
- }
- return false;
-}
diff --git a/src/starboard/shared/uwp/media_set_output_protection.cc b/src/starboard/shared/uwp/media_set_output_protection.cc
deleted file mode 100644
index b29e94a..0000000
--- a/src/starboard/shared/uwp/media_set_output_protection.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/media.h"
-
-#include "starboard/log.h"
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/time.h"
-
-using starboard::shared::uwp::ApplicationUwp;
-
-bool SbMediaSetOutputProtection(bool should_enable_dhcp) {
- bool is_hdcp_on = SbMediaIsOutputProtected();
-
- if (is_hdcp_on == should_enable_dhcp) {
- return true;
- }
-
- SB_LOG(INFO) << "Attempting to "
- << (should_enable_dhcp ? "enable" : "disable")
- << " output protection. Current status: "
- << (is_hdcp_on ? "enabled" : "disabled");
- SbTimeMonotonic tick = SbTimeGetMonotonicNow();
-
- bool hdcp_success = false;
- if (should_enable_dhcp) {
- hdcp_success = ApplicationUwp::Get()->TurnOnHdcp();
- } else {
- hdcp_success = ApplicationUwp::Get()->TurnOffHdcp();
- }
-
- is_hdcp_on = (hdcp_success ? should_enable_dhcp : !should_enable_dhcp);
-
- SbTimeMonotonic tock = SbTimeGetMonotonicNow();
- SB_LOG(INFO) << "Output protection is "
- << (is_hdcp_on ? "enabled" : "disabled")
- << ". Toggling HDCP took " << (tock - tick) / kSbTimeMillisecond
- << " milliseconds.";
- return hdcp_success;
-}
diff --git a/src/starboard/shared/uwp/microphone_impl.cc b/src/starboard/shared/uwp/microphone_impl.cc
deleted file mode 100644
index dc1baeb..0000000
--- a/src/starboard/shared/uwp/microphone_impl.cc
+++ /dev/null
@@ -1,594 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/starboard/microphone/microphone_internal.h"
-
-// Windows headers.
-#include <collection.h>
-#include <MemoryBuffer.h>
-#include <ppltasks.h>
-
-// C++ headers.
-#include <algorithm>
-#include <deque>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "starboard/atomic.h"
-#include "starboard/common/semaphore.h"
-#include "starboard/common/thread.h"
-#include "starboard/log.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/uwp/app_accessors.h"
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/shared/uwp/async_utils.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-#include "starboard/time.h"
-#include "starboard/user.h"
-
-#if !SB_HAS(MICROPHONE)
-#error Microphone expected to be enabled when compiling a microphone impl.
-#endif
-
-using concurrency::task_continuation_context;
-using Microsoft::WRL::ComPtr;
-using starboard::Mutex;
-using starboard::scoped_ptr;
-using starboard::ScopedLock;
-using starboard::Semaphore;
-using starboard::shared::uwp::ApplicationUwp;
-using starboard::shared::win32::CheckResult;
-using starboard::shared::win32::platformStringToString;
-using Windows::Devices::Enumeration::DeviceInformation;
-using Windows::Devices::Enumeration::DeviceInformationCollection;
-using Windows::Foundation::EventRegistrationToken;
-using Windows::Foundation::IMemoryBufferByteAccess;
-using Windows::Foundation::IMemoryBufferReference;
-using Windows::Foundation::TypedEventHandler;
-using Windows::Foundation::Uri;
-using Windows::Media::Audio::AudioDeviceInputNode;
-using Windows::Media::Audio::AudioDeviceNodeCreationStatus;
-using Windows::Media::Audio::AudioFrameOutputNode;
-using Windows::Media::Audio::AudioGraph;
-using Windows::Media::Audio::AudioGraphCreationStatus;
-using Windows::Media::Audio::AudioGraphSettings;
-using Windows::Media::Audio::CreateAudioDeviceInputNodeResult;
-using Windows::Media::Audio::CreateAudioGraphResult;
-using Windows::Media::Audio::QuantumSizeSelectionMode;
-using Windows::Media::AudioBuffer;
-using Windows::Media::AudioBufferAccessMode;
-using Windows::Media::AudioFrame;
-using Windows::Media::Capture::MediaCategory;
-using Windows::Media::Devices::MediaDevice;
-using Windows::Media::MediaProperties::AudioEncodingProperties;
-using Windows::Media::Render::AudioRenderCategory;
-using Windows::System::Launcher;
-
-namespace {
-
-// It appears that cobalt will only request 16khz.
-const int kMinSampleRate = 16000;
-const int kMaxSampleRate = 44100;
-const int kNumChannels = 1;
-const int kOutputBytesPerSample = sizeof(int16_t);
-const int kMinReadSizeBytes = 4096;
-const int kMicGain = 1;
-
-// Controls the amount of time that a microphone will record muted audio
-// before it signals a read error. Without this trigger, the app
-// will continuously wait for audio data. This happens with the Kinect
-// device, which when disconnected will still record 0-value samples.
-const SbTime kTimeMutedThreshold = 3 * kSbTimeSecond;
-
-// Maps [-1.0f, 1.0f] -> [-32768, 32767]
-// Values outside of [-1.0f, 1.0] are clamped.
-int16_t To16BitPcm(float val) {
- static const float kMaxFloatValue = std::numeric_limits<int16_t>::max();
- static const float kLowFloatValue = std::numeric_limits<int16_t>::lowest();
- if (val == 0.0f) {
- return 0;
- } else if (val > 0.0f) {
- if (val > 1.0f) {
- val = 1.0;
- }
- return static_cast<int16_t>(val * kMaxFloatValue);
- } else {
- if (val < -1.0f) {
- val = -1.0;
- }
- return static_cast<int16_t>(-1.0f * val * kLowFloatValue);
- }
-}
-
-const char* ToString(AudioDeviceNodeCreationStatus status) {
- switch (status) {
- case AudioDeviceNodeCreationStatus::AccessDenied:
- return "AccessDenied";
- case AudioDeviceNodeCreationStatus::DeviceNotAvailable:
- return "DeviceNotAvailable";
- case AudioDeviceNodeCreationStatus::FormatNotSupported:
- return "FormatNotSupported";
- case AudioDeviceNodeCreationStatus::Success:
- return "Success";
- case AudioDeviceNodeCreationStatus::UnknownFailure:
- return "UnknownFailure";
- }
- return "Unknown";
-}
-
-bool IsUiThread() {
- auto dispatcher = starboard::shared::uwp::GetDispatcher();
- // Is UI thread.
- return dispatcher->HasThreadAccess;
-}
-
-void LaunchMicrophonePermissionsAppAsync() {
- // Schedule a task to run on the main thread which will launch a URI to
- // request microphone permissions.
- auto main_thread_task = []() {
- auto uri = ref new Uri("ms-settings:privacy-microphone");
-
- concurrency::create_task(Launcher::LaunchUriAsync(uri))
- .then([](concurrency::task<bool> previous_task){
- try {
- bool launched_ok = !!previous_task.get();
- SB_LOG_IF(ERROR, !launched_ok);
- } catch (Platform::Exception^ e) {
- HRESULT hr = e->HResult;
- std::string msg = platformStringToString(e->Message);
- SB_LOG(ERROR)
- << "Exception while launching permissions app, HRESULT: " << hr
- << ", msg: " << msg;
- }
- });
- };
- starboard::shared::uwp::RunInMainThreadAsync(main_thread_task);
-}
-
-std::vector<DeviceInformation^> GetAllMicrophoneDevices() {
- std::vector<DeviceInformation^> output;
- Platform::String^ audio_str = MediaDevice::GetAudioCaptureSelector();
- DeviceInformationCollection^ all_devices =
- starboard::shared::uwp::WaitForResult(
- DeviceInformation::FindAllAsync(audio_str));
- for (DeviceInformation^ dev_info : all_devices) {
- output.push_back(dev_info);
- }
-
- return output;
-}
-
-AudioGraph^ CreateAudioGraph(AudioRenderCategory category,
- QuantumSizeSelectionMode selection_mode) {
- AudioGraphSettings^ settings = ref new AudioGraphSettings(category);
- settings->QuantumSizeSelectionMode = selection_mode;
- CreateAudioGraphResult^ result =
- starboard::shared::uwp::WaitForResult(AudioGraph::CreateAsync(settings));
- SB_DCHECK(result->Status == AudioGraphCreationStatus::Success);
- AudioGraph^ graph = result->Graph;
- return graph;
-}
-std::vector<AudioDeviceInputNode^> GenerateAudioInputNodes(
- const std::vector<DeviceInformation^>& microphone_devices,
- AudioEncodingProperties^ encoding_properties,
- AudioGraph^ graph) {
- std::vector<AudioDeviceInputNode^> output;
-
- SbTime start_time = SbTimeGetMonotonicNow();
-
- bool had_permissions_error = false;
- for (DeviceInformation^ mic : microphone_devices) {
- auto create_microphone_input_task = graph->CreateDeviceInputNodeAsync(
- MediaCategory::Speech, encoding_properties, mic);
- CreateAudioDeviceInputNodeResult^ deviceInputNodeResult =
- starboard::shared::uwp::WaitForResult(create_microphone_input_task);
-
- auto status = deviceInputNodeResult->Status;
- AudioDeviceInputNode^ input_node = deviceInputNodeResult->DeviceInputNode;
-
- if (status != AudioDeviceNodeCreationStatus::Success) {
- SB_LOG(INFO) << "Failed to create microphone with device name \""
- << platformStringToString(mic->Name) << "\" because "
- << ToString(status);
- if (status == AudioDeviceNodeCreationStatus::AccessDenied) {
- // The user hasn't given cobalt access to the microphone because they
- // declined access to the microphone now or previously.
- had_permissions_error = true;
- }
- continue;
- }
- SB_LOG(INFO) << "Created a microphone with device \""
- << platformStringToString(mic->Name) << "\"";
- input_node->ConsumeInput = true;
- input_node->OutgoingGain = kMicGain;
- output.push_back(input_node);
- }
-
- SbTime delta_time = SbTimeGetMonotonicNow() - start_time;
- const bool had_ui_interaction = delta_time > (kSbTimeMillisecond*250);
-
- // We only care to retry permissions if there were
- // 1. No microphones that could be opened.
- // 2. There are 1 or more microphones that had errors.
- // 3. There was no UI interaction, which is detected if the audio
- // node creation completed really quickly. A quick action suggests
- // that there was no user interaction and therefore we are in a
- // permissions "cooldown" period. These typically last for 30 minutes
- // and the work around requires an explicit permissions request.
- const bool do_launch_microphone_permissions_app =
- output.empty() && had_permissions_error &&
- !had_ui_interaction;
-
- if (do_launch_microphone_permissions_app) {
- LaunchMicrophonePermissionsAppAsync();
- }
- return output;
-}
-
-// Reinterprets underlying buffer type to match destination vector.
-void ExtractRawAudioData(AudioFrameOutputNode^ node,
- std::vector<float>* destination) {
- AudioFrame^ audio_frame = node->GetFrame();
- AudioBuffer^ audio_buffer =
- audio_frame->LockBuffer(AudioBufferAccessMode::Read);
- IMemoryBufferReference^ memory_buffer_reference =
- audio_buffer->CreateReference();
-
- ComPtr<IMemoryBufferByteAccess> memory_byte_access;
- HRESULT hr = reinterpret_cast<IInspectable*>(memory_buffer_reference)
- ->QueryInterface(IID_PPV_ARGS(&memory_byte_access));
- CheckResult(hr);
-
- BYTE* data = nullptr;
- UINT32 capacity = 0;
- hr = memory_byte_access->GetBuffer(&data, &capacity);
- CheckResult(hr);
-
- // Audio data is float data, so the buffer must be a multiple of 4.
- SB_DCHECK(capacity % sizeof(float) == 0);
-
- if (capacity > 0) {
- float* typed_data = reinterpret_cast<float*>(data);
- const size_t typed_data_size = capacity / sizeof(float);
- destination->insert(destination->end(), typed_data,
- typed_data + typed_data_size);
- }
-}
-
-// Timer useful for detecting that the microphone has been muted for a certain
-// amount of time.
-class MutedTrigger {
- public:
- void SignalMuted() {
- if (state_ == kIsMuted) {
- return;
- }
- state_ = kIsMuted;
- time_start_ = SbTimeGetMonotonicNow();
- }
-
- void SignalSound() {
- state_ = kFoundSound;
- }
-
- bool IsMuted(SbTimeMonotonic duration_theshold) const {
- if (state_ != kIsMuted) {
- return false;
- }
- SbTimeMonotonic duration = SbTimeGetMonotonicNow() - time_start_;
- return duration > duration_theshold;
- }
-
- private:
- enum State {
- kInitialized,
- kIsMuted,
- kFoundSound
- };
- State state_ = kInitialized;
- SbTimeMonotonic time_start_ = 0;
-};
-
-// MicrophoneProcessor encapsulates Microsoft's audio api. All available
-// microphones are queried and instantiated. This class will mix the audio
-// together into one signed 16-bit pcm stream.
-//
-// When the microphone is created it will find all available microphones and
-// immediately start recording. A callback will be created which will process
-// audio data when new samples are available. The Microphone will stop
-// recording when Close() is called.
-class MicrophoneProcessor : public starboard::Thread {
- public:
- // This will try and create a microphone. This will fail (return null) if
- // there are not available microphones.
- static scoped_ptr<MicrophoneProcessor> TryCreateAndStartRecording(
- size_t max_num_samples,
- int sample_rate) {
- scoped_ptr<MicrophoneProcessor> output;
-
- std::vector<DeviceInformation^> microphone_devices =
- GetAllMicrophoneDevices();
- if (microphone_devices.empty()) { // Unexpected condition.
- return output.Pass();
- }
-
- output.reset(new MicrophoneProcessor(
- max_num_samples, sample_rate, microphone_devices));
-
- if (output->input_nodes_.empty()) {
- output.reset(nullptr);
- }
- return output.Pass();
- }
-
- virtual ~MicrophoneProcessor() {
- Thread::Join();
- audio_graph_->Stop();
- }
-
- // Returns the number of elements that have been written, or -1 if there
- // was a read error.
- int Read(int16_t* out_audio_data, size_t out_audio_count) {
- ScopedLock lock(mutex_);
- if (muted_timer_.IsMuted(kTimeMutedThreshold)) {
- return -1;
- }
-
- out_audio_count = std::min(out_audio_count,
- pcm_audio_data_.size());
- using iter = std::vector<int16_t>::iterator;
- iter it_begin = pcm_audio_data_.begin();
- iter it_end = pcm_audio_data_.begin() + out_audio_count;
- std::copy(it_begin, it_end, out_audio_data);
- pcm_audio_data_.erase(it_begin, it_end);
- return static_cast<int>(out_audio_count);
- }
-
- private:
- explicit MicrophoneProcessor(
- size_t max_num_samples,
- int sample_rate,
- const std::vector<DeviceInformation^>& microphone_devices)
- : Thread("MicrophoneProcessor"),
- max_num_samples_(max_num_samples) {
- audio_graph_ = CreateAudioGraph(AudioRenderCategory::Speech,
- QuantumSizeSelectionMode::SystemDefault);
- wave_encoder_ =
- AudioEncodingProperties::CreatePcm(sample_rate, kNumChannels,
- 16); // 4-byte float.
- SB_DCHECK(audio_graph_);
- input_nodes_ = GenerateAudioInputNodes(microphone_devices, wave_encoder_,
- audio_graph_);
- for (AudioDeviceInputNode^ input_node : input_nodes_) {
- AudioFrameOutputNode^ audio_frame_node =
- audio_graph_->CreateFrameOutputNode(wave_encoder_);
- audio_frame_node->ConsumeInput = true;
- input_node->AddOutgoingConnection(audio_frame_node);
- audio_channel_.emplace_back(new std::vector<float>());
- audio_frame_nodes_.push_back(audio_frame_node);
- }
- // Update the audio data whenever a new audio sample has been finished.
- audio_graph_->Start();
- Thread::Start();
- }
-
- void Run() override {
- while (!join_called()) {
- SleepMilliseconds(1);
- Process();
- }
- }
-
- void Process() {
- ScopedLock lock(mutex_);
- if (audio_frame_nodes_.empty()) {
- return;
- }
- for (size_t i = 0; i < audio_frame_nodes_.size(); ++i) {
- ExtractRawAudioData(audio_frame_nodes_[i], audio_channel_[i].get());
- }
-
- size_t num_elements = max_num_samples_;
- for (const auto& audio_datum : audio_channel_) {
- num_elements = std::min(audio_datum->size(), num_elements);
- }
- if (num_elements == 0) {
- return;
- }
-
- bool is_muted = true;
- // Mix all available audio channels together and convert to output buffer
- // format. Detect if audio is muted.
- for (int i = 0; i < num_elements; ++i) {
- float mixed_sample = 0.0f;
- for (const auto& audio_datum : audio_channel_) {
- float sample = (*audio_datum)[i];
- if (sample != 0.0) {
- is_muted = false;
- }
- mixed_sample += sample;
- }
- pcm_audio_data_.push_back(To16BitPcm(mixed_sample));
- }
-
- // Trim values from finished pcm_data if the buffer has exceeded it's
- // allowed size.
- if (pcm_audio_data_.size() > max_num_samples_) {
- size_t num_delete = pcm_audio_data_.size() - max_num_samples_;
- pcm_audio_data_.erase(pcm_audio_data_.begin(),
- pcm_audio_data_.begin() + num_delete);
- }
-
- if (is_muted) {
- muted_timer_.SignalMuted();
- } else {
- muted_timer_.SignalSound();
- }
- // Trim values from source channels that were just transfered to
- // pcm_audio_data.
- for (const auto& audio_datum : audio_channel_) {
- audio_datum->erase(audio_datum->begin(),
- audio_datum->begin() + num_elements);
- }
- }
-
- AudioGraph^ audio_graph_ = nullptr;
- AudioEncodingProperties^ wave_encoder_;
- std::vector<AudioDeviceInputNode^> input_nodes_;
- std::vector<AudioFrameOutputNode^> audio_frame_nodes_;
- std::vector<std::unique_ptr<std::vector<float>>> audio_channel_;
- std::vector<int16_t> pcm_audio_data_;
- size_t max_num_samples_ = 0;
- MutedTrigger muted_timer_;
- Mutex mutex_;
-};
-
-// Implements the SbMicrophonePrivate interface.
-class MicrophoneImpl : public SbMicrophonePrivate {
- public:
- MicrophoneImpl(int sample_rate, int buffer_size_bytes)
- : buffer_size_bytes_(buffer_size_bytes),
- sample_rate_(sample_rate) {
- }
-
- ~MicrophoneImpl() { Close(); }
-
- bool Open() override {
- if (!microphone_) {
- if (IsUiThread()) {
- SB_LOG(INFO) << "Could not open microphone from UI thread.";
- return false;
- }
- microphone_ = MicrophoneProcessor::TryCreateAndStartRecording(
- buffer_size_bytes_ / kOutputBytesPerSample,
- sample_rate_);
- }
- return microphone_ != nullptr;
- }
-
- bool Close() override {
- microphone_.reset(nullptr);
- return true;
- }
-
- int Read(void* out_audio_data, int audio_data_size) override {
- if (!microphone_) {
- return -1;
- }
- int16_t* pcm_buffer = reinterpret_cast<int16*>(out_audio_data);
- size_t pcm_buffer_count = audio_data_size / kOutputBytesPerSample;
- int n_samples = microphone_->Read(pcm_buffer, pcm_buffer_count);
- if (n_samples < 0) {
- return -1; // Is error.
- } else {
- return n_samples * kOutputBytesPerSample;
- }
- }
-
- private:
- const int buffer_size_bytes_;
- const int sample_rate_;
- scoped_ptr<MicrophoneProcessor> microphone_;
-};
-
-// Singleton access is required by the microphone interface as specified by
-// nplb.
-const SbMicrophoneId kSingletonId =
- reinterpret_cast<SbMicrophoneId>(0x1);
-starboard::atomic_pointer<MicrophoneImpl*> s_singleton_pointer;
-
-} // namespace.
-
-int SbMicrophonePrivate::GetAvailableMicrophones(
- SbMicrophoneInfo* out_info_array,
- int info_array_size) {
-
- std::vector<DeviceInformation^> mic_devices = GetAllMicrophoneDevices();
- if (mic_devices.empty()) {
- return 0;
- }
- if (out_info_array && (info_array_size >= 1)) {
- SbMicrophoneInfo info;
- info.id = kSingletonId;
- info.type = kSBMicrophoneAnalogHeadset;
- info.max_sample_rate_hz = kMaxSampleRate;
- info.min_read_size = kMinReadSizeBytes;
-
-#if SB_API_VERSION >= 9
- std::stringstream all_mic_names;
- for (size_t i = 0; i < mic_devices.size(); ++i) {
- DeviceInformation^ mic_dev = mic_devices[i];
- if (i > 0) {
- all_mic_names << ", ";
- }
- all_mic_names << "[" << platformStringToString(mic_dev->Name) << "]";
- }
- SbStringCopy(info.label, all_mic_names.str().c_str(),
- SB_ARRAY_SIZE(info.label));
-#endif // SB_API_VERSION >= 9
- out_info_array[0] = info;
- }
- return 1;
-}
-
-bool SbMicrophonePrivate::IsMicrophoneSampleRateSupported(
- SbMicrophoneId id,
- int sample_rate_in_hz) {
- if (!SbMicrophoneIdIsValid(id)) {
- return false;
- }
- return (kMinSampleRate <= sample_rate_in_hz) &&
- (sample_rate_in_hz <= kMaxSampleRate);
-}
-
-SbMicrophone SbMicrophonePrivate::CreateMicrophone(SbMicrophoneId id,
- int sample_rate_in_hz,
- int buffer_size_bytes) {
- if (!SbMicrophoneIdIsValid(id)) {
- return kSbMicrophoneInvalid;
- }
- if (sample_rate_in_hz < kMinSampleRate) {
- return kSbMicrophoneInvalid;
- }
- if (sample_rate_in_hz > kMaxSampleRate) {
- return kSbMicrophoneInvalid;
- }
- if (buffer_size_bytes <= 0) {
- return kSbMicrophoneInvalid;
- }
- // Required to conform to nplb test.
- if (buffer_size_bytes >= (std::numeric_limits<int>::max() - 1)) {
- return kSbMicrophoneInvalid;
- }
- // Id will either by 1 or 0. At this time there is only one microphone.
- SB_DCHECK(id == kSingletonId);
- if (s_singleton_pointer.load()) {
- return kSbMicrophoneInvalid;
- }
- MicrophoneImpl* new_microphone =
- new MicrophoneImpl(sample_rate_in_hz, buffer_size_bytes);
-
- s_singleton_pointer.store(new_microphone);
- return new_microphone;
-}
-
-void SbMicrophonePrivate::DestroyMicrophone(SbMicrophone microphone) {
- SB_DCHECK(microphone == s_singleton_pointer.load());
- s_singleton_pointer.store(nullptr);
- delete microphone;
-}
diff --git a/src/starboard/shared/uwp/player_components_impl.cc b/src/starboard/shared/uwp/player_components_impl.cc
deleted file mode 100644
index 4359801..0000000
--- a/src/starboard/shared/uwp/player_components_impl.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/starboard/player/filter/player_components.h"
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/shared/libvpx_xb1/vpx_xb1_video_decoder.h"
-#include "starboard/shared/starboard/media/media_support_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.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_render_algorithm_impl.h"
-#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
-#include "starboard/shared/win32/audio_decoder.h"
-#include "starboard/shared/win32/video_decoder.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-namespace filter {
-
-namespace {
-
-class PlayerComponentsImpl : public PlayerComponents {
- void CreateAudioComponents(
- const AudioParameters& audio_parameters,
- scoped_ptr<AudioDecoder>* audio_decoder,
- scoped_ptr<AudioRendererSink>* audio_renderer_sink) override {
- using AudioDecoderImpl = ::starboard::shared::win32::AudioDecoder;
-
- SB_DCHECK(audio_decoder);
- SB_DCHECK(audio_renderer_sink);
-
- audio_decoder->reset(new AudioDecoderImpl(audio_parameters.audio_codec,
- audio_parameters.audio_header,
- audio_parameters.drm_system));
- audio_renderer_sink->reset(new AudioRendererSinkImpl);
- }
-
- void CreateVideoComponents(
- const VideoParameters& video_parameters,
- scoped_ptr<VideoDecoder>* video_decoder,
- scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
- scoped_refptr<VideoRendererSink>* video_renderer_sink) override {
- using VideoDecoderImpl = ::starboard::shared::win32::VideoDecoder;
- using VideoDecoderVpx = ::starboard::shared::vpx::VideoDecoder;
-
- SB_DCHECK(video_decoder);
- SB_DCHECK(video_render_algorithm);
- SB_DCHECK(video_renderer_sink);
-
- const bool use_software_vp9 =
- (video_parameters.video_codec == kSbMediaVideoCodecVp9) &&
- !IsVp9HwDecoderSupported();
-
- if (use_software_vp9) {
- // Software vp9 is the special case.
- scoped_ptr<VideoDecoderVpx> video_decoder_impl(new VideoDecoderVpx(
- video_parameters.video_codec, video_parameters.output_mode,
- video_parameters.decode_target_graphics_context_provider));
- *video_renderer_sink = video_decoder_impl->GetSink();
- video_decoder->reset(video_decoder_impl.release());
- video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
- } else {
- scoped_ptr<VideoDecoderImpl> video_decoder_impl(new VideoDecoderImpl(
- video_parameters.video_codec, video_parameters.output_mode,
- video_parameters.decode_target_graphics_context_provider,
- video_parameters.drm_system));
- *video_renderer_sink = video_decoder_impl->GetSink();
- video_decoder->reset(video_decoder_impl.release());
- video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
- }
- }
-
- void GetAudioRendererParams(int* max_cached_frames,
- int* max_frames_per_append) const override {
- SB_DCHECK(max_cached_frames);
- SB_DCHECK(max_frames_per_append);
-
- *max_cached_frames = 256 * 1024;
- *max_frames_per_append = 16384;
- }
-};
-
-} // namespace
-
-// static
-scoped_ptr<PlayerComponents> PlayerComponents::Create() {
- return make_scoped_ptr<PlayerComponents>(new PlayerComponentsImpl);
-}
-
-} // namespace filter
-} // namespace player
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/sso.cc b/src/starboard/shared/uwp/sso.cc
deleted file mode 100644
index 1378c78..0000000
--- a/src/starboard/shared/uwp/sso.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/uwp/app_accessors.h"
-
-#include "starboard/file.h"
-#include "starboard/log.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-namespace sbuwp = starboard::shared::uwp;
-namespace sbwin32 = starboard::shared::win32;
-
-using Windows::Security::Authentication::Web::Core::
- WebAuthenticationCoreManager;
-using Windows::Security::Authentication::Web::Core::WebTokenRequest;
-using Windows::Security::Authentication::Web::Core::WebTokenResponse;
-using Windows::Security::Authentication::Web::Core::WebTokenRequestResult;
-using Windows::Security::Authentication::Web::Core::WebTokenRequestStatus;
-using Windows::Security::Credentials::WebAccountProvider;
-
-namespace {
-
-const char kXboxLiveAccountProviderId[] = "https://xsts.auth.xboxlive.com";
-
-// Filename, placed in the cache dir, whose presence indicates
-// that the user has rejected the SSO approval dialog
-const char kSsoRejectionFilename[] = "sso_rejection";
-
-std::string GetSsoRejectionFilePath() {
- char path[SB_FILE_MAX_PATH];
- bool success = SbSystemGetPath(kSbSystemPathCacheDirectory,
- path, SB_FILE_MAX_PATH);
- SB_DCHECK(success);
- std::string file_path(path);
- file_path.append(1, '/');
- file_path.append(kSsoRejectionFilename);
-
- return file_path;
-}
-
-} // namespace
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-concurrency::task<WebTokenRequestResult^> TryToFetchSsoToken(
- const std::string& url, bool prompt);
-
-concurrency::task<WebTokenRequestResult^> TryToFetchSsoToken(
- const std::string& url) {
- return TryToFetchSsoToken(url, false);
-}
-
-concurrency::task<WebTokenRequestResult^> TryToFetchSsoTokenAndPrompt(
- const std::string& url) {
- return TryToFetchSsoToken(url, true);
-}
-
-concurrency::task<WebTokenRequestResult^> TryToFetchSsoToken(
- const std::string& url, bool prompt) {
- if (SbFileExists(GetSsoRejectionFilePath().c_str())) {
- concurrency::task_completion_event<WebTokenRequestResult^> result;
- result.set(nullptr);
- return concurrency::task<WebTokenRequestResult^>(result);
- }
- return concurrency::create_task(
- WebAuthenticationCoreManager::FindAccountProviderAsync(
- sbwin32::stringToPlatformString(kXboxLiveAccountProviderId)))
- .then([url, prompt](concurrency::task<WebAccountProvider^> previous_task) {
- WebAccountProvider^ xbox_provider = nullptr;
- try {
- xbox_provider = previous_task.get();
- } catch (Platform::Exception^) {
- SB_LOG(INFO) << "Exception with FindAccountProviderAsync";
- concurrency::task_completion_event<WebTokenRequestResult^> result;
- result.set(nullptr);
- return concurrency::task<WebTokenRequestResult^>(result);
- }
- WebTokenRequest^ request = ref new WebTokenRequest(xbox_provider);
- request->Properties->Insert("Url",
- sbwin32::stringToPlatformString(url));
- request->Properties->Insert("Target", "xboxlive.signin");
- request->Properties->Insert("Policy", "DELEGATION");
-
- bool main_thread = sbuwp::GetDispatcher()->HasThreadAccess;
-
- if (main_thread && prompt) {
- return concurrency::create_task(
- WebAuthenticationCoreManager::RequestTokenAsync(request));
- } else {
- return concurrency::create_task(
- WebAuthenticationCoreManager::GetTokenSilentlyAsync(request));
- }
- })
- .then([url](concurrency::task<WebTokenRequestResult^> previous_task) {
- WebTokenRequestResult^ token_result = previous_task.get();
- try {
- token_result = previous_task.get();
- } catch(Platform::Exception^ ex) {
- SB_LOG(INFO) << "Exception during RequestTokenAsync";
- concurrency::task_completion_event<WebTokenRequestResult^> result;
- result.set_exception(ex);
- return concurrency::task<WebTokenRequestResult^>(result);
- }
-
- if (!token_result) {
- return previous_task;
- }
-
- switch (token_result->ResponseStatus) {
- case WebTokenRequestStatus::UserCancel: {
- bool created;
- SbFile file = SbFileOpen(GetSsoRejectionFilePath().c_str(),
- kSbFileRead | kSbFileOpenAlways, &created, nullptr);
- SbFileClose(file);
- return previous_task;
- }
-
- case WebTokenRequestStatus::UserInteractionRequired: {
- concurrency::task_completion_event<WebTokenRequestResult^> completion;
- RunInMainThreadAsync([url, completion]() {
- // When we run TryToFetchSsoTokenAndPrompt in the main thread,
- // we'll always ask for user input via RequestTokenAsync, which
- // never returns this case.
- TryToFetchSsoTokenAndPrompt(url)
- .then([completion](concurrency::task<WebTokenRequestResult^> result) {
- try {
- completion.set(result.get());
- } catch (Platform::Exception^ ex) {
- completion.set_exception(ex);
- }
- });
- });
-
- return concurrency::task<WebTokenRequestResult^>(completion);
- }
-
- default:
- return previous_task;
- }
- });
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi
deleted file mode 100644
index 612944d..0000000
--- a/src/starboard/shared/uwp/starboard_platform.gypi
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'variables': {
- 'starboard_platform_dependent_files': [
- 'analog_thumbstick_input.cc',
- 'analog_thumbstick_input.h',
- 'analog_thumbstick_input_thread.cc',
- 'analog_thumbstick_input_thread.h',
- 'application_uwp_key_event.cc',
- 'application_uwp_get_commandline_pointer.cc',
- 'application_uwp.cc',
- 'application_uwp.h',
- 'async_utils.h',
- 'get_home_directory.cc',
- 'log_file_impl.cc',
- 'log_file_impl.h',
- 'log_raw.cc',
- 'log_raw_format.cc',
- 'log_writer_interface.h',
- 'log_writer_uwp.cc',
- 'log_writer_uwp.h',
- 'log_writer_win32.cc',
- 'log_writer_win32.h',
- 'media_get_audio_configuration.cc',
- 'media_is_audio_supported.cc',
- 'media_is_output_protected.cc',
- 'media_set_output_protection.cc',
- 'sso.cc',
- 'system_clear_platform_error.cc',
- 'system_get_device_type.cc',
- 'system_get_property.cc',
- 'system_get_total_cpu_memory.cc',
- 'system_get_used_cpu_memory.cc',
- 'system_platform_error_internal.cc',
- 'system_platform_error_internal.h',
- 'system_raise_platform_error.cc',
- 'wasapi_audio.cc',
- 'wasapi_audio.h',
- 'watchdog_log.cc',
- 'watchdog_log.h',
- 'window_create.cc',
- 'window_destroy.cc',
- 'window_get_platform_handle.cc',
- 'window_get_size.cc',
- 'window_internal.cc',
- 'window_internal.h',
- 'window_set_default_options.cc',
- 'winrt_workaround.h',
-
- 'private/keys.cc',
- 'private/keys.h',
-
- # Microphone section.
- '<(DEPTH)/starboard/shared/uwp/microphone_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_get_available.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_get_available.h',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_close.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_close.h',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_create.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_create.h',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_destroy.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_destroy.h',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_get_available.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_get_available.h',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_internal.h',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_is_sample_rate_supported.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_open.cc',
- '<(DEPTH)/starboard/shared/starboard/microphone/microphone_read.cc',
-
- '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
- '<(DEPTH)/starboard/shared/starboard/system_supports_resume.cc',
- ]
- }
-}
diff --git a/src/starboard/shared/uwp/system_clear_platform_error.cc b/src/starboard/shared/uwp/system_clear_platform_error.cc
deleted file mode 100644
index 55a2a37..0000000
--- a/src/starboard/shared/uwp/system_clear_platform_error.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include "starboard/shared/uwp/system_platform_error_internal.h"
-
-void SbSystemClearPlatformError(SbSystemPlatformError handle) {
- if (SbSystemPlatformErrorIsValid(handle)) {
- handle->ClearAndDelete();
- }
-}
diff --git a/src/starboard/shared/uwp/system_get_device_type.cc b/src/starboard/shared/uwp/system_get_device_type.cc
deleted file mode 100644
index 33ce323..0000000
--- a/src/starboard/shared/uwp/system_get_device_type.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <string>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-using Windows::System::Profile::AnalyticsInfo;
-using Windows::System::Profile::AnalyticsVersionInfo;
-
-SbSystemDeviceType SbSystemGetDeviceType() {
- AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
- std::string family = starboard::shared::win32::platformStringToString(
- version_info->DeviceFamily);
-
- if (family.compare("Windows.Desktop") == 0) {
- return kSbSystemDeviceTypeDesktopPC;
- }
- if (family.compare("Windows.Xbox") == 0) {
- return kSbSystemDeviceTypeGameConsole;
- }
- SB_NOTREACHED();
- return kSbSystemDeviceTypeUnknown;
-}
diff --git a/src/starboard/shared/uwp/system_get_property.cc b/src/starboard/shared/uwp/system_get_property.cc
deleted file mode 100644
index cc81814..0000000
--- a/src/starboard/shared/uwp/system_get_property.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <string>
-
-#include "starboard/log.h"
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/shared/uwp/private/keys.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-
-using Windows::Security::ExchangeActiveSyncProvisioning::
- EasClientDeviceInformation;
-using Windows::System::Profile::AnalyticsInfo;
-using Windows::System::Profile::AnalyticsVersionInfo;
-
-namespace sbwin32 = starboard::shared::win32;
-
-namespace {
-bool CopyStringAndTestIfSuccess(char* out_value,
- int value_length,
- const char* from_value) {
- if (SbStringGetLength(from_value) + 1 > value_length)
- return false;
- SbStringCopy(out_value, from_value, value_length);
- return true;
-}
-
-bool StartsWith(const std::string& str, const char* prefix) {
- size_t len = SbStringGetLength(prefix);
- if (str.size() < len) {
- return false;
- }
-
- return 0 == str.compare(0, len, prefix);
-}
-
-const std::size_t kOsVersionSize = 128;
-
-struct WindowsVersion {
- uint16_t major_version;
- uint16_t minor_version;
- uint16_t build_version;
- uint16_t revision;
-};
-
-bool GetWindowsVersion(WindowsVersion* version) {
- SB_DCHECK(version);
- AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
- std::string device_family_version =
- starboard::shared::win32::platformStringToString(
- version_info->DeviceFamilyVersion);
- if (device_family_version.empty()) {
- return false;
- }
- uint64_t version_info_all =
- SbStringParseUInt64(device_family_version.c_str(), nullptr, 10);
- if (version_info_all == 0) {
- return false;
- }
- version->major_version = (version_info_all >> 48) & 0xFFFF;
- version->minor_version = (version_info_all >> 32) & 0xFFFF;
- version->build_version = (version_info_all >> 16) & 0xFFFF;
- version->revision = version_info_all & 0xFFFF;
- return true;
-}
-
-const char kXboxOneSkuPrefix[] = "XBOX_ONE_";
-
-} // namespace
-
-bool SbSystemGetProperty(SbSystemPropertyId property_id,
- char* out_value,
- int value_length) {
- if (!out_value || !value_length) {
- return false;
- }
-
- using sbwin32::platformStringToString;
- using starboard::shared::uwp::SpeechApiKey;
-
- switch (property_id) {
- case kSbSystemPropertyModelYear:
- case kSbSystemPropertyNetworkOperatorName:
- case kSbSystemPropertyUserAgentAuxField:
- return false;
- case kSbSystemPropertySpeechApiKey:
- CopyStringAndTestIfSuccess(out_value, value_length, SpeechApiKey());
- return true;
- case kSbSystemPropertyBrandName: {
- EasClientDeviceInformation^ current_device_info =
- ref new EasClientDeviceInformation();
- std::string brand_name =
- platformStringToString(current_device_info->SystemManufacturer);
- if (brand_name.empty()) {
- return false;
- }
- return CopyStringAndTestIfSuccess(out_value, value_length,
- brand_name.c_str());
- }
- case kSbSystemPropertyFirmwareVersion: {
- WindowsVersion version = {0};
- if (!GetWindowsVersion(&version)) {
- return false;
- }
- int return_value = SbStringFormatF(
- out_value, value_length, "%u.%u.%u.%u", version.major_version,
- version.minor_version, version.build_version, version.revision);
- return ((return_value > 0) && (return_value < value_length));
- }
- case kSbSystemPropertyChipsetModelNumber: {
- std::string sku = platformStringToString(
- (ref new EasClientDeviceInformation())->SystemSku);
-
- std::string result;
- if (StartsWith(sku, kXboxOneSkuPrefix)) {
- result = "XboxOne";
- } else {
- result = sku;
- }
-
- return CopyStringAndTestIfSuccess(out_value, value_length,
- result.c_str());
- }
- case kSbSystemPropertyModelName: {
- std::string sku = platformStringToString(
- (ref new EasClientDeviceInformation())->SystemSku);
-
- std::string friendly_name;
-
- if (sku == "XBOX_ONE_DU") {
- friendly_name = "XboxOne";
- } else if (sku == "XBOX_ONE_ED") {
- friendly_name = "XboxOne S";
- } else if (sku == "XBOX_ONE_CH" || sku == "XBOX_ONE_SC") {
- friendly_name = "XboxOne X";
- } else if (StartsWith(sku, kXboxOneSkuPrefix)) {
- friendly_name = "XboxOne " + sku;
- } else {
- friendly_name = sku;
- }
-
- return CopyStringAndTestIfSuccess(out_value, value_length,
- friendly_name.c_str());
- }
- case kSbSystemPropertyFriendlyName: {
- EasClientDeviceInformation^ current_device_info =
- ref new EasClientDeviceInformation();
- std::string friendly_name =
- platformStringToString(current_device_info->FriendlyName);
- if (friendly_name.empty()) {
- return false;
- }
- return CopyStringAndTestIfSuccess(out_value, value_length,
- friendly_name.c_str());
- }
- case kSbSystemPropertyPlatformName: {
- EasClientDeviceInformation^ current_device_info =
- ref new EasClientDeviceInformation();
- std::string operating_system =
- platformStringToString(current_device_info->OperatingSystem);
-
- AnalyticsVersionInfo^ version_info = AnalyticsInfo::VersionInfo;
- std::string os_name_and_version =
- starboard::shared::win32::platformStringToString(
- current_device_info->OperatingSystem);
- if (os_name_and_version.empty()) {
- return false;
- }
-
- WindowsVersion os_version;
- if (!GetWindowsVersion(&os_version)) {
- return false;
- }
-
- os_name_and_version += " ";
- char os_version_buffer[kOsVersionSize];
- os_version_buffer[0] = '\0';
-
- int return_value =
- SbStringFormatF(os_version_buffer, value_length, "%u.%u",
- os_version.major_version, os_version.minor_version);
- if ((return_value < 0) || (return_value >= value_length)) {
- return false;
- }
-
- os_name_and_version.append(os_version_buffer);
-
- return CopyStringAndTestIfSuccess(out_value, value_length,
- os_name_and_version.c_str());
- }
-#if SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION
- case kSbSystemPropertyPlatformUuid: {
- SB_NOTIMPLEMENTED();
- return CopyStringAndTestIfSuccess(out_value, value_length, "N/A");
- }
-#endif // SB_API_VERSION < SB_PROPERTY_UUID_REMOVED_API_VERSION
- default:
- SB_DLOG(WARNING) << __FUNCTION__
- << ": Unrecognized property: " << property_id;
- break;
- }
- return false;
-}
diff --git a/src/starboard/shared/uwp/system_get_total_cpu_memory.cc b/src/starboard/shared/uwp/system_get_total_cpu_memory.cc
deleted file mode 100644
index 293fd7d..0000000
--- a/src/starboard/shared/uwp/system_get_total_cpu_memory.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-using Windows::System::MemoryManager;
-
-int64_t SbSystemGetTotalCPUMemory() {
- return static_cast<int64_t>(MemoryManager::AppMemoryUsageLimit);
-}
diff --git a/src/starboard/shared/uwp/system_get_used_cpu_memory.cc b/src/starboard/shared/uwp/system_get_used_cpu_memory.cc
deleted file mode 100644
index adc9c0d..0000000
--- a/src/starboard/shared/uwp/system_get_used_cpu_memory.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-using Windows::System::MemoryManager;
-
-int64_t SbSystemGetUsedCPUMemory() {
- return static_cast<int64_t>(MemoryManager::AppMemoryUsage);
-}
diff --git a/src/starboard/shared/uwp/system_platform_error_internal.cc b/src/starboard/shared/uwp/system_platform_error_internal.cc
deleted file mode 100644
index 0b46f70..0000000
--- a/src/starboard/shared/uwp/system_platform_error_internal.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/uwp/system_platform_error_internal.h"
-
-using Windows::Foundation::AsyncOperationCompletedHandler;
-using Windows::Foundation::AsyncStatus;
-using Windows::Foundation::IAsyncOperation;
-using Windows::UI::Popups::IUICommand;
-using Windows::UI::Popups::MessageDialog;
-using Windows::UI::Popups::UICommand;
-using Windows::UI::Popups::UICommandInvokedHandler;
-
-SbAtomic32 SbSystemPlatformErrorPrivate::s_error_count = 0;
-
-SbSystemPlatformErrorPrivate::SbSystemPlatformErrorPrivate(ApplicationUwp* app,
- SbSystemPlatformErrorType type, SbSystemPlatformErrorCallback callback,
- void* user_data)
- : callback_(callback),
- user_data_(user_data) {
- SB_DCHECK(type == kSbSystemPlatformErrorTypeConnectionError);
-
- // Only one error dialog can be displayed at a time.
- if (SbAtomicNoBarrier_Increment(&s_error_count, 1) != 1) {
- SbAtomicNoBarrier_Increment(&s_error_count, -1);
- return;
- }
-
- MessageDialog^ dialog = ref new MessageDialog(
- app->GetString("UNABLE_TO_CONTACT_YOUTUBE_1",
- "Sorry, could not connect to YouTube."));
- dialog->Commands->Append(
- MakeUICommand(
- app,
- "OFFLINE_MESSAGE_TRY_AGAIN", "Try again",
- kSbSystemPlatformErrorResponsePositive));
- dialog->Commands->Append(
- MakeUICommand(
- app,
- "EXIT_BUTTON", "Exit",
- kSbSystemPlatformErrorResponseCancel));
- dialog->DefaultCommandIndex = 0;
- dialog->CancelCommandIndex = 1;
-
- try {
- dialog_operation_ = dialog->ShowAsync();
- // NOTE: It's possible to use the "Completed" callback to delete this
- // object once the dialog is finished. However, this introduces a
- // possible race condition between deleting the object and
- // SbSystemClearPlatformError(). Other implementations delete the object
- // only when SbSystemClearPlatformError() is called. Go with this
- // approach to avoid the possible race condition.
- dialog_operation_->Completed =
- ref new AsyncOperationCompletedHandler<IUICommand ^>(
- [](IAsyncOperation<IUICommand^>^, AsyncStatus) {
- SB_DCHECK(SbAtomicNoBarrier_Load(&s_error_count) > 0);
- SbAtomicNoBarrier_Increment(&s_error_count, -1);
- });
- } catch(Platform::Exception^) {
- SB_LOG(ERROR) << "Unable to raise SbSystemPlatformError";
- SbAtomicNoBarrier_Increment(&s_error_count, -1);
- }
-}
-
-bool SbSystemPlatformErrorPrivate::IsValid() const {
- return dialog_operation_.Get() != nullptr;
-}
-
-void SbSystemPlatformErrorPrivate::ClearAndDelete() {
- if (IsValid()) {
- dialog_operation_->Cancel();
- }
- delete this;
-}
-
-IUICommand^ SbSystemPlatformErrorPrivate::MakeUICommand(ApplicationUwp* app,
- const char* id, const char* fallback,
- SbSystemPlatformErrorResponse response) {
- Platform::String^ label = app->GetString(id, fallback);
- return ref new UICommand(label,
- ref new UICommandInvokedHandler(
- [this, response](IUICommand^ command) {
- callback_(response, user_data_);
- }));
-}
diff --git a/src/starboard/shared/uwp/system_platform_error_internal.h b/src/starboard/shared/uwp/system_platform_error_internal.h
deleted file mode 100644
index 76546e7..0000000
--- a/src/starboard/shared/uwp/system_platform_error_internal.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 Google Inc. 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_UWP_SYSTEM_PLATFORM_ERROR_INTERNAL_H_
-#define STARBOARD_SHARED_UWP_SYSTEM_PLATFORM_ERROR_INTERNAL_H_
-
-#include "starboard/atomic.h"
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/system.h"
-
-// Note that this is a "struct" and not a "class" because
-// that's how it's defined in starboard/system.h
-struct SbSystemPlatformErrorPrivate {
- typedef starboard::shared::uwp::ApplicationUwp ApplicationUwp;
-
- SbSystemPlatformErrorPrivate(const SbSystemPlatformErrorPrivate&) = delete;
- SbSystemPlatformErrorPrivate& operator=(const SbSystemPlatformErrorPrivate&)
- = delete;
-
- SbSystemPlatformErrorPrivate(ApplicationUwp* app,
- SbSystemPlatformErrorType type, SbSystemPlatformErrorCallback callback,
- void* user_data);
- bool IsValid() const;
- void ClearAndDelete();
-
- private:
- typedef Windows::UI::Popups::IUICommand IUICommand;
- typedef Windows::Foundation::IAsyncOperation<IUICommand^> DialogOperation;
-
- IUICommand^ MakeUICommand(ApplicationUwp* app, const char* id,
- const char* fallback, SbSystemPlatformErrorResponse response);
-
- SbSystemPlatformErrorCallback callback_;
- void* user_data_;
- Platform::Agile<DialogOperation> dialog_operation_;
-
- static SbAtomic32 s_error_count;
-};
-
-#endif // STARBOARD_SHARED_UWP_SYSTEM_PLATFORM_ERROR_INTERNAL_H_
diff --git a/src/starboard/shared/uwp/system_raise_platform_error.cc b/src/starboard/shared/uwp/system_raise_platform_error.cc
deleted file mode 100644
index 00d2959..0000000
--- a/src/starboard/shared/uwp/system_raise_platform_error.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/shared/uwp/system_platform_error_internal.h"
-
-using starboard::shared::uwp::ApplicationUwp;
-
-SbSystemPlatformError SbSystemRaisePlatformError(
- SbSystemPlatformErrorType type,
- SbSystemPlatformErrorCallback callback,
- void* user_data) {
- ApplicationUwp* app = ApplicationUwp::Get();
- if (!app) {
- return kSbSystemPlatformErrorInvalid;
- }
-
- SbSystemPlatformError handle = new SbSystemPlatformErrorPrivate(
- app, type, callback, user_data);
- if (!handle->IsValid()) {
- handle->ClearAndDelete();
- handle = kSbSystemPlatformErrorInvalid;
- }
-
- return handle;
-}
diff --git a/src/starboard/shared/uwp/wasapi_audio.cc b/src/starboard/shared/uwp/wasapi_audio.cc
deleted file mode 100644
index ddc7fd1..0000000
--- a/src/starboard/shared/uwp/wasapi_audio.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/uwp/wasapi_audio.h"
-
-#include "starboard/log.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-using Microsoft::WRL::ComPtr;
-using Windows::Media::Devices::MediaDevice;
-
-namespace {
-const int kWaitForActivateTimeout = 500; // 0.5 sec
-} // namespace
-
-WASAPIAudioDevice::WASAPIAudioDevice() {
- InitializeAudioDevice();
- CloseHandle(activate_completed_);
-}
-
-void WASAPIAudioDevice::InitializeAudioDevice() {
- ComPtr<IActivateAudioInterfaceAsyncOperation> async_operation;
-
- // Default Audio Device Renderer
- Platform::String ^ deviceIdString = MediaDevice::GetDefaultAudioRenderId(
- Windows::Media::Devices::AudioDeviceRole::Default);
-
- if (FAILED(ActivateAudioInterfaceAsync(deviceIdString->Data(),
- __uuidof(IAudioClient3), nullptr, this,
- &async_operation))) {
- return;
- }
- WaitForSingleObject(activate_completed_, kWaitForActivateTimeout);
-}
-
-HRESULT WASAPIAudioDevice::ActivateCompleted(
- IActivateAudioInterfaceAsyncOperation* operation) {
- HRESULT hr = S_OK;
- HRESULT hr_activate = S_OK;
- ComPtr<IUnknown> audio_interface;
-
- // Check for a successful activation result
- hr = operation->GetActivateResult(&hr_activate, &audio_interface);
-
- if (SUCCEEDED(hr) && SUCCEEDED(hr_activate)) {
- // Get the pointer for the Audio Client.
- hr = audio_interface->QueryInterface(IID_PPV_ARGS(&audio_client_));
- SB_DCHECK(audio_client_);
- if (SUCCEEDED(hr)) {
- AudioClientProperties audio_props = {0};
- audio_props.cbSize = sizeof(AudioClientProperties);
- audio_props.bIsOffload = false;
- audio_props.eCategory = AudioCategory_Media;
-
- hr = audio_client_->SetClientProperties(&audio_props);
- if (SUCCEEDED(hr)) {
- WAVEFORMATEX* format;
- hr = audio_client_->GetMixFormat(&format);
- SB_DCHECK(format);
- if (SUCCEEDED(hr)) {
- bitrate_ = format->nSamplesPerSec * format->wBitsPerSample;
- channels_ = format->nChannels;
- CoTaskMemFree(format);
- } // audio_client_->GetMixFormat
- } // audio_client_->SetClientProperties
- } // audio_interface->QueryInterface
- }
-
- audio_client_ = nullptr;
- SetEvent(activate_completed_);
-
- // Need to return S_OK
- return S_OK;
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/wasapi_audio.h b/src/starboard/shared/uwp/wasapi_audio.h
deleted file mode 100644
index af25d66..0000000
--- a/src/starboard/shared/uwp/wasapi_audio.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2018 Google Inc. 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_UWP_WASAPI_AUDIO_H_
-#define STARBOARD_SHARED_UWP_WASAPI_AUDIO_H_
-
-#include <Audioclient.h>
-#include <mmdeviceapi.h>
-#include <wrl\implements.h>
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-class WASAPIAudioDevice
- : public Microsoft::WRL::RuntimeClass<
- Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
- Microsoft::WRL::FtmBase,
- IActivateAudioInterfaceCompletionHandler> {
- public:
- WASAPIAudioDevice::WASAPIAudioDevice();
-
- int GetNumChannels() const { return channels_; }
- int GetBitrate() const { return bitrate_; }
-
- private:
- STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation*);
- void InitializeAudioDevice();
-
- HANDLE activate_completed_ = CreateEvent(nullptr, TRUE, FALSE, nullptr);
- Microsoft::WRL::ComPtr<IAudioClient3> audio_client_ = nullptr;
- int bitrate_ = -1;
- int channels_ = -1;
-};
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_WASAPI_AUDIO_H_
diff --git a/src/starboard/shared/uwp/watchdog_log.cc b/src/starboard/shared/uwp/watchdog_log.cc
deleted file mode 100644
index 6fcd19b..0000000
--- a/src/starboard/shared/uwp/watchdog_log.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2018 Google Inc. 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 "starboard/shared/uwp/watchdog_log.h"
-
-#include <string>
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/common/semaphore.h"
-#include "starboard/common/thread.h"
-#include "starboard/file.h"
-#include "starboard/log.h"
-#include "starboard/string.h"
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-namespace {
-
-// The WatchDogThread will print out "alive: <COUNT>\n" periodically to a
-// file. On destruction WatchDogThread will write out "done\n".
-// This allows a test runner to accurately determine whether this process
-// is still alive, finished, or crashed.
-class WatchDogThread : public Thread {
- public:
- explicit WatchDogThread(const std::string& file_path)
- : Thread("WatchDogLog"), file_path_(file_path) {
- Start();
- }
-
- ~WatchDogThread() {
- Join();
- }
-
- void Run() override {
- static const SbTime kSleepTime = kSbTimeMillisecond * 250;
- int counter = 0;
- bool created_ok = false;
- SbFileError out_error = kSbFileOk;
- SbFile file_handle = SbFileOpen(file_path_.c_str(),
- kSbFileCreateAlways | kSbFileWrite,
- &created_ok,
- &out_error);
- if (!created_ok) {
- SB_LOG(ERROR) << "Could not create watchdog file " << file_path_;
- return;
- }
- while (!WaitForJoin(kSleepTime)) {
- std::stringstream ss;
- ss << "alive: " << counter++ << "\n";
- std::string str = ss.str();
- SbFileWrite(file_handle, str.c_str(), static_cast<int>(str.size()));
- SbFileFlush(file_handle);
- }
- const char kDone[] = "done\n";
- SbFileWrite(file_handle, kDone, static_cast<int>(SbStringGetLength(kDone)));
- SbFileFlush(file_handle);
- SbThreadSleep(50 * kSbTimeMillisecond);
- bool closed = SbFileClose(file_handle);
- SB_LOG_IF(ERROR, closed) << "Could not close file " << file_path_;
- }
-
- private:
- std::string file_path_;
-};
-starboard::scoped_ptr<WatchDogThread> s_watchdog_singleton_;
-} // namespace.
-
-void StartWatchdogLog(const std::string& path) {
- if (s_watchdog_singleton_.get()) {
- SB_LOG(ERROR) << "WatchDogThread exists, aborting.";
- return;
- }
- s_watchdog_singleton_.reset(new WatchDogThread(path));
-}
-
-void CloseWatchdogLog() {
- s_watchdog_singleton_.reset(nullptr);
-}
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/uwp/watchdog_log.h b/src/starboard/shared/uwp/watchdog_log.h
deleted file mode 100644
index 940bcb2..0000000
--- a/src/starboard/shared/uwp/watchdog_log.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2018 Google Inc. 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_UWP_WATCHDOG_LOG_H_
-#define STARBOARD_SHARED_UWP_WATCHDOG_LOG_H_
-
-#include <string>
-
-namespace starboard {
-namespace shared {
-namespace uwp {
-
-// Starts a watch dog log file. This file has "alive" printed
-// to it periodically and "done" when it finishes. Useful for
-// tests to determine when the process exits. Only one watchdog
-// log can be active in the process.
-void StartWatchdogLog(const std::string& path);
-void CloseWatchdogLog();
-
-} // namespace uwp
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_UWP_WATCHDOG_LOG_H_
diff --git a/src/starboard/shared/uwp/window_create.cc b/src/starboard/shared/uwp/window_create.cc
deleted file mode 100644
index 2ca5590..0000000
--- a/src/starboard/shared/uwp/window_create.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/window.h"
-
-#include "starboard/shared/uwp/application_uwp.h"
-
-SbWindow SbWindowCreate(const SbWindowOptions* options) {
- SbWindowOptions default_options;
- if (options == nullptr) {
- SbWindowSetDefaultOptions(&default_options);
- options = &default_options;
- }
- return starboard::shared::uwp::ApplicationUwp::Get()->CreateWindowForUWP(
- options);
-}
diff --git a/src/starboard/shared/uwp/window_destroy.cc b/src/starboard/shared/uwp/window_destroy.cc
deleted file mode 100644
index 34b7393..0000000
--- a/src/starboard/shared/uwp/window_destroy.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/window.h"
-
-#include "starboard/shared/uwp/application_uwp.h"
-
-bool SbWindowDestroy(SbWindow window) {
- return starboard::shared::uwp::ApplicationUwp::Get()->DestroyWindow(window);
-}
diff --git a/src/starboard/shared/uwp/window_get_platform_handle.cc b/src/starboard/shared/uwp/window_get_platform_handle.cc
deleted file mode 100644
index ba780bd..0000000
--- a/src/starboard/shared/uwp/window_get_platform_handle.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 <EGL/egl.h>
-
-#include "starboard/shared/uwp/window_internal.h"
-#include "starboard/window.h"
-
-void* SbWindowGetPlatformHandle(SbWindow window) {
- if (!SbWindowIsValid(window)) {
- return NULL;
- }
-
- return reinterpret_cast<EGLNativeWindowType>(window->egl_native_window());
-}
diff --git a/src/starboard/shared/uwp/window_get_size.cc b/src/starboard/shared/uwp/window_get_size.cc
deleted file mode 100644
index 418a538..0000000
--- a/src/starboard/shared/uwp/window_get_size.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/window.h"
-
-#include "starboard/log.h"
-#include "starboard/shared/uwp/window_internal.h"
-
-bool SbWindowGetSize(SbWindow window, SbWindowSize* size) {
- if (!SbWindowIsValid(window)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid window.";
- return false;
- }
-
- size->width = window->width;
- size->height = window->height;
- // The video resolution is the same as the graphics resolution.
- size->video_pixel_ratio = 1.0f;
- return true;
-}
diff --git a/src/starboard/shared/uwp/window_internal.cc b/src/starboard/shared/uwp/window_internal.cc
deleted file mode 100644
index 1123c59..0000000
--- a/src/starboard/shared/uwp/window_internal.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 Google Inc. 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 <EGL/egl.h>
-#include <windows.h>
-
-#include "starboard/shared/uwp/application_uwp.h"
-#include "starboard/shared/uwp/window_internal.h"
-#include "third_party/angle/include/angle_windowsstore.h"
-
-using Windows::UI::Core::CoreWindow;
-
-SbWindowPrivate::SbWindowPrivate(int width, int height)
- : width(width),
- height(height) {
- angle_property_set = ref new Windows::Foundation::Collections::PropertySet();
- angle_property_set->Insert(
- ref new Platform::String(EGLNativeWindowTypeProperty),
- starboard::shared::uwp::ApplicationUwp::Get()->GetCoreWindow().Get());
- angle_property_set->Insert(
- ref new Platform::String(EGLRenderSurfaceSizeProperty),
- Windows::Foundation::PropertyValue::CreateSize(
- Windows::Foundation::Size(width, height)));
-}
-
-SbWindowPrivate::~SbWindowPrivate() {}
diff --git a/src/starboard/shared/uwp/window_internal.h b/src/starboard/shared/uwp/window_internal.h
deleted file mode 100644
index 19cbadb..0000000
--- a/src/starboard/shared/uwp/window_internal.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 Google Inc. 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_UWP_WINDOW_INTERNAL_H_
-#define STARBOARD_SHARED_UWP_WINDOW_INTERNAL_H_
-
-#include <EGL/egl.h>
-
-#include "starboard/atomic.h"
-#include "starboard/time.h"
-#include "starboard/window.h"
-
-struct SbWindowPrivate {
- SbWindowPrivate(int width, int height);
- ~SbWindowPrivate();
-
- EGLNativeWindowType egl_native_window() const {
- return reinterpret_cast<EGLNativeWindowType>(angle_property_set);
- }
-
- // The width of this window.
- int width;
-
- // The height of this window.
- int height;
-
- private:
- Windows::Foundation::Collections::PropertySet^ angle_property_set;
-};
-
-#endif // STARBOARD_SHARED_UWP_WINDOW_INTERNAL_H_
diff --git a/src/starboard/shared/uwp/window_set_default_options.cc b/src/starboard/shared/uwp/window_set_default_options.cc
deleted file mode 100644
index 38b5500..0000000
--- a/src/starboard/shared/uwp/window_set_default_options.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-#include "starboard/window.h"
-
-void SbWindowSetDefaultOptions(SbWindowOptions* options) {
- SbMemorySet(options, 0, sizeof(*options));
- options->size.width = 1920;
- options->size.height = 1080;
-}
diff --git a/src/starboard/shared/wayland/application_wayland.cc b/src/starboard/shared/wayland/application_wayland.cc
index 51b93e2..0da78f9 100644
--- a/src/starboard/shared/wayland/application_wayland.cc
+++ b/src/starboard/shared/wayland/application_wayland.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Samsung Electronics. All Rights Reserved.
+// Copyright 2018 Samsung Electronics. 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.
@@ -16,8 +16,8 @@
#include <EGL/egl.h>
#include <poll.h>
-#include <sys/eventfd.h>
#include <string.h>
+#include <sys/eventfd.h>
#include <unistd.h>
#include "starboard/log.h"
@@ -45,12 +45,10 @@
void GlobalObjectRemove(void*, struct wl_registry*, uint32_t) {}
-static struct wl_registry_listener registry_listener = {
- &GlobalObjectAvailable,
- &GlobalObjectRemove
-};
+static struct wl_registry_listener registry_listener = {&GlobalObjectAvailable,
+ &GlobalObjectRemove};
-}
+} // namespace
// Tizen application engine using the generic queue and a tizen implementation.
ApplicationWayland::ApplicationWayland(float video_pixel_ratio)
diff --git a/src/starboard/shared/wayland/dev_input.cc b/src/starboard/shared/wayland/dev_input.cc
index 9df9f7c..7b8d718 100644
--- a/src/starboard/shared/wayland/dev_input.cc
+++ b/src/starboard/shared/wayland/dev_input.cc
@@ -403,14 +403,11 @@
}
const struct wl_keyboard_listener keyboard_listener = {
- &KeyboardHandleKeyMap,
- &KeyboardHandleEnter,
- &KeyboardHandleLeave,
- &KeyboardHandleKey,
- &KeyboardHandleModifiers,
+ &KeyboardHandleKeyMap, &KeyboardHandleEnter, &KeyboardHandleLeave,
+ &KeyboardHandleKey, &KeyboardHandleModifiers,
};
-}
+} // namespace
DevInput::DevInput()
: wl_seat_(NULL),
@@ -497,9 +494,11 @@
uint32_t time,
uint32_t key,
uint32_t state) {
- bool repeatable = (key == KEY_LEFT || key == KEY_RIGHT || key == KEY_UP || key == KEY_DOWN);
- SB_DLOG(INFO) << "[Key] Key :" << key << ", state:" << state << " repeatable " << repeatable
- << " key_repeat_key_ " << key_repeat_key_ << " key_repeat_state_ " << key_repeat_state_;
+ bool repeatable =
+ (key == KEY_LEFT || key == KEY_RIGHT || key == KEY_UP || key == KEY_DOWN);
+ SB_DLOG(INFO) << "[Key] Key :" << key << ", state:" << state << " repeatable "
+ << repeatable << " key_repeat_key_ " << key_repeat_key_
+ << " key_repeat_state_ " << key_repeat_state_;
if (state && repeatable && key == key_repeat_key_ && key_repeat_state_)
return;
diff --git a/src/starboard/shared/wayland/egl_workaround.cc b/src/starboard/shared/wayland/egl_workaround.cc
index 24bd097..9463d72 100644
--- a/src/starboard/shared/wayland/egl_workaround.cc
+++ b/src/starboard/shared/wayland/egl_workaround.cc
@@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/log.h"
#include "cobalt/renderer/backend/egl/display.h"
+#include "starboard/log.h"
#include "starboard/shared/wayland/native_display_type.h"
extern "C" EGLDisplay __real_eglGetDisplay(EGLNativeDisplayType native_display);
extern "C" EGLDisplay __wrap_eglGetDisplay(EGLNativeDisplayType native_display);
-extern "C" EGLDisplay __wrap_eglGetDisplay(EGLNativeDisplayType native_display) {
+extern "C" EGLDisplay __wrap_eglGetDisplay(
+ EGLNativeDisplayType native_display) {
SB_LOG(INFO) << " __wrap_eglGetDisplay ";
return __real_eglGetDisplay((EGLNativeDisplayType)WaylandNativeDisplayType());
}
diff --git a/src/starboard/shared/wayland/native_display_type.cc b/src/starboard/shared/wayland/native_display_type.cc
index 03e118c..0ab5538 100644
--- a/src/starboard/shared/wayland/native_display_type.cc
+++ b/src/starboard/shared/wayland/native_display_type.cc
@@ -17,9 +17,9 @@
#include "starboard/log.h"
#include "starboard/shared/wayland/application_wayland.h"
-NativeDisplayType WaylandNativeDisplayType()
-{
- wl_display* display = starboard::shared::wayland::ApplicationWayland::Get()->GetWLDisplay();
+NativeDisplayType WaylandNativeDisplayType() {
+ wl_display* display =
+ starboard::shared::wayland::ApplicationWayland::Get()->GetWLDisplay();
SB_LOG(INFO) << " SbNativeDisplayType() " << display;
return (NativeDisplayType)display;
}
diff --git a/src/starboard/shared/wayland/native_display_type.h b/src/starboard/shared/wayland/native_display_type.h
index 4eef08c..b4c116d 100644
--- a/src/starboard/shared/wayland/native_display_type.h
+++ b/src/starboard/shared/wayland/native_display_type.h
@@ -24,4 +24,4 @@
#ifdef __cplusplus
}
#endif
-#endif //STARBOARD_SHARED_WAYLAND_NATIVE_DISPLAY_TYPE_H_
+#endif // STARBOARD_SHARED_WAYLAND_NATIVE_DISPLAY_TYPE_H_
diff --git a/src/starboard/shared/wayland/window_internal.cc b/src/starboard/shared/wayland/window_internal.cc
index bce7bc7..6f323c4 100644
--- a/src/starboard/shared/wayland/window_internal.cc
+++ b/src/starboard/shared/wayland/window_internal.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 Samsung Electronics. All Rights Reserved.
+// Copyright 2018 Samsung Electronics. 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.
@@ -46,12 +46,8 @@
void ShellSurfacePopupDone(void*, struct wl_shell_surface*) {}
struct wl_shell_surface_listener shell_surface_listener = {
- &ShellSurfacePing,
- &ShellSurfaceConfigure,
- &ShellSurfacePopupDone
-};
-
-}
+ &ShellSurfacePing, &ShellSurfaceConfigure, &ShellSurfacePopupDone};
+} // namespace
SbWindowPrivate::SbWindowPrivate(wl_compositor* compositor,
wl_shell* shell,
diff --git a/src/starboard/shared/win32/__init__.py b/src/starboard/shared/win32/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/starboard/shared/win32/__init__.py
+++ /dev/null
diff --git a/src/starboard/shared/win32/adapter_utils.cc b/src/starboard/shared/win32/adapter_utils.cc
deleted file mode 100644
index d102314..0000000
--- a/src/starboard/shared/win32/adapter_utils.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/adapter_utils.h"
-
-#include <winsock2.h>
-
-#include <iphlpapi.h>
-
-#include <memory>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-#include "starboard/socket.h"
-
-namespace {
-const ULONG kDefaultAdapterInfoBufferSizeInBytes = 16 * 1024;
-} // namespace
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-bool GetAdapters(const SbSocketAddressType address_type,
- std::unique_ptr<char[]>* adapter_info) {
- SB_DCHECK(adapter_info);
-
- ULONG family = 0;
- int address_length_bytes = 0;
-
- switch (address_type) {
- case kSbSocketAddressTypeIpv4:
- family = AF_INET;
- address_length_bytes = kAddressLengthIpv4;
- break;
- case kSbSocketAddressTypeIpv6:
- family = AF_INET6;
- address_length_bytes = kAddressLengthIpv6;
- break;
- default:
- SB_NOTREACHED() << "Invalid address type: " << address_type;
- return false;
- }
-
- ULONG adapter_addresses_number_bytes = kDefaultAdapterInfoBufferSizeInBytes;
-
- for (int try_count = 0; try_count != 2; ++try_count) {
- // Using auto for return value here, since different versions of windows use
- // slightly different datatypes. These differences do not matter to us, but
- // the compiler might warn on them.
- adapter_info->reset(new char[adapter_addresses_number_bytes]);
- PIP_ADAPTER_ADDRESSES adapter_addresses =
- reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info->get());
-
- // Note: If |GetAdapterAddresses| deems that buffer supplied is not enough,
- // it will return the recommended number of bytes in
- // |adapter_addresses_number_bytes|.
- auto retval = GetAdaptersAddresses(
- family, GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_DNS_SERVER, nullptr,
- adapter_addresses, &adapter_addresses_number_bytes);
-
- if (retval == ERROR_SUCCESS) {
- return true;
- }
- if (retval != ERROR_BUFFER_OVERFLOW) {
- // Only retry with more memory if the error says so.
- break;
- }
- SB_LOG(ERROR) << "GetAdapterAddresses() failed with error code " << retval;
- }
- return false;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/adapter_utils.h b/src/starboard/shared/win32/adapter_utils.h
deleted file mode 100644
index bbe11fa..0000000
--- a/src/starboard/shared/win32/adapter_utils.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_ADAPTER_UTILS_H_
-#define STARBOARD_SHARED_WIN32_ADAPTER_UTILS_H_
-
-#include <winsock2.h>
-
-#include <ifdef.h>
-#include <iphlpapi.h>
-
-#include <memory>
-
-#include "starboard/socket.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// Returns all of the results for wi32's
-bool GetAdapters(const SbSocketAddressType address_type,
- std::unique_ptr<char[]>* adapter_info);
-
-// Returns true if a IP_ADAPTER_ADDRESSES IfType is a
-// non-loopback Ethernet interface.
-inline bool IsIfTypeEthernet(DWORD iftype) {
- switch (iftype) {
- case IF_TYPE_ETHERNET_CSMACD:
- case IF_TYPE_IEEE80211:
- return true;
- case IF_TYPE_SOFTWARE_LOOPBACK:
- default:
- return false;
- }
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_ADAPTER_UTILS_H_
diff --git a/src/starboard/shared/win32/application_win32.cc b/src/starboard/shared/win32/application_win32.cc
deleted file mode 100644
index 79cdbc6..0000000
--- a/src/starboard/shared/win32/application_win32.cc
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/application_win32.h"
-
-#include <windows.h> // NOLINT(build/include_order)
-#include <windowsx.h> // NOLINT(build/include_order)
-
-#include <cstdio>
-#include <string>
-
-#include "starboard/input.h"
-#include "starboard/key.h"
-#include "starboard/shared/starboard/application.h"
-#include "starboard/shared/win32/dialog.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/thread_private.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/shared/win32/window_internal.h"
-#include "starboard/system.h"
-
-using starboard::shared::starboard::Application;
-using starboard::shared::win32::ApplicationWin32;
-using starboard::shared::win32::CStringToWString;
-using starboard::shared::win32::DebugLogWinError;
-
-namespace {
-
-static const int kSbMouseDeviceId = 1;
-
-static const TCHAR kWindowClassName[] = L"window_class_name";
-
-LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM w_param, LPARAM l_param) {
- return ApplicationWin32::Get()->WindowProcess(hWnd, msg, w_param, l_param);
-}
-
-bool RegisterWindowClass() {
- WNDCLASSEX window_class;
- window_class.cbSize = sizeof(WNDCLASSEX);
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ff729176(v=vs.85).aspx
- window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
- window_class.lpfnWndProc = WndProc;
- window_class.cbClsExtra = 0;
- window_class.cbWndExtra = 0;
- window_class.hInstance = GetModuleHandle(nullptr);
- // TODO: Add YouTube icon.
- window_class.hIcon = LoadIcon(window_class.hInstance, IDI_APPLICATION);
- window_class.hIconSm = LoadIcon(window_class.hInstance, IDI_APPLICATION);
- window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
- window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- window_class.lpszMenuName = NULL;
- window_class.lpszClassName = kWindowClassName;
-
- if (!::RegisterClassEx(&window_class)) {
- SB_LOG(ERROR) << "Failed to register window";
- DebugLogWinError();
- return false;
- }
- return true;
-}
-
-// Create a Windows window.
-HWND CreateWindowInstance(const SbWindowOptions& options) {
- DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_CLIPSIBLINGS |
- WS_CLIPCHILDREN | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
- if (options.windowed) {
- dwStyle |= WS_MAXIMIZE;
- }
- const std::wstring wide_window_name = CStringToWString(options.name);
- const HWND window = CreateWindow(
- kWindowClassName, wide_window_name.c_str(), dwStyle, CW_USEDEFAULT,
- CW_USEDEFAULT, options.size.width, options.size.height, nullptr, nullptr,
- GetModuleHandle(nullptr), nullptr);
- SetForegroundWindow(window);
- if (window == nullptr) {
- SB_LOG(ERROR) << "Failed to create window.";
- DebugLogWinError();
- }
-
- return window;
-}
-
-} // namespace
-
-// Note that this is a "struct" and not a "class" because
-// that's how it's defined in starboard/system.h
-struct SbSystemPlatformErrorPrivate {
- SbSystemPlatformErrorPrivate(const SbSystemPlatformErrorPrivate&) = delete;
- SbSystemPlatformErrorPrivate& operator=(const SbSystemPlatformErrorPrivate&) =
- delete;
-
- SbSystemPlatformErrorPrivate(SbSystemPlatformErrorType type,
- SbSystemPlatformErrorCallback callback,
- void* user_data)
- : callback_(callback), user_data_(user_data) {
- if (type != kSbSystemPlatformErrorTypeConnectionError)
- SB_NOTREACHED();
-
- ApplicationWin32* app = ApplicationWin32::Get();
- const bool created_dialog = starboard::shared::win32::ShowOkCancelDialog(
- app->GetCoreWindow()->GetWindowHandle(),
- "", // No title.
- app->GetLocalizedString("UNABLE_TO_CONTACT_YOUTUBE_1",
- "Sorry, could not connect to YouTube."),
- app->GetLocalizedString("RETRY_BUTTON", "Retry"),
- [this, callback, user_data]() {
- callback(kSbSystemPlatformErrorResponsePositive, user_data);
- },
- app->GetLocalizedString("EXIT_BUTTON", "Exit"),
- [this, callback, user_data]() {
- callback(kSbSystemPlatformErrorResponseNegative, user_data);
- });
- SB_DCHECK(!created_dialog);
- if (!created_dialog) {
- SB_LOG(ERROR) << "Failed to create dialog!";
- }
- }
-
- void Clear() { starboard::shared::win32::CancelDialog(); }
-
- private:
- SbSystemPlatformErrorCallback callback_;
- void* user_data_;
-};
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-ApplicationWin32::ApplicationWin32()
- : localized_strings_(SbSystemGetLocaleId()) {}
-ApplicationWin32::~ApplicationWin32() {}
-
-SbWindow ApplicationWin32::CreateWindowForWin32(
- const SbWindowOptions* options) {
- if (SbWindowIsValid(window_.get())) {
- SB_LOG(WARNING) << "Returning existing window instance.";
- return window_.get();
- }
-
- RegisterWindowClass();
- HWND window;
- if (options) {
- window = CreateWindowInstance(*options);
- window_.reset(new SbWindowPrivate(options, window));
- } else {
- SbWindowOptions default_options;
- SbWindowSetDefaultOptions(&default_options);
- window = CreateWindowInstance(default_options);
- window_.reset(new SbWindowPrivate(&default_options, window));
- }
- ShowWindow(window, SW_SHOW);
- UpdateWindow(window);
- return window_.get();
-}
-
-bool ApplicationWin32::DestroyWindow(SbWindow window) {
- if (!SbWindowIsValid(window) || window != window_.get()) {
- return false;
- }
- window_.reset();
- return true;
-}
-
-SbSystemPlatformError ApplicationWin32::OnSbSystemRaisePlatformError(
- SbSystemPlatformErrorType type,
- SbSystemPlatformErrorCallback callback,
- void* user_data) {
- return new SbSystemPlatformErrorPrivate(type, callback, user_data);
-}
-
-void ApplicationWin32::OnSbSystemClearPlatformError(
- SbSystemPlatformError handle) {
- if (handle == kSbSystemPlatformErrorInvalid) {
- return;
- }
- static_cast<SbSystemPlatformErrorPrivate*>(handle)->Clear();
- // TODO: Determine if this should actually be deleted and if so, delete or
- // don't consistently across platforms.
- delete handle;
-}
-
-Application::Event* ApplicationWin32::WaitForSystemEventWithTimeout(
- SbTime time) {
- ProcessNextSystemMessage();
- if (pending_event_) {
- Event* out = pending_event_;
- pending_event_ = nullptr;
- return out;
- }
-
- ScopedLock lock(stop_waiting_for_system_events_mutex_);
- if (time <= SbTimeGetMonotonicNow() || stop_waiting_for_system_events_) {
- stop_waiting_for_system_events_ = false;
- return nullptr;
- }
-
- return WaitForSystemEventWithTimeout(time);
-}
-
-LRESULT ApplicationWin32::WindowProcess(HWND hWnd,
- UINT msg,
- WPARAM w_param,
- LPARAM l_param) {
- switch (msg) {
- // Input message handling.
- case WM_MBUTTONDOWN:
- case WM_LBUTTONDOWN:
- case WM_RBUTTONDOWN:
- case WM_MBUTTONUP:
- case WM_LBUTTONUP:
- case WM_RBUTTONUP:
- case WM_MOUSEMOVE:
- case WM_MOUSEWHEEL:
- pending_event_ =
- ProcessWinMouseEvent(GetCoreWindow(), msg, w_param, l_param);
- break;
- case WM_KEYDOWN:
- case WM_SYSKEYDOWN:
- case WM_KEYUP:
- case WM_SYSKEYUP:
- pending_event_ =
- ProcessWinKeyEvent(GetCoreWindow(), msg, w_param, l_param);
- break;
- case WM_DESTROY:
- SB_LOG(INFO) << "Received destroy message; posting Quit message";
- // Pause and suspend the application first so we can do some cleanup
- // before the window is destroyed (e.g. stopping rasterization).
- DispatchAndDelete(new Event(kSbEventTypePause, NULL, NULL));
- DispatchAndDelete(new Event(kSbEventTypeSuspend, NULL, NULL));
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProcW(hWnd, msg, w_param, l_param);
- }
- return 0;
-}
-
-int ApplicationWin32::Run(int argc, char** argv) {
- int return_val = Application::Run(argc, argv);
- return return_val;
-}
-
-void ApplicationWin32::ProcessNextSystemMessage() {
- MSG msg;
- BOOL peek_message_return = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
- if (peek_message_return == 0) { // 0 indicates no messages available.
- return;
- }
-
- if (!DialogHandleMessage(&msg)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- if (msg.message == WM_QUIT) {
- SB_LOG(INFO) << "Received Quit message; stopping application";
- SbSystemRequestStop(msg.wParam);
- }
-}
-
-Application::Event* ApplicationWin32::ProcessWinMouseEvent(SbWindow window,
- UINT msg,
- WPARAM w_param,
- LPARAM l_param) {
- SbInputData* data = new SbInputData();
- SbMemorySet(data, 0, sizeof(*data));
-
- data->window = window;
- data->device_type = kSbInputDeviceTypeMouse;
- data->device_id = kSbMouseDeviceId;
- switch (msg) {
- case WM_LBUTTONDOWN:
- data->key = kSbKeyMouse1;
- data->type = kSbInputEventTypePress;
- break;
- case WM_RBUTTONDOWN:
- data->key = kSbKeyMouse2;
- data->type = kSbInputEventTypePress;
- break;
- case WM_MBUTTONDOWN:
- data->key = kSbKeyMouse3;
- data->type = kSbInputEventTypePress;
- break;
- case WM_LBUTTONUP:
- data->key = kSbKeyMouse1;
- data->type = kSbInputEventTypeUnpress;
- break;
- case WM_RBUTTONUP:
- data->key = kSbKeyMouse2;
- data->type = kSbInputEventTypeUnpress;
- break;
- case WM_MBUTTONUP:
- data->key = kSbKeyMouse3;
- data->type = kSbInputEventTypeUnpress;
- break;
- case WM_MOUSEMOVE:
- data->type = kSbInputEventTypeMove;
- break;
- case WM_MOUSEWHEEL: {
- data->type = kSbInputEventTypeWheel;
- int wheel_delta = GET_WHEEL_DELTA_WPARAM(w_param);
- // Per MSFT, standard mouse wheel increments are multiples of 120. For
- // smooth scrolling, this may be less.
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
- data->delta.y = wheel_delta / 120.0f;
- } break;
- default:
- SB_LOG(WARNING) << "Received unrecognized MSG code " << msg;
- return nullptr;
- }
-
- data->pressure = NAN;
- data->size = {NAN, NAN};
- data->tilt = {NAN, NAN};
- data->position.x = GET_X_LPARAM(l_param);
- data->position.y = GET_Y_LPARAM(l_param);
-
- return new Application::Event(kSbEventTypeInput, data,
- &Application::DeleteDestructor<SbInputData>);
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/application_win32.h b/src/starboard/shared/win32/application_win32.h
deleted file mode 100644
index d243a95..0000000
--- a/src/starboard/shared/win32/application_win32.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_APPLICATION_WIN32_H_
-#define STARBOARD_SHARED_WIN32_APPLICATION_WIN32_H_
-
-// Windows headers.
-#include <windows.h>
-
-#include <memory>
-#include <string>
-#include <unordered_map>
-
-#include "starboard/mutex.h"
-#include "starboard/shared/starboard/application.h"
-#include "starboard/shared/starboard/localized_strings.h"
-#include "starboard/shared/starboard/queue_application.h"
-#include "starboard/shared/win32/window_internal.h"
-#include "starboard/system.h"
-#include "starboard/window.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class ApplicationWin32 : public starboard::QueueApplication {
- public:
- ApplicationWin32();
- ~ApplicationWin32() override;
-
- static ApplicationWin32* Get() {
- return static_cast<ApplicationWin32*>(
- ::starboard::shared::starboard::Application::Get());
- }
-
- SbWindow CreateWindowForWin32(const SbWindowOptions* options);
-
- bool DestroyWindow(SbWindow window);
-
- void DispatchStart() { starboard::Application::DispatchStart(); }
-
- SbWindow GetCoreWindow() {
- return SbWindowIsValid(window_.get()) ? window_.get() : kSbWindowInvalid;
- }
-
- SbSystemPlatformError OnSbSystemRaisePlatformError(
- SbSystemPlatformErrorType type,
- SbSystemPlatformErrorCallback callback,
- void* user_data);
-
- void OnSbSystemClearPlatformError(SbSystemPlatformError handle);
-
- std::string GetLocalizedString(const char* id, const char* fallback) const {
- return localized_strings_.GetString(id, fallback);
- }
-
- // Returns true if it is valid to poll/query for system events.
- bool MayHaveSystemEvents() override { return true; }
-
- // Waits for an event until the timeout |time| runs out. If an event occurs
- // in this time, it is returned, otherwise NULL is returned. If |time| is zero
- // or negative, then this should function effectively like a no-wait poll.
- Event* WaitForSystemEventWithTimeout(SbTime time) override;
-
- // Wakes up any thread waiting within a call to
- // WaitForSystemEventWithTimeout().
- void WakeSystemEventWait() override {
- ScopedLock lock(stop_waiting_for_system_events_mutex_);
- stop_waiting_for_system_events_ = true;
- }
-
- LRESULT WindowProcess(HWND hWnd, UINT msg, WPARAM w_param, LPARAM l_param);
- VOID TimedEventCallback(PVOID lp, BOOLEAN timer_or_wait_fired);
-
- // Non-virtual override. Calls into super class Run().
- int Run(int argc, char** argv);
-
- private:
- // --- Application overrides ---
- bool IsStartImmediate() override { return true; }
- void Initialize() override {}
- void Teardown() override {}
-
- void ProcessNextSystemMessage();
- SbTimeMonotonic GetNextTimedEventTargetTime() override {
- return SbTimeGetMonotonicNow();
- }
-
- // Processes window key events, returning a corresponding Event instance.
- // This transfers ownership of the returned Event.
- Event* ProcessWinKeyEvent(SbWindow window,
- UINT msg,
- WPARAM w_param,
- LPARAM l_param);
-
- // Processes window mouse events, returning a corresponding Event instance.
- // This transfers ownership of the returned Event. The Event may be nullptr.
- Event* ProcessWinMouseEvent(SbWindow window,
- UINT msg,
- WPARAM w_param,
- LPARAM l_param);
-
- Event* pending_event_ = nullptr;
-
- // The single open window, if any.
- std::unique_ptr<SbWindowPrivate> window_;
-
- starboard::LocalizedStrings localized_strings_;
-
- Mutex stop_waiting_for_system_events_mutex_;
- bool stop_waiting_for_system_events_;
-
- // The current depressed SbKeyModifiers - if there are any.
- unsigned int current_key_modifiers_ = 0;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_APPLICATION_WIN32_H_
diff --git a/src/starboard/shared/win32/application_win32_key_event.cc b/src/starboard/shared/win32/application_win32_key_event.cc
deleted file mode 100644
index ae0883a..0000000
--- a/src/starboard/shared/win32/application_win32_key_event.cc
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/application_win32.h"
-
-#include <windows.h>
-
-#include "starboard/event.h"
-#include "starboard/input.h"
-#include "starboard/key.h"
-#include "starboard/shared/starboard/application.h"
-
-using starboard::shared::starboard::Application;
-
-namespace {
-
-const int kSbKeyboardDeviceId = 1;
-
-SbKey VirtualKeyCodeToSbKey(WPARAM virtual_key_code) {
- // Keyboard code reference:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
- switch (virtual_key_code) {
- case VK_CANCEL: return kSbKeyCancel;
- case VK_BACK: return kSbKeyBack;
- case VK_TAB: return kSbKeyTab;
- case VK_CLEAR: return kSbKeyClear;
- case VK_RETURN: return kSbKeyReturn;
- case VK_SHIFT: return kSbKeyShift;
- case VK_CONTROL: return kSbKeyControl;
- case VK_MENU: return kSbKeyMenu;
- case VK_PAUSE: return kSbKeyPause;
- case VK_CAPITAL: return kSbKeyCapital;
- // Hangul and Kana have the same VirtualKey constant
- case VK_KANA: return kSbKeyKana;
- case VK_JUNJA: return kSbKeyJunja;
- case VK_FINAL: return kSbKeyFinal;
- // Hanja and Kanji have the same VirtualKey constant
- case VK_HANJA: return kSbKeyHanja;
- case VK_ESCAPE: return kSbKeyEscape;
- case VK_CONVERT: return kSbKeyConvert;
- case VK_NONCONVERT: return kSbKeyNonconvert;
- case VK_ACCEPT: return kSbKeyAccept;
- case VK_MODECHANGE: return kSbKeyModechange;
- case VK_SPACE: return kSbKeySpace;
- case VK_PRIOR: return kSbKeyPrior;
- case VK_NEXT: return kSbKeyNext;
- case VK_END: return kSbKeyEnd;
- case VK_HOME: return kSbKeyHome;
- case VK_LEFT: return kSbKeyLeft;
- case VK_UP: return kSbKeyUp;
- case VK_RIGHT: return kSbKeyRight;
- case VK_DOWN: return kSbKeyDown;
- case VK_SELECT: return kSbKeySelect;
- case VK_PRINT: return kSbKeyPrint;
- case VK_EXECUTE: return kSbKeyExecute;
- case VK_SNAPSHOT: return kSbKeySnapshot;
- case VK_INSERT: return kSbKeyInsert;
- case VK_DELETE: return kSbKeyDelete;
- case VK_OEM_PERIOD: return kSbKeyOemPeriod;
- case 0x30: return kSbKey0;
- case 0x31: return kSbKey1;
- case 0x32: return kSbKey2;
- case 0x33: return kSbKey3;
- case 0x34: return kSbKey4;
- case 0x35: return kSbKey5;
- case 0x36: return kSbKey6;
- case 0x37: return kSbKey7;
- case 0x38: return kSbKey8;
- case 0x39: return kSbKey9;
- case 0x41: return kSbKeyA;
- case 0x42: return kSbKeyB;
- case 0x43: return kSbKeyC;
- case 0x44: return kSbKeyD;
- case 0x45: return kSbKeyE;
- case 0x46: return kSbKeyF;
- case 0x47: return kSbKeyG;
- case 0x48: return kSbKeyH;
- case 0x49: return kSbKeyI;
- case 0x4A: return kSbKeyJ;
- case 0x4B: return kSbKeyK;
- case 0x4C: return kSbKeyL;
- case 0x4D: return kSbKeyM;
- case 0x4E: return kSbKeyN;
- case 0x4F: return kSbKeyO;
- case 0x50: return kSbKeyP;
- case 0x51: return kSbKeyQ;
- case 0x52: return kSbKeyR;
- case 0x53: return kSbKeyS;
- case 0x54: return kSbKeyT;
- case 0x55: return kSbKeyU;
- case 0x56: return kSbKeyV;
- case 0x57: return kSbKeyW;
- case 0x58: return kSbKeyX;
- case 0x59: return kSbKeyY;
- case 0x5A: return kSbKeyZ;
- case VK_LWIN: return kSbKeyLwin;
- case VK_RWIN: return kSbKeyRwin;
- case VK_APPS: return kSbKeyApps;
- case VK_SLEEP: return kSbKeySleep;
- case VK_NUMPAD0: return kSbKeyNumpad0;
- case VK_NUMPAD1: return kSbKeyNumpad1;
- case VK_NUMPAD2: return kSbKeyNumpad2;
- case VK_NUMPAD3: return kSbKeyNumpad3;
- case VK_NUMPAD4: return kSbKeyNumpad4;
- case VK_NUMPAD5: return kSbKeyNumpad5;
- case VK_NUMPAD6: return kSbKeyNumpad6;
- case VK_NUMPAD7: return kSbKeyNumpad7;
- case VK_NUMPAD8: return kSbKeyNumpad8;
- case VK_NUMPAD9: return kSbKeyNumpad9;
- case VK_MULTIPLY: return kSbKeyMultiply;
- case VK_ADD: return kSbKeyAdd;
- case VK_SEPARATOR: return kSbKeySeparator;
- case VK_SUBTRACT: return kSbKeySubtract;
- case VK_DECIMAL: return kSbKeyDecimal;
- case VK_DIVIDE: return kSbKeyDivide;
- case VK_F1: return kSbKeyF1;
- case VK_F2: return kSbKeyF2;
- case VK_F3: return kSbKeyF3;
- case VK_F4: return kSbKeyF4;
- case VK_F5: return kSbKeyF5;
- case VK_F6: return kSbKeyF6;
- case VK_F7: return kSbKeyF7;
- case VK_F8: return kSbKeyF8;
- case VK_F9: return kSbKeyF9;
- case VK_F10: return kSbKeyF10;
- case VK_F11: return kSbKeyF11;
- case VK_F12: return kSbKeyF12;
- case VK_F13: return kSbKeyF13;
- case VK_F14: return kSbKeyF14;
- case VK_F15: return kSbKeyF15;
- case VK_F16: return kSbKeyF16;
- case VK_F17: return kSbKeyF17;
- case VK_F18: return kSbKeyF18;
- case VK_F19: return kSbKeyF19;
- case VK_F20: return kSbKeyF20;
- case VK_F21: return kSbKeyF21;
- case VK_F22: return kSbKeyF22;
- case VK_F23: return kSbKeyF23;
- case VK_F24: return kSbKeyF24;
- case VK_NUMLOCK: return kSbKeyNumlock;
- case VK_SCROLL: return kSbKeyScroll;
- case VK_LSHIFT: return kSbKeyLshift;
- case VK_RSHIFT: return kSbKeyRshift;
- case VK_LCONTROL: return kSbKeyLcontrol;
- case VK_RCONTROL: return kSbKeyRcontrol;
- case VK_LMENU: return kSbKeyLmenu;
- case VK_RMENU: return kSbKeyRmenu;
- case VK_BROWSER_BACK: return kSbKeyBrowserBack;
- case VK_BROWSER_FORWARD: return kSbKeyBrowserForward;
- case VK_BROWSER_REFRESH: return kSbKeyBrowserRefresh;
- case VK_BROWSER_STOP: return kSbKeyBrowserStop;
- case VK_BROWSER_SEARCH: return kSbKeyBrowserSearch;
- case VK_BROWSER_FAVORITES: return kSbKeyBrowserFavorites;
- case VK_BROWSER_HOME: return kSbKeyBrowserHome;
- case VK_LBUTTON: return kSbKeyMouse1;
- case VK_RBUTTON: return kSbKeyMouse2;
- case VK_MBUTTON: return kSbKeyMouse3;
- case VK_XBUTTON1: return kSbKeyMouse4;
- case VK_XBUTTON2: return kSbKeyMouse5;
- default:
- SB_LOG(WARNING) << "Unrecognized key hit.";
- return kSbKeyUnknown;
- }
-}
-
-} // namespace
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// TODO: Plug into XInput APIs for Xbox controller input?
-Application::Event* ApplicationWin32::ProcessWinKeyEvent(SbWindow window,
- UINT msg,
- WPARAM w_param,
- LPARAM l_param) {
- SbInputData* data = new SbInputData();
- SbMemorySet(data, 0, sizeof(*data));
-
- data->window = window;
- data->device_type = kSbInputDeviceTypeKeyboard;
- // TODO: Do some more intelligent handling logic here to determine
- // a unique device ID.
- data->device_id = kSbKeyboardDeviceId;
- data->key = VirtualKeyCodeToSbKey(w_param);
-
- const bool was_down = ((l_param & (1 << 30)) != 0);
- const bool up = msg != WM_KEYDOWN && msg != WM_SYSKEYDOWN;
-
- data->type = up ? kSbInputEventTypeUnpress : kSbInputEventTypePress;
-
- SbKeyModifiers current_modifier = kSbKeyModifiersNone;
- if (data->key == kSbKeyShift || data->key == kSbKeyRshift ||
- data->key == kSbKeyLshift) {
- current_modifier = kSbKeyModifiersShift;
- } else if (data->key == kSbKeyMenu || data->key == kSbKeyRmenu ||
- data->key == kSbKeyLmenu) {
- current_modifier = kSbKeyModifiersAlt;
- } else if (data->key == kSbKeyControl || data->key == kSbKeyRcontrol ||
- data->key == kSbKeyLcontrol) {
- current_modifier = kSbKeyModifiersCtrl;
- } else if (data->key == kSbKeyRwin || data->key == kSbKeyLwin) {
- current_modifier = kSbKeyModifiersMeta;
- }
-
- // Either add or remove the current modifier key being pressed or released.
- // This noops for kSbKeyModifiersNone.
- if (up) {
- current_key_modifiers_ &= ~current_modifier;
- } else {
- current_key_modifiers_ |= current_modifier;
- }
- data->key_modifiers = current_key_modifiers_;
-
- switch (data->key) {
- case kSbKeyLshift:
- case kSbKeyLmenu:
- case kSbKeyLcontrol:
- case kSbKeyLwin:
- data->key_location = kSbKeyLocationLeft;
- break;
- case kSbKeyRshift:
- case kSbKeyRmenu:
- case kSbKeyRcontrol:
- case kSbKeyRwin:
- data->key_location = kSbKeyLocationRight;
- break;
- default:
- break;
- }
-
- return new Application::Event(kSbEventTypeInput, data,
- &Application::DeleteDestructor<SbInputData>);
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/atomic_public.h b/src/starboard/shared/win32/atomic_public.h
deleted file mode 100644
index 56088b3..0000000
--- a/src/starboard/shared/win32/atomic_public.h
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_ATOMIC_PUBLIC_H_
-#define STARBOARD_SHARED_WIN32_ATOMIC_PUBLIC_H_
-
-#include "starboard/atomic.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Declarations for Windows Intrinsic Functions
-// Defined here to avoid including Windows headers
-// See https://msdn.microsoft.com/en-us/library/w5405h95.aspx
-
-long _InterlockedCompareExchange(
- long volatile * Destination,
- long Exchange,
- long Comparand
-);
-#pragma intrinsic(_InterlockedCompareExchange)
-
-__int64 _InterlockedCompareExchange64(
- __int64 volatile * Destination,
- __int64 Exchange,
- __int64 Comparand
-);
-#pragma intrinsic(_InterlockedCompareExchange64)
-
-long _InterlockedExchange(
- long volatile * Target,
- long Value
-);
-#pragma intrinsic(_InterlockedExchange)
-
-__int64 _InterlockedExchange64(
- __int64 volatile * Target,
- __int64 Value
-);
-#pragma intrinsic(_InterlockedExchange64)
-
-long _InterlockedExchangeAdd(
- long volatile * Addend,
- long Value
-);
-#pragma intrinsic(_InterlockedExchangeAdd)
-
-__int64 _InterlockedExchangeAdd64(
- __int64 volatile * Addend,
- __int64 Value
-);
-#pragma intrinsic(_InterlockedExchangeAdd64)
-
-void _ReadWriteBarrier(void);
-#pragma intrinsic(_ReadWriteBarrier)
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicNoBarrier_CompareAndSwap(volatile SbAtomic32* ptr,
- SbAtomic32 old_value,
- SbAtomic32 new_value) {
- // Note this does a full memory barrier
- return _InterlockedCompareExchange(
- (volatile long*) ptr, (long) new_value, (long) old_value);
-}
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicNoBarrier_Exchange(volatile SbAtomic32* ptr, SbAtomic32 new_value) {
- // Note this does a full memory barrier
- return _InterlockedExchange((volatile long*)ptr, (long)new_value);
-}
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicNoBarrier_Increment(volatile SbAtomic32* ptr, SbAtomic32 increment) {
- return SbAtomicBarrier_Increment(ptr, increment);
-}
-
-SB_C_FORCE_INLINE SbAtomic32 SbAtomicBarrier_Increment(volatile SbAtomic32* ptr,
- SbAtomic32 increment) {
- // Note InterlockedExchangeAdd does a full memory barrier
- return increment + _InterlockedExchangeAdd(
- (volatile long *)ptr, (long)increment);
-}
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicAcquire_CompareAndSwap(volatile SbAtomic32* ptr,
- SbAtomic32 old_value,
- SbAtomic32 new_value) {
- // Note this does a full memory barrier
- return _InterlockedCompareExchange(
- (volatile long*) ptr, (long) new_value, (long) old_value);
-}
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicRelease_CompareAndSwap(volatile SbAtomic32* ptr,
- SbAtomic32 old_value,
- SbAtomic32 new_value) {
- // Note this does a full memory barrier
- return _InterlockedCompareExchange(
- (volatile long*) ptr, (long) new_value, (long) old_value);
-}
-
-// NOTE: https://msdn.microsoft.com/en-us/library/f20w0x5e.aspx
-// states _ReadWriteBarrier() is deprecated and
-// recommends "atomic_thread_fence", which is C++11 and violates
-// Starboard's "C-only header" policy
-SB_C_FORCE_INLINE void SbAtomicMemoryBarrier() {
- _ReadWriteBarrier();
-}
-
-SB_C_FORCE_INLINE void SbAtomicNoBarrier_Store(volatile SbAtomic32* ptr,
- SbAtomic32 value) {
- *ptr = value;
-}
-
-SB_C_FORCE_INLINE void SbAtomicAcquire_Store(volatile SbAtomic32* ptr,
- SbAtomic32 value) {
- *ptr = value;
- SbAtomicMemoryBarrier();
-}
-
-SB_C_FORCE_INLINE void SbAtomicRelease_Store(volatile SbAtomic32* ptr,
- SbAtomic32 value) {
- SbAtomicMemoryBarrier();
- *ptr = value;
-}
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicNoBarrier_Load(volatile const SbAtomic32* ptr) {
- return *ptr;
-}
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicAcquire_Load(volatile const SbAtomic32* ptr) {
- SbAtomic32 value = *ptr;
- SbAtomicMemoryBarrier();
- return value;
-}
-
-SB_C_FORCE_INLINE SbAtomic32
-SbAtomicRelease_Load(volatile const SbAtomic32* ptr) {
- SbAtomicMemoryBarrier();
- return *ptr;
-}
-
-// 64-bit atomic operations (only available on 64-bit processors).
-#if SB_HAS(64_BIT_ATOMICS)
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicNoBarrier_CompareAndSwap64(volatile SbAtomic64* ptr,
- SbAtomic64 old_value,
- SbAtomic64 new_value) {
- return _InterlockedCompareExchange64(ptr, new_value, old_value);
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicNoBarrier_Exchange64(volatile SbAtomic64* ptr, SbAtomic64 new_value) {
- return _InterlockedExchange64(ptr, new_value);
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicNoBarrier_Increment64(volatile SbAtomic64* ptr, SbAtomic64 increment) {
- return increment + _InterlockedExchangeAdd64(ptr, increment);
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicBarrier_Increment64(volatile SbAtomic64* ptr, SbAtomic64 increment) {
- // Note this does a full memory barrier
- return increment + _InterlockedExchangeAdd64(ptr, increment);
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicAcquire_CompareAndSwap64(volatile SbAtomic64* ptr,
- SbAtomic64 old_value,
- SbAtomic64 new_value) {
- // Note this does a full memory barrier
- return _InterlockedCompareExchange64(ptr, new_value, old_value);
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicRelease_CompareAndSwap64(volatile SbAtomic64* ptr,
- SbAtomic64 old_value,
- SbAtomic64 new_value) {
- // Note this does a full memory barrier
- return _InterlockedCompareExchange64(ptr, new_value, old_value);
-}
-
-SB_C_FORCE_INLINE void SbAtomicNoBarrier_Store64(volatile SbAtomic64* ptr,
- SbAtomic64 value) {
- *ptr = value;
-}
-
-SB_C_FORCE_INLINE void SbAtomicAcquire_Store64(volatile SbAtomic64* ptr,
- SbAtomic64 value) {
- *ptr = value;
- SbAtomicMemoryBarrier();
-}
-
-SB_C_FORCE_INLINE void SbAtomicRelease_Store64(volatile SbAtomic64* ptr,
- SbAtomic64 value) {
- SbAtomicMemoryBarrier();
- *ptr = value;
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicNoBarrier_Load64(volatile const SbAtomic64* ptr) {
- return *ptr;
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicAcquire_Load64(volatile const SbAtomic64* ptr) {
- SbAtomic64 value = *ptr;
- SbAtomicMemoryBarrier();
- return value;
-}
-
-SB_C_FORCE_INLINE SbAtomic64
-SbAtomicRelease_Load64(volatile const SbAtomic64* ptr) {
- SbAtomicMemoryBarrier();
- return *ptr;
-}
-#endif // SB_HAS(64_BIT_ATOMICS)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // STARBOARD_SHARED_WIN32_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/shared/win32/atomic_queue.h b/src/starboard/shared/win32/atomic_queue.h
deleted file mode 100644
index 676813a..0000000
--- a/src/starboard/shared/win32/atomic_queue.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_ATOMIC_QUEUE_H_
-#define STARBOARD_SHARED_WIN32_ATOMIC_QUEUE_H_
-
-#include <deque>
-
-#include "starboard/mutex.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// A simple thread-safe producer / consumer queue. Elements are added via
-// PopBack() and removed via PopFront().
-template <typename Data>
-class AtomicQueue {
- public:
- size_t PushBack(Data data_ptr) {
- ScopedLock lock(mutex_);
- data_queue_.push_back(data_ptr);
- return data_queue_.size();
- }
-
- Data PopFront() {
- ScopedLock lock(mutex_);
- if (data_queue_.empty()) {
- Data empty = Data();
- return empty;
- }
- Data data_ptr = data_queue_.front();
- data_queue_.pop_front();
- return data_ptr;
- }
-
- bool IsEmpty() const {
- ScopedLock lock(mutex_);
- return data_queue_.empty();
- }
-
- size_t Size() const {
- ScopedLock lock(mutex_);
- return data_queue_.size();
- }
-
- void Clear() {
- ScopedLock lock(mutex_);
- std::deque<Data> empty;
- data_queue_.swap(empty);
- }
-
- private:
- using Mutex = ::starboard::Mutex;
- using ScopedLock = ::starboard::ScopedLock;
- std::deque<Data> data_queue_;
- ::starboard::Mutex mutex_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_ATOMIC_QUEUE_H_
diff --git a/src/starboard/shared/win32/audio_decoder.cc b/src/starboard/shared/win32/audio_decoder.cc
deleted file mode 100644
index d851857..0000000
--- a/src/starboard/shared/win32/audio_decoder.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/audio_decoder.h"
-
-#include "starboard/atomic.h"
-#include "starboard/audio_sink.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/media_common.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class AudioDecoder::CallbackScheduler : private JobOwner {
- public:
- CallbackScheduler() : callback_signaled_(false) {}
-
- void SetCallbackOnce(ConsumedCB cb) {
- SB_DCHECK(cb);
- ::starboard::ScopedLock lock(mutex_);
- if (!cb_) {
- cb_ = cb;
- }
- }
-
- void ScheduleCallbackIfNecessary() {
- ::starboard::ScopedLock lock(mutex_);
- if (!cb_ || callback_signaled_) {
- return;
- }
- callback_signaled_ = true;
- JobOwner::Schedule(cb_);
- }
-
- void OnCallbackSignaled() {
- ::starboard::ScopedLock lock(mutex_);
- callback_signaled_ = false;
- }
-
- ConsumedCB cb_;
- ::starboard::Mutex mutex_;
- bool callback_signaled_;
-};
-
-AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
- const SbMediaAudioHeader& audio_header,
- SbDrmSystem drm_system)
- : audio_codec_(audio_codec),
- audio_header_(audio_header),
- drm_system_(drm_system),
- sample_type_(kSbMediaAudioSampleTypeFloat32),
- stream_ended_(false) {
- SB_DCHECK(audio_codec == kSbMediaAudioCodecAac);
-}
-
-AudioDecoder::~AudioDecoder() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- decoder_thread_.reset(nullptr);
- decoder_impl_.reset(nullptr);
- callback_scheduler_.reset(nullptr);
-}
-
-void AudioDecoder::Initialize(const OutputCB& output_cb,
- const ErrorCB& error_cb) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_UNREFERENCED_PARAMETER(error_cb);
-
- SB_DCHECK(output_cb);
- SB_DCHECK(!output_cb_);
- output_cb_ = output_cb;
- decoder_impl_ = AbstractWin32AudioDecoder::Create(
- audio_codec_, GetStorageType(), GetSampleType(), audio_header_,
- drm_system_);
- decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
- callback_scheduler_.reset(new CallbackScheduler());
-}
-
-void AudioDecoder::Decode(const scoped_refptr<InputBuffer>& input_buffer,
- const ConsumedCB& consumed_cb) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(input_buffer);
-
- callback_scheduler_->SetCallbackOnce(consumed_cb);
- callback_scheduler_->OnCallbackSignaled();
- const bool can_take_more_data = decoder_thread_->QueueInput(input_buffer);
- if (can_take_more_data) {
- callback_scheduler_->ScheduleCallbackIfNecessary();
- }
-
- if (stream_ended_) {
- SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
- return;
- }
-}
-
-void AudioDecoder::WriteEndOfStream() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- ::starboard::ScopedLock lock(mutex_);
- stream_ended_ = true;
- decoder_thread_->QueueEndOfStream();
-}
-
-scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- DecodedAudioPtr data = decoded_data_.PopFront();
- SB_DCHECK(data);
- return data;
-}
-
-void AudioDecoder::Reset() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- decoder_thread_.reset(nullptr);
- decoder_impl_->Reset();
-
- decoded_data_.Clear();
- stream_ended_ = false;
- callback_scheduler_.reset(new CallbackScheduler());
- CancelPendingJobs();
-
- decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
-}
-
-SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- return sample_type_;
-}
-
-int AudioDecoder::GetSamplesPerSecond() const {
- return decoder_impl_->GetSamplesPerSecond();
-}
-
-void AudioDecoder::OnAudioDecoded(DecodedAudioPtr data) {
- decoded_data_.PushBack(data);
- if (output_cb_) {
- Schedule(output_cb_);
- }
- callback_scheduler_->ScheduleCallbackIfNecessary();
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/audio_decoder.h b/src/starboard/shared/win32/audio_decoder.h
deleted file mode 100644
index 4dd6cf4..0000000
--- a/src/starboard/shared/win32/audio_decoder.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_AUDIO_DECODER_H_
-#define STARBOARD_SHARED_WIN32_AUDIO_DECODER_H_
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/configuration.h"
-#include "starboard/drm.h"
-#include "starboard/media.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/player/decoded_audio_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/job_queue.h"
-#include "starboard/shared/starboard/thread_checker.h"
-#include "starboard/shared/win32/atomic_queue.h"
-#include "starboard/shared/win32/audio_decoder_thread.h"
-#include "starboard/shared/win32/media_common.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class AudioDecoder
- : public ::starboard::shared::starboard::player::filter::AudioDecoder,
- private ::starboard::shared::starboard::player::JobQueue::JobOwner,
- private AudioDecodedCallback {
- public:
- AudioDecoder(SbMediaAudioCodec audio_codec,
- const SbMediaAudioHeader& audio_header,
- SbDrmSystem drm_system);
- ~AudioDecoder() override;
-
- void Initialize(const OutputCB& output_cb, const ErrorCB& error_cb) override;
- void Decode(const scoped_refptr<InputBuffer>& input_buffer,
- const ConsumedCB& consumed_cb) override;
- void WriteEndOfStream() override;
- scoped_refptr<DecodedAudio> Read() override;
- void Reset() override;
- SbMediaAudioSampleType GetSampleType() const override;
- int GetSamplesPerSecond() const override;
-
- SbMediaAudioFrameStorageType GetStorageType() const override {
- return kSbMediaAudioFrameStorageTypeInterleaved;
- }
- void OnAudioDecoded(DecodedAudioPtr data) override;
-
- private:
- class CallbackScheduler;
-
- ::starboard::shared::starboard::ThreadChecker thread_checker_;
-
- const SbMediaAudioCodec audio_codec_;
- const SbMediaAudioHeader audio_header_;
- SbDrmSystem const drm_system_;
- const SbMediaAudioSampleType sample_type_;
- bool stream_ended_;
-
- AtomicQueue<DecodedAudioPtr> decoded_data_;
- scoped_ptr<AudioDecoder::CallbackScheduler> callback_scheduler_;
- scoped_ptr<AbstractWin32AudioDecoder> decoder_impl_;
- scoped_ptr<AudioDecoderThread> decoder_thread_;
- OutputCB output_cb_;
-
- Mutex mutex_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_AUDIO_DECODER_H_
diff --git a/src/starboard/shared/win32/audio_decoder_thread.cc b/src/starboard/shared/win32/audio_decoder_thread.cc
deleted file mode 100644
index 7d53b63..0000000
--- a/src/starboard/shared/win32/audio_decoder_thread.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/audio_decoder_thread.h"
-
-#include <deque>
-#include <vector>
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-namespace {
-
-// Size of the queue for audio units.
-const size_t kMaxProcessingElements = 64;
-
-size_t WriteAsMuchAsPossible(
- std::deque<scoped_refptr<InputBuffer> >* data_queue,
- AbstractWin32AudioDecoder* audio_decoder) {
- const size_t original_size = data_queue->size();
- while (!data_queue->empty()) {
- scoped_refptr<InputBuffer> buff = data_queue->front();
- data_queue->pop_front();
-
- if (buff) {
- if (!audio_decoder->TryWrite(buff)) {
- data_queue->push_front(buff);
- break;
- }
- } else {
- audio_decoder->WriteEndOfStream();
- }
- }
- return original_size - data_queue->size();
-}
-
-std::vector<DecodedAudioPtr> ReadAllDecodedAudioSamples(
- AbstractWin32AudioDecoder* audio_decoder) {
- std::vector<DecodedAudioPtr> decoded_audio_out;
- while (DecodedAudioPtr decoded_datum = audio_decoder->ProcessAndRead()) {
- decoded_audio_out.push_back(decoded_datum);
- }
- return decoded_audio_out;
-}
-
-} // namespace.
-
-AudioDecoderThread::AudioDecoderThread(AbstractWin32AudioDecoder* decoder_impl,
- AudioDecodedCallback* callback)
- : Thread("AudioDecoderThread"),
- win32_audio_decoder_(decoder_impl),
- callback_(callback) {
- Start();
-}
-
-AudioDecoderThread::~AudioDecoderThread() {
- Join();
-}
-
-bool AudioDecoderThread::QueueInput(const scoped_refptr<InputBuffer>& buffer) {
- {
- ::starboard::ScopedLock lock(input_buffer_queue_mutex_);
- input_buffer_queue_.push_back(buffer);
- }
-
- // increment() returns the previous value.
- size_t element_count = processing_elements_.increment() + 1;
- semaphore_.Put();
- return element_count < kMaxProcessingElements;
-}
-
-void AudioDecoderThread::QueueEndOfStream() {
- scoped_refptr<InputBuffer> empty;
- QueueInput(empty);
-}
-
-void AudioDecoderThread::Run() {
- std::deque<scoped_refptr<InputBuffer> > local_queue;
-
- while (!join_called()) {
- if (local_queue.empty()) {
- TransferPendingInputTo(&local_queue);
- }
- bool work_done = false;
- size_t number_written =
- WriteAsMuchAsPossible(&local_queue, win32_audio_decoder_);
- if (number_written > 0) {
- processing_elements_.fetch_sub(static_cast<int32_t>(number_written));
- work_done = true;
- }
-
- std::vector<DecodedAudioPtr> decoded_audio =
- ReadAllDecodedAudioSamples(win32_audio_decoder_);
-
- if (!decoded_audio.empty()) {
- work_done = true;
- for (auto it = decoded_audio.begin(); it != decoded_audio.end(); ++it) {
- callback_->OnAudioDecoded(*it);
- }
- }
-
- if (!work_done) {
- semaphore_.TakeWait(kSbTimeMillisecond);
- }
- }
-}
-
-void AudioDecoderThread::TransferPendingInputTo(
- std::deque<scoped_refptr<InputBuffer> >* destination) {
- ::starboard::ScopedLock lock(input_buffer_queue_mutex_);
- while (!input_buffer_queue_.empty()) {
- destination->push_back(input_buffer_queue_.front());
- input_buffer_queue_.pop_front();
- }
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/audio_decoder_thread.h b/src/starboard/shared/win32/audio_decoder_thread.h
deleted file mode 100644
index b8710fd..0000000
--- a/src/starboard/shared/win32/audio_decoder_thread.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_AUDIO_DECODER_THREAD_H_
-#define STARBOARD_SHARED_WIN32_AUDIO_DECODER_THREAD_H_
-
-#include <deque>
-#include <queue>
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/common/semaphore.h"
-#include "starboard/common/thread.h"
-#include "starboard/media.h"
-#include "starboard/shared/starboard/player/decoded_audio_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/shared/win32/media_common.h"
-#include "starboard/shared/win32/win32_audio_decoder.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class AudioDecodedCallback {
- public:
- virtual ~AudioDecodedCallback() {}
- virtual void OnAudioDecoded(DecodedAudioPtr data) = 0;
-};
-
-// This decoder thread simplifies decoding media. Data is pushed in via
-// QueueInput() and QueueEndOfStream() and output data is pushed via
-// the AudioDecodedCallback.
-class AudioDecoderThread : private Thread {
- public:
- AudioDecoderThread(AbstractWin32AudioDecoder* decoder_impl,
- AudioDecodedCallback* callback);
- ~AudioDecoderThread() override;
-
- // Returns true if more input can be pushed to this thread.
- bool QueueInput(const scoped_refptr<InputBuffer>& buffer);
- void QueueEndOfStream();
-
- private:
- void Run() override;
- void TransferPendingInputTo(
- std::deque<scoped_refptr<InputBuffer> >* destination);
- AbstractWin32AudioDecoder* win32_audio_decoder_;
- AudioDecodedCallback* callback_;
-
- std::deque<scoped_refptr<InputBuffer> > input_buffer_queue_;
- ::starboard::Mutex input_buffer_queue_mutex_;
- atomic_int32_t processing_elements_;
- Semaphore semaphore_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_AUDIO_DECODER_THREAD_H_
diff --git a/src/starboard/shared/win32/audio_sink.cc b/src/starboard/shared/win32/audio_sink.cc
deleted file mode 100644
index 70aafb3..0000000
--- a/src/starboard/shared/win32/audio_sink.cc
+++ /dev/null
@@ -1,446 +0,0 @@
-// Copyright 2017 Google Inc. 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 <basetyps.h>
-#include <wrl.h>
-#include <xaudio2.h>
-
-#include <algorithm>
-#include <limits>
-#include <vector>
-
-#include "starboard/atomic.h"
-#include "starboard/condition_variable.h"
-#include "starboard/configuration.h"
-#include "starboard/log.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
-#include "starboard/thread.h"
-#include "starboard/time.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-namespace {
-
-using Microsoft::WRL::ComPtr;
-
-const int kMaxBuffersSubmittedPerLoop = 2;
-
-// Fails an SB_DCHECK if an HRESULT is not S_OK
-void CHECK_HRESULT_OK(HRESULT hr) {
- SB_DCHECK(SUCCEEDED(hr)) << std::hex << hr;
-}
-
-WORD SampleTypeToFormatTag(SbMediaAudioSampleType type) {
- switch (type) {
-#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
- case kSbMediaAudioSampleTypeInt16:
- return WAVE_FORMAT_PCM;
-#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
- case kSbMediaAudioSampleTypeFloat32:
- return WAVE_FORMAT_IEEE_FLOAT;
- default:
- SB_NOTREACHED();
- return 0;
- }
-}
-
-WORD SampleTypeToBitsPerSample(SbMediaAudioSampleType type) {
- switch (type) {
-#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
- case kSbMediaAudioSampleTypeInt16:
- return 16;
-#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
- case kSbMediaAudioSampleTypeFloat32:
- return 32;
- default:
- SB_NOTREACHED();
- return 0;
- }
-}
-
-class XAudioAudioSinkType;
-
-class XAudioAudioSink : public SbAudioSinkPrivate {
- public:
- XAudioAudioSink(XAudioAudioSinkType* type,
- IXAudio2SourceVoice* source_voice,
- const WAVEFORMATEX& wfx,
- SbAudioSinkFrameBuffers frame_buffers,
- int frame_buffers_size_in_frames,
- SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
- SbAudioSinkConsumeFramesFunc consume_frame_func,
- void* context);
- ~XAudioAudioSink() override {}
-
- bool IsType(Type* type) override;
- void SetPlaybackRate(double playback_rate) override {
- SB_DCHECK(playback_rate >= 0.0);
- if (playback_rate != 0.0 && playback_rate != 1.0) {
- SB_NOTIMPLEMENTED() << "TODO: Only playback rates of 0.0 and 1.0 are "
- "currently supported.";
- playback_rate = (playback_rate > 0.0) ? 1.0 : 0.0;
- }
- ScopedLock lock(mutex_);
- playback_rate_ = playback_rate;
- }
- void SetVolume(double volume) override {
- ScopedLock lock(mutex_);
- volume_ = volume;
- }
- void Process();
-
- void StopCallbacks() {
- SbAtomicBarrier_Increment(&stop_callbacks_, 1);
- // Make sure that any call to Process() returns so we know that
- // no future callbacks will be invoked.
- process_mutex_.Acquire();
- process_mutex_.Release();
-
- // This must happen on a non-XAudio callback thread.
- source_voice_->DestroyVoice();
- }
-
- private:
- bool AreCallbacksStopped() const {
- return SbAtomicAcquire_Load(&stop_callbacks_) != 0;
- }
- void SubmitSourceBuffer(int offset_in_frames, int count_frames);
-
- // If true, this instance's source_voice_ has been destroyed and
- // future Process() calls should return immediately.
- SbAtomic32 stop_callbacks_;
-
- XAudioAudioSinkType* const type_;
- const SbAudioSinkUpdateSourceStatusFunc update_source_status_func_;
- const SbAudioSinkConsumeFramesFunc consume_frame_func_;
- void* const context_;
-
- SbAudioSinkFrameBuffers frame_buffers_;
- const int frame_buffers_size_in_frames_;
- const WAVEFORMATEX wfx_;
-
- // Note: despite some documentation to the contrary, it appears
- // that IXAudio2SourceVoice cannot be a ComPtr.
- IXAudio2SourceVoice* source_voice_;
-
- // |process_mutex_| is held during Process. Others may rapidly
- // acquire/release to ensure they wait until the current Process() ends.
- Mutex process_mutex_;
- // |mutex_| protects |playback_rate_| and |volume_|.
- Mutex mutex_;
- double playback_rate_;
- double volume_;
- // The following variables are only used inside Process(). To keep it in the
- // class simply to allow them to be kept between Process() calls.
- int submited_frames_;
- int samples_played_;
- int queued_buffers_;
- bool was_playing_;
- double current_volume_;
-};
-
-class XAudioAudioSinkType : public SbAudioSinkPrivate::Type,
- private IXAudio2EngineCallback {
- public:
- XAudioAudioSinkType();
-
- SbAudioSink Create(
- int channels,
- int sampling_frequency_hz,
- SbMediaAudioSampleType audio_sample_type,
- SbMediaAudioFrameStorageType audio_frame_storage_type,
- SbAudioSinkFrameBuffers frame_buffers,
- int frame_buffers_size_in_frames,
- SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
- SbAudioSinkConsumeFramesFunc consume_frames_func,
- void* context);
-
- bool IsValid(SbAudioSink audio_sink) override {
- return audio_sink != kSbAudioSinkInvalid && audio_sink->IsType(this);
- }
-
- void Destroy(SbAudioSink audio_sink) override;
-
- private:
- // IXAudio2EngineCallback methods
- // This function will be called periodically with an interval of ~10ms.
- void OnProcessingPassStart() override;
- void OnProcessingPassEnd() override {}
- void OnCriticalError(HRESULT) override {}
-
- ComPtr<IXAudio2> x_audio2_;
- IXAudio2MasteringVoice* mastering_voice_;
-
- // This mutex protects |audio_sinks_to_add_| and |audio_sinks_to_delete_|.
- Mutex mutex_;
- std::vector<XAudioAudioSink*> audio_sinks_to_add_;
- std::vector<SbAudioSink> audio_sinks_to_delete_;
-
- // This must only be accessed from the OnProcessingPassStart callback
- std::vector<XAudioAudioSink*> audio_sinks_on_xaudio_callbacks_;
-};
-
-XAudioAudioSink::XAudioAudioSink(
- XAudioAudioSinkType* type,
- IXAudio2SourceVoice* source_voice,
- const WAVEFORMATEX& wfx,
- SbAudioSinkFrameBuffers frame_buffers,
- int frame_buffers_size_in_frames,
- SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
- SbAudioSinkConsumeFramesFunc consume_frame_func,
- void* context)
- : stop_callbacks_(0),
- type_(type),
- source_voice_(source_voice),
- update_source_status_func_(update_source_status_func),
- consume_frame_func_(consume_frame_func),
- context_(context),
- frame_buffers_(frame_buffers),
- frame_buffers_size_in_frames_(frame_buffers_size_in_frames),
- wfx_(wfx),
- playback_rate_(1.0),
- volume_(1.0),
- submited_frames_(0),
- samples_played_(0),
- queued_buffers_(0),
- was_playing_(false),
- current_volume_(1.0) {
- CHECK_HRESULT_OK(source_voice_->Stop(0));
-}
-
-bool XAudioAudioSink::IsType(Type* type) {
- return type_ == type;
-}
-
-void XAudioAudioSink::Process() {
- ScopedLock process_lock(process_mutex_);
- if (AreCallbacksStopped()) {
- // We must not continue in this case, since |source_voice_| has been
- // destroyed.
- return;
- }
- int frames_in_buffer, offset_in_frames;
- bool is_playing, is_eos_reached;
- bool is_playback_rate_zero = false;
- bool should_set_volume = false;
-
- // This function is run on the XAudio thread and shouldn't be blocked.
- if (mutex_.AcquireTry()) {
- is_playback_rate_zero = playback_rate_ == 0.0;
- should_set_volume = current_volume_ != volume_;
- current_volume_ = volume_;
- mutex_.Release();
- }
-
- if (should_set_volume) {
- CHECK_HRESULT_OK(source_voice_->SetVolume(current_volume_));
- }
-
- update_source_status_func_(&frames_in_buffer, &offset_in_frames, &is_playing,
- &is_eos_reached, context_);
- if (is_playback_rate_zero) {
- is_playing = false;
- }
-
- if (is_playing != was_playing_) {
- if (is_playing) {
- CHECK_HRESULT_OK(source_voice_->Start(0));
- } else {
- CHECK_HRESULT_OK(source_voice_->Stop(0));
- }
- }
- was_playing_ = is_playing;
-
- // TODO: make sure that frames_in_buffer is large enough
- // that it exceeds the voice state pool interval
- if (!is_playing || frames_in_buffer == 0 || is_playback_rate_zero) {
- return;
- }
- int unsubmitted_frames = frames_in_buffer - submited_frames_;
- int unsubmitted_start =
- (offset_in_frames + submited_frames_) % frame_buffers_size_in_frames_;
- if (unsubmitted_frames == 0 || queued_buffers_ + kMaxBuffersSubmittedPerLoop >
- XAUDIO2_MAX_QUEUED_BUFFERS) {
- // submit nothing
- } else if (unsubmitted_start + unsubmitted_frames <=
- frame_buffers_size_in_frames_) {
- SubmitSourceBuffer(unsubmitted_start, unsubmitted_frames);
- } else {
- int count_tail_frames = frame_buffers_size_in_frames_ - unsubmitted_start;
- // Note since we can submit up to two source buffers at a time,
- // kMaxBuffersSubmittedPerLoop = 2.
- SubmitSourceBuffer(unsubmitted_start, count_tail_frames);
- SubmitSourceBuffer(0, unsubmitted_frames - count_tail_frames);
- }
- submited_frames_ = frames_in_buffer;
-
- XAUDIO2_VOICE_STATE voice_state;
- source_voice_->GetState(&voice_state);
-
- int64_t consumed_frames = voice_state.SamplesPlayed - samples_played_;
- SB_DCHECK(consumed_frames >= 0);
- SB_DCHECK(consumed_frames <= std::numeric_limits<int>::max());
- int consumed_frames_int = static_cast<int>(consumed_frames);
-
- consume_frame_func_(consumed_frames_int, context_);
- submited_frames_ -= consumed_frames_int;
- samples_played_ = voice_state.SamplesPlayed;
- queued_buffers_ = voice_state.BuffersQueued;
-}
-
-void XAudioAudioSink::SubmitSourceBuffer(int offset_in_frames,
- int count_frames) {
- XAUDIO2_BUFFER audio_buffer_info;
-
- audio_buffer_info.Flags = 0;
- audio_buffer_info.AudioBytes = wfx_.nChannels *
- frame_buffers_size_in_frames_ *
- (wfx_.wBitsPerSample / 8);
- audio_buffer_info.pAudioData = static_cast<const BYTE*>(frame_buffers_[0]);
- audio_buffer_info.PlayBegin = offset_in_frames;
- audio_buffer_info.PlayLength = count_frames;
- audio_buffer_info.LoopBegin = 0;
- audio_buffer_info.LoopLength = 0;
- audio_buffer_info.LoopCount = 0;
- audio_buffer_info.pContext = nullptr;
- CHECK_HRESULT_OK(source_voice_->SubmitSourceBuffer(&audio_buffer_info));
-}
-
-XAudioAudioSinkType::XAudioAudioSinkType() {
- CHECK_HRESULT_OK(XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR));
-
-#if !defined(COBALT_BUILD_TYPE_GOLD)
- XAUDIO2_DEBUG_CONFIGURATION debug_config = {};
- debug_config.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS |
- XAUDIO2_LOG_INFO | XAUDIO2_LOG_DETAIL |
- XAUDIO2_LOG_TIMING | XAUDIO2_LOG_LOCKS;
- debug_config.LogThreadID = TRUE;
- debug_config.LogFileline = TRUE;
- debug_config.LogFunctionName = TRUE;
- debug_config.LogTiming = TRUE;
- x_audio2_->SetDebugConfiguration(&debug_config, NULL);
-#endif // !defined(COBALT_BUILD_TYPE_GOLD)
-
- x_audio2_->RegisterForCallbacks(this);
- CHECK_HRESULT_OK(x_audio2_->CreateMasteringVoice(&mastering_voice_));
-}
-
-SbAudioSink XAudioAudioSinkType::Create(
- int channels,
- int sampling_frequency_hz,
- SbMediaAudioSampleType audio_sample_type,
- SbMediaAudioFrameStorageType audio_frame_storage_type,
- SbAudioSinkFrameBuffers frame_buffers,
- int frame_buffers_size_in_frames,
- SbAudioSinkUpdateSourceStatusFunc update_source_status_func,
- SbAudioSinkConsumeFramesFunc consume_frames_func,
- void* context) {
- SB_DCHECK(audio_frame_storage_type ==
- kSbMediaAudioFrameStorageTypeInterleaved);
-
- WAVEFORMATEX wfx;
-
- wfx.wFormatTag = SampleTypeToFormatTag(audio_sample_type);
- wfx.nChannels = static_cast<WORD>(channels);
- wfx.nSamplesPerSec = sampling_frequency_hz;
- wfx.nAvgBytesPerSec = channels *
- SampleTypeToBitsPerSample(audio_sample_type) *
- sampling_frequency_hz / 8;
- wfx.wBitsPerSample = SampleTypeToBitsPerSample(audio_sample_type);
- wfx.nBlockAlign = static_cast<WORD>((channels * wfx.wBitsPerSample) / 8);
- wfx.cbSize = 0;
-
- IXAudio2SourceVoice* source_voice;
- CHECK_HRESULT_OK(x_audio2_->CreateSourceVoice(&source_voice, &wfx,
- XAUDIO2_VOICE_NOPITCH, 1.f));
-
- XAudioAudioSink* audio_sink = new XAudioAudioSink(
- this, source_voice, wfx, frame_buffers, frame_buffers_size_in_frames,
- update_source_status_func, consume_frames_func, context);
-
- ScopedLock lock(mutex_);
- audio_sinks_to_add_.push_back(audio_sink);
- return audio_sink;
-}
-
-void XAudioAudioSinkType::Destroy(SbAudioSink audio_sink) {
- if (audio_sink == kSbAudioSinkInvalid) {
- return;
- }
- if (!IsValid(audio_sink)) {
- SB_LOG(WARNING) << "audio_sink is invalid.";
- return;
- }
- // Previous versions of this code waited for the next OnProcessingPassStart()
- // call to occur before returning. However, various circumstances could
- // cause that never to happen, especially during UWP suspend.
- // Instead, we return immediately, ensuring no SbAudioSink callbacks occur
- // and postpone the delete itself until the next OnProcessingPassStart()
- static_cast<XAudioAudioSink*>(audio_sink)->StopCallbacks();
-
- ScopedLock lock(mutex_);
- audio_sinks_to_delete_.push_back(audio_sink);
-}
-
-void XAudioAudioSinkType::OnProcessingPassStart() {
- if (mutex_.AcquireTry()) {
- if (!audio_sinks_to_add_.empty()) {
- audio_sinks_on_xaudio_callbacks_.insert(
- audio_sinks_on_xaudio_callbacks_.end(), audio_sinks_to_add_.begin(),
- audio_sinks_to_add_.end());
- audio_sinks_to_add_.clear();
- }
- if (!audio_sinks_to_delete_.empty()) {
- for (auto sink : audio_sinks_to_delete_) {
- audio_sinks_on_xaudio_callbacks_.erase(
- std::find(audio_sinks_on_xaudio_callbacks_.begin(),
- audio_sinks_on_xaudio_callbacks_.end(), sink));
- delete sink;
- }
- audio_sinks_to_delete_.clear();
- }
- mutex_.Release();
- }
-
- for (XAudioAudioSink* sink : audio_sinks_on_xaudio_callbacks_) {
- sink->Process();
- }
-}
-
-} // namespace
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-namespace {
-SbAudioSinkPrivate::Type* audio_sink_;
-} // namespace
-
-// static
-void SbAudioSinkPrivate::PlatformInitialize() {
- SB_DCHECK(!audio_sink_);
- audio_sink_ = new starboard::shared::win32::XAudioAudioSinkType();
- SetPrimaryType(audio_sink_);
- EnableFallbackToStub();
-}
-
-// static
-void SbAudioSinkPrivate::PlatformTearDown() {
- SB_DCHECK(audio_sink_ == GetPrimaryType());
- SetPrimaryType(nullptr);
- delete audio_sink_;
- audio_sink_ = nullptr;
-}
diff --git a/src/starboard/shared/win32/audio_sink_get_max_channels.cc b/src/starboard/shared/win32/audio_sink_get_max_channels.cc
deleted file mode 100644
index 00d7f93..0000000
--- a/src/starboard/shared/win32/audio_sink_get_max_channels.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/audio_sink.h"
-
-int SbAudioSinkGetMaxChannels() {
- return 6;
-}
diff --git a/src/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc b/src/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc
deleted file mode 100644
index c9d3740..0000000
--- a/src/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/audio_sink.h"
-
-#include "starboard/log.h"
-
-int SbAudioSinkGetNearestSupportedSampleFrequency(int sampling_frequency_hz) {
- if (sampling_frequency_hz <= 0) {
- SB_LOG(ERROR) << "Invalid audio sampling frequency "
- << sampling_frequency_hz;
- return 1;
- }
- return sampling_frequency_hz;
-}
diff --git a/src/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc b/src/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc
deleted file mode 100644
index 16a603a..0000000
--- a/src/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/audio_sink.h"
-
-#include "starboard/log.h"
-
-bool SbAudioSinkIsAudioFrameStorageTypeSupported(
- SbMediaAudioFrameStorageType audio_frame_storage_type) {
- switch (audio_frame_storage_type) {
- case kSbMediaAudioFrameStorageTypeInterleaved:
- return true;
- case kSbMediaAudioFrameStorageTypePlanar:
- return false;
- default:
- SB_NOTREACHED();
- return false;
- }
-}
diff --git a/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc b/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc
deleted file mode 100644
index 6f3c417..0000000
--- a/src/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/audio_sink.h"
-
-#include "starboard/log.h"
-
-bool SbAudioSinkIsAudioSampleTypeSupported(
- SbMediaAudioSampleType audio_sample_type) {
- switch (audio_sample_type) {
-#if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
- case kSbMediaAudioSampleTypeInt16:
- return true;
-#endif // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
- case kSbMediaAudioSampleTypeFloat32:
- return true;
- default:
- SB_NOTREACHED();
- return false;
- }
-}
diff --git a/src/starboard/shared/win32/audio_transform.cc b/src/starboard/shared/win32/audio_transform.cc
deleted file mode 100644
index 85c03b1..0000000
--- a/src/starboard/shared/win32/audio_transform.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/audio_transform.h"
-
-#include <vector>
-
-#include "starboard/memory.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/media_common.h"
-#include "starboard/shared/win32/media_foundation_utils.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-using Microsoft::WRL::ComPtr;
-
-namespace {
-
-GUID ConvertToWin32AudioCodec(SbMediaAudioCodec codec) {
- switch (codec) {
- case kSbMediaAudioCodecNone: {
- return MFAudioFormat_PCM;
- }
- case kSbMediaAudioCodecAac: {
- return MFAudioFormat_AAC;
- }
- case kSbMediaAudioCodecOpus: {
- return MFAudioFormat_Opus;
- }
- default: {
- SB_NOTIMPLEMENTED();
- return MFAudioFormat_PCM;
- }
- }
-}
-
-class WinAudioFormat {
- public:
- explicit WinAudioFormat(const SbMediaAudioHeader& audio_header) {
- // The WAVEFORMATEX structure has many specializations with varying
- // data attached at the end.
- format_buffer_.resize(sizeof(WAVEFORMATEX) +
- audio_header.audio_specific_config_size);
- WAVEFORMATEX* wave_format = WaveFormatData();
-
- wave_format->nAvgBytesPerSec = audio_header.average_bytes_per_second;
- wave_format->nBlockAlign = audio_header.block_alignment;
- wave_format->nChannels = audio_header.number_of_channels;
- wave_format->nSamplesPerSec = audio_header.samples_per_second;
- wave_format->wBitsPerSample = audio_header.bits_per_sample;
- wave_format->wFormatTag = audio_header.format_tag;
- wave_format->cbSize = audio_header.audio_specific_config_size;
-
- if (audio_header.audio_specific_config_size > 0) {
- uint8_t* config_data = format_buffer_.data() + sizeof(WAVEFORMATEX);
- SbMemoryCopy(config_data,
- audio_header.audio_specific_config,
- audio_header.audio_specific_config_size);
- }
- }
-
- WAVEFORMATEX* WaveFormatData() {
- return reinterpret_cast<WAVEFORMATEX*>(format_buffer_.data());
- }
- UINT32 Size() const { return static_cast<UINT32>(format_buffer_.size()); }
-
- private:
- std::vector<uint8_t> format_buffer_;
-};
-
-} // namespace.
-
-scoped_ptr<MediaTransform> CreateAudioTransform(
- const SbMediaAudioHeader& audio,
- SbMediaAudioCodec codec) {
- ComPtr<IMFTransform> transform;
- HRESULT hr = CreateDecoderTransform(CLSID_MSAACDecMFT, &transform);
- CheckResult(hr);
-
- ComPtr<IMFMediaType> input_type;
- hr = MFCreateMediaType(&input_type);
- CheckResult(hr);
-
- WinAudioFormat audio_fmt(audio);
- hr = MFInitMediaTypeFromWaveFormatEx(
- input_type.Get(), audio_fmt.WaveFormatData(), audio_fmt.Size());
-
- CheckResult(hr);
-
- hr = input_type->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, 0); // raw aac
- CheckResult(hr);
-
- GUID win32_audio_type = ConvertToWin32AudioCodec(codec);
- hr = input_type->SetGUID(MF_MT_SUBTYPE, win32_audio_type);
- CheckResult(hr);
-
- std::vector<ComPtr<IMFMediaType>> available_types =
- GetAllInputMediaTypes(MediaTransform::kStreamId, transform.Get());
-
- available_types = FilterMediaBySubType(available_types, win32_audio_type);
- SB_DCHECK(available_types.size());
-
- ComPtr<IMFMediaType> selected = available_types[0];
- std::vector<GUID> attribs = {
- MF_MT_AUDIO_BLOCK_ALIGNMENT, MF_MT_AUDIO_SAMPLES_PER_SECOND,
- MF_MT_AUDIO_AVG_BYTES_PER_SECOND, MF_MT_AUDIO_NUM_CHANNELS,
- };
-
- for (auto it = attribs.begin(); it != attribs.end(); ++it) {
- CopyUint32Property(*it, input_type.Get(), selected.Get());
- }
-
- scoped_ptr<MediaTransform> output(new MediaTransform(transform));
- output->SetInputType(selected);
- output->SetOutputTypeBySubType(MFAudioFormat_Float);
-
- return output.Pass();
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/audio_transform.h b/src/starboard/shared/win32/audio_transform.h
deleted file mode 100644
index 173b23f..0000000
--- a/src/starboard/shared/win32/audio_transform.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_AUDIO_TRANSFORM_H_
-#define STARBOARD_SHARED_WIN32_AUDIO_TRANSFORM_H_
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/media.h"
-#include "starboard/shared/win32/media_transform.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-scoped_ptr<MediaTransform> CreateAudioTransform(
- const SbMediaAudioHeader& audio,
- SbMediaAudioCodec codec);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_AUDIO_TRANSFORM_H_
diff --git a/src/starboard/shared/win32/auto_event_handle.h b/src/starboard/shared/win32/auto_event_handle.h
deleted file mode 100644
index 24c3ccf..0000000
--- a/src/starboard/shared/win32/auto_event_handle.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_AUTO_EVENT_HANDLE_H_
-#define STARBOARD_SHARED_WIN32_AUTO_EVENT_HANDLE_H_
-
-#include <winsock2.h>
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class AutoEventHandle {
- public:
- explicit AutoEventHandle(WSAEVENT event) : event_(event) {}
-
- ~AutoEventHandle() { CleanupExistingEvent(); }
-
- void Reset(WSAEVENT new_event) {
- CleanupExistingEvent();
- event_ = new_event;
- }
-
- bool IsValid() const { return event_ != WSA_INVALID_EVENT; }
-
- WSAEVENT GetEvent() { return event_; }
-
- private:
- AutoEventHandle(const AutoEventHandle&) = delete;
- AutoEventHandle& operator=(const AutoEventHandle&) = delete;
-
- void CleanupExistingEvent() {
- if (IsValid()) {
- WSACloseEvent(event_);
- event_ = WSA_INVALID_EVENT;
- }
- }
-
- WSAEVENT event_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_AUTO_EVENT_HANDLE_H_
diff --git a/src/starboard/shared/win32/byte_swap.cc b/src/starboard/shared/win32/byte_swap.cc
deleted file mode 100644
index 45f4fd9..0000000
--- a/src/starboard/shared/win32/byte_swap.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Not breaking these functions up because however one is implemented, the
-// others should be implemented similarly.
-
-#include "starboard/byte_swap.h"
-
-#include <stdlib.h>
-
-int16_t SbByteSwapS16(int16_t value) {
- return _byteswap_ushort(value);
-}
-
-uint16_t SbByteSwapU16(uint16_t value) {
- return _byteswap_ushort(value);
-}
-
-int32_t SbByteSwapS32(int32_t value) {
- return _byteswap_ulong(value);
-}
-
-uint32_t SbByteSwapU32(uint32_t value) {
- return _byteswap_ulong(value);
-}
-
-int64_t SbByteSwapS64(int64_t value) {
- return _byteswap_uint64(value);
-}
-
-uint64_t SbByteSwapU64(uint64_t value) {
- return _byteswap_uint64(value);
-}
diff --git a/src/starboard/shared/win32/condition_variable_broadcast.cc b/src/starboard/shared/win32/condition_variable_broadcast.cc
deleted file mode 100644
index f2b9ea5..0000000
--- a/src/starboard/shared/win32/condition_variable_broadcast.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/condition_variable.h"
-
-#include <windows.h>
-
-bool SbConditionVariableBroadcast(SbConditionVariable* condition) {
- if (!condition) {
- return false;
- }
- WakeAllConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(condition));
- return true;
-}
diff --git a/src/starboard/shared/win32/condition_variable_create.cc b/src/starboard/shared/win32/condition_variable_create.cc
deleted file mode 100644
index 9d74a6f..0000000
--- a/src/starboard/shared/win32/condition_variable_create.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/condition_variable.h"
-
-#include <windows.h>
-
-bool SbConditionVariableCreate(SbConditionVariable* out_condition,
- SbMutex* /*opt_mutex*/) {
- if (!out_condition) {
- return false;
- }
- InitializeConditionVariable(
- reinterpret_cast<PCONDITION_VARIABLE>(out_condition));
- return true;
-}
diff --git a/src/starboard/shared/win32/condition_variable_destroy.cc b/src/starboard/shared/win32/condition_variable_destroy.cc
deleted file mode 100644
index 4a24e47..0000000
--- a/src/starboard/shared/win32/condition_variable_destroy.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/condition_variable.h"
-
-#include <windows.h>
-
-bool SbConditionVariableDestroy(SbConditionVariable* condition) {
- if (!condition) {
- return false;
- }
- return true;
-}
diff --git a/src/starboard/shared/win32/condition_variable_signal.cc b/src/starboard/shared/win32/condition_variable_signal.cc
deleted file mode 100644
index 5e8e67b..0000000
--- a/src/starboard/shared/win32/condition_variable_signal.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/condition_variable.h"
-
-#include <windows.h>
-
-bool SbConditionVariableSignal(SbConditionVariable* condition) {
- if (!condition) {
- return false;
- }
- WakeConditionVariable(reinterpret_cast<PCONDITION_VARIABLE>(condition));
- return true;
-}
diff --git a/src/starboard/shared/win32/condition_variable_wait.cc b/src/starboard/shared/win32/condition_variable_wait.cc
deleted file mode 100644
index a224afc..0000000
--- a/src/starboard/shared/win32/condition_variable_wait.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/condition_variable.h"
-
-#include <windows.h>
-
-SbConditionVariableResult SbConditionVariableWait(
- SbConditionVariable* condition,
- SbMutex* mutex) {
- if (!condition || !mutex) {
- return kSbConditionVariableFailed;
- }
- bool result = SleepConditionVariableSRW(
- reinterpret_cast<PCONDITION_VARIABLE>(condition),
- reinterpret_cast<PSRWLOCK>(mutex), INFINITE, 0);
-
- return result ? kSbConditionVariableSignaled : kSbConditionVariableFailed;
-}
diff --git a/src/starboard/shared/win32/condition_variable_wait_timed.cc b/src/starboard/shared/win32/condition_variable_wait_timed.cc
deleted file mode 100644
index e967695..0000000
--- a/src/starboard/shared/win32/condition_variable_wait_timed.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/condition_variable.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/time_utils.h"
-
-using starboard::shared::win32::ConvertSbTimeToMillisRoundUp;
-
-SbConditionVariableResult SbConditionVariableWaitTimed(
- SbConditionVariable* condition,
- SbMutex* mutex,
- SbTime timeout) {
- if (!condition || !mutex) {
- return kSbConditionVariableFailed;
- }
-
- if (timeout < 0) {
- timeout = 0;
- }
- bool result = SleepConditionVariableSRW(
- reinterpret_cast<PCONDITION_VARIABLE>(condition),
- reinterpret_cast<PSRWLOCK>(mutex),
- ConvertSbTimeToMillisRoundUp(timeout), 0);
-
- if (timeout == 0) {
- // Per documentation, "If the |timeout_duration| value is less than
- // or equal to zero, the function returns as quickly as possible with a
- // kSbConditionVariableTimedOut result."
- return kSbConditionVariableTimedOut;
- }
-
- if (result) {
- return kSbConditionVariableSignaled;
- }
-
- if (GetLastError() == ERROR_TIMEOUT) {
- return kSbConditionVariableTimedOut;
- }
- return kSbConditionVariableFailed;
-}
diff --git a/src/starboard/shared/win32/decode_target_internal.cc b/src/starboard/shared/win32/decode_target_internal.cc
deleted file mode 100644
index 2faf32a..0000000
--- a/src/starboard/shared/win32/decode_target_internal.cc
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/decode_target_internal.h"
-
-#include "starboard/configuration.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "third_party/angle/include/EGL/egl.h"
-#include "third_party/angle/include/EGL/eglext.h"
-#include "third_party/angle/include/GLES2/gl2.h"
-#include "third_party/angle/include/GLES2/gl2ext.h"
-
-namespace {
-
-using Microsoft::WRL::ComPtr;
-using starboard::shared::win32::CheckResult;
-
-// {3C3A43AB-C69B-46C9-AA8D-B0CFFCD4596D}
-const GUID kCobaltNv12BindChroma = {
- 0x3c3a43ab,
- 0xc69b,
- 0x46c9,
- {0xaa, 0x8d, 0xb0, 0xcf, 0xfc, 0xd4, 0x59, 0x6d}};
-
-// {C62BF18D-B5EE-46B1-9C31-F61BD8AE3B0D}
-const GUID kCobaltDxgiBuffer = {
- 0Xc62bf18d,
- 0Xb5ee,
- 0X46b1,
- {0X9c, 0X31, 0Xf6, 0X1b, 0Xd8, 0Xae, 0X3b, 0X0d}};
-
-ComPtr<ID3D11Texture2D> AllocateTexture(
- const ComPtr<ID3D11Device>& d3d_device, int width, int height) {
- ComPtr<ID3D11Texture2D> texture;
- D3D11_TEXTURE2D_DESC texture_desc = {};
- texture_desc.Width = width;
- texture_desc.Height = height;
- texture_desc.MipLevels = 1;
- texture_desc.ArraySize = 1;
- texture_desc.Format = DXGI_FORMAT_NV12;
- texture_desc.SampleDesc.Count = 1;
- texture_desc.SampleDesc.Quality = 0;
- texture_desc.Usage = D3D11_USAGE_DEFAULT;
- texture_desc.BindFlags =
- D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
- CheckResult(d3d_device->CreateTexture2D(&texture_desc, nullptr,
- texture.GetAddressOf()));
- return texture;
-}
-
-void UpdateTexture(
- const ComPtr<ID3D11Texture2D>& texture,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample, const RECT& video_area) {
- ComPtr<IMFMediaBuffer> media_buffer;
- CheckResult(video_sample->GetBufferByIndex(0, media_buffer.GetAddressOf()));
-
- ComPtr<IMFDXGIBuffer> dxgi_buffer;
- CheckResult(media_buffer.As(&dxgi_buffer));
-
- ComPtr<ID3D11Texture2D> input_texture;
- CheckResult(dxgi_buffer->GetResource(IID_PPV_ARGS(&input_texture)));
-
- // The VideoProcessor needs to know what subset of the decoded
- // frame contains active pixels that should be displayed to the user.
- video_context->VideoProcessorSetStreamSourceRect(
- video_processor.Get(), 0, TRUE, &video_area);
-
- D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC input_desc = {};
- input_desc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
- input_desc.Texture2D.MipSlice = 0;
- dxgi_buffer->GetSubresourceIndex(&input_desc.Texture2D.ArraySlice);
-
- ComPtr<ID3D11VideoProcessorInputView> input_view;
- CheckResult(video_device->CreateVideoProcessorInputView(
- input_texture.Get(), video_enumerator.Get(), &input_desc,
- input_view.GetAddressOf()));
-
- D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC output_desc = {};
- output_desc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
- output_desc.Texture2D.MipSlice = 0;
-
- ComPtr<ID3D11VideoProcessorOutputView> output_view;
- CheckResult(video_device->CreateVideoProcessorOutputView(
- texture.Get(), video_enumerator.Get(), &output_desc,
- output_view.GetAddressOf()));
-
- // We have a single video stream, which is enabled for display.
- D3D11_VIDEO_PROCESSOR_STREAM stream_info = {};
- stream_info.Enable = TRUE;
- stream_info.pInputSurface = input_view.Get();
- CheckResult(video_context->VideoProcessorBlt(
- video_processor.Get(), output_view.Get(), 0, 1, &stream_info));
-}
-
-} // namespace
-
-SbDecodeTargetPrivate::SbDecodeTargetPrivate(
- const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample, const RECT& video_area)
- : refcount(1) {
- SbMemorySet(&info, 0, sizeof(info));
- info.format = kSbDecodeTargetFormat2PlaneYUVNV12;
- info.is_opaque = true;
- info.width = video_area.right;
- info.height = video_area.bottom;
-
- d3d_texture = AllocateTexture(d3d_device, info.width, info.height);
- if (video_sample) {
- UpdateTexture(d3d_texture, video_device, video_context, video_enumerator,
- video_processor, video_sample, video_area);
- }
-
- SbDecodeTargetInfoPlane* planeY = &(info.planes[kSbDecodeTargetPlaneY]);
- SbDecodeTargetInfoPlane* planeUV = &(info.planes[kSbDecodeTargetPlaneUV]);
-
- planeY->width = info.width;
- planeY->height = info.height;
- planeY->content_region.left = 0;
- planeY->content_region.top = info.height;
- planeY->content_region.right = info.width;
- planeY->content_region.bottom = 0;
-
- planeUV->width = info.width / 2;
- planeUV->height = info.height / 2;
- planeUV->content_region.left = planeY->content_region.left / 2;
- planeUV->content_region.top = planeY->content_region.top / 2;
- planeUV->content_region.right = planeY->content_region.right / 2;
- planeUV->content_region.bottom = planeY->content_region.bottom / 2;
-
- EGLint luma_texture_attributes[] = {EGL_WIDTH,
- static_cast<EGLint>(info.width),
- EGL_HEIGHT,
- static_cast<EGLint>(info.height),
- EGL_TEXTURE_TARGET,
- EGL_TEXTURE_2D,
- EGL_TEXTURE_FORMAT,
- EGL_TEXTURE_RGBA,
- EGL_NONE};
-
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
- EGLConfig config;
- EGLint attribute_list[] = {
- EGL_SURFACE_TYPE, // this must be first
- EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
- EGL_RED_SIZE,
- 8,
- EGL_GREEN_SIZE,
- 8,
- EGL_BLUE_SIZE,
- 8,
- EGL_ALPHA_SIZE,
- 8,
- EGL_BIND_TO_TEXTURE_RGBA,
- EGL_TRUE,
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES2_BIT,
- EGL_NONE
- };
-
- EGLint num_configs;
- bool ok = eglChooseConfig(
- display, attribute_list, &config, 1, &num_configs);
- SB_DCHECK(ok);
- SB_DCHECK(num_configs == 1);
-
- GLuint gl_textures[2] = {0};
- glGenTextures(2, gl_textures);
- SB_DCHECK(glGetError() == GL_NO_ERROR);
-
- // This tells ANGLE that the texture it creates should draw
- // the luma channel on R8.
- HRESULT hr = d3d_texture->SetPrivateData(kCobaltNv12BindChroma, 0, nullptr);
- SB_DCHECK(SUCCEEDED(hr));
-
- surface[0] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
- d3d_texture.Get(), config,
- luma_texture_attributes);
-
- SB_DCHECK(surface[0] != EGL_NO_SURFACE);
-
- glBindTexture(GL_TEXTURE_2D, gl_textures[0]);
- SB_DCHECK(glGetError() == GL_NO_ERROR);
-
- ok = eglBindTexImage(display, surface[0], EGL_BACK_BUFFER);
- SB_DCHECK(ok);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- planeY->texture = gl_textures[0];
- planeY->gl_texture_target = GL_TEXTURE_2D;
- planeY->gl_texture_format = GL_RED_EXT;
-
- // This tells ANGLE that the texture it creates should draw
- // the chroma channel on R8G8.
- bool bind_chroma = true;
- hr = d3d_texture->SetPrivateData(kCobaltNv12BindChroma, 1, &bind_chroma);
- SB_DCHECK(SUCCEEDED(hr));
-
- EGLint chroma_texture_attributes[] = {
- EGL_WIDTH,
- static_cast<EGLint>(info.width) / 2,
- EGL_HEIGHT,
- static_cast<EGLint>(info.height) / 2,
- EGL_TEXTURE_TARGET,
- EGL_TEXTURE_2D,
- EGL_TEXTURE_FORMAT,
- EGL_TEXTURE_RGBA,
- EGL_NONE};
- surface[1] = eglCreatePbufferFromClientBuffer(display, EGL_D3D_TEXTURE_ANGLE,
- d3d_texture.Get(), config,
- chroma_texture_attributes);
-
- SB_DCHECK(surface[1] != EGL_NO_SURFACE);
-
- glBindTexture(GL_TEXTURE_2D, gl_textures[1]);
- SB_DCHECK(glGetError() == GL_NO_ERROR);
-
- ok = eglBindTexImage(display, surface[1], EGL_BACK_BUFFER);
- SB_DCHECK(ok);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- planeUV->texture = gl_textures[1];
- planeUV->gl_texture_target = GL_TEXTURE_2D;
- planeUV->gl_texture_format = GL_RG_EXT;
-
- hr = d3d_texture->SetPrivateData(kCobaltDxgiBuffer, 0, nullptr);
- SB_DCHECK(SUCCEEDED(hr));
-}
-
-SbDecodeTargetPrivate::~SbDecodeTargetPrivate() {
- glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneY].texture));
- glDeleteTextures(1, &(info.planes[kSbDecodeTargetPlaneUV].texture));
-
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
- eglReleaseTexImage(display, surface[0], EGL_BACK_BUFFER);
- eglDestroySurface(display, surface[0]);
-
- eglReleaseTexImage(display, surface[1], EGL_BACK_BUFFER);
- eglDestroySurface(display, surface[1]);
-}
-
-bool SbDecodeTargetPrivate::Update(
- const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area) {
- // Only allow updating if this is the only reference. Otherwise the update
- // may change something that's currently being used.
- if (SbAtomicNoBarrier_Load(&refcount) > 1) {
- return false;
- }
-
- // The decode target info must be compatible. The resolution should match
- // exactly, otherwise the shader may sample invalid texels along the
- // texture border.
- if (info.format != kSbDecodeTargetFormat2PlaneYUVNV12 ||
- info.is_opaque != true ||
- info.width != video_area.right ||
- info.height != video_area.bottom) {
- return false;
- }
-
- SB_UNREFERENCED_PARAMETER(d3d_device);
- UpdateTexture(d3d_texture, video_device, video_context, video_enumerator,
- video_processor, video_sample, video_area);
- return true;
-}
-
-void SbDecodeTargetPrivate::AddRef() {
- SbAtomicBarrier_Increment(&refcount, 1);
-}
-
-void SbDecodeTargetPrivate::Release() {
- int new_count = SbAtomicBarrier_Increment(&refcount, -1);
- SB_DCHECK(new_count >= 0);
- if (new_count == 0) {
- delete this;
- }
-}
-
-void SbDecodeTargetRelease(SbDecodeTarget decode_target) {
- if (SbDecodeTargetIsValid(decode_target)) {
- decode_target->Release();
- }
-}
-
-SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget decode_target) {
- // Note that kSbDecodeTargetFormat2PlaneYUVNV12 represents DXGI_FORMAT_NV12.
- SB_DCHECK(kSbDecodeTargetFormat2PlaneYUVNV12 ==
- decode_target->info.format);
- return decode_target->info.format;
-}
-
-bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
- SbDecodeTargetInfo* out_info) {
- if (!out_info || !SbMemoryIsZero(out_info, sizeof(*out_info))) {
- SB_DCHECK(false) << "out_info must be zeroed out.";
- return false;
- }
- SbMemoryCopy(out_info, &decode_target->info, sizeof(*out_info));
- return true;
-}
diff --git a/src/starboard/shared/win32/decode_target_internal.h b/src/starboard/shared/win32/decode_target_internal.h
deleted file mode 100644
index a0cce17..0000000
--- a/src/starboard/shared/win32/decode_target_internal.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_DECODE_TARGET_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_DECODE_TARGET_INTERNAL_H_
-
-#include <D3d11_1.h>
-#include <mfidl.h>
-#include <wrl/client.h>
-
-#include "starboard/atomic.h"
-#include "starboard/decode_target.h"
-
-struct SbDecodeTargetPrivate {
- template <typename T>
- using ComPtr = Microsoft::WRL::ComPtr<T>;
-
- SbAtomic32 refcount;
-
- // Publicly accessible information about the decode target.
- SbDecodeTargetInfo info;
-
- ComPtr<ID3D11Texture2D> d3d_texture;
-
- // EGLSurface is defined as void* in "third_party/angle/include/EGL/egl.h".
- // Use void* directly here to avoid `egl.h` being included broadly.
- void* surface[2];
-
- SbDecodeTargetPrivate(
- const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area);
- ~SbDecodeTargetPrivate();
-
- // Update the existing texture with the given video_sample's data.
- // If the current object is not compatible with the new video_sample, then
- // this will return false, and the caller should just create a new
- // decode target for the sample.
- bool Update(
- const ComPtr<ID3D11Device>& d3d_device,
- const ComPtr<ID3D11VideoDevice1>& video_device,
- const ComPtr<ID3D11VideoContext>& video_context,
- const ComPtr<ID3D11VideoProcessorEnumerator>& video_enumerator,
- const ComPtr<ID3D11VideoProcessor>& video_processor,
- const ComPtr<IMFSample>& video_sample,
- const RECT& video_area);
-
- void AddRef();
- void Release();
-};
-
-#endif // STARBOARD_SHARED_WIN32_DECODE_TARGET_INTERNAL_H_
diff --git a/src/starboard/shared/win32/decrypting_decoder.cc b/src/starboard/shared/win32/decrypting_decoder.cc
deleted file mode 100644
index 725cb6c..0000000
--- a/src/starboard/shared/win32/decrypting_decoder.cc
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/decrypting_decoder.h"
-
-#include <algorithm>
-#include <numeric>
-
-#include "starboard/byte_swap.h"
-#include "starboard/common/ref_counted.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/media_foundation_utils.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-namespace {
-
-ComPtr<IMFSample> CreateSample(const void* data,
- int size,
- int64_t win32_timestamp) {
- ComPtr<IMFMediaBuffer> buffer;
- HRESULT hr = MFCreateMemoryBuffer(size, &buffer);
- CheckResult(hr);
-
- BYTE* buffer_ptr;
- hr = buffer->Lock(&buffer_ptr, 0, 0);
- CheckResult(hr);
-
- SbMemoryCopy(buffer_ptr, data, size);
-
- hr = buffer->Unlock();
- CheckResult(hr);
-
- hr = buffer->SetCurrentLength(size);
- CheckResult(hr);
-
- ComPtr<IMFSample> input;
- hr = MFCreateSample(&input);
- CheckResult(hr);
-
- hr = input->AddBuffer(buffer.Get());
- CheckResult(hr);
-
- // sample time is in 100 nanoseconds.
- input->SetSampleTime(win32_timestamp);
- return input;
-}
-
-void AttachDrmDataToSample(ComPtr<IMFSample> sample,
- int sample_size,
- const uint8_t* key_id,
- int key_id_size,
- const uint8_t* iv,
- int iv_size,
- const SbDrmSubSampleMapping* subsample_mapping,
- int subsample_count) {
- if (iv_size == 16 && SbMemoryIsZero(iv + 8, 8)) {
- // For iv that is 16 bytes long but the the last 8 bytes is 0, we treat
- // it as an 8 bytes iv.
- iv_size = 8;
- }
- sample->SetBlob(MFSampleExtension_Encryption_SampleID,
- reinterpret_cast<const UINT8*>(iv),
- static_cast<UINT32>(iv_size));
- SB_DCHECK(key_id_size == sizeof(GUID));
- GUID guid = *reinterpret_cast<const GUID*>(key_id);
-
- guid.Data1 = SbByteSwapU32(guid.Data1);
- guid.Data2 = SbByteSwapU16(guid.Data2);
- guid.Data3 = SbByteSwapU16(guid.Data3);
-
- sample->SetGUID(MFSampleExtension_Content_KeyID, guid);
-
- SB_DCHECK(sizeof(DWORD) * 2 == sizeof(SbDrmSubSampleMapping));
-
- SbDrmSubSampleMapping default_subsample = {0, sample_size};
- if (subsample_count == 0) {
- subsample_mapping = &default_subsample;
- subsample_count = 1;
- }
- sample->SetBlob(
- MFSampleExtension_Encryption_SubSampleMappingSplit,
- reinterpret_cast<const UINT8*>(subsample_mapping),
- static_cast<UINT32>(subsample_count * sizeof(SbDrmSubSampleMapping)));
-}
-
-} // namespace
-
-DecryptingDecoder::DecryptingDecoder(const std::string& type,
- scoped_ptr<MediaTransform> decoder,
- SbDrmSystem drm_system)
- : type_(type), decoder_(decoder.Pass()) {
- SB_DCHECK(decoder_.get());
- drm_system_ = static_cast<SbDrmSystemPlayready*>(drm_system);
-}
-
-DecryptingDecoder::~DecryptingDecoder() {
- Reset();
-}
-
-bool DecryptingDecoder::TryWriteInputBuffer(
- const scoped_refptr<InputBuffer>& input_buffer,
- int bytes_to_skip_in_sample) {
- SB_DCHECK(input_buffer);
- SB_DCHECK(bytes_to_skip_in_sample >= 0);
-
- ComPtr<IMFSample> input_sample;
-
- const SbDrmSampleInfo* drm_info = input_buffer->drm_info();
- const uint8_t* key_id = NULL;
- int key_id_size = 0;
- bool encrypted = false;
-
- if (drm_info != NULL && drm_info->identifier_size == 16 &&
- (drm_info->initialization_vector_size == 8 ||
- drm_info->initialization_vector_size == 16)) {
- key_id = drm_info->identifier;
- key_id_size = drm_info->identifier_size;
- encrypted = true;
- }
-
- if (input_buffer == last_input_buffer_) {
- SB_DCHECK(last_input_sample_);
- input_sample = last_input_sample_;
- } else {
- if (input_buffer->size() < bytes_to_skip_in_sample) {
- SB_NOTREACHED();
- return false;
- }
-
- const void* data = input_buffer->data() + bytes_to_skip_in_sample;
- int size = input_buffer->size() - bytes_to_skip_in_sample;
-
- std::int64_t win32_timestamp =
- ConvertToWin32Time(input_buffer->timestamp());
- const uint8_t* iv = NULL;
- int iv_size = 0;
- const SbDrmSubSampleMapping* subsample_mapping = NULL;
- int subsample_count = 0;
-
- if (drm_info != NULL && drm_info->initialization_vector_size != 0) {
- if (bytes_to_skip_in_sample != 0) {
- if (drm_info->subsample_count != 0 && drm_info->subsample_count != 1) {
- return false;
- }
- if (drm_info->subsample_count == 1) {
- if (drm_info->subsample_mapping[0].clear_byte_count !=
- bytes_to_skip_in_sample) {
- return false;
- }
- }
- } else {
- subsample_mapping = drm_info->subsample_mapping;
- subsample_count = drm_info->subsample_count;
- }
-
- iv = drm_info->initialization_vector;
- iv_size = drm_info->initialization_vector_size;
- }
-
- // MFSampleExtension_CleanPoint is a key-frame for the video + audio. It is
- // not set here because the win32 system is smart enough to figure this out.
- // It will probably be totally ok to not set this at all. Resolution: If
- // there are problems with win32 video decoding, come back to this and see
- // if setting this will fix it. THis will be used if
- // SbMediaVideoSampleInfo::is_key_frame is true inside of the this function
- // (which will receive an InputBuffer).
- input_sample = CreateSample(data, size, win32_timestamp);
-
- if (encrypted) {
- AttachDrmDataToSample(input_sample, size, key_id, key_id_size, iv,
- iv_size, subsample_mapping, subsample_count);
- }
- last_input_buffer_ = input_buffer;
- last_input_sample_ = input_sample;
- }
-
- if (encrypted) {
- if (!decryptor_) {
- if (decoder_->draining()) {
- return false;
- }
- if (!decoder_->drained()) {
- decoder_->Drain();
- return false;
- }
- decoder_->ResetFromDrained();
- scoped_refptr<SbDrmSystemPlayready::License> license =
- drm_system_->GetLicense(key_id, key_id_size);
- if (license && license->usable()) {
- decryptor_.reset(new MediaTransform(license->decryptor()));
- bool success = ActivateDecryptor();
- if (!success) {
- decryptor_.reset();
- return false;
- }
- }
- }
- if (!decryptor_) {
- SB_NOTREACHED();
- return false;
- }
- }
-
- if (encrypted) {
- return decryptor_->TryWrite(input_sample);
- }
- return decoder_->TryWrite(input_sample);
-}
-
-bool DecryptingDecoder::ProcessAndRead(ComPtr<IMFSample>* output,
- ComPtr<IMFMediaType>* new_type) {
- bool did_something = false;
-
- *output = decoder_->TryRead(new_type);
- did_something |= *output != NULL;
-
- if (decryptor_) {
- if (!pending_decryptor_output_) {
- ComPtr<IMFMediaType> ignored_type;
- pending_decryptor_output_ = decryptor_->TryRead(&ignored_type);
- did_something |= pending_decryptor_output_ != NULL;
- }
-
- if (pending_decryptor_output_) {
- if (decoder_->TryWrite(pending_decryptor_output_)) {
- pending_decryptor_output_.Reset();
- did_something = true;
- }
- }
-
- if (decryptor_->drained() && !decoder_->draining() &&
- !decoder_->drained()) {
- decoder_->Drain();
- did_something = true;
- }
- }
-
- return did_something;
-}
-
-void DecryptingDecoder::Drain() {
- if (decryptor_) {
- decryptor_->Drain();
- } else {
- decoder_->Drain();
- }
-}
-
-bool DecryptingDecoder::ActivateDecryptor() {
- SB_DCHECK(decryptor_);
-
- ComPtr<IMFMediaType> decoder_output_type = decoder_->GetCurrentOutputType();
- decryptor_->SetInputType(decoder_->GetCurrentInputType());
-
- GUID original_sub_type;
- decoder_output_type->GetGUID(MF_MT_SUBTYPE, &original_sub_type);
-
- // Ensure that the decryptor and the decoder agrees on the protection of
- // samples transferred between them.
- ComPtr<IMFSampleProtection> decryption_sample_protection =
- decryptor_->GetSampleProtection();
- SB_DCHECK(decryption_sample_protection);
-
- DWORD decryption_protection_version;
- HRESULT hr = decryption_sample_protection->GetOutputProtectionVersion(
- &decryption_protection_version);
- CheckResult(hr);
-
- ComPtr<IMFSampleProtection> decoder_sample_protection =
- decoder_->GetSampleProtection();
- SB_DCHECK(decoder_sample_protection);
-
- DWORD decoder_protection_version;
- hr = decoder_sample_protection->GetInputProtectionVersion(
- &decoder_protection_version);
- CheckResult(hr);
-
- DWORD protection_version =
- std::min(decoder_protection_version, decryption_protection_version);
- if (protection_version < SAMPLE_PROTECTION_VERSION_RC4) {
- SB_NOTREACHED();
- return true;
- }
-
- BYTE* cert_data = NULL;
- DWORD cert_data_size = 0;
-
- hr = decoder_sample_protection->GetProtectionCertificate(
- protection_version, &cert_data, &cert_data_size);
- CheckResult(hr);
-
- BYTE* crypt_seed = NULL;
- DWORD crypt_seed_size = 0;
- hr = decryption_sample_protection->InitOutputProtection(
- protection_version, 0, cert_data, cert_data_size, &crypt_seed,
- &crypt_seed_size);
- if (FAILED(hr)) {
- // This can happen if we call InitOutputProtection while procesing
- // a UWP resume event or shortly after.
- return false;
- }
- CheckResult(hr);
-
- hr = decoder_sample_protection->InitInputProtection(
- protection_version, 0, crypt_seed, crypt_seed_size);
- CheckResult(hr);
-
- CoTaskMemFree(cert_data);
- CoTaskMemFree(crypt_seed);
-
- // Ensure that the input type of the decoder is the output type of the
- // decryptor.
- ComPtr<IMFMediaType> decoder_input_type;
- std::vector<ComPtr<IMFMediaType>> decryptor_output_types =
- decryptor_->GetAvailableOutputTypes();
- SB_DCHECK(!decryptor_output_types.empty());
-
- decryptor_->SetOutputType(decryptor_output_types[0]);
- decoder_->SetInputType(decryptor_output_types[0]);
-
- std::vector<ComPtr<IMFMediaType>> decoder_output_types =
- decoder_->GetAvailableOutputTypes();
- for (auto output_type : decoder_output_types) {
- GUID sub_type;
- output_type->GetGUID(MF_MT_SUBTYPE, &sub_type);
- if (IsEqualGUID(sub_type, original_sub_type)) {
- decoder_->SetOutputType(output_type);
- return true;
- }
- }
- return true;
-}
-
-void DecryptingDecoder::Reset() {
- if (decryptor_) {
- decryptor_->Reset();
- }
- decoder_->Reset();
-
- last_input_buffer_ = nullptr;
- last_input_sample_ = nullptr;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/decrypting_decoder.h b/src/starboard/shared/win32/decrypting_decoder.h
deleted file mode 100644
index 225d612..0000000
--- a/src/starboard/shared/win32/decrypting_decoder.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_DECRYPTING_DECODER_H_
-#define STARBOARD_SHARED_WIN32_DECRYPTING_DECODER_H_
-
-#include <D3D11.h>
-#include <mfapi.h>
-#include <mferror.h>
-#include <mfidl.h>
-#include <wrl\client.h>
-
-#include <string>
-#include <vector>
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/drm.h"
-#include "starboard/media.h"
-#include "starboard/shared/win32/drm_system_playready.h"
-#include "starboard/shared/win32/media_common.h"
-#include "starboard/shared/win32/media_transform.h"
-#include "starboard/types.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// This class maintains a MediaTransform based decoding stream. When the
-// stream is encrypted, it also contains a MediaTransform based decryptor and
-// manages the interaction between the decryptor and the decoder.
-class DecryptingDecoder {
- public:
- DecryptingDecoder(const std::string& type,
- scoped_ptr<MediaTransform> decoder,
- SbDrmSystem drm_system);
- ~DecryptingDecoder();
-
- MediaTransform* GetDecoder() { return decoder_.get(); }
-
- bool TryWriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer,
- int bytes_to_skip_in_sample);
-
- // Return true if there is any internal actions succeeded, this implies that
- // the caller can call this function again to process further.
- // |output| contains the decrypted and decoded output if there is any.
- // |new_type| contains the new output type in case the output type of the
- // decoding stream is changed.
- bool ProcessAndRead(ComPtr<IMFSample>* output,
- ComPtr<IMFMediaType>* new_type);
- void Drain();
- void Reset();
-
- private:
- bool ActivateDecryptor();
-
- // TODO: Clarify the thread pattern of this class.
- const std::string type_; // For debugging purpose.
- SbDrmSystemPlayready* drm_system_;
-
- scoped_ptr<MediaTransform> decryptor_;
- scoped_ptr<MediaTransform> decoder_;
-
- scoped_refptr<InputBuffer> last_input_buffer_;
- ComPtr<IMFSample> last_input_sample_;
-
- ComPtr<IMFSample> pending_decryptor_output_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_DECRYPTING_DECODER_H_
diff --git a/src/starboard/shared/win32/dialog.cc b/src/starboard/shared/win32/dialog.cc
deleted file mode 100644
index 7ac5ae0..0000000
--- a/src/starboard/shared/win32/dialog.cc
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/dialog.h"
-
-#include <windef.h>
-#include <windows.h>
-#include <windowsx.h>
-
-#include <functional>
-#include <vector>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-typedef std::function<void()> DialogCallback;
-
-using starboard::shared::win32::DebugLogWinError;
-using starboard::shared::win32::CStringToWString;
-
-namespace {
-HWND g_current_dialog_handle = nullptr;
-DialogCallback g_ok_callback;
-DialogCallback g_cancel_callback;
-} // namespace
-
-// Wraps the win32 Dialog interface for building Dialogs at runtime.
-// https://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
-class DialogTemplateBuilder {
- public:
- LPCDLGTEMPLATE BuildTemplate() { return (LPCDLGTEMPLATE)&v[0]; }
- void AlignToDword() {
- if (v.size() % 4)
- Write(NULL, 4 - (v.size() % 4));
- }
- void Write(LPCVOID pvWrite, DWORD cbWrite) {
- v.insert(v.end(), cbWrite, 0);
- if (pvWrite)
- CopyMemory(&v[v.size() - cbWrite], pvWrite, cbWrite);
- }
- template <typename T>
- void Write(T t) {
- Write(&t, sizeof(T));
- }
- void WriteString(LPCWSTR psz) {
- Write(psz, (lstrlenW(psz) + 1) * sizeof(WCHAR));
- }
-
- private:
- std::vector<BYTE> v;
-};
-
-INT_PTR CALLBACK DialogProcedureCallback(HWND dialog_handle,
- UINT message,
- WPARAM w_param,
- LPARAM /*l_param*/) {
- SB_CHECK(!g_current_dialog_handle || dialog_handle == g_current_dialog_handle)
- << "Received callback on non-active dialog! Only one dialog at a time is "
- "supported.";
- switch (message) {
- case WM_INITDIALOG:
- return TRUE;
- case WM_COMMAND:
- auto command_id = GET_WM_COMMAND_ID(w_param, l_param);
- if (command_id == IDCANCEL) {
- g_cancel_callback();
- } else if (command_id == IDOK) {
- g_ok_callback();
- } else {
- return FALSE;
- }
- EndDialog(dialog_handle, 0);
- g_current_dialog_handle = nullptr;
- return TRUE;
- }
- return FALSE;
-}
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-bool ShowOkCancelDialog(HWND hwnd,
- const std::string& title,
- const std::string& message,
- const std::string& ok_message,
- DialogCallback ok_callback,
- const std::string& cancel_message,
- DialogCallback cancel_callback) {
- if (g_current_dialog_handle != nullptr) {
- SB_LOG(WARNING) << "Already showing a dialog; cancelling existing and "
- "replacing with new dialog";
- CancelDialog();
- }
- g_ok_callback = ok_callback;
- g_cancel_callback = cancel_callback;
- // Get the device context (DC) and from the system so we can scale our fonts
- // correctly.
- HDC hdc = GetDC(NULL);
- SB_CHECK(hdc);
- NONCLIENTMETRICSW ncm = {sizeof(ncm)};
- bool retrieved_system_params =
- SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
-
- if (!retrieved_system_params) {
- DebugLogWinError();
- ReleaseDC(NULL, hdc);
- return false;
- }
- DialogTemplateBuilder dialog_template;
- const int help_id = 0;
- const int extended_style = 0;
-
- const int window_width = 200;
- const int window_height = 80;
- const int window_x = 32;
- const int window_y = 32;
-
- const int edge_padding = 7;
-
- const int button_height = 14;
- const int button_width = 50;
- const int button_y = window_height - button_height - edge_padding;
- const int left_button_x = window_width / 2 - button_width - edge_padding / 2;
- const int right_button_x = window_width / 2 + edge_padding / 2;
- const int text_width = window_width - edge_padding * 2;
- const int text_height = window_width - edge_padding * 3 - button_height;
- const int extra_data = 0;
-
- // Create a dialog template.
- // The following MSDN blogposts explains how this is all laid out:
- // https://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753
- // https://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
- // More official documentation:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644996(v=vs.85).aspx#modeless_box
- dialog_template.Write<WORD>(1); // dialog version
- dialog_template.Write<WORD>(0xFFFF); // extended dialog template
- dialog_template.Write<DWORD>(help_id);
- dialog_template.Write<DWORD>(extended_style);
- dialog_template.Write<DWORD>(WS_CAPTION | WS_SYSMENU | DS_SETFONT |
- DS_MODALFRAME);
- dialog_template.Write<WORD>(3); // number of controls
- dialog_template.Write<WORD>(window_x);
- dialog_template.Write<WORD>(window_y);
- dialog_template.Write<WORD>(window_width);
- dialog_template.Write<WORD>(window_height);
- dialog_template.WriteString(L""); // no menu
- dialog_template.WriteString(L""); // default dialog class
- // Title.
- dialog_template.WriteString(
- (LPCWSTR)starboard::shared::win32::CStringToWString(title.c_str())
- .c_str());
-
- // See following for info on how the font styling is calculated:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ff684173(v=vs.85).aspx
- if (ncm.lfMessageFont.lfHeight < 0) {
- ncm.lfMessageFont.lfHeight =
- -MulDiv(ncm.lfMessageFont.lfHeight, 72, GetDeviceCaps(hdc, LOGPIXELSY));
- }
- dialog_template.Write<WORD>((WORD)ncm.lfMessageFont.lfHeight);
- dialog_template.Write<WORD>((WORD)ncm.lfMessageFont.lfWeight);
- dialog_template.Write<BYTE>(ncm.lfMessageFont.lfItalic);
- dialog_template.Write<BYTE>(ncm.lfMessageFont.lfCharSet);
- dialog_template.WriteString(ncm.lfMessageFont.lfFaceName);
-
- // Message text.
- dialog_template.AlignToDword();
- dialog_template.Write<DWORD>(help_id);
- dialog_template.Write<DWORD>(extended_style);
- dialog_template.Write<DWORD>(WS_CHILD | WS_VISIBLE);
- dialog_template.Write<WORD>(edge_padding);
- dialog_template.Write<WORD>(edge_padding);
- dialog_template.Write<WORD>(text_width);
- dialog_template.Write<WORD>(text_height);
- dialog_template.Write<DWORD>((DWORD)-1);
- dialog_template.Write<DWORD>(0x0082FFFF);
- dialog_template.WriteString(
- (LPCWSTR)starboard::shared::win32::CStringToWString(message.c_str())
- .c_str());
- dialog_template.Write<WORD>(extra_data);
-
- // Cancel button.
- dialog_template.AlignToDword();
- dialog_template.Write<DWORD>(help_id);
- dialog_template.Write<DWORD>(extended_style);
- dialog_template.Write<DWORD>(WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP |
- BS_DEFPUSHBUTTON);
- dialog_template.Write<WORD>(left_button_x);
- dialog_template.Write<WORD>(button_y);
- dialog_template.Write<WORD>(button_width);
- dialog_template.Write<WORD>(button_height);
- dialog_template.Write<DWORD>(IDCANCEL);
- dialog_template.Write<DWORD>(0x0080FFFF);
- dialog_template.WriteString(
- (LPCWSTR)starboard::shared::win32::CStringToWString(
- cancel_message.c_str())
- .c_str());
- dialog_template.Write<WORD>(extra_data);
-
- // Ok button.
- dialog_template.AlignToDword();
- dialog_template.Write<DWORD>(help_id);
- dialog_template.Write<DWORD>(extended_style);
- dialog_template.Write<DWORD>(WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP |
- BS_DEFPUSHBUTTON); // style
- dialog_template.Write<WORD>(right_button_x);
- dialog_template.Write<WORD>(button_y);
- dialog_template.Write<WORD>(button_width);
- dialog_template.Write<WORD>(button_height);
- dialog_template.Write<DWORD>(IDOK);
- dialog_template.Write<DWORD>(0x0080FFFF);
- dialog_template.WriteString(
- (LPCWSTR)starboard::shared::win32::CStringToWString(ok_message.c_str())
- .c_str());
- dialog_template.Write<WORD>(extra_data);
-
- ReleaseDC(NULL, hdc);
- // Template is ready - go display it.
- g_current_dialog_handle = CreateDialogIndirect(
- GetModuleHandle(nullptr), dialog_template.BuildTemplate(), hwnd,
- DialogProcedureCallback);
- ShowWindow(g_current_dialog_handle, SW_SHOW);
- return g_current_dialog_handle != nullptr;
-}
-
-bool DialogHandleMessage(MSG* msg) {
- return IsWindow(g_current_dialog_handle) &&
- IsDialogMessage(g_current_dialog_handle, msg);
-}
-
-void CancelDialog() {
- if (g_current_dialog_handle != nullptr) {
- EndDialog(g_current_dialog_handle, 0);
- g_current_dialog_handle = nullptr;
- }
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/dialog.h b/src/starboard/shared/win32/dialog.h
deleted file mode 100644
index 74adf29..0000000
--- a/src/starboard/shared/win32/dialog.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_DIALOG_H_
-#define STARBOARD_SHARED_WIN32_DIALOG_H_
-
-#include <windows.h>
-
-#include <functional>
-#include <string>
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-typedef std::function<void()> DialogCallback;
-
-// Shows a modeless OK/Cancel-style dialog. Only one dialog may be shown at a
-// time.
-bool ShowOkCancelDialog(HWND hwnd,
- const std::string& title,
- const std::string& message,
- const std::string& ok_message,
- DialogCallback ok_callback,
- const std::string& cancel_message,
- DialogCallback cancel_callback);
-
-// Cancels the current dialog that is showing, if there is one.
-void CancelDialog();
-
-bool DialogHandleMessage(MSG* msg);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_DIALOG_H_
diff --git a/src/starboard/shared/win32/directory_can_open.cc b/src/starboard/shared/win32/directory_can_open.cc
deleted file mode 100644
index cb8cb30..0000000
--- a/src/starboard/shared/win32/directory_can_open.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/directory.h"
-
-#include <windows.h>
-#include <algorithm>
-
-#include "starboard/shared/win32/directory_internal.h"
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-bool SbDirectoryCanOpen(const char* path) {
- using starboard::shared::win32::IsAbsolutePath;
- using starboard::shared::win32::IsValidHandle;
- using starboard::shared::win32::NormalizeWin32Path;
- using starboard::shared::win32::TrimExtraFileSeparators;
-
- if ((path == nullptr) || (path[0] == '\0')) {
- return false;
- }
-
- std::wstring path_wstring = NormalizeWin32Path(path);
- TrimExtraFileSeparators(&path_wstring);
-
- if (!IsAbsolutePath(path_wstring)) {
- return false;
- }
-
- WIN32_FIND_DATA find_data = {0};
-
- HANDLE search_handle = FindFirstFileExW(
- path_wstring.c_str(), FindExInfoStandard, &find_data,
- FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE);
- if (!IsValidHandle(search_handle)) {
- return false;
- }
-
- FindClose(search_handle);
-
- return find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
-}
diff --git a/src/starboard/shared/win32/directory_close.cc b/src/starboard/shared/win32/directory_close.cc
deleted file mode 100644
index 6d7765a..0000000
--- a/src/starboard/shared/win32/directory_close.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/directory.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/directory_internal.h"
-
-bool SbDirectoryClose(SbDirectory directory) {
- if (!SbDirectoryIsValid(directory)) {
- return false;
- }
-
- bool success = CloseHandle(directory->directory_handle);
-
- delete directory;
-
- return success;
-}
diff --git a/src/starboard/shared/win32/directory_create.cc b/src/starboard/shared/win32/directory_create.cc
deleted file mode 100644
index ac6daf9..0000000
--- a/src/starboard/shared/win32/directory_create.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/directory.h"
-
-#include <vector>
-#include <windows.h>
-
-#include "starboard/shared/win32/directory_internal.h"
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-using starboard::shared::win32::DirectoryExists;
-using starboard::shared::win32::DirectoryExistsOrCreated;
-using starboard::shared::win32::IsAbsolutePath;
-using starboard::shared::win32::NormalizeWin32Path;
-using starboard::shared::win32::TrimExtraFileSeparators;
-
-bool SbDirectoryCreate(const char* path) {
- if ((path == nullptr) || (path[0] == '\0')) {
- return false;
- }
-
- std::wstring path_wstring = NormalizeWin32Path(path);
- TrimExtraFileSeparators(&path_wstring);
-
- if (!IsAbsolutePath(path_wstring)) {
- return false;
- }
-
- return DirectoryExistsOrCreated(path_wstring);
-}
diff --git a/src/starboard/shared/win32/directory_get_next.cc b/src/starboard/shared/win32/directory_get_next.cc
deleted file mode 100644
index 514c1b6..0000000
--- a/src/starboard/shared/win32/directory_get_next.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/directory.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/string.h"
-#include "starboard/shared/win32/directory_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-namespace {
-// One of the entries of FILE_ID_BOTH_DIR_INFO is a file path, so make the
-// buffer at-least one path big.
-const std::size_t kDirectoryInfoBufferSize =
- SB_FILE_MAX_PATH + sizeof(FILE_ID_BOTH_DIR_INFO);
-
-std::deque<std::string> GetDirectoryEntries(HANDLE directory_handle) {
- // According to
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364226(v=vs.85).aspx,
- // FILE_ID_BOTH_DIR_INFO must be aligned on a DWORDLONG boundary.
- alignas(
- sizeof(DWORDLONG)) char directory_info_buffer[kDirectoryInfoBufferSize];
-
- std::deque<std::string> entries;
- BOOL directory_info_success = GetFileInformationByHandleEx(
- directory_handle, FileIdBothDirectoryInfo, directory_info_buffer,
- SB_ARRAY_SIZE(directory_info_buffer));
-
- if (!directory_info_success) {
- return entries;
- }
-
- const char* directory_info_pointer = directory_info_buffer;
- DWORD next_entry_offset = 0;
-
- do {
- auto directory_info =
- reinterpret_cast<const FILE_ID_BOTH_DIR_INFO*>(directory_info_pointer);
-
- // FileName is in Unicode, so divide by 2 to get the real length.
- DWORD number_characters_in_filename = directory_info->FileNameLength / 2;
- std::string ascii_path = starboard::shared::win32::wchar_tToUTF8(
- directory_info->FileName, number_characters_in_filename);
- SB_DCHECK(ascii_path.size() == number_characters_in_filename);
- bool is_dotted_directory =
- !ascii_path.compare(".") || !ascii_path.compare("..");
- if (!is_dotted_directory) {
- entries.emplace_back(std::move(ascii_path));
- }
- next_entry_offset = directory_info->NextEntryOffset;
- directory_info_pointer += next_entry_offset;
- } while (next_entry_offset != 0);
-
- return entries;
-}
-
-} // namespace
-
-bool SbDirectoryGetNext(SbDirectory directory, SbDirectoryEntry* out_entry) {
- if (!SbDirectoryIsValid(directory) || (out_entry == nullptr)) {
- return false;
- }
-
- auto& next_directory_entries = directory->next_directory_entries;
- if (next_directory_entries.empty()) {
- next_directory_entries = GetDirectoryEntries(directory->directory_handle);
- }
-
- if (next_directory_entries.empty()) {
- return false;
- }
-
- bool success = true;
- const int entry_name_size = SB_ARRAY_SIZE_INT(out_entry->name);
- if (SbStringCopy(out_entry->name, next_directory_entries.rbegin()->c_str(),
- entry_name_size) >= entry_name_size) {
- success = false;
- }
- directory->next_directory_entries.pop_back();
- return success;
-}
diff --git a/src/starboard/shared/win32/directory_internal.cc b/src/starboard/shared/win32/directory_internal.cc
deleted file mode 100644
index bc57e41..0000000
--- a/src/starboard/shared/win32/directory_internal.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/directory_internal.h"
-
-#include "starboard/directory.h"
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/win32/file_internal.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-bool HasValidHandle(SbDirectory directory) {
- if (!SbDirectoryIsValid(directory)) {
- return false;
- }
- return directory->HasValidHandle();
-}
-
-// This function strips trailing file separators from a directory name.
-// For example if the directory name was "C:\\Temp\\\\\\", it would
-// strip them, so that the directory name is now to be "C:\\temp".
-void TrimExtraFileSeparators(std::wstring* dirname_pointer) {
- SB_DCHECK(dirname_pointer);
- std::wstring& dirname = *dirname_pointer;
- auto new_end =
- std::find_if_not(dirname.rbegin(), dirname.rend(), [](wchar_t c) {
- return c == SB_FILE_SEP_CHAR || c == SB_FILE_ALT_SEP_CHAR;
- });
- dirname.erase(new_end.base(), dirname.end());
-}
-
-bool IsAbsolutePath(const std::wstring& path) {
- wchar_t full_path[SB_FILE_MAX_PATH];
- DWORD full_path_size =
- GetFullPathNameW(path.c_str(), SB_ARRAY_SIZE(full_path), full_path, NULL);
- if (full_path_size == 0) {
- return false;
- }
-
- int path_size = static_cast<int>(path.size());
- return CompareStringEx(LOCALE_NAME_USER_DEFAULT, NORM_IGNORECASE,
- path.c_str(), path_size, full_path, full_path_size,
- NULL, NULL, 0) == CSTR_EQUAL;
-}
-
-bool DirectoryExists(const std::wstring& dir_path) {
- if (dir_path.empty()) {
- return false;
- }
- std::wstring norm_dir_path = NormalizeWin32Path(dir_path);
- WIN32_FILE_ATTRIBUTE_DATA attribute_data = {0};
- if (!GetFileAttributesExW(norm_dir_path.c_str(), GetFileExInfoStandard,
- &attribute_data)) {
- return false;
- }
- return (attribute_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
-}
-
-bool CreateDirectoryHiearchy(const std::wstring& wfull_path) {
- if (DirectoryExistsOrCreated(wfull_path)) {
- return true;
- }
- const wchar_t kPathSeparators[] = { SB_FILE_SEP_CHAR, SB_FILE_ALT_SEP_CHAR };
- size_t path_end = 0;
- do {
- path_end = wfull_path.find_first_of(kPathSeparators, path_end,
- SB_ARRAY_SIZE(kPathSeparators));
- if (path_end == std::wstring::npos) {
- path_end = wfull_path.size();
- }
- std::wstring temp_path = wfull_path.substr(0, path_end);
- DirectoryExistsOrCreated(temp_path);
- } while (path_end < wfull_path.size());
-
- return DirectoryExistsOrCreated(wfull_path);
-}
-
-bool DirectoryExistsOrCreated(const std::wstring& wpath) {
- return DirectoryExists(wpath) || CreateDirectoryW(wpath.c_str(), NULL);
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/directory_internal.h b/src/starboard/shared/win32/directory_internal.h
deleted file mode 100644
index 1a82ddd..0000000
--- a/src/starboard/shared/win32/directory_internal.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_DIRECTORY_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_DIRECTORY_INTERNAL_H_
-
-#include "starboard/directory.h"
-
-#include <algorithm>
-#include <deque>
-#include <string>
-#include <vector>
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/win32/file_internal.h"
-
-#pragma warning(push)
-
-// SbFilePrivate is defined as a struct, but for windows implementation
-// enough functionality has been added so that it warrants being a class
-// per Google's C++ style guide. This mismatch causes the Microsoft's compiler
-// to generate a warning.
-#pragma warning(disable : 4099)
-
-class SbDirectoryPrivate {
- public:
- explicit SbDirectoryPrivate(HANDLE handle) : directory_handle(handle) {}
-
- bool HasValidHandle() const {
- return starboard::shared::win32::IsValidHandle(directory_handle);
- }
-
- HANDLE directory_handle;
- std::deque<std::string> next_directory_entries;
-
- // SbDirectoryPrivate is neither copyable nor movable.
- SbDirectoryPrivate(const SbDirectoryPrivate&) = delete;
- SbDirectoryPrivate& operator=(const SbDirectoryPrivate&) = delete;
-};
-#pragma warning(pop)
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-bool HasValidHandle(SbDirectory directory);
-
-// This function strips trailing file separators from a directory name.
-// For example if the directory name was "C:\\Temp\\\\\\", it would
-// strip them, so that the directory name is now to be "C:\\temp".
-void TrimExtraFileSeparators(std::wstring* dirname_pointer);
-
-bool IsAbsolutePath(const std::wstring& path);
-
-bool DirectoryExists(const std::wstring& dir_path);
-
-// Directory hierarchy is created from tip down to root. This is necessary
-// because UWP has issues with bottom up directory creation due to permissions.
-bool CreateDirectoryHiearchy(const std::wstring& wfull_path);
-
-bool DirectoryExistsOrCreated(const std::wstring& wpath);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_DIRECTORY_INTERNAL_H_
diff --git a/src/starboard/shared/win32/directory_open.cc b/src/starboard/shared/win32/directory_open.cc
deleted file mode 100644
index adad7aa..0000000
--- a/src/starboard/shared/win32/directory_open.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/directory.h"
-
-#include "starboard/shared/win32/directory_internal.h"
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-SbDirectory SbDirectoryOpen(const char* path, SbFileError* out_error) {
- using starboard::shared::win32::CStringToWString;
- using starboard::shared::win32::NormalizeWin32Path;
-
- if ((path == nullptr) || (path[0] == '\0')) {
- if (out_error) {
- *out_error = kSbFileErrorNotFound;
- }
- return kSbDirectoryInvalid;
- }
-
- std::wstring path_wstring = NormalizeWin32Path(path);
-
- if (!starboard::shared::win32::IsAbsolutePath(path_wstring)) {
- if (out_error) {
- *out_error = kSbFileErrorNotFound;
- }
- return kSbDirectoryInvalid;
- }
-
- HANDLE directory_handle = starboard::shared::win32::OpenFileOrDirectory(
- path, kSbFileOpenOnly | kSbFileRead, nullptr, out_error);
-
- if (!starboard::shared::win32::IsValidHandle(directory_handle)) {
- return kSbDirectoryInvalid;
- }
-
- FILE_BASIC_INFO basic_info = {0};
- BOOL basic_info_success = GetFileInformationByHandleEx(
- directory_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO));
-
- if (!basic_info_success ||
- !(basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
- CloseHandle(directory_handle);
- if (out_error) {
- *out_error = kSbFileErrorNotADirectory;
- }
- return kSbDirectoryInvalid;
- }
-
- return new SbDirectoryPrivate(directory_handle);
-}
diff --git a/src/starboard/shared/win32/drm_create_system.cc b/src/starboard/shared/win32/drm_create_system.cc
deleted file mode 100644
index e5b8a55..0000000
--- a/src/starboard/shared/win32/drm_create_system.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/drm.h"
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/drm_system_playready.h"
-#include "starboard/string.h"
-
-SbDrmSystem SbDrmCreateSystem(
- const char* key_system,
- void* context,
- SbDrmSessionUpdateRequestFunc update_request_callback,
- SbDrmSessionUpdatedFunc session_updated_callback,
- SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
- SbDrmSessionClosedFunc session_closed_callback) {
- using ::starboard::shared::win32::SbDrmSystemPlayready;
-
- if (SbStringCompareAll(key_system, "com.youtube.playready") != 0) {
- SB_DLOG(WARNING) << "Invalid key system " << key_system;
- return kSbDrmSystemInvalid;
- }
-
- return new SbDrmSystemPlayready(context, update_request_callback,
- session_updated_callback,
- key_statuses_changed_callback,
- session_closed_callback);
-}
diff --git a/src/starboard/shared/win32/drm_system_playready.cc b/src/starboard/shared/win32/drm_system_playready.cc
deleted file mode 100644
index 39bb1a4..0000000
--- a/src/starboard/shared/win32/drm_system_playready.cc
+++ /dev/null
@@ -1,334 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/drm_system_playready.h"
-
-#include <algorithm>
-#include <cctype>
-#include <sstream>
-#include <vector>
-
-#include "starboard/configuration.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/mutex.h"
-#include "starboard/once.h"
-#include "starboard/string.h"
-
-namespace {
-
-const bool kLogPlayreadyChallengeResponse = false;
-
-std::string GetHexRepresentation(const void* data, size_t size) {
- const char kHex[] = "0123456789ABCDEF";
-
- std::stringstream representation;
- std::stringstream ascii;
- const uint8_t* binary = static_cast<const uint8_t*>(data);
- bool new_line = true;
- for (size_t i = 0; i < size; ++i) {
- if (new_line) {
- new_line = false;
- } else {
- representation << ' ';
- }
- ascii << (std::isprint(binary[i]) ? static_cast<char>(binary[i]) : '?');
- representation << kHex[binary[i] / 16] << kHex[binary[i] % 16];
- if (i % 16 == 15 && i != size - 1) {
- representation << " (" << ascii.str() << ')' << std::endl;
- std::stringstream empty;
- ascii.swap(empty); // Clear the ascii stream
- new_line = true;
- }
- }
-
- if (!ascii.str().empty()) {
- representation << '(' << ascii.str() << ')' << std::endl;
- }
-
- return representation.str();
-}
-
-template <typename T>
-std::string GetHexRepresentation(const T& value) {
- return GetHexRepresentation(&value, sizeof(T));
-}
-
-class ActiveDrmSystems {
- public:
- ::starboard::Mutex mutex_;
- std::vector<starboard::shared::win32::SbDrmSystemPlayready*> active_systems_;
-};
-
-SB_ONCE_INITIALIZE_FUNCTION(ActiveDrmSystems, GetActiveDrmSystems);
-
-} // namespace
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-void DrmSystemOnUwpResume() {
- ::starboard::ScopedLock lock(GetActiveDrmSystems()->mutex_);
- for (SbDrmSystemPlayready* item : GetActiveDrmSystems()->active_systems_) {
- item->OnUwpResume();
- }
-}
-
-SbDrmSystemPlayready::SbDrmSystemPlayready(
- void* context,
- SbDrmSessionUpdateRequestFunc session_update_request_callback,
- SbDrmSessionUpdatedFunc session_updated_callback,
- SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
- SbDrmSessionClosedFunc session_closed_callback)
- : context_(context),
- session_update_request_callback_(session_update_request_callback),
- session_updated_callback_(session_updated_callback),
- key_statuses_changed_callback_(key_statuses_changed_callback),
- session_closed_callback_(session_closed_callback),
- current_session_id_(1) {
- SB_DCHECK(session_update_request_callback);
- SB_DCHECK(session_updated_callback);
- SB_DCHECK(key_statuses_changed_callback);
- SB_DCHECK(session_closed_callback);
-
- ScopedLock lock(GetActiveDrmSystems()->mutex_);
- GetActiveDrmSystems()->active_systems_.push_back(this);
-}
-
-SbDrmSystemPlayready::~SbDrmSystemPlayready() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- ScopedLock lock(GetActiveDrmSystems()->mutex_);
- auto& active_systems = GetActiveDrmSystems()->active_systems_;
- active_systems.erase(std::remove(
- active_systems.begin(), active_systems.end(), this));
-}
-
-void SbDrmSystemPlayready::GenerateSessionUpdateRequest(
- int ticket,
- const char* type,
- const void* initialization_data,
- int initialization_data_size) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- if (SbStringCompareAll("cenc", type) != 0) {
- SB_NOTREACHED() << "Invalid initialization data type " << type;
- return;
- }
-
- std::string session_id = GenerateAndAdvanceSessionId();
- scoped_refptr<License> license =
- License::Create(initialization_data, initialization_data_size);
- const std::string& challenge = license->license_challenge();
- if (challenge.empty()) {
- // Signal an error with |session_id| as NULL.
- SB_LOG(ERROR) << "Failed to generate license challenge";
- session_update_request_callback_(this, context_, ticket, NULL, 0, NULL, 0,
- NULL);
- return;
- }
-
- SB_LOG(INFO) << "Send challenge for key id "
- << GetHexRepresentation(license->key_id()) << " in session "
- << session_id;
- SB_LOG_IF(INFO, kLogPlayreadyChallengeResponse)
- << GetHexRepresentation(challenge.data(), challenge.size());
-
- session_update_request_callback_(this, context_, ticket, session_id.c_str(),
- static_cast<int>(session_id.size()),
- challenge.c_str(),
- static_cast<int>(challenge.size()), NULL);
- pending_requests_[session_id] = license;
-}
-
-void SbDrmSystemPlayready::UpdateSession(int ticket,
- const void* key,
- int key_size,
- const void* session_id,
- int session_id_size) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- std::string session_id_copy(static_cast<const char*>(session_id),
- session_id_size);
- auto iter = pending_requests_.find(session_id_copy);
- SB_DCHECK(iter != pending_requests_.end());
- if (iter == pending_requests_.end()) {
- SB_NOTREACHED() << "Invalid session id " << session_id_copy;
- return;
- }
-
- scoped_refptr<License> license = iter->second;
-
- SB_LOG(INFO) << "Adding playready response for key id "
- << GetHexRepresentation(license->key_id());
- SB_LOG_IF(INFO, kLogPlayreadyChallengeResponse)
- << GetHexRepresentation(key, key_size);
-
- license->UpdateLicense(key, key_size);
-
- if (license->usable()) {
- SB_LOG(INFO) << "Successfully add key for key id "
- << GetHexRepresentation(license->key_id()) << " in session "
- << session_id_copy;
- {
- ScopedLock lock(mutex_);
- successful_requests_[iter->first] =
- LicenseInfo(kSbDrmKeyStatusUsable, license);
- }
- session_updated_callback_(this, context_, ticket, session_id,
- session_id_size, true);
-
- {
- ScopedLock lock(mutex_);
- ReportKeyStatusChanged_Locked(session_id_copy);
- }
- pending_requests_.erase(iter);
- } else {
- SB_LOG(INFO) << "Failed to add key for session " << session_id_copy;
- // Don't report it as a failure as otherwise the JS player is going to
- // terminate the video.
- session_updated_callback_(this, context_, ticket, session_id,
- session_id_size, true);
- // When UpdateLicense() fails, the |license| must have generated a new
- // challenge internally. Send this challenge again.
- const std::string& challenge = license->license_challenge();
- if (challenge.empty()) {
- SB_NOTREACHED();
- return;
- }
-
- SB_LOG(INFO) << "Send challenge again for key id "
- << GetHexRepresentation(license->key_id()) << " in session "
- << session_id;
- SB_LOG_IF(INFO, kLogPlayreadyChallengeResponse)
- << GetHexRepresentation(challenge.data(), challenge.size());
-
- // We have use |kSbDrmTicketInvalid| as the license challenge is not a
- // result of GenerateSessionUpdateRequest().
- session_update_request_callback_(
- this, context_, kSbDrmTicketInvalid, session_id, session_id_size,
- challenge.c_str(), static_cast<int>(challenge.size()), NULL);
- }
-}
-
-void SbDrmSystemPlayready::CloseSession(const void* session_id,
- int session_id_size) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- key_statuses_changed_callback_(this, context_, session_id, session_id_size, 0,
- nullptr, nullptr);
-
- std::string session_id_copy(static_cast<const char*>(session_id),
- session_id_size);
- pending_requests_.erase(session_id_copy);
-
- ScopedLock lock(mutex_);
- successful_requests_.erase(session_id_copy);
-}
-
-SbDrmSystemPrivate::DecryptStatus SbDrmSystemPlayready::Decrypt(
- InputBuffer* buffer) {
- const SbDrmSampleInfo* drm_info = buffer->drm_info();
-
- if (drm_info == NULL || drm_info->initialization_vector_size == 0) {
- return kSuccess;
- }
-
- GUID key_id;
- if (drm_info->identifier_size != sizeof(key_id)) {
- return kRetry;
- }
- key_id = *reinterpret_cast<const GUID*>(drm_info->identifier);
-
- ScopedLock lock(mutex_);
- for (auto& item : successful_requests_) {
- if (item.second.license_->key_id() == key_id) {
- if (buffer->sample_type() == kSbMediaTypeAudio) {
- return kSuccess;
- }
-
- if (item.second.license_->IsHDCPRequired()) {
- if (!SbMediaSetOutputProtection(true)) {
- SB_LOG(INFO) << "HDCP required but not available";
- item.second.status_ = kSbDrmKeyStatusRestricted;
- ReportKeyStatusChanged_Locked(item.first);
- return kRetry;
- }
- }
-
- return kSuccess;
- }
- }
-
- return kRetry;
-}
-
-void SbDrmSystemPlayready::ReportKeyStatusChanged_Locked(
- const std::string& session_id) {
- // mutex_ must be held by caller
- ::starboard::ScopedTryLock lock_should_fail(mutex_);
- SB_DCHECK(!lock_should_fail.is_locked());
-
- LicenseInfo& item = successful_requests_[session_id];
-
- GUID key_id = item.license_->key_id();
- SbDrmKeyId drm_key_id;
- SB_DCHECK(sizeof(drm_key_id.identifier) >= sizeof(key_id));
- SbMemoryCopy(&(drm_key_id.identifier), &key_id, sizeof(key_id));
- drm_key_id.identifier_size = sizeof(key_id);
-
- key_statuses_changed_callback_(this, context_,
- session_id.data(), static_cast<int>(session_id.size()),
- 1, &drm_key_id, &(item.status_));
-}
-
-scoped_refptr<SbDrmSystemPlayready::License> SbDrmSystemPlayready::GetLicense(
- const uint8_t* key_id,
- int key_id_size) {
- GUID key_id_copy;
- if (key_id_size != sizeof(key_id_copy)) {
- return NULL;
- }
- key_id_copy = *reinterpret_cast<const GUID*>(key_id);
-
- ScopedLock lock(mutex_);
-
- for (auto& item : successful_requests_) {
- if (item.second.license_->key_id() == key_id_copy) {
- return item.second.license_;
- }
- }
-
- return NULL;
-}
-
-void SbDrmSystemPlayready::OnUwpResume() {
- for (auto& item : successful_requests_) {
- session_closed_callback_(this, context_,
- item.first.data(), static_cast<int>(item.first.size()));
- }
-}
-
-std::string SbDrmSystemPlayready::GenerateAndAdvanceSessionId() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- std::stringstream ss;
- ss << current_session_id_;
- ++current_session_id_;
- return ss.str();
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/drm_system_playready.h b/src/starboard/shared/win32/drm_system_playready.h
deleted file mode 100644
index 37444b3..0000000
--- a/src/starboard/shared/win32/drm_system_playready.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_DRM_SYSTEM_PLAYREADY_H_
-#define STARBOARD_SHARED_WIN32_DRM_SYSTEM_PLAYREADY_H_
-
-#include <mfapi.h>
-#include <mfidl.h>
-#include <wrl.h>
-#include <wrl/client.h>
-
-#include <map>
-#include <string>
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/starboard/drm/drm_system_internal.h"
-#include "starboard/shared/starboard/thread_checker.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// Adapts PlayReady decryption module to Starboard's |SbDrmSystem|.
-class SbDrmSystemPlayready : public SbDrmSystemPrivate {
- public:
- class License : public RefCountedThreadSafe<License> {
- public:
- static scoped_refptr<License> Create(const void* initialization_data,
- int initialization_data_size);
-
- virtual ~License() {}
-
- virtual GUID key_id() const = 0;
- virtual bool usable() const = 0;
- virtual std::string license_challenge() const = 0;
- virtual Microsoft::WRL::ComPtr<IMFTransform> decryptor() = 0;
- virtual void UpdateLicense(const void* license, int license_size) = 0;
- virtual bool IsHDCPRequired() = 0;
- };
-
- SbDrmSystemPlayready(
- void* context,
- SbDrmSessionUpdateRequestFunc session_update_request_callback,
- SbDrmSessionUpdatedFunc session_updated_callback,
- SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback,
- SbDrmSessionClosedFunc session_closed_callback);
- ~SbDrmSystemPlayready() override;
-
- // From |SbDrmSystemPrivate|.
- void GenerateSessionUpdateRequest(int ticket,
- const char* type,
- const void* initialization_data,
- int initialization_data_size) override;
-
- void UpdateSession(int ticket,
- const void* key,
- int key_size,
- const void* session_id,
- int session_id_size) override;
-
- void CloseSession(const void* session_id, int session_id_size) override;
-
- DecryptStatus Decrypt(InputBuffer* buffer) override;
-
- // Used by audio and video decoders to retrieve the decryptors.
- scoped_refptr<License> GetLicense(const uint8_t* key_id, int key_id_size);
-
- void OnUwpResume();
-
- private:
- std::string GenerateAndAdvanceSessionId();
- // Note: requires mutex_ to be held
- void ReportKeyStatusChanged_Locked(const std::string& session_id);
-
- ::starboard::shared::starboard::ThreadChecker thread_checker_;
-
- void* context_;
- SbDrmSessionUpdateRequestFunc session_update_request_callback_;
- SbDrmSessionUpdatedFunc session_updated_callback_;
- SbDrmSessionKeyStatusesChangedFunc key_statuses_changed_callback_;
- SbDrmSessionClosedFunc session_closed_callback_;
- int current_session_id_;
-
- std::map<std::string, scoped_refptr<License> > pending_requests_;
-
- // |successful_requests_| can be accessed from more than one thread. Guard
- // it by a mutex.
- Mutex mutex_;
-
- struct LicenseInfo {
- LicenseInfo(SbDrmKeyStatus status, const scoped_refptr<License>& license)
- : status_(status), license_(license) {}
- LicenseInfo() : status_(kSbDrmKeyStatusError) {}
-
- SbDrmKeyStatus status_;
- scoped_refptr<License> license_;
- };
-
- std::map<std::string, LicenseInfo> successful_requests_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_DRM_SYSTEM_PLAYREADY_H_
diff --git a/src/starboard/shared/win32/dx_context_video_decoder.cc b/src/starboard/shared/win32/dx_context_video_decoder.cc
deleted file mode 100644
index 49d34c2..0000000
--- a/src/starboard/shared/win32/dx_context_video_decoder.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/dx_context_video_decoder.h"
-
-#include <D3D11.h>
-#include <D3D11_4.h>
-#include <mfapi.h>
-
-#include "third_party/angle/include/EGL/egl.h"
-#include "third_party/angle/include/EGL/eglext.h"
-
-#include "starboard/log.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-HardwareDecoderContext GetDirectXForHardwareDecoding() {
- HRESULT result = S_OK;
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- PFNEGLQUERYDISPLAYATTRIBEXTPROC query_display;
-
- query_display = reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
- eglGetProcAddress("eglQueryDisplayAttribEXT"));
- SB_DCHECK(query_display != nullptr);
-
- PFNEGLQUERYDEVICEATTRIBEXTPROC query_device;
- query_device = reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>(
- eglGetProcAddress("eglQueryDeviceAttribEXT"));
- SB_DCHECK(query_device != nullptr);
-
- intptr_t egl_device = 0;
- query_display(display, EGL_DEVICE_EXT, &egl_device);
- SB_DCHECK(egl_device != 0);
-
- intptr_t device;
- query_device(reinterpret_cast<EGLDeviceEXT>(egl_device),
- EGL_D3D11_DEVICE_ANGLE, &device);
-
- ID3D11Device* output_dx_device = reinterpret_cast<ID3D11Device*>(device);
- IMFDXGIDeviceManager* dxgi_device_mgr = nullptr;
-
- UINT token = 0;
- result = MFCreateDXGIDeviceManager(&token, &dxgi_device_mgr);
- SB_DCHECK(result == S_OK);
- SB_DCHECK(dxgi_device_mgr);
-
- result = dxgi_device_mgr->ResetDevice(output_dx_device, token);
- SB_DCHECK(SUCCEEDED(result));
-
- HardwareDecoderContext output = {
- output_dx_device,
- dxgi_device_mgr
- };
- return output;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/dx_context_video_decoder.h b/src/starboard/shared/win32/dx_context_video_decoder.h
deleted file mode 100644
index fddf4ea..0000000
--- a/src/starboard/shared/win32/dx_context_video_decoder.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_DX_CONTEXT_VIDEO_DECODER_H_
-#define STARBOARD_SHARED_WIN32_DX_CONTEXT_VIDEO_DECODER_H_
-
-#include <wrl\client.h> // For ComPtr.
-
-struct ID3D11Device;
-struct ID3D11DeviceContext;
-struct IMFDXGIDeviceManager;
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-struct HardwareDecoderContext {
- Microsoft::WRL::ComPtr<ID3D11Device> dx_device_out;
- Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> dxgi_device_manager_out;
-};
-
-HardwareDecoderContext GetDirectXForHardwareDecoding();
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-#endif // STARBOARD_SHARED_WIN32_DX_CONTEXT_VIDEO_DECODER_H_
diff --git a/src/starboard/shared/win32/error_utils.cc b/src/starboard/shared/win32/error_utils.cc
deleted file mode 100644
index d152821..0000000
--- a/src/starboard/shared/win32/error_utils.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Not breaking these functions up because however one is implemented, the
-// others should be implemented similarly.
-
-#include "starboard/shared/win32/error_utils.h"
-
-#include <Mfapi.h>
-#include <Mferror.h>
-#include <propvarutil.h>
-
-#include <utility>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-namespace {
-
-std::string GetFormatHresultMessage(HRESULT hr) {
- std::stringstream ss;
- LPWSTR error_message;
- int message_size = FormatMessage(
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, // Unused with FORMAT_MESSAGE_FROM_SYSTEM.
- hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&error_message,
- 0, // Minimum size for output buffer.
- nullptr);
- SB_DCHECK(message_size);
- ss << wchar_tToUTF8(error_message);
- LocalFree(error_message);
- return ss.str();
-}
-
-
-#define MAKE_HR_PAIR(X) std::pair<HRESULT, std::string>(X, #X)
-const std::pair<HRESULT, std::string> kHresultValueStrings[] = {
- MAKE_HR_PAIR(S_OK),
- MAKE_HR_PAIR(MF_E_PLATFORM_NOT_INITIALIZED),
- MAKE_HR_PAIR(MF_E_BUFFERTOOSMALL),
- MAKE_HR_PAIR(MF_E_INVALIDREQUEST),
- MAKE_HR_PAIR(MF_E_INVALIDSTREAMNUMBER),
- MAKE_HR_PAIR(MF_E_INVALIDMEDIATYPE),
- MAKE_HR_PAIR(MF_E_NOTACCEPTING),
- MAKE_HR_PAIR(MF_E_NOT_INITIALIZED),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_REPRESENTATION),
- MAKE_HR_PAIR(MF_E_NO_MORE_TYPES),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_SERVICE),
- MAKE_HR_PAIR(MF_E_UNEXPECTED),
- MAKE_HR_PAIR(MF_E_INVALIDNAME),
- MAKE_HR_PAIR(MF_E_INVALIDTYPE),
- MAKE_HR_PAIR(MF_E_INVALID_FILE_FORMAT),
- MAKE_HR_PAIR(MF_E_INVALIDINDEX),
- MAKE_HR_PAIR(MF_E_INVALID_TIMESTAMP),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_SCHEME),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_BYTESTREAM_TYPE),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_TIME_FORMAT),
- MAKE_HR_PAIR(MF_E_NO_SAMPLE_TIMESTAMP),
- MAKE_HR_PAIR(MF_E_NO_SAMPLE_DURATION),
- MAKE_HR_PAIR(MF_E_INVALID_STREAM_DATA),
- MAKE_HR_PAIR(MF_E_RT_UNAVAILABLE),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_RATE),
- MAKE_HR_PAIR(MF_E_THINNING_UNSUPPORTED),
- MAKE_HR_PAIR(MF_E_REVERSE_UNSUPPORTED),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_RATE_TRANSITION),
- MAKE_HR_PAIR(MF_E_RATE_CHANGE_PREEMPTED),
- MAKE_HR_PAIR(MF_E_NOT_FOUND),
- MAKE_HR_PAIR(MF_E_NOT_AVAILABLE),
- MAKE_HR_PAIR(MF_E_NO_CLOCK),
- MAKE_HR_PAIR(MF_S_MULTIPLE_BEGIN),
- MAKE_HR_PAIR(MF_E_MULTIPLE_BEGIN),
- MAKE_HR_PAIR(MF_E_MULTIPLE_SUBSCRIBERS),
- MAKE_HR_PAIR(MF_E_TIMER_ORPHANED),
- MAKE_HR_PAIR(MF_E_STATE_TRANSITION_PENDING),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_STATE_TRANSITION),
- MAKE_HR_PAIR(MF_E_UNRECOVERABLE_ERROR_OCCURRED),
- MAKE_HR_PAIR(MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS),
- MAKE_HR_PAIR(MF_E_SAMPLE_NOT_WRITABLE),
- MAKE_HR_PAIR(MF_E_INVALID_KEY),
- MAKE_HR_PAIR(MF_E_BAD_STARTUP_VERSION),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_CAPTION),
- MAKE_HR_PAIR(MF_E_INVALID_POSITION),
- MAKE_HR_PAIR(MF_E_ATTRIBUTENOTFOUND),
- MAKE_HR_PAIR(MF_E_PROPERTY_TYPE_NOT_ALLOWED),
- MAKE_HR_PAIR(MF_E_TOPO_INVALID_OPTIONAL_NODE),
- MAKE_HR_PAIR(MF_E_TOPO_CANNOT_FIND_DECRYPTOR),
- MAKE_HR_PAIR(MF_E_TOPO_CODEC_NOT_FOUND),
- MAKE_HR_PAIR(MF_E_TOPO_CANNOT_CONNECT),
- MAKE_HR_PAIR(MF_E_TOPO_UNSUPPORTED),
- MAKE_HR_PAIR(MF_E_TOPO_INVALID_TIME_ATTRIBUTES),
- MAKE_HR_PAIR(MF_E_TOPO_LOOPS_IN_TOPOLOGY),
- MAKE_HR_PAIR(MF_E_TOPO_MISSING_PRESENTATION_DESCRIPTOR),
- MAKE_HR_PAIR(MF_E_TOPO_MISSING_STREAM_DESCRIPTOR),
- MAKE_HR_PAIR(MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED),
- MAKE_HR_PAIR(MF_E_TOPO_MISSING_SOURCE),
- MAKE_HR_PAIR(MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED),
- MAKE_HR_PAIR(MF_E_TRANSFORM_TYPE_NOT_SET),
- MAKE_HR_PAIR(MF_E_TRANSFORM_STREAM_CHANGE),
- MAKE_HR_PAIR(MF_E_TRANSFORM_INPUT_REMAINING),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROFILE_MISSING),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROFILE_INVALID_OR_CORRUPT),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROFILE_TRUNCATED),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROPERTY_PID_NOT_RECOGNIZED),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROPERTY_VARIANT_TYPE_WRONG),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROPERTY_NOT_WRITEABLE),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROPERTY_ARRAY_VALUE_WRONG_NUM_DIM),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROPERTY_VALUE_SIZE_WRONG),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROPERTY_VALUE_OUT_OF_RANGE),
- MAKE_HR_PAIR(MF_E_TRANSFORM_PROPERTY_VALUE_INCOMPATIBLE),
- MAKE_HR_PAIR(MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_OUTPUT_MEDIATYPE),
- MAKE_HR_PAIR(MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_INPUT_MEDIATYPE),
- MAKE_HR_PAIR(MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_MEDIATYPE_COMBINATION),
- MAKE_HR_PAIR(MF_E_TRANSFORM_CONFLICTS_WITH_OTHER_CURRENTLY_ENABLED_FEATURES),
- MAKE_HR_PAIR(MF_E_TRANSFORM_NEED_MORE_INPUT),
- MAKE_HR_PAIR(MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_SPKR_CONFIG),
- MAKE_HR_PAIR(MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING),
- MAKE_HR_PAIR(MF_S_TRANSFORM_DO_NOT_PROPAGATE_EVENT),
- MAKE_HR_PAIR(MF_E_UNSUPPORTED_D3D_TYPE),
- MAKE_HR_PAIR(MF_E_TRANSFORM_ASYNC_LOCKED),
- MAKE_HR_PAIR(MF_E_TRANSFORM_CANNOT_INITIALIZE_ACM_DRIVER),
-};
-#undef MAKE_HR_PAIR
-
-bool FindHResultEnumString(HRESULT hr, std::string* output) {
- const size_t n = sizeof(kHresultValueStrings) /
- sizeof(*kHresultValueStrings);
-
- for (auto i = 0; i < n; ++i) {
- const auto& elems = kHresultValueStrings[i];
- if (hr == elems.first) {
- *output = elems.second;
- return true;
- }
- }
- return false;
-}
-
-}
-
-std::ostream& operator<<(std::ostream& os, const Win32ErrorCode& error_code) {
- LPWSTR error_message;
- int message_size = FormatMessage(
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, // Unused with FORMAT_MESSAGE_FROM_SYSTEM.
- error_code.GetHRESULT(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&error_message,
- 0, // Minimum size for output buffer.
- nullptr);
- SB_DCHECK(message_size);
- os << wchar_tToUTF8(error_message);
- LocalFree(error_message);
-
- return os;
-}
-
-void DebugLogWinError() {
-#if defined(_DEBUG)
- DWORD error_code = GetLastError();
- if (!error_code)
- return;
-
- SB_LOG(ERROR) << Win32ErrorCode(error_code);
-#endif // defined(_DEBUG)
-}
-
-std::string HResultToString(HRESULT hr) {
- std::string enum_str;
- bool has_enum_str = FindHResultEnumString(hr, &enum_str);
- std::string error_message = GetFormatHresultMessage(hr);
-
- std::stringstream ss;
- if (has_enum_str) {
- ss << enum_str << ": ";
- }
-
- ss << "\"" << error_message << "\"";
- return ss.str();
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/error_utils.h b/src/starboard/shared/win32/error_utils.h
deleted file mode 100644
index b3761d9..0000000
--- a/src/starboard/shared/win32/error_utils.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Not breaking these functions up because however one is implemented, the
-// others should be implemented similarly.
-
-#ifndef STARBOARD_SHARED_WIN32_ERROR_UTILS_H_
-#define STARBOARD_SHARED_WIN32_ERROR_UTILS_H_
-
-#include <windows.h>
-
-#include <iostream>
-#include <string>
-
-#include "starboard/log.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class Win32ErrorCode {
- public:
- explicit Win32ErrorCode(DWORD error_code) : error_code_(error_code) {}
-
- HRESULT GetHRESULT() const { return HRESULT_FROM_WIN32(error_code_); }
-
- private:
- DWORD error_code_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Win32ErrorCode& error_code);
-
-// Checks for system errors and logs a human-readable error if GetLastError()
-// returns an error code. Noops on non-debug builds.
-void DebugLogWinError();
-
-std::string HResultToString(HRESULT hr);
-
-inline void CheckResult(HRESULT hr) {
- SB_DCHECK(SUCCEEDED(hr)) << "HRESULT was " << std::hex << hr
- << " which translates to\n---> \"" << HResultToString(hr) << "\"";
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_ERROR_UTILS_H_
diff --git a/src/starboard/shared/win32/file_can_open.cc b/src/starboard/shared/win32/file_can_open.cc
deleted file mode 100644
index f4f19f6..0000000
--- a/src/starboard/shared/win32/file_can_open.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-bool SbFileCanOpen(const char* path, int flags) {
- if ((path == nullptr) || (path[0] == '\0')) {
- return false;
- }
-
- bool can_read = flags & kSbFileRead;
- bool can_write = flags & kSbFileWrite;
- if (!can_read && !can_write) {
- return false;
- }
-
- std::wstring path_wstring = starboard::shared::win32::CStringToWString(path);
- WIN32_FIND_DATA find_data = {0};
-
- HANDLE search_handle = FindFirstFileExW(
- path_wstring.c_str(), FindExInfoStandard, &find_data,
- FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE);
- if (!starboard::shared::win32::IsValidHandle(search_handle)) {
- return false;
- }
-
- bool can_open = true;
-
- if (((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && can_write) ||
- !starboard::shared::win32::PathEndsWith(path_wstring, find_data.cFileName)) {
- can_open = false;
- }
-
- FindClose(search_handle);
-
- return can_open;
-}
diff --git a/src/starboard/shared/win32/file_close.cc b/src/starboard/shared/win32/file_close.cc
deleted file mode 100644
index 5241359..0000000
--- a/src/starboard/shared/win32/file_close.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/file_internal.h"
-
-using starboard::shared::win32::DebugLogWinError;
-
-bool SbFileClose(SbFile file) {
- if (!SbFileIsValid(file)) {
- return false;
- }
-
- bool success = CloseHandle(file->file_handle);
-
- if (!success) {
- DebugLogWinError();
- }
-
- delete file;
-
- return success;
-}
diff --git a/src/starboard/shared/win32/file_delete.cc b/src/starboard/shared/win32/file_delete.cc
deleted file mode 100644
index effa21c..0000000
--- a/src/starboard/shared/win32/file_delete.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-bool SbFileDelete(const char* path) {
- using starboard::shared::win32::CStringToWString;
- using starboard::shared::win32::NormalizeWin32Path;
-
- if ((path == nullptr) || *path == '\0') {
- return false;
- }
-
- if (!SbFileExists(path)) {
- return true;
- }
-
- std::wstring path_wstring = NormalizeWin32Path(path);
-
- // Remove file or empty directory.
- return DeleteFileW(path_wstring.c_str()) ||
- RemoveDirectoryW(path_wstring.c_str());
-}
diff --git a/src/starboard/shared/win32/file_exists.cc b/src/starboard/shared/win32/file_exists.cc
deleted file mode 100644
index 2757cb2..0000000
--- a/src/starboard/shared/win32/file_exists.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-bool SbFileExists(const char* path) {
- using starboard::shared::win32::CStringToWString;
- using starboard::shared::win32::IsValidHandle;
- using starboard::shared::win32::NormalizeWin32Path;
- using starboard::shared::win32::PathEndsWith;
-
- if ((path == nullptr) || (path[0] == '\0')) {
- return false;
- }
-
- std::wstring path_wstring = NormalizeWin32Path(path);
- // Win32 does not like a trailing "\\" on the path names for directories,
- // so it's needs to be chopped off.
- if (!path_wstring.empty() && (path_wstring.back() == '\\')) {
- path_wstring.pop_back();
- }
-
- WIN32_FIND_DATA find_data = {0};
-
- HANDLE search_handle = FindFirstFileExW(
- path_wstring.c_str(), FindExInfoStandard, &find_data,
- FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE);
-
- if (!IsValidHandle(search_handle)) {
- return false;
- }
-
- FindClose(search_handle);
-
- return PathEndsWith(path_wstring, find_data.cFileName);
-}
diff --git a/src/starboard/shared/win32/file_flush.cc b/src/starboard/shared/win32/file_flush.cc
deleted file mode 100644
index 1c9655c..0000000
--- a/src/starboard/shared/win32/file_flush.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/file_internal.h"
-
-bool SbFileFlush(SbFile file) {
- if (!starboard::shared::win32::HasValidHandle(file)) {
- return false;
- }
-
- return FlushFileBuffers(file->file_handle);
-}
diff --git a/src/starboard/shared/win32/file_get_info.cc b/src/starboard/shared/win32/file_get_info.cc
deleted file mode 100644
index 9a25753..0000000
--- a/src/starboard/shared/win32/file_get_info.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/time_utils.h"
-
-bool SbFileGetInfo(SbFile file, SbFileInfo* out_info) {
- if (!starboard::shared::win32::HasValidHandle(file) || !out_info) {
- return false;
- }
-
- FILE_BASIC_INFO basic_info = {0};
- BOOL basic_info_success = GetFileInformationByHandleEx(
- file->file_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO));
- if (!basic_info_success) {
- return false;
- }
-
- FILE_STANDARD_INFO standard_info = {0};
- BOOL standard_info_success =
- GetFileInformationByHandleEx(file->file_handle, FileStandardInfo,
- &standard_info, sizeof(FILE_STANDARD_INFO));
- if (!standard_info_success) {
- return false;
- }
-
- out_info->size = standard_info.EndOfFile.QuadPart;
- SB_DCHECK(out_info->size >= 0);
-
- using starboard::shared::win32::ConvertFileTimeTicksToSbTime;
-
- out_info->creation_time =
- ConvertFileTimeTicksToSbTime(basic_info.CreationTime);
- out_info->last_accessed =
- ConvertFileTimeTicksToSbTime(basic_info.LastAccessTime);
- out_info->last_modified =
- ConvertFileTimeTicksToSbTime(basic_info.LastWriteTime);
-
- out_info->is_symbolic_link = false;
- out_info->is_directory = standard_info.Directory;
-
- return true;
-}
diff --git a/src/starboard/shared/win32/file_get_path_info.cc b/src/starboard/shared/win32/file_get_path_info.cc
deleted file mode 100644
index 94e0071..0000000
--- a/src/starboard/shared/win32/file_get_path_info.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/time_utils.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-namespace {
-
-bool IsDriveLetter(wchar_t drive_letter) {
- if (L'A' <= drive_letter && drive_letter <= 'Z') {
- return true;
- }
-
- if (L'a' <= drive_letter && drive_letter <= 'z') {
- return true;
- }
- return false;
-}
-
-bool IsRootDirectory(std::wstring wpath) {
- if (wpath.length() > 3) {
- return false;
- }
-
- // Strip optional trailing slash.
- if (wpath.length() == 3) {
- if (wpath.back() != L'\\') {
- return false;
- }
- wpath.pop_back();
- }
-
- if (wpath.length() == 2) {
- if (IsDriveLetter(wpath[0]) && wpath[1] == ':') {
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
-bool SbFileGetPathInfo(const char* path, SbFileInfo* out_info) {
- using starboard::shared::win32::CStringToWString;
- using starboard::shared::win32::NormalizeWin32Path;
-
- if (!path || path[0] == '\0' || !out_info) {
- return false;
- }
-
- std::wstring path_wstring = NormalizeWin32Path(path);
-
- // GetFileAttributesExW(...) does not handle root directories
- // we we have to handle it here.
- if (IsRootDirectory(path_wstring)) {
- out_info->is_directory = true;
- out_info->last_modified = 0;
- out_info->last_accessed = 0;
- out_info->creation_time = 0;
- return true;
- }
-
- WIN32_FILE_ATTRIBUTE_DATA attribute_data = {0};
- if (!GetFileAttributesExW(path_wstring.c_str(), GetFileExInfoStandard,
- &attribute_data)) {
- return false;
- }
-
- out_info->size = static_cast<int64_t>(attribute_data.nFileSizeHigh) << 32 |
- attribute_data.nFileSizeLow;
- SB_DCHECK(out_info->size >= 0);
-
- using starboard::shared::win32::ConvertFileTimeToSbTime;
-
- out_info->creation_time =
- ConvertFileTimeToSbTime(attribute_data.ftCreationTime);
- out_info->last_accessed =
- ConvertFileTimeToSbTime(attribute_data.ftLastAccessTime);
- out_info->last_modified =
- ConvertFileTimeToSbTime(attribute_data.ftLastWriteTime);
-
- out_info->is_symbolic_link = false;
- out_info->is_directory =
- (attribute_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
-
- return true;
-}
diff --git a/src/starboard/shared/win32/file_internal.cc b/src/starboard/shared/win32/file_internal.cc
deleted file mode 100644
index 602641b..0000000
--- a/src/starboard/shared/win32/file_internal.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/file_internal.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-namespace {
-const char kUnixSep[] = "/";
-const char kWin32Sep[] = "\\";
-const wchar_t kUnixSepW[] = L"/";
-const wchar_t kWin32SepW[] = L"\\";
-
-bool IsPathNormalized(const std::string& string) {
- return string.find(kUnixSep) == std::string::npos;
-}
-
-bool IsPathNormalized(const std::wstring& string) {
- return string.find(kUnixSepW) == std::wstring::npos;
-}
-
-std::string NormalizePathSeparator(std::string str) {
- size_t start_pos = 0;
- while ((start_pos = str.find(kUnixSep, start_pos)) != std::string::npos) {
- str.replace(start_pos, sizeof(kUnixSep) - 1, kWin32Sep);
- start_pos += sizeof(kWin32Sep) - 1;
- }
- return str;
-}
-
-std::wstring NormalizePathSeparator(std::wstring str) {
- size_t start_pos = 0;
- while ((start_pos = str.find(kUnixSepW, start_pos)) != std::wstring::npos) {
- str.replace(start_pos, sizeof(kUnixSepW) / 2 - 1, kWin32SepW);
- start_pos += sizeof(kWin32SepW) / 2 - 1;
- }
- return str;
-}
-
-bool StringCanNarrow(const std::wstring& str) {
- for (wchar_t value : str) {
- char narrow_val = static_cast<char>(value);
- if (value != narrow_val) {
- return false;
- }
- }
- return true;
-}
-
-} // namespace
-
-std::wstring NormalizeWin32Path(std::string str) {
- return NormalizeWin32Path(CStringToWString(str.c_str()));
-}
-
-std::wstring NormalizeWin32Path(std::wstring str) {
- return NormalizePathSeparator(str);
-}
-
-HANDLE OpenFileOrDirectory(const char* path,
- int flags,
- bool* out_created,
- SbFileError* out_error) {
- const DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
-
- DWORD creation_disposition = 0;
- if (flags & kSbFileCreateOnly) {
- SB_DCHECK(!creation_disposition);
- SB_DCHECK(!(flags & kSbFileCreateAlways));
- creation_disposition = CREATE_NEW;
- }
-
- if (out_created) {
- *out_created = false;
- }
-
- if (flags & kSbFileCreateAlways) {
- SB_DCHECK(!creation_disposition);
- SB_DCHECK(!(flags & kSbFileCreateOnly));
- creation_disposition = CREATE_ALWAYS;
- }
-
- if (flags & kSbFileOpenTruncated) {
- SB_DCHECK(!creation_disposition);
- SB_DCHECK(flags & kSbFileWrite);
- creation_disposition = TRUNCATE_EXISTING;
- }
-
- if (flags & kSbFileOpenOnly) {
- SB_DCHECK(!(flags & kSbFileOpenAlways));
- creation_disposition = OPEN_EXISTING;
- }
-
- if (flags & kSbFileOpenAlways) {
- SB_DCHECK(!(flags & kSbFileOpenOnly));
- creation_disposition = OPEN_ALWAYS;
- }
-
- if (!creation_disposition && !(flags & kSbFileOpenOnly) &&
- !(flags & kSbFileOpenAlways)) {
- SB_NOTREACHED();
- errno = ENOTSUP;
- if (out_error) {
- *out_error = kSbFileErrorFailed;
- }
-
- return kSbFileInvalid;
- }
-
- DWORD desired_access = 0;
- if (flags & kSbFileRead) {
- desired_access |= GENERIC_READ;
- }
-
- const bool open_file_in_write_mode = flags & kSbFileWrite;
- if (open_file_in_write_mode) {
- desired_access |= GENERIC_WRITE;
- }
-
- // TODO: Support asynchronous IO, if necessary.
- SB_DCHECK(!(flags & kSbFileAsync));
-
- SB_DCHECK(desired_access != 0) << "Invalid permission flag.";
-
- std::wstring path_wstring = NormalizeWin32Path(path);
-
- CREATEFILE2_EXTENDED_PARAMETERS create_ex_params = {0};
- // Enabling |FILE_FLAG_BACKUP_SEMANTICS| allows us to figure out if the path
- // is a directory.
- create_ex_params.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
- create_ex_params.dwFileFlags |= FILE_FLAG_POSIX_SEMANTICS;
- create_ex_params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
- create_ex_params.dwSecurityQosFlags = SECURITY_ANONYMOUS;
- create_ex_params.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
-
- const bool file_exists_prior_to_open = SbFileExists(path);
-
- HANDLE file_handle =
- CreateFile2(path_wstring.c_str(), desired_access, share_mode,
- creation_disposition, &create_ex_params);
-
- const bool file_exists_after_open = SbFileExists(path);
-
- if (out_created && starboard::shared::win32::IsValidHandle(file_handle)) {
- if (flags & kSbFileCreateAlways) {
- *out_created = file_exists_after_open;
- } else {
- *out_created = (!file_exists_prior_to_open && file_exists_after_open);
- }
- }
-
- const DWORD last_error = GetLastError();
-
- if (out_error) {
- if (starboard::shared::win32::IsValidHandle(file_handle)) {
- *out_error = kSbFileOk;
- } else {
- switch (last_error) {
- case ERROR_ACCESS_DENIED:
- *out_error = kSbFileErrorAccessDenied;
- break;
- case ERROR_FILE_EXISTS: {
- if (flags & kSbFileCreateOnly) {
- *out_error = kSbFileErrorExists;
- } else {
- *out_error = kSbFileErrorAccessDenied;
- }
- break;
- }
- case ERROR_FILE_NOT_FOUND:
- *out_error = kSbFileErrorNotFound;
- break;
- default:
- *out_error = kSbFileErrorFailed;
- }
- }
- }
-
- return file_handle;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/file_internal.h b/src/starboard/shared/win32/file_internal.h
deleted file mode 100644
index ad872e2..0000000
--- a/src/starboard/shared/win32/file_internal.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_FILE_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_FILE_INTERNAL_H_
-
-#include <wtypes.h>
-
-#include <cwchar> // This file included for std::wcslen.
-#include <string>
-
-#include "starboard/file.h"
-#include "starboard/shared/internal_only.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-inline bool IsValidHandle(HANDLE handle) {
- return handle != INVALID_HANDLE_VALUE;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#pragma warning(push)
-
-// SbFilePrivate is defined as a struct, but for windows implementation
-// enough functionality has been added so that it warrants being a class
-// per Google's C++ style guide. This mismatch causes the Microsoft's compiler
-// to generate a warning.
-#pragma warning(disable : 4099)
-
-class SbFilePrivate {
- public:
- explicit SbFilePrivate(HANDLE handle) : file_handle(handle) {}
-
- bool HasValidHandle() const {
- return starboard::shared::win32::IsValidHandle(file_handle);
- }
-
- HANDLE file_handle;
-
- // SbFilePrivate is neither copyable nor movable.
- SbFilePrivate(const SbFilePrivate&) = delete;
- SbFilePrivate& operator=(const SbFilePrivate&) = delete;
-};
-#pragma warning(pop)
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-inline bool HasValidHandle(SbFile file) {
- if (!SbFileIsValid(file)) {
- return false;
- }
-
- return file->HasValidHandle();
-}
-
-inline bool PathEndsWith(const std::wstring& path, const wchar_t* filename) {
- size_t filename_length = std::wcslen(filename);
- if (filename_length > path.size()) {
- return false;
- }
-
- size_t path_offset = path.size() - filename_length;
-
- return wcscmp(path.c_str() + path_offset, filename) == 0;
-}
-
-// Path's from cobalt use "/" as a path separator. This function will
-// replace all of the "/" with "\".
-std::wstring NormalizeWin32Path(std::string str);
-std::wstring NormalizeWin32Path(std::wstring str);
-
-HANDLE OpenFileOrDirectory(const char* path,
- int flags,
- bool* out_created,
- SbFileError* out_error);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_FILE_INTERNAL_H_
diff --git a/src/starboard/shared/win32/file_open.cc b/src/starboard/shared/win32/file_open.cc
deleted file mode 100644
index 78407c5..0000000
--- a/src/starboard/shared/win32/file_open.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include "starboard/shared/win32/file_internal.h"
-
-SbFile SbFileOpen(const char* path,
- int flags,
- bool* out_created,
- SbFileError* out_error) {
- if ((path == nullptr) || (path[0] == '\0')) {
- if (out_created) {
- *out_created = false;
- }
- if (out_error) {
- *out_error = kSbFileErrorNotAFile;
- }
- return kSbFileInvalid;
- }
-
- HANDLE file_handle =
- starboard::shared::win32::OpenFileOrDirectory(path, flags, out_created, out_error);
-
- if (!starboard::shared::win32::IsValidHandle(file_handle)) {
- return kSbFileInvalid;
- }
-
- return new SbFilePrivate(file_handle);
-}
diff --git a/src/starboard/shared/win32/file_read.cc b/src/starboard/shared/win32/file_read.cc
deleted file mode 100644
index b2e6db8..0000000
--- a/src/starboard/shared/win32/file_read.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/file_internal.h"
-
-int SbFileRead(SbFile file, char* data, int size) {
- SB_DCHECK((size == 0) || (data != nullptr));
-
- if (!starboard::shared::win32::HasValidHandle(file)) {
- return -1;
- }
-
- if (size < 0) {
- SB_NOTREACHED();
- return -1;
- } else if (size == 0) {
- return 0;
- }
-
- DWORD number_bytes_read = 0;
- BOOL success =
- ReadFile(file->file_handle, data, size, &number_bytes_read, nullptr);
-
- // Since we are only doing synchornous IO, success == FALSE implies that
- // something is wrong.
- if (!success) {
- return -1;
- }
-
- return number_bytes_read;
-}
diff --git a/src/starboard/shared/win32/file_seek.cc b/src/starboard/shared/win32/file_seek.cc
deleted file mode 100644
index 54f56fa..0000000
--- a/src/starboard/shared/win32/file_seek.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/file_internal.h"
-
-int64_t SbFileSeek(SbFile file, SbFileWhence whence, int64_t offset) {
- if (!starboard::shared::win32::HasValidHandle(file)) {
- return -1;
- }
-
- LARGE_INTEGER new_file_pointer = {0};
- LARGE_INTEGER offset_argument = {0};
- offset_argument.QuadPart = offset;
- BOOL success =
- SetFilePointerEx(file->file_handle, offset_argument, &new_file_pointer,
- static_cast<DWORD>(whence));
-
- if (!success) {
- return -1;
- }
-
- return new_file_pointer.QuadPart;
-}
diff --git a/src/starboard/shared/win32/file_truncate.cc b/src/starboard/shared/win32/file_truncate.cc
deleted file mode 100644
index dd2626b..0000000
--- a/src/starboard/shared/win32/file_truncate.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include <algorithm>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/file_internal.h"
-
-namespace {
-static const char k4KZeroPage[4 * 1024] = {0};
-
-bool WriteZerosToFile(HANDLE file_handle,
- LARGE_INTEGER begin,
- LARGE_INTEGER end) {
- SB_DCHECK(starboard::shared::win32::IsValidHandle(file_handle));
- int64_t bytes_left_to_write = end.QuadPart - begin.QuadPart;
- if (bytes_left_to_write <= 0) {
- return true;
- }
-
- // Set the file pointer to |begin|.
- if (!SetFilePointerEx(file_handle, begin, NULL, FILE_BEGIN)) {
- return false;
- }
-
- // Write from zeros from |begin| to |end|.
- while (bytes_left_to_write > 0) {
- int64_t bytes_to_write =
- std::min<int64_t>(SB_ARRAY_SIZE(k4KZeroPage), bytes_left_to_write);
- SB_DCHECK(bytes_to_write <= kSbInt32Max);
-
- DWORD bytes_written = 0;
- if (!WriteFile(file_handle, k4KZeroPage, static_cast<int>(bytes_to_write),
- &bytes_written, NULL)) {
- return false;
- }
-
- bytes_left_to_write -= bytes_written;
- }
-
- return true;
-}
-
-} // namespace
-
-bool SbFileTruncate(SbFile file, int64_t length) {
- if (!starboard::shared::win32::HasValidHandle(file) || length < 0) {
- return false;
- }
-
- HANDLE file_handle = file->file_handle;
-
- // Get current position.
- LARGE_INTEGER current_position = {0};
- BOOL success =
- SetFilePointerEx(file_handle, {0}, ¤t_position, FILE_CURRENT);
-
- if (!success) {
- return false;
- }
-
- bool return_value = false;
- do {
- LARGE_INTEGER old_eof = {0};
- if (!SetFilePointerEx(file_handle, {0}, &old_eof, FILE_END)) {
- break;
- }
-
- LARGE_INTEGER new_eof = {0};
- new_eof.QuadPart = length;
- if (!SetFilePointerEx(file_handle, new_eof, NULL, FILE_BEGIN)) {
- break;
- }
-
- if (!SetEndOfFile(file_handle)) {
- break;
- }
-
- WriteZerosToFile(file_handle, old_eof, new_eof);
- return_value = true;
- } while (0);
-
- // Set the file pointer position back where it was.
- SetFilePointerEx(file_handle, current_position, NULL, FILE_BEGIN);
-
- return return_value;
-}
diff --git a/src/starboard/shared/win32/file_write.cc b/src/starboard/shared/win32/file_write.cc
deleted file mode 100644
index d63bf68..0000000
--- a/src/starboard/shared/win32/file_write.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/file.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/file_internal.h"
-
-int SbFileWrite(SbFile file, const char* data, int size) {
- // TODO: Support asynchronous IO, if necessary.
- SB_DCHECK((size == 0) || (data != nullptr));
- if (!SbFileIsValid(file)) {
- return -1;
- }
- if (size < 0) {
- SB_NOTREACHED();
- return -1;
- } else if (size == 0) {
- return 0;
- }
-
- DWORD bytes_written = 0;
- bool success = WriteFile(file->file_handle, data, size, &bytes_written, NULL);
- if (!success) {
- return -1;
- }
-
- return bytes_written;
-}
diff --git a/src/starboard/shared/win32/get_home_directory.cc b/src/starboard/shared/win32/get_home_directory.cc
deleted file mode 100644
index f4e11e5..0000000
--- a/src/starboard/shared/win32/get_home_directory.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2017 Google Inc. 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 <objbase.h>
-#include <shlobj.h>
-#include <shlwapi.h>
-
-// Windows defines GetCommandLine as a macro to GetCommandLineW which
-// breaks our Application::GetCommandLine call below; thus we undefine
-// it after including our Windows headers.
-#undef GetCommandLine
-
-#include <cstdlib>
-#include <cstring>
-#include <string>
-
-#include "starboard/log.h"
-#include "starboard/shared/nouser/user_internal.h"
-#include "starboard/shared/starboard/application.h"
-#include "starboard/shared/starboard/command_line.h"
-#include "starboard/shared/win32/application_win32.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-#include "starboard/system.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-using starboard::shared::starboard::CommandLine;
-
-namespace starboard {
-namespace shared {
-namespace nouser {
-
-bool GetHomeDirectory(SbUser user, char* out_path, int path_size) {
- if (user != SbUserGetCurrent()) {
- return false;
- }
-
- PWSTR local_app_data_path = nullptr;
-
- if (S_OK == SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr,
- &local_app_data_path)) {
- TCHAR wide_path[MAX_PATH];
- wcscpy(wide_path, local_app_data_path);
- CoTaskMemFree(local_app_data_path);
- // Instead of using the raw local AppData directory, we create a program
- // app directory if it doesn't exist already.
- TCHAR program_name[MAX_PATH];
- DWORD program_name_length = GetModuleFileName(NULL, program_name, MAX_PATH);
- if (program_name_length == 0) {
- SB_LOG(ERROR) << "GetModuleFileName failed";
- sbwin32::DebugLogWinError();
- return false;
- }
- PathStripPath(program_name);
- PathAppend(wide_path, program_name);
- if (!PathFileExists(wide_path)) {
- SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
- NULL, TRUE};
- const BOOL created_directory =
- CreateDirectory(wide_path, &security_attributes);
- if (!created_directory) {
- SB_LOG(ERROR) << "Failed to create home directory";
- sbwin32::DebugLogWinError();
- return false;
- }
- }
-
- const size_t actual_path_length = wcslen(wide_path);
- if (path_size < actual_path_length) {
- SB_LOG(ERROR) << "Home directory length exceeds max path size";
- return false;
- }
- std::wcstombs(out_path, wide_path, actual_path_length + 1);
- return true;
- }
- SB_LOG(ERROR) << "Unable to open local AppData as home directory.";
- sbwin32::DebugLogWinError();
- return false;
-}
-
-} // namespace nouser
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/gyp_configuration.py b/src/starboard/shared/win32/gyp_configuration.py
deleted file mode 100644
index eb9ec4f..0000000
--- a/src/starboard/shared/win32/gyp_configuration.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-"""Starboard win32 shared platform configuration for gyp_cobalt."""
-
-import logging
-import os
-import re
-import subprocess
-import sys
-
-import config.base
-import starboard.shared.win32.sdk_configuration as sdk_configuration
-from starboard.tools.paths import STARBOARD_ROOT
-from starboard.tools.testing import test_filter
-
-
-def GetWindowsVersion():
- out = subprocess.check_output('ver', universal_newlines=True, shell=True)
- lines = [l for l in out.split('\n') if l]
- for l in lines:
- m = re.search(r'Version\s([0-9\.]+)', out)
- if m and m.group(1):
- major, minor, build = m.group(1).split('.')
- return (int(major), int(minor), int(build))
- raise IOError('Could not retrieve windows version')
-
-
-def _QuotePath(path):
- return '"' + path + '"'
-
-
-class Win32Configuration(config.base.PlatformConfigBase):
- """Starboard Microsoft Windows platform configuration."""
-
- def __init__(self, platform):
- super(Win32Configuration, self).__init__(platform)
- self.sdk = sdk_configuration.SdkConfiguration()
-
- def GetVariables(self, configuration):
- sdk = self.sdk
- variables = super(Win32Configuration, self).GetVariables(configuration)
- variables.update({
- 'visual_studio_install_path': sdk.vs_install_dir_with_version,
- 'windows_sdk_path': sdk.windows_sdk_path,
- 'windows_sdk_version': sdk.required_sdk_version,
- })
- return variables
-
- def GetEnvironmentVariables(self):
- sdk = self.sdk
- cl = _QuotePath(os.path.join(sdk.vs_host_tools_path, 'cl.exe'))
- lib = _QuotePath(os.path.join(sdk.vs_host_tools_path, 'lib.exe'))
- link = _QuotePath(os.path.join(sdk.vs_host_tools_path, 'link.exe'))
- rc = _QuotePath(os.path.join(sdk.windows_sdk_host_tools, 'rc.exe'))
- env_variables = {
- 'AR': lib,
- 'AR_HOST': lib,
- 'CC': cl,
- 'CXX': cl,
- 'LD': link,
- 'RC': rc,
- 'VS_INSTALL_DIR': sdk.vs_install_dir,
- 'CC_HOST': cl,
- 'CXX_HOST': cl,
- 'LD_HOST': link,
- 'ARFLAGS_HOST': 'rcs',
- 'ARTHINFLAGS_HOST': 'rcsT',
- }
- return env_variables
-
- def GetBuildFormat(self):
- """Returns the desired build format."""
- # The comma means that ninja, msvs_makefile, will be chained and use the
- # same input information so that .gyp files will only have to be parsed
- # once.
- return 'ninja,msvs_makefile'
-
- def GetGeneratorVariables(self, configuration):
- """Returns a dict of generator variables for the given configuration."""
- _ = configuration
- generator_variables = {
- 'msvs_version': 2017,
- 'msvs_platform': 'x64',
- 'msvs_template_prefix': 'win/',
- 'msvs_deploy_dir': '',
- 'qtcreator_session_name_prefix': 'cobalt',
- }
- return generator_variables
-
- def GetToolchain(self):
- sys.path.append(os.path.join(STARBOARD_ROOT, 'shared', 'msvc', 'uwp'))
- from msvc_toolchain import MSVCUWPToolchain # pylint: disable=g-import-not-at-top,g-bad-import-order
- return MSVCUWPToolchain()
-
- def IsWin10orHigher(self):
- try:
- # Both Win10 and Win2016-Server will return 10.0+
- major, _, _ = GetWindowsVersion()
- return major >= 10
- except Exception as e:
- print 'Error while getting version for windows: ' + str(e)
- return False
-
- def GetTestFilters(self):
- """Gets all tests to be excluded from a unit test run.
-
- Returns:
- A list of initialized TestFilter objects.
- """
-
- if not self.IsWin10orHigher():
- logging.error('Tests can only be executed on Win10 and higher.')
- return [test_filter.DISABLE_TESTING]
- else:
- filters = super(Win32Configuration, self).GetTestFilters()
- for target, tests in self._FILTERED_TESTS.iteritems():
- filters.extend(test_filter.TestFilter(target, test) for test in tests)
- return filters
-
- _FILTERED_TESTS = {
- 'bindings_test': [
- 'EvaluateScriptTest.ThreeArguments',
- 'GarbageCollectionTest.*',
- ],
- 'nplb': [test_filter.FILTER_ALL],
- 'nplb_blitter_pixel_tests': [test_filter.FILTER_ALL],
- 'poem_unittests': [test_filter.FILTER_ALL],
- 'starboard_platform_tests': [test_filter.FILTER_ALL],
- 'webdriver_test': [test_filter.FILTER_ALL],
-
- # The Windows platform uses D3D9 which doesn't let you create a D3D
- # device without a display, causing these unit tests to erroneously
- # fail on the buildbots, so they are disabled for Windows only.
- 'layout_tests': [test_filter.FILTER_ALL],
- 'renderer_test': [test_filter.FILTER_ALL],
-
- # TODO: enable player filter tests.
- 'player_filter_tests': [test_filter.FILTER_ALL],
-
- # No network on Windows, yet.
- 'web_platform_tests': [test_filter.FILTER_ALL],
- 'net_unittests': [test_filter.FILTER_ALL],
- }
diff --git a/src/starboard/shared/win32/log.cc b/src/starboard/shared/win32/log.cc
deleted file mode 100644
index 7e1cb85..0000000
--- a/src/starboard/shared/win32/log.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-void SbLog(SbLogPriority /*priority*/, const char* message) {
- SbLogRaw(message);
-}
diff --git a/src/starboard/shared/win32/log_file_impl.cc b/src/starboard/shared/win32/log_file_impl.cc
deleted file mode 100644
index 0cfb95d..0000000
--- a/src/starboard/shared/win32/log_file_impl.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/log_file_impl.h"
-
-#include <string>
-
-#include "starboard/file.h"
-#include "starboard/memory.h"
-#include "starboard/mutex.h"
-#include "starboard/once.h"
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/string.h"
-
-namespace {
-
-SbMutex log_mutex = SB_MUTEX_INITIALIZER;
-SbFile log_file = kSbFileInvalid;
-
-// SbMutex is not reentrant, so factor out close log file functionality for use
-// by other functions.
-void CloseLogFileWithoutLock() {
- if (SbFileIsValid(log_file)) {
- SbFileFlush(log_file);
- SbFileClose(log_file);
- log_file = kSbFileInvalid;
- }
-}
-
-} // namespace
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-void CloseLogFile() {
- SbMutexAcquire(&log_mutex);
- CloseLogFileWithoutLock();
- SbMutexRelease(&log_mutex);
-}
-
-void OpenLogInCacheDirectory(const char* log_file_name, int creation_flags) {
- SB_DCHECK((creation_flags & kSbFileOpenAlways) ||
- (creation_flags & kSbFileCreateAlways));
- SB_DCHECK(SbStringGetLength(log_file_name) != 0);
- SB_DCHECK(SbStringFindCharacter(log_file_name, SB_FILE_SEP_CHAR) == nullptr);
- char out_path[SB_FILE_MAX_PATH + 1];
- out_path[0] = '\0';
-
- if (!SbSystemGetPath(kSbSystemPathCacheDirectory, out_path,
- SB_ARRAY_SIZE_INT(out_path))) {
- return;
- }
-
- const int path_size = SB_ARRAY_SIZE_INT(out_path);
- if (SbStringConcat(out_path, SB_FILE_SEP_STRING, path_size) >= path_size) {
- return;
- }
- if (SbStringConcat(out_path, log_file_name, path_size) >= path_size) {
- return;
- }
-
- OpenLogFile(out_path, creation_flags);
-}
-
-void OpenLogFile(const char* path, const int creation_flags) {
- SB_DCHECK((creation_flags & kSbFileOpenAlways) ||
- (creation_flags & kSbFileCreateAlways));
- SB_DLOG(INFO) << "Logging to [" << path << "]";
-
- int flags = creation_flags | kSbFileWrite;
-
- SbMutexAcquire(&log_mutex);
- CloseLogFileWithoutLock();
- if ((path != nullptr) && (path[0] != '\0')) {
- log_file = SbFileOpen(path, flags, nullptr, nullptr);
- SB_DCHECK(SbFileIsValid(log_file));
- }
-
- SbMutexRelease(&log_mutex);
-}
-
-void WriteToLogFile(const char* text, const int text_length) {
- if (text_length <= 0) {
- return;
- }
- SbMutexAcquire(&log_mutex);
- if (!SbFileIsValid(log_file)) {
- SbMutexRelease(&log_mutex);
- return;
- }
-
- int bytes_written = SbFileWriteAll(log_file, text, text_length);
- SB_DCHECK(text_length == bytes_written);
-
- SbFileFlush(log_file);
- SbMutexRelease(&log_mutex);
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/log_file_impl.h b/src/starboard/shared/win32/log_file_impl.h
deleted file mode 100644
index 2ed28b9..0000000
--- a/src/starboard/shared/win32/log_file_impl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// This header provides a mechanism for multiple Android logging
-// formats to share a single log file handle.
-
-#ifndef STARBOARD_SHARED_WIN32_LOG_FILE_IMPL_H_
-#define STARBOARD_SHARED_WIN32_LOG_FILE_IMPL_H_
-
-#include "starboard/file.h"
-#include "starboard/mutex.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// Closes the log file.
-void CloseLogFile();
-
-// Opens a file in |kSbSystemPathCacheDirectory| directory.
-// |log_file_name|: C-style string of a filename
-// |creation_flags|: Must be kSbFileCreateAlways (which will truncate the file)
-// |kSbFileOpenAlways|, which can be used to append to the file.
-void OpenLogInCacheDirectory(const char* log_file_name, int creation_flags);
-
-// Opens a file at |log_file_path| with |creation_flags|.
-// |log_file_name|: C-style string of a filename
-// |creation_flags|: Must be kSbFileCreateAlways (which will truncate the file)
-// |kSbFileOpenAlways|, which can be used to append to the file.
-void OpenLogFile(const char* log_file_path, int creation_flags);
-
-// Writes |text_length| bytes starting from |text| to the current log file.
-void WriteToLogFile(const char* text, const int text_length);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_LOG_FILE_IMPL_H_
diff --git a/src/starboard/shared/win32/log_flush.cc b/src/starboard/shared/win32/log_flush.cc
deleted file mode 100644
index a75af43..0000000
--- a/src/starboard/shared/win32/log_flush.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-#include <stdio.h>
-
-void SbLogFlush() {
- fflush(stderr);
-}
diff --git a/src/starboard/shared/win32/log_format.cc b/src/starboard/shared/win32/log_format.cc
deleted file mode 100644
index ef5430b..0000000
--- a/src/starboard/shared/win32/log_format.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-void SbLogFormat(const char* format, va_list arguments) {
- SbLogRawFormat(format, arguments);
-}
diff --git a/src/starboard/shared/win32/log_is_tty.cc b/src/starboard/shared/win32/log_is_tty.cc
deleted file mode 100644
index 40dbc77..0000000
--- a/src/starboard/shared/win32/log_is_tty.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-bool SbLogIsTty() {
- return false;
-}
diff --git a/src/starboard/shared/win32/log_raw.cc b/src/starboard/shared/win32/log_raw.cc
deleted file mode 100644
index 4aeed07..0000000
--- a/src/starboard/shared/win32/log_raw.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-#include <stdio.h>
-#include <windows.h>
-
-#include "starboard/shared/starboard/net_log.h"
-#include "starboard/shared/win32/log_file_impl.h"
-#include "starboard/string.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-void SbLogRaw(const char* message) {
- fprintf(stderr, "%s", message);
- OutputDebugStringA(message);
- sbwin32::WriteToLogFile(
- message, static_cast<int>(SbStringGetLength(message)));
-
- starboard::shared::starboard::NetLogWrite(message);
-}
diff --git a/src/starboard/shared/win32/log_raw_dump_stack.cc b/src/starboard/shared/win32/log_raw_dump_stack.cc
deleted file mode 100644
index 142a056..0000000
--- a/src/starboard/shared/win32/log_raw_dump_stack.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-void SbLogRawDumpStack(int /*frames_to_skip*/) {
- SbLogRaw("TODO stack dump not implemented\n");
-}
diff --git a/src/starboard/shared/win32/log_raw_format.cc b/src/starboard/shared/win32/log_raw_format.cc
deleted file mode 100644
index 3eb4207..0000000
--- a/src/starboard/shared/win32/log_raw_format.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-
-#include <stdio.h>
-#include <windows.h>
-
-#include "starboard/shared/win32/log_file_impl.h"
-
-static const int kMaxLogLineChars = 16 * 1024;
-
-namespace sbwin32 = starboard::shared::win32;
-
-void SbLogRawFormat(const char* format, va_list arguments) {
- char log_buffer[kMaxLogLineChars] = {0};
- int result = vsprintf_s(log_buffer, kMaxLogLineChars-1, format, arguments);
- if (result > 0) {
- SbLogRaw(log_buffer);
- } else {
- SbLogRaw("[log line too long]");
- }
-}
diff --git a/src/starboard/shared/win32/media_common.cc b/src/starboard/shared/win32/media_common.cc
deleted file mode 100644
index a0e6938..0000000
--- a/src/starboard/shared/win32/media_common.cc
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/media_common.h"
-
-#include <Mfapi.h>
-#include <Mferror.h>
-#include <Mfidl.h>
-#include <Mfobjects.h>
-#include <Rpc.h>
-#include <comutil.h>
-#include <wrl\client.h> // For ComPtr.
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/configuration.h"
-#include "starboard/log.h"
-#include "starboard/media.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/starboard/player/filter/player_components.h"
-#include "starboard/shared/starboard/player/input_buffer_internal.h"
-#include "starboard/string.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// Converts microseconds to 10Mhz (100ns time).
-int64_t ConvertToWin32Time(SbTime input) {
- int64_t out = input;
- out *= 10;
- return out;
-}
-
-// Convert the other way around.
-SbTime ConvertToSbTime(int64_t input) {
- SbTime out = input;
- out /= 10;
- return out;
-}
-
-std::vector<ComPtr<IMFMediaType>> GetAllOutputMediaTypes(
- int stream_id,
- IMFTransform* decoder) {
- std::vector<ComPtr<IMFMediaType>> output;
- for (int index = 0;; ++index) {
- ComPtr<IMFMediaType> media_type;
- HRESULT hr = decoder->GetOutputAvailableType(stream_id, index, &media_type);
- if (SUCCEEDED(hr)) {
- output.push_back(media_type);
- } else {
- SB_DCHECK(hr == MF_E_NO_MORE_TYPES);
- break;
- }
- }
- return output;
-}
-
-std::vector<ComPtr<IMFMediaType>> GetAllInputMediaTypes(
- int stream_id,
- IMFTransform* transform) {
- std::vector<ComPtr<IMFMediaType>> input_types;
-
- for (DWORD i = 0;; ++i) {
- ComPtr<IMFMediaType> curr_type;
- HRESULT hr = transform->GetInputAvailableType(stream_id, i,
- curr_type.GetAddressOf());
- if (FAILED(hr)) {
- break;
- }
- input_types.push_back(curr_type);
- }
- return input_types;
-}
-
-std::vector<ComPtr<IMFMediaType>> FilterMediaBySubType(
- const std::vector<ComPtr<IMFMediaType>>& input,
- GUID sub_type_filter) {
- std::vector<ComPtr<IMFMediaType>> output;
- for (auto it = input.begin(); it != input.end(); ++it) {
- ComPtr<IMFMediaType> media_type = *it;
- GUID media_sub_type = {0};
- media_type->GetGUID(MF_MT_SUBTYPE, &media_sub_type);
- if (IsEqualGUID(media_sub_type, sub_type_filter)) {
- output.push_back(media_type);
- }
- }
- return output;
-}
-
-HRESULT CreateDecoderTransform(const GUID& decoder_guid,
- ComPtr<IMFTransform>* transform) {
- return CoCreateInstance(decoder_guid, NULL, CLSCTX_INPROC_SERVER,
- IID_PPV_ARGS(transform->GetAddressOf()));
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/media_common.h b/src/starboard/shared/win32/media_common.h
deleted file mode 100644
index fb3f677..0000000
--- a/src/starboard/shared/win32/media_common.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_MEDIA_COMMON_H_
-#define STARBOARD_SHARED_WIN32_MEDIA_COMMON_H_
-
-#include <D3D11.h>
-#include <Mfapi.h>
-#include <Mferror.h>
-#include <Mfidl.h>
-#include <Mfobjects.h>
-#include <Rpc.h>
-#include <comutil.h>
-#include <wrl\client.h> // For ComPtr.
-#include <vector>
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/configuration.h"
-#include "starboard/log.h"
-#include "starboard/media.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/starboard/player/filter/player_components.h"
-#include "starboard/shared/starboard/player/filter/video_frame_internal.h"
-#include "starboard/shared/starboard/player/input_buffer_internal.h"
-#include "starboard/string.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-using DecodedAudio = ::starboard::shared::starboard::player::DecodedAudio;
-using DecodedAudioPtr = ::starboard::scoped_refptr<DecodedAudio>;
-using InputBuffer = ::starboard::shared::starboard::player::InputBuffer;
-using PlayerComponents =
- ::starboard::shared::starboard::player::filter::PlayerComponents;
-using Status =
- ::starboard::shared::starboard::player::filter::VideoDecoder::Status;
-using VideoFrame = ::starboard::shared::starboard::player::filter::VideoFrame;
-using VideoFramePtr = ::starboard::scoped_refptr<VideoFrame>;
-using Microsoft::WRL::ComPtr;
-
-// Converts microseconds to 10Mhz (100ns time).
-int64_t ConvertToWin32Time(SbTime input);
-
-// Convert the other way around.
-SbTime ConvertToSbTime(int64_t input);
-
-std::vector<ComPtr<IMFMediaType>> GetAllOutputMediaTypes(int stream_id,
- IMFTransform* decoder);
-std::vector<ComPtr<IMFMediaType>> GetAllInputMediaTypes(
- int stream_id,
- IMFTransform* transform);
-
-std::vector<ComPtr<IMFMediaType>> FilterMediaBySubType(
- const std::vector<ComPtr<IMFMediaType>>& input,
- GUID sub_type_filter);
-
-HRESULT CreateDecoderTransform(const GUID& decoder_guid,
- ComPtr<IMFTransform>* transform);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_MEDIA_COMMON_H_
diff --git a/src/starboard/shared/win32/media_foundation_utils.cc b/src/starboard/shared/win32/media_foundation_utils.cc
deleted file mode 100644
index 08e9f03..0000000
--- a/src/starboard/shared/win32/media_foundation_utils.cc
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/media_foundation_utils.h"
-
-#include <Mfapi.h>
-#include <Mferror.h>
-#include <propvarutil.h>
-
-#include <ios>
-#include <sstream>
-#include <utility>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/error_utils.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-using ::starboard::shared::win32::CheckResult;
-
-namespace {
-
-#define MAKE_GUID_PAIR(X) std::pair<GUID, std::string>(X, #X)
-
-const std::pair<GUID, std::string> kMfMtAudio[] = {
- MAKE_GUID_PAIR(MF_MT_AAC_PAYLOAD_TYPE),
- MAKE_GUID_PAIR(MF_MT_AUDIO_AVG_BYTES_PER_SECOND),
- MAKE_GUID_PAIR(MF_MT_AUDIO_BITS_PER_SAMPLE),
- MAKE_GUID_PAIR(MF_MT_AUDIO_BLOCK_ALIGNMENT),
- MAKE_GUID_PAIR(MF_MT_AUDIO_CHANNEL_MASK),
- MAKE_GUID_PAIR(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND),
- MAKE_GUID_PAIR(MF_MT_AUDIO_NUM_CHANNELS),
- MAKE_GUID_PAIR(MF_MT_AUDIO_SAMPLES_PER_BLOCK),
- MAKE_GUID_PAIR(MF_MT_AUDIO_SAMPLES_PER_SECOND),
- MAKE_GUID_PAIR(MF_MT_AUDIO_NUM_CHANNELS),
- MAKE_GUID_PAIR(MF_MT_MAJOR_TYPE),
- MAKE_GUID_PAIR(MF_MT_AUDIO_PREFER_WAVEFORMATEX),
- MAKE_GUID_PAIR(MF_MT_USER_DATA),
- MAKE_GUID_PAIR(MF_MT_SUBTYPE),
- MAKE_GUID_PAIR(MFAudioFormat_AAC),
- MAKE_GUID_PAIR(MFAudioFormat_ADTS),
- MAKE_GUID_PAIR(MFAudioFormat_ALAC),
- MAKE_GUID_PAIR(MFAudioFormat_AMR_NB),
- MAKE_GUID_PAIR(MFAudioFormat_AMR_WB),
- MAKE_GUID_PAIR(MFAudioFormat_AMR_WP),
- MAKE_GUID_PAIR(MFAudioFormat_Dolby_AC3),
- MAKE_GUID_PAIR(MFAudioFormat_Dolby_AC3_SPDIF),
- MAKE_GUID_PAIR(MFAudioFormat_Dolby_DDPlus),
- MAKE_GUID_PAIR(MFAudioFormat_DRM),
- MAKE_GUID_PAIR(MFAudioFormat_DTS),
- MAKE_GUID_PAIR(MFAudioFormat_FLAC),
- MAKE_GUID_PAIR(MFAudioFormat_Float),
- MAKE_GUID_PAIR(MFAudioFormat_Float_SpatialObjects),
- MAKE_GUID_PAIR(MFAudioFormat_MP3),
- MAKE_GUID_PAIR(MFAudioFormat_MPEG),
- MAKE_GUID_PAIR(MFAudioFormat_MSP1),
- MAKE_GUID_PAIR(MFAudioFormat_Opus),
- MAKE_GUID_PAIR(MFAudioFormat_PCM),
- MAKE_GUID_PAIR(MFAudioFormat_WMASPDIF),
- MAKE_GUID_PAIR(MFAudioFormat_WMAudio_Lossless),
- MAKE_GUID_PAIR(MFAudioFormat_WMAudioV8),
- MAKE_GUID_PAIR(MFAudioFormat_WMAudioV9),
- MAKE_GUID_PAIR(MFAudioFormat_WMAudioV9),
- MAKE_GUID_PAIR(MFMediaType_Audio),
-};
-#undef MAKE_GUID_PAIR
-
-std::string GuidToFallbackString(GUID guid) {
- std::stringstream ss;
- wchar_t* guid_str = nullptr;
- StringFromCLSID(guid, &guid_str);
- ss << guid_str;
- CoTaskMemFree(guid_str);
- return ss.str();
-}
-
-std::string MfGuidToString(GUID guid) {
- const size_t n = sizeof(kMfMtAudio) / sizeof(*kMfMtAudio);
- for (auto i = 0; i < n; ++i) {
- const auto& elems = kMfMtAudio[i];
- if (guid == elems.first) {
- return elems.second;
- }
- }
- return GuidToFallbackString(guid);
-}
-
-std::string ImfAttributesToString(IMFAttributes* type) {
- std::stringstream ss;
- UINT32 n = 0;
- HRESULT hr = type->GetCount(&n);
- CheckResult(hr);
- for (UINT32 i = 0; i < n; ++i) {
- GUID key;
- PROPVARIANT val;
- type->GetItemByIndex(i, &key, &val);
-
- MF_ATTRIBUTE_TYPE attrib_type;
- hr = type->GetItemType(key, &attrib_type);
- CheckResult(hr);
-
- std::string key_str = MfGuidToString(key);
- ss << key_str << ": ";
-
- switch (attrib_type) {
- case MF_ATTRIBUTE_GUID: {
- GUID value_guid;
- hr = type->GetGUID(key, &value_guid);
- ss << MfGuidToString(value_guid) << "\n";
- break;
- }
-
- case MF_ATTRIBUTE_DOUBLE: {
- double value = 0;
- hr = type->GetDouble(key, &value);
- ss << value << "\n";
- break;
- }
-
- case MF_ATTRIBUTE_BLOB: {
- // Skip.
- ss << "<BLOB>" << "\n";
- break;
- }
-
- case MF_ATTRIBUTE_UINT32: {
- UINT32 int_val = 0;
- hr = type->GetUINT32(key, &int_val);
- ss << int_val << "\n";
- break;
- }
-
- case MF_ATTRIBUTE_UINT64: {
- UINT64 int_val = 0;
- hr = type->GetUINT64(key, &int_val);
- ss << int_val << "\n";
- break;
- }
-
- case MF_ATTRIBUTE_STRING: {
- wchar_t buff[128];
- UINT buff_size = sizeof(buff);
- hr = type->GetString(key, buff, buff_size, NULL);
- CheckResult(hr);
- ss << buff << "\n";
- break;
- }
- default: {
- SB_NOTIMPLEMENTED();
- break;
- }
- }
- }
- ss << "\n";
- return ss.str();
-}
-
-} // namespace.
-
-void CopyUint32Property(GUID key, const IMFMediaType* source,
- IMFMediaType* destination) {
- UINT32 val = 0;
- const_cast<IMFMediaType*>(source)->GetUINT32(key, &val);
- HRESULT hr = destination->SetUINT32(key, val);
- CheckResult(hr);
-}
-
-std::ostream& operator<<(std::ostream& os, const IMFMediaType& media_type) {
- const IMFAttributes* attribs = &media_type; // Upcast.
- std::string output_str =
- ImfAttributesToString(const_cast<IMFAttributes*>(attribs));
- os << output_str;
- return os;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/media_foundation_utils.h b/src/starboard/shared/win32/media_foundation_utils.h
deleted file mode 100644
index 088cc5c..0000000
--- a/src/starboard/shared/win32/media_foundation_utils.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 Google Inc. 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 <wrl/client.h>
-#include <Mfobjects.h>
-
-#include <string>
-
-#ifndef STARBOARD_SHARED_WIN32_MEDIA_FOUNDATION_UTILS_H_
-#define STARBOARD_SHARED_WIN32_MEDIA_FOUNDATION_UTILS_H_
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-std::ostream& operator<<(std::ostream& os, const IMFMediaType& media_type);
-
-std::string ToString(IMFAttributes* type);
-
-void CopyUint32Property(GUID key, const IMFMediaType* source,
- IMFMediaType* destination);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_MEDIA_FOUNDATION_UTILS_H_
diff --git a/src/starboard/shared/win32/media_is_audio_supported.cc b/src/starboard/shared/win32/media_is_audio_supported.cc
deleted file mode 100644
index 1c6936d..0000000
--- a/src/starboard/shared/win32/media_is_audio_supported.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/starboard/media/media_support_internal.h"
-
-#include "starboard/configuration.h"
-#include "starboard/media.h"
-
-SB_EXPORT bool SbMediaIsAudioSupported(SbMediaAudioCodec audio_codec,
- int64_t bitrate) {
- // TODO: Add Opus.
- if (audio_codec != kSbMediaAudioCodecAac) {
- return false;
- }
- return bitrate <= SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND;
-}
diff --git a/src/starboard/shared/win32/media_is_output_protected.cc b/src/starboard/shared/win32/media_is_output_protected.cc
deleted file mode 100644
index 7eb3533..0000000
--- a/src/starboard/shared/win32/media_is_output_protected.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/media.h"
-
-bool SbMediaIsOutputProtected() {
- SB_NOTIMPLEMENTED();
- return false;
-}
diff --git a/src/starboard/shared/win32/media_is_supported.cc b/src/starboard/shared/win32/media_is_supported.cc
deleted file mode 100644
index 127f625..0000000
--- a/src/starboard/shared/win32/media_is_supported.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-#include "starboard/media.h"
-#include "starboard/string.h"
-
-// TODO: Fill this in for DRM.
-SB_EXPORT bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
- SbMediaAudioCodec audio_codec,
- const char* key_system) {
- SB_UNREFERENCED_PARAMETER(video_codec);
- SB_UNREFERENCED_PARAMETER(audio_codec);
- SB_UNREFERENCED_PARAMETER(key_system);
- return 0 == SbStringCompareAll(key_system, "com.youtube.playready");
-}
diff --git a/src/starboard/shared/win32/media_is_video_supported.cc b/src/starboard/shared/win32/media_is_video_supported.cc
deleted file mode 100644
index 7e1ca35..0000000
--- a/src/starboard/shared/win32/media_is_video_supported.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/starboard/media/media_support_internal.h"
-
-#include <d3d11.h>
-#include <mfapi.h>
-#include <mfidl.h>
-#include <wrl/client.h>
-
-namespace {
-
-#if SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
-// Cache the VP9 support status since the check may be expensive.
-enum Vp9Support {
- kVp9SupportUnknown,
- kVp9SupportYes,
- kVp9SupportNo
-};
-Vp9Support s_vp9_support = kVp9SupportUnknown;
-
-// Check for VP9 support. Since this is used by a starboard function, it
-// cannot depend on other modules (e.g. ANGLE).
-bool IsVp9Supported() {
- if (s_vp9_support == kVp9SupportUnknown) {
- // Try initializing the VP9 decoder to determine if it is supported.
- HRESULT hr;
-
- Microsoft::WRL::ComPtr<ID3D11Device> d3d_device;
- hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0,
- nullptr, 0, D3D11_SDK_VERSION,
- d3d_device.GetAddressOf(), nullptr, nullptr);
-
- UINT reset_token = 0;
- Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> device_manager;
- if (SUCCEEDED(hr)) {
- hr = MFCreateDXGIDeviceManager(&reset_token,
- device_manager.GetAddressOf());
- }
- if (SUCCEEDED(hr)) {
- hr = device_manager->ResetDevice(d3d_device.Get(), reset_token);
- }
-
- Microsoft::WRL::ComPtr<IMFTransform> transform;
- if (SUCCEEDED(hr)) {
- hr = CoCreateInstance(CLSID_MSVPxDecoder, nullptr, CLSCTX_INPROC_SERVER,
- IID_PPV_ARGS(transform.GetAddressOf()));
- }
-
- if (SUCCEEDED(hr)) {
- hr = transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
- ULONG_PTR(device_manager.Get()));
- }
-
- s_vp9_support = SUCCEEDED(hr) ? kVp9SupportYes : kVp9SupportNo;
- }
- return s_vp9_support == kVp9SupportYes;
-}
-#else // SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
-bool IsVp9Supported() {
- return false;
-}
-#endif
-
-} // namespace
-
-SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
- int frame_width,
- int frame_height,
- int64_t bitrate,
- int fps) {
- int max_width = 1920;
- int max_height = 1080;
-
- if (video_codec == kSbMediaVideoCodecVp9) {
- // Vp9 supports 8k only in whitelisted platforms, up to 4k in the others.
-#ifdef ENABLE_VP9_8K_SUPPORT
- max_width = 7680;
- max_height = 4320;
-#else // ENABLE_VP9_8K_SUPPORT
- max_width = 3840;
- max_height = 2160;
-#endif // ENABLE_VP9_8K_SUPPORT
- } else if (video_codec == kSbMediaVideoCodecH264) {
- // Not all devices can support 4k H264; some (e.g. xb1) may crash in
- // the decoder if provided too high of a resolution. Therefore
- // platforms must explicitly opt-in to support 4k H264.
-#ifdef ENABLE_H264_4K_SUPPORT
- max_width = 3840;
- max_height = 2160;
-#endif // ENABLE_H264_4K_SUPPORT
- }
-
- if (frame_width > max_width || frame_height > max_height) {
- return false;
- }
-
- // Is bitrate in range?
- if (bitrate > SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND) {
- return false;
- }
- if (fps > 60) {
- return false;
- }
- if (video_codec == kSbMediaVideoCodecH264) {
- return true;
- }
- if (video_codec == kSbMediaVideoCodecVp9) {
- return IsVp9Supported();
- }
- return false;
-}
diff --git a/src/starboard/shared/win32/media_transform.cc b/src/starboard/shared/win32/media_transform.cc
deleted file mode 100644
index 0465a9d..0000000
--- a/src/starboard/shared/win32/media_transform.cc
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/media_transform.h"
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/log.h"
-#include "starboard/shared/win32/audio_transform.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/media_common.h"
-#include "starboard/shared/win32/media_foundation_utils.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-using Microsoft::WRL::ComPtr;
-using ::starboard::shared::starboard::ThreadChecker;
-
-namespace {
-
-template <typename T>
-void ReleaseIfNotNull(T** ptr) {
- if (*ptr) {
- (*ptr)->Release();
- *ptr = NULL;
- }
-}
-
-} // namespace
-
-MediaTransform::MediaTransform(CLSID clsid)
- : thread_checker_(ThreadChecker::kSetThreadIdOnFirstCheck),
- state_(kCanAcceptInput),
- stream_begun_(false),
- discontinuity_(true),
- throttle_inputs_(false) {
- HRESULT hr = CreateDecoderTransform(clsid, &transform_);
- CheckResult(hr);
-}
-
-MediaTransform::MediaTransform(
- const Microsoft::WRL::ComPtr<IMFTransform>& transform)
- : transform_(transform),
- thread_checker_(ThreadChecker::kSetThreadIdOnFirstCheck),
- state_(kCanAcceptInput),
- stream_begun_(false),
- discontinuity_(true),
- throttle_inputs_(false) {
- SB_DCHECK(transform_);
-}
-
-bool MediaTransform::TryWrite(const ComPtr<IMFSample>& input) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- if (state_ != kCanAcceptInput) {
- return false;
- }
-
- if (!stream_begun_) {
- SendMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING);
- SendMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM);
- stream_begun_ = true;
- }
-
- HRESULT hr = transform_->ProcessInput(kStreamId, input.Get(), 0);
-
- if (SUCCEEDED(hr)) {
- // Some transforms do not return MF_E_NOTACCEPTING. To avoid flooding
- // these transforms, input is only allowed when ProcessOutput returns
- // MF_E_TRANSFORM_NEED_MORE_INPUT.
- if (throttle_inputs_) {
- state_ = kCanProvideOutput;
- }
- return true;
- }
- if (hr == MF_E_NOTACCEPTING) {
- state_ = kCanProvideOutput;
- return false;
- }
- SB_NOTREACHED() << "Unexpected return value " << hr;
- return false;
-}
-
-ComPtr<IMFSample> MediaTransform::TryRead(ComPtr<IMFMediaType>* new_type) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(new_type);
-
- if (state_ == kDrained) {
- return NULL;
- }
-
- ComPtr<IMFSample> sample;
- HRESULT hr = ProcessOutput(&sample);
-
- if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
- hr = transform_->GetOutputAvailableType(kStreamId,
- 0, // TypeIndex
- new_type->GetAddressOf());
- CheckResult(hr);
- SetOutputType(*new_type);
-
- hr = ProcessOutput(&sample);
- }
-
- if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
- if (state_ == kCanProvideOutput) {
- state_ = kCanAcceptInput;
- }
- if (state_ == kDraining) {
- state_ = kDrained;
- }
- return NULL;
- }
-
- if (FAILED(hr)) {
- // Sometimes the decryptor refuse to emit output after shutting down.
- SB_DCHECK(hr == MF_E_INVALIDREQUEST);
- if (state_ == kDraining) {
- state_ = kDrained;
- }
- return NULL;
- }
-
- SB_DCHECK(sample);
-
- if (discontinuity_) {
- sample->SetUINT32(MFSampleExtension_Discontinuity, TRUE);
- discontinuity_ = false;
- }
-
- return sample;
-}
-
-void MediaTransform::Drain() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(state_ != kDraining && state_ != kDrained);
-
- if (state_ == kDraining || state_ == kDrained) {
- return;
- }
-
- if (!stream_begun_) {
- state_ = kDrained;
- return;
- }
-
- // The VP9 codec may crash when MFT_MESSAGE_NOTIFY_END_OF_STREAM is processed
- // at the same time an IMFSample is released. Per documentation, the client
- // is not required to send this message for IMFTransforms. Avoid the possible
- // race condition by not sending this message.
- // SendMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM);
- SendMessage(MFT_MESSAGE_COMMAND_DRAIN);
- state_ = kDraining;
-}
-
-bool MediaTransform::draining() const {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- return state_ == kDraining;
-}
-
-bool MediaTransform::drained() const {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- return state_ == kDrained;
-}
-
-void MediaTransform::ResetFromDrained() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(drained());
-
- state_ = kCanAcceptInput;
- stream_begun_ = false;
- discontinuity_ = true;
-}
-
-ComPtr<IMFMediaType> MediaTransform::GetCurrentInputType() {
- ComPtr<IMFMediaType> type;
- HRESULT hr = transform_->GetInputCurrentType(kStreamId, type.GetAddressOf());
- CheckResult(hr);
- return type;
-}
-
-void MediaTransform::SetInputType(const ComPtr<IMFMediaType>& input_type) {
- HRESULT hr = transform_->SetInputType(0, input_type.Get(), 0);
- CheckResult(hr);
-}
-
-std::vector<ComPtr<IMFMediaType>> MediaTransform::GetAvailableInputTypes() {
- return GetAllInputMediaTypes(kStreamId, transform_.Get());
-}
-
-ComPtr<IMFMediaType> MediaTransform::GetCurrentOutputType() {
- ComPtr<IMFMediaType> type;
- HRESULT hr = transform_->GetOutputCurrentType(kStreamId, type.GetAddressOf());
- CheckResult(hr);
- return type;
-}
-
-void MediaTransform::SetOutputType(const ComPtr<IMFMediaType>& output_type) {
- HRESULT hr = transform_->SetOutputType(0, output_type.Get(), 0);
- CheckResult(hr);
-}
-
-std::vector<ComPtr<IMFMediaType>> MediaTransform::GetAvailableOutputTypes() {
- std::vector<ComPtr<IMFMediaType>> output_types;
-
- for (DWORD i = 0;; ++i) {
- ComPtr<IMFMediaType> curr_type;
- HRESULT hr = transform_->GetOutputAvailableType(kStreamId, i,
- curr_type.GetAddressOf());
- if (FAILED(hr)) {
- break;
- }
- output_types.push_back(curr_type);
- }
-
- return output_types;
-}
-
-void MediaTransform::SetOutputTypeBySubType(GUID subtype) {
- for (int index = 0;; ++index) {
- ComPtr<IMFMediaType> media_type;
- HRESULT hr =
- transform_->GetOutputAvailableType(kStreamId, index, &media_type);
- if (SUCCEEDED(hr)) {
- GUID media_sub_type = {};
- media_type->GetGUID(MF_MT_SUBTYPE, &media_sub_type);
- if (media_sub_type == subtype) {
- SetOutputType(media_type);
- return;
- }
- } else {
- SB_DCHECK(hr == MF_E_NO_MORE_TYPES);
- break;
- }
- }
- SB_NOTREACHED();
-}
-
-ComPtr<IMFAttributes> MediaTransform::GetAttributes() {
- ComPtr<IMFAttributes> attributes;
- HRESULT hr = transform_->GetAttributes(attributes.GetAddressOf());
- CheckResult(hr);
- return attributes;
-}
-
-ComPtr<IMFAttributes> MediaTransform::GetOutputStreamAttributes() {
- ComPtr<IMFAttributes> attributes;
- HRESULT hr =
- transform_->GetOutputStreamAttributes(0, attributes.GetAddressOf());
- CheckResult(hr);
- return attributes;
-}
-
-ComPtr<IMFSampleProtection> MediaTransform::GetSampleProtection() {
- ComPtr<IMFSampleProtection> sample_protection;
- HRESULT hr = transform_.As(&sample_protection);
- CheckResult(hr);
- return sample_protection;
-}
-
-void MediaTransform::GetStreamCount(DWORD* input_stream_count,
- DWORD* output_stream_count) {
- SB_DCHECK(input_stream_count);
- SB_DCHECK(output_stream_count);
- HRESULT hr =
- transform_->GetStreamCount(input_stream_count, output_stream_count);
- CheckResult(hr);
-}
-
-void MediaTransform::SendMessage(MFT_MESSAGE_TYPE msg, ULONG_PTR data /*= 0*/) {
- HRESULT hr = transform_->ProcessMessage(msg, data);
- CheckResult(hr);
-}
-
-void MediaTransform::Reset() {
- if (stream_begun_) {
- SendMessage(MFT_MESSAGE_COMMAND_FLUSH);
- }
- state_ = kCanAcceptInput;
- discontinuity_ = true;
- thread_checker_.Detach();
-}
-
-void MediaTransform::PrepareOutputDataBuffer(
- MFT_OUTPUT_DATA_BUFFER* output_data_buffer) {
- output_data_buffer->dwStreamID = kStreamId;
- output_data_buffer->pSample = NULL;
- output_data_buffer->dwStatus = 0;
- output_data_buffer->pEvents = NULL;
-
- MFT_OUTPUT_STREAM_INFO output_stream_info;
- HRESULT hr = transform_->GetOutputStreamInfo(kStreamId, &output_stream_info);
- CheckResult(hr);
-
- static const DWORD kFlagAutoAllocateMemory =
- MFT_OUTPUT_STREAM_PROVIDES_SAMPLES |
- MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES;
- if ((output_stream_info.dwFlags & kFlagAutoAllocateMemory) != 0) {
- // Try to let the IMFTransform allocate the memory if possible.
- return;
- }
-
- ComPtr<IMFSample> sample;
- hr = MFCreateSample(&sample);
- CheckResult(hr);
-
- ComPtr<IMFMediaBuffer> buffer;
- hr = MFCreateAlignedMemoryBuffer(output_stream_info.cbSize,
- output_stream_info.cbAlignment, &buffer);
- CheckResult(hr);
-
- hr = sample->AddBuffer(buffer.Get());
- CheckResult(hr);
-
- output_data_buffer->pSample = sample.Detach();
-}
-
-HRESULT MediaTransform::ProcessOutput(ComPtr<IMFSample>* sample) {
- SB_DCHECK(sample);
-
- MFT_OUTPUT_DATA_BUFFER output_data_buffer;
- PrepareOutputDataBuffer(&output_data_buffer);
-
- const DWORD kFlags = 0;
- const DWORD kNumberOfBuffers = 1;
- DWORD status = 0;
- HRESULT hr = transform_->ProcessOutput(kFlags, kNumberOfBuffers,
- &output_data_buffer, &status);
-
- SB_DCHECK(!output_data_buffer.pEvents);
-
- *sample = output_data_buffer.pSample;
- ReleaseIfNotNull(&output_data_buffer.pEvents);
- ReleaseIfNotNull(&output_data_buffer.pSample);
-
- if (output_data_buffer.dwStatus == MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) {
- hr = MF_E_TRANSFORM_NEED_MORE_INPUT;
- }
-
- return hr;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/media_transform.h b/src/starboard/shared/win32/media_transform.h
deleted file mode 100644
index ed8736e..0000000
--- a/src/starboard/shared/win32/media_transform.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_MEDIA_TRANSFORM_H_
-#define STARBOARD_SHARED_WIN32_MEDIA_TRANSFORM_H_
-
-#include <mfapi.h>
-#include <mferror.h>
-#include <mfidl.h>
-#include <wrl\client.h>
-
-#include <vector>
-
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/media.h"
-#include "starboard/shared/starboard/thread_checker.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// Wrapper class for IMFTransform with the following functionalities:
-// 1. State management:
-// It supports a one way life cycle from "kCanAcceptInput/kCanProvideOutput"
-// to "kDraining" then to "kDrained".
-// 2. Manages states like input/output types, various attributes, and sample
-// protection, etc.
-// 3. Send message to the underlying transform.
-// This simplifies the implementation of higher level decoder mechanism that
-// may deal with two IMFTransforms: one decoder and one decryptor.
-class MediaTransform {
- public:
- enum { kStreamId = 0 };
- explicit MediaTransform(CLSID clsid);
- explicit MediaTransform(
- const Microsoft::WRL::ComPtr<IMFTransform>& transform);
-
- // By default, the input throttle is disabled, and inputs can be written to
- // the transform until rejected; i.e. the transform is kept full of inputs.
- // However, some transforms may never report that they are full, so enabling
- // the input throttle will allow inputs only when the transform reports that
- // it needs more input.
- void EnableInputThrottle(bool enable) { throttle_inputs_ = enable; }
-
- bool TryWrite(const Microsoft::WRL::ComPtr<IMFSample>& input);
- Microsoft::WRL::ComPtr<IMFSample> TryRead(
- Microsoft::WRL::ComPtr<IMFMediaType>* new_type);
- void Drain();
- bool draining() const;
- bool drained() const;
- // Once the transform is drained, this function can be called to allow the
- // transform to accept new input as a newly created transform. This allows
- // the reuse of existing transform without re-negotiating all types and
- // attributes.
- void ResetFromDrained();
-
- Microsoft::WRL::ComPtr<IMFMediaType> GetCurrentInputType();
- void SetInputType(const Microsoft::WRL::ComPtr<IMFMediaType>& type);
- std::vector<Microsoft::WRL::ComPtr<IMFMediaType>> GetAvailableInputTypes();
-
- Microsoft::WRL::ComPtr<IMFMediaType> GetCurrentOutputType();
- void SetOutputType(const Microsoft::WRL::ComPtr<IMFMediaType>& type);
- std::vector<Microsoft::WRL::ComPtr<IMFMediaType>> GetAvailableOutputTypes();
- void SetOutputTypeBySubType(GUID subtype);
-
- Microsoft::WRL::ComPtr<IMFAttributes> GetAttributes();
- Microsoft::WRL::ComPtr<IMFAttributes> GetOutputStreamAttributes();
-
- Microsoft::WRL::ComPtr<IMFSampleProtection> GetSampleProtection();
- void GetStreamCount(DWORD* input_streamcount, DWORD* output_stream_count);
-
- void SendMessage(MFT_MESSAGE_TYPE msg, ULONG_PTR data = 0);
-
- // Reset the media transform to its original state.
- void Reset();
-
- private:
- enum State { kCanAcceptInput, kCanProvideOutput, kDraining, kDrained };
-
- void PrepareOutputDataBuffer(MFT_OUTPUT_DATA_BUFFER* output_data_buffer);
- HRESULT ProcessOutput(Microsoft::WRL::ComPtr<IMFSample>* sample);
-
- Microsoft::WRL::ComPtr<IMFTransform> transform_;
-
- ::starboard::shared::starboard::ThreadChecker thread_checker_;
- State state_;
- bool stream_begun_;
- bool discontinuity_;
- bool throttle_inputs_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_MEDIA_TRANSFORM_H_
diff --git a/src/starboard/shared/win32/memory_allocate_aligned_unchecked.cc b/src/starboard/shared/win32/memory_allocate_aligned_unchecked.cc
deleted file mode 100644
index 0b161fb..0000000
--- a/src/starboard/shared/win32/memory_allocate_aligned_unchecked.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <malloc.h>
-
-void* SbMemoryAllocateAlignedUnchecked(size_t alignment, size_t size) {
- return _aligned_malloc(size, alignment);
-}
diff --git a/src/starboard/shared/win32/memory_allocate_unchecked.cc b/src/starboard/shared/win32/memory_allocate_unchecked.cc
deleted file mode 100644
index c185f91..0000000
--- a/src/starboard/shared/win32/memory_allocate_unchecked.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <windows.h>
-
-void* SbMemoryAllocateUnchecked(size_t size) {
- return HeapAlloc(GetProcessHeap(), 0, size);
-}
diff --git a/src/starboard/shared/win32/memory_free.cc b/src/starboard/shared/win32/memory_free.cc
deleted file mode 100644
index 42b83fc..0000000
--- a/src/starboard/shared/win32/memory_free.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <windows.h>
-
-void SbMemoryFree(void* memory) {
- HeapFree(GetProcessHeap(), 0, memory);
-}
diff --git a/src/starboard/shared/win32/memory_free_aligned.cc b/src/starboard/shared/win32/memory_free_aligned.cc
deleted file mode 100644
index f40d0c6..0000000
--- a/src/starboard/shared/win32/memory_free_aligned.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <malloc.h>
-
-void SbMemoryFreeAligned(void* memory) {
- _aligned_free(memory);
-}
diff --git a/src/starboard/shared/win32/memory_get_stack_bounds.cc b/src/starboard/shared/win32/memory_get_stack_bounds.cc
deleted file mode 100644
index fec877e..0000000
--- a/src/starboard/shared/win32/memory_get_stack_bounds.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <windows.h>
-
-void SbMemoryGetStackBounds(void** out_high, void** out_low) {
- _NT_TIB* tib = reinterpret_cast<_NT_TIB*>(NtCurrentTeb());
- *out_high = tib->StackBase;
- *out_low = tib->StackLimit;
-}
diff --git a/src/starboard/shared/win32/memory_map.cc b/src/starboard/shared/win32/memory_map.cc
deleted file mode 100644
index 554a5ec..0000000
--- a/src/starboard/shared/win32/memory_map.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/starboard/memory_reporter_internal.h"
-
-void* SbMemoryMap(int64_t size_bytes, int flags, const char* name) {
- SB_UNREFERENCED_PARAMETER(name);
- if (size_bytes == 0) {
- return SB_MEMORY_MAP_FAILED;
- }
- ULONG protect = PAGE_NOACCESS;
- // |flags| is a bitmask of SbMemoryMapFlags, but |protect| is not a bitmask.
- switch (flags) {
- case kSbMemoryMapProtectRead:
- protect = PAGE_READONLY;
- break;
- case kSbMemoryMapProtectWrite:
- // Windows does not provide write only mode privileges
- // are escalated to read/write.
- case kSbMemoryMapProtectReadWrite:
- protect = PAGE_READWRITE;
- break;
- default:
- SB_NOTREACHED();
- }
- void* memory = VirtualAllocFromApp(NULL, size_bytes, MEM_COMMIT, protect);
- if (PAGE_READONLY != protect) {
- SbMemoryReporterReportMappedMemory(memory, size_bytes);
- }
- return memory;
-}
diff --git a/src/starboard/shared/win32/memory_reallocate_unchecked.cc b/src/starboard/shared/win32/memory_reallocate_unchecked.cc
deleted file mode 100644
index 68ee689..0000000
--- a/src/starboard/shared/win32/memory_reallocate_unchecked.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <windows.h>
-
-void* SbMemoryReallocateUnchecked(void* memory, size_t size) {
- if (memory == NULL) {
- return SbMemoryAllocateUnchecked(size);
- }
- return HeapReAlloc(GetProcessHeap(), 0, memory, size);
-}
diff --git a/src/starboard/shared/win32/memory_unmap.cc b/src/starboard/shared/win32/memory_unmap.cc
deleted file mode 100644
index eaf62fb..0000000
--- a/src/starboard/shared/win32/memory_unmap.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-
-#include <windows.h>
-
-#include "starboard/shared/starboard/memory_reporter_internal.h"
-
-bool SbMemoryUnmap(void* virtual_address, int64_t size_bytes) {
- // Note that SbMemoryUnmap documentation says that "This function can
- // unmap multiple contiguous regions that were mapped with separate calls
- // to SbMemoryMap()". Because of that, we cannot use MEM_FREE here.
- SbMemoryReporterReportUnmappedMemory(virtual_address, size_bytes);
- return VirtualFree(virtual_address, size_bytes, MEM_DECOMMIT);
-}
diff --git a/src/starboard/shared/win32/mutex_acquire.cc b/src/starboard/shared/win32/mutex_acquire.cc
deleted file mode 100644
index daca05d..0000000
--- a/src/starboard/shared/win32/mutex_acquire.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/mutex.h"
-
-#include <windows.h>
-
-SbMutexResult SbMutexAcquire(SbMutex* mutex) {
- AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(mutex));
- return kSbMutexAcquired;
-}
diff --git a/src/starboard/shared/win32/mutex_acquire_try.cc b/src/starboard/shared/win32/mutex_acquire_try.cc
deleted file mode 100644
index 0c4cee2..0000000
--- a/src/starboard/shared/win32/mutex_acquire_try.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/mutex.h"
-
-#include <windows.h>
-
-SbMutexResult SbMutexAcquireTry(SbMutex* mutex) {
- bool result = TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(mutex));
- return result ? kSbMutexAcquired : kSbMutexBusy;
-}
diff --git a/src/starboard/shared/win32/mutex_create.cc b/src/starboard/shared/win32/mutex_create.cc
deleted file mode 100644
index 5e4c1ce..0000000
--- a/src/starboard/shared/win32/mutex_create.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/mutex.h"
-
-#include <windows.h>
-
-bool SbMutexCreate(SbMutex* mutex) {
- if (!mutex) {
- return false;
- }
- InitializeSRWLock(reinterpret_cast<PSRWLOCK>(mutex));
- return true;
-}
diff --git a/src/starboard/shared/win32/mutex_destroy.cc b/src/starboard/shared/win32/mutex_destroy.cc
deleted file mode 100644
index 1028687..0000000
--- a/src/starboard/shared/win32/mutex_destroy.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/mutex.h"
-
-#include <windows.h>
-
-bool SbMutexDestroy(SbMutex* mutex) {
- if (!mutex) {
- return false;
- }
- if (SbMutexAcquireTry(mutex) == kSbMutexBusy) {
- return false;
- }
- return true;
-}
diff --git a/src/starboard/shared/win32/mutex_release.cc b/src/starboard/shared/win32/mutex_release.cc
deleted file mode 100644
index ac50090..0000000
--- a/src/starboard/shared/win32/mutex_release.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/mutex.h"
-
-#include <windows.h>
-
-bool SbMutexRelease(SbMutex* mutex) {
- ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(mutex));
- return true;
-}
diff --git a/src/starboard/shared/win32/once.cc b/src/starboard/shared/win32/once.cc
deleted file mode 100644
index 970916c..0000000
--- a/src/starboard/shared/win32/once.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/once.h"
-
-#include <windows.h>
-
-namespace {
-BOOL CALLBACK OnceTrampoline(PINIT_ONCE once_control,
- void* parameter,
- void** context) {
- SB_UNREFERENCED_PARAMETER(context);
- SB_UNREFERENCED_PARAMETER(once_control);
- static_cast<SbOnceInitRoutine>(parameter)();
- return true;
-}
-
-} // namespace
-
-bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
- if (!once_control || !init_routine) {
- return false;
- }
- return InitOnceExecuteOnce(reinterpret_cast<PINIT_ONCE>(once_control),
- OnceTrampoline, init_routine, NULL);
-}
diff --git a/src/starboard/shared/win32/player_components_impl.cc b/src/starboard/shared/win32/player_components_impl.cc
deleted file mode 100644
index e4a70b2..0000000
--- a/src/starboard/shared/win32/player_components_impl.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/starboard/player/filter/player_components.h"
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.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_render_algorithm_impl.h"
-#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
-#include "starboard/shared/win32/audio_decoder.h"
-#include "starboard/shared/win32/video_decoder.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-namespace filter {
-
-namespace {
-
-class PlayerComponentsImpl : public PlayerComponents {
- void CreateAudioComponents(
- const AudioParameters& audio_parameters,
- scoped_ptr<AudioDecoder>* audio_decoder,
- scoped_ptr<AudioRendererSink>* audio_renderer_sink) override {
- using AudioDecoderImpl = ::starboard::shared::win32::AudioDecoder;
-
- SB_DCHECK(audio_decoder);
- SB_DCHECK(audio_renderer_sink);
-
- audio_decoder->reset(new AudioDecoderImpl(audio_parameters.audio_codec,
- audio_parameters.audio_header,
- audio_parameters.drm_system));
- audio_renderer_sink->reset(new AudioRendererSinkImpl);
- }
-
- void CreateVideoComponents(
- const VideoParameters& video_parameters,
- scoped_ptr<VideoDecoder>* video_decoder,
- scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm,
- scoped_refptr<VideoRendererSink>* video_renderer_sink) override {
- using VideoDecoderImpl = ::starboard::shared::win32::VideoDecoder;
-
- SB_DCHECK(video_decoder);
- SB_DCHECK(video_render_algorithm);
- SB_DCHECK(video_renderer_sink);
-
- scoped_ptr<VideoDecoderImpl> video_decoder_impl(new VideoDecoderImpl(
- video_parameters.video_codec, video_parameters.output_mode,
- video_parameters.decode_target_graphics_context_provider,
- video_parameters.drm_system));
- *video_renderer_sink = video_decoder_impl->GetSink();
- video_decoder->reset(video_decoder_impl.release());
- video_render_algorithm->reset(new VideoRenderAlgorithmImpl);
- }
-
- void GetAudioRendererParams(int* max_cached_frames,
- int* max_frames_per_append) const override {
- SB_DCHECK(max_cached_frames);
- SB_DCHECK(max_frames_per_append);
-
- *max_cached_frames = 256 * 1024;
- *max_frames_per_append = 16384;
- }
-};
-
-} // namespace
-
-// static
-scoped_ptr<PlayerComponents> PlayerComponents::Create() {
- return make_scoped_ptr<PlayerComponents>(new PlayerComponentsImpl);
-}
-
-} // namespace filter
-} // namespace player
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/playready_license.cc b/src/starboard/shared/win32/playready_license.cc
deleted file mode 100644
index 5b9b11e..0000000
--- a/src/starboard/shared/win32/playready_license.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/drm_system_playready.h"
-
-#include "starboard/configuration.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-scoped_refptr<SbDrmSystemPlayready::License>
-SbDrmSystemPlayready::License::Create(const void* initialization_data,
- int initialization_data_size) {
- SB_UNREFERENCED_PARAMETER(initialization_data);
- SB_UNREFERENCED_PARAMETER(initialization_data_size);
- return NULL;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/sdk_configuration.py b/src/starboard/shared/win32/sdk_configuration.py
deleted file mode 100644
index bed103d..0000000
--- a/src/starboard/shared/win32/sdk_configuration.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-
-import logging
-import os
-
-class SdkConfiguration:
- required_sdk_version = '10.0.15063.0'
-
- # Default Windows SDK bin directory.
- windows_sdk_bin_dir = 'C:\\Program Files (x86)\\Windows Kits\\10\\bin'
-
- # windows_sdk_host_tools will be set to, eg,
- # 'C:\\Program Files (x86)\\Windows Kits\\10\\bin\10.0.15063.0'
-
- # windows_sdk_path will be set to, eg
- # 'C:\\Program Files (x86)\\Windows Kits\\10'
-
- # Default Visual Studio Install directory.
- vs_install_dir = ('C:\\Program Files (x86)\\Microsoft Visual Studio'
- + '\\2017\\Professional')
-
- # vs_install_dir_with_version will be set to, eg
- # "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools
- # \MSVC\14.10.25017
-
- # vs_host_tools_path will be set to, eg
- # "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools
- # \MSVC\14.10.25017\bin\HostX64\x64
-
- def __init__(self):
- # Maybe override Windows SDK bin directory with environment variable.
- windows_sdk_bin_var = 'WindowsSdkBinPath'
- if windows_sdk_bin_var in os.environ:
- self.windows_sdk_bin_dir = os.environ[windows_sdk_bin_var]
- elif not os.path.exists(self.windows_sdk_bin_dir):
- # If the above fails, this is our last guess.
- self.windows_sdk_bin_dir = self.windows_sdk_bin_dir.replace(
- 'Program Files (x86)', 'mappedProgramFiles')
-
- self.windows_sdk_host_tools = os.path.join(
- self.windows_sdk_bin_dir, self.required_sdk_version, 'x64')
-
- self.windows_sdk_path = os.path.dirname(self.windows_sdk_bin_dir)
-
- # Maybe override Visual Studio install directory with environment variable.
- vs_install_dir_var = 'VSINSTALLDIR'
- if vs_install_dir_var in os.environ:
- self.vs_install_dir = os.environ[vs_install_dir_var]
- elif not os.path.exists(self.vs_install_dir):
- # If the above fails, this is our last guess.
- self.vs_install_dir = self.vs_install_dir.replace('Program Files (x86)',
- 'mappedProgramFiles')
-
- self.vs_install_dir_with_version = (self.vs_install_dir
- + '\\VC\\Tools\\MSVC' + '\\14.10.25017')
- self.vs_host_tools_path = (self.vs_install_dir_with_version
- + '\\bin\\HostX64\\x64')
-
- if not os.path.exists(self.vs_host_tools_path):
- logging.critical('Expected Visual Studio path \"%s\" not found.',
- self.vs_host_tools_path)
- self._CheckWindowsSdkVersion()
-
- def _CheckWindowsSdkVersion(self):
- if os.path.exists(self.windows_sdk_host_tools):
- return True
-
- if os.path.exists(self.windows_sdk_bin_dir):
- contents = os.listdir(self.windows_sdk_bin_dir)
- contents = [content for content in contents
- if os.path.isdir(
- os.path.join(self.windows_sdk_bin_dir, content))]
- non_sdk_dirs = ['arm', 'arm64', 'x64', 'x86']
- installed_sdks = [content for content in contents
- if content not in non_sdk_dirs]
- logging.critical('Windows SDK versions \"%s\" found." \"%s\" required.',
- installed_sdks, self.required_sdk_version)
- else:
- logging.critical('Windows SDK versions \"%s\" required.',
- self.required_sdk_version)
- return False
-
diff --git a/src/starboard/shared/win32/set_non_blocking_internal.cc b/src/starboard/shared/win32/set_non_blocking_internal.cc
deleted file mode 100644
index 35d74aa..0000000
--- a/src/starboard/shared/win32/set_non_blocking_internal.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/set_non_blocking_internal.h"
-
-#include <winsock2.h>
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-bool SetNonBlocking(SOCKET socket_handle) {
- u_long kOne = 1;
- bool success = (ioctlsocket(socket_handle, FIONBIO, &kOne) == 0);
- return success;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/set_non_blocking_internal.h b/src/starboard/shared/win32/set_non_blocking_internal.h
deleted file mode 100644
index 745a14b..0000000
--- a/src/starboard/shared/win32/set_non_blocking_internal.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_SET_NON_BLOCKING_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_SET_NON_BLOCKING_INTERNAL_H_
-
-#include "starboard/shared/internal_only.h"
-
-#include <winsock2.h>
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// Makes the socket file descriptor non-blocking.
-bool SetNonBlocking(SOCKET socket_handle);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_SET_NON_BLOCKING_INTERNAL_H_
diff --git a/src/starboard/shared/win32/socket_accept.cc b/src/starboard/shared/win32/socket_accept.cc
deleted file mode 100644
index dfc68db..0000000
--- a/src/starboard/shared/win32/socket_accept.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/set_non_blocking_internal.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-SbSocket SbSocketAccept(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- return kSbSocketInvalid;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
-
- SOCKET socket_handle = accept(socket->socket_handle, nullptr, nullptr);
- if (socket_handle == INVALID_SOCKET) {
- socket->error = sbwin32::TranslateSocketErrorStatus(WSAGetLastError());
- return kSbSocketInvalid;
- }
-
- // All Starboard sockets are non-blocking, so let's ensure it.
- if (!sbwin32::SetNonBlocking(socket_handle)) {
- // Something went wrong, we'll clean up and return failure.
- socket->error = sbwin32::TranslateSocketErrorStatus(WSAGetLastError());
- closesocket(socket_handle);
- return kSbSocketInvalid;
- }
-
- socket->error = kSbSocketOk;
-
- // Adopt the newly accepted socket.
- return new SbSocketPrivate(socket->address_type, socket->protocol,
- socket_handle,
- SbSocketPrivate::BindTarget::kAccepted);
-}
diff --git a/src/starboard/shared/win32/socket_bind.cc b/src/starboard/shared/win32/socket_bind.cc
deleted file mode 100644
index 100c8e1..0000000
--- a/src/starboard/shared/win32/socket_bind.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-namespace {
-
-bool IsIpv6InaddrAny(const SbSocketAddress* local_address) {
- return SbMemoryIsZero(local_address->address, sbwin32::kAddressLengthIpv6);
-}
-bool IsIpv4InaddrAny(const SbSocketAddress* local_address) {
- return SbMemoryIsZero(local_address->address, sbwin32::kAddressLengthIpv4);
-}
-
-} // namespace
-
-SbSocketError SbSocketBind(SbSocket socket,
- const SbSocketAddress* local_address) {
- if (!SbSocketIsValid(socket)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid socket";
- return kSbSocketErrorFailed;
- }
-
- sbwin32::SockAddr sock_addr;
- if (!sock_addr.FromSbSocketAddress(local_address)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid address";
- return (socket->error = sbwin32::TranslateSocketErrorStatus(EINVAL));
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- if (local_address->type != socket->address_type) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Incompatible addresses: "
- << "socket type = " << socket->address_type
- << ", argument type = " << local_address->type;
- return (socket->error = sbwin32::TranslateSocketErrorStatus(EAFNOSUPPORT));
- }
-
- switch (local_address->type) {
- case kSbSocketAddressTypeIpv6:
- if (!IsIpv6InaddrAny(local_address)) {
- socket->bound_to = SbSocketPrivate::BindTarget::kOther;
- break;
- }
-
- socket->bound_to = SbSocketPrivate::BindTarget::kAny;
-
- // When binding to the IPV6 any address, ensure that the IPV6_V6ONLY flag
- // is off to allow incoming IPV4 connections on the same socket.
- // See https://www.ietf.org/rfc/rfc3493.txt for details.
- if (!sbwin32::SetBooleanSocketOption(socket, IPPROTO_IPV6, IPV6_V6ONLY,
- "IPV6_V6ONLY", false)) {
- // Silently ignore errors, assume the default behavior is as expected.
- socket->error = kSbSocketOk;
- }
-
- break;
- case kSbSocketAddressTypeIpv4:
- socket->bound_to = IsIpv4InaddrAny(local_address)
- ? SbSocketPrivate::BindTarget::kAny
- : SbSocketPrivate::BindTarget::kOther;
- break;
- }
-
- int result =
- bind(socket->socket_handle, sock_addr.sockaddr(), sock_addr.length);
- if (result == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << __FUNCTION__
- << ": Bind failed. last_error=" << last_error;
- return (socket->error = sbwin32::TranslateSocketErrorStatus(last_error));
- }
-
- return (socket->error = kSbSocketOk);
-}
diff --git a/src/starboard/shared/win32/socket_clear_last_error.cc b/src/starboard/shared/win32/socket_clear_last_error.cc
deleted file mode 100644
index 3c28d9b..0000000
--- a/src/starboard/shared/win32/socket_clear_last_error.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include "starboard/shared/win32/socket_internal.h"
-
-bool SbSocketClearLastError(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- socket->error = kSbSocketOk;
- return true;
-}
diff --git a/src/starboard/shared/win32/socket_connect.cc b/src/starboard/shared/win32/socket_connect.cc
deleted file mode 100644
index 4c33768..0000000
--- a/src/starboard/shared/win32/socket_connect.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-SbSocketError SbSocketConnect(SbSocket socket, const SbSocketAddress* address) {
- if (!SbSocketIsValid(socket)) {
- return kSbSocketErrorFailed;
- }
-
- sbwin32::SockAddr sock_addr;
- if (!sock_addr.FromSbSocketAddress(address)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid address";
- return (socket->error = kSbSocketErrorFailed);
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- if (address->type != socket->address_type) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Incompatible addresses: "
- << "socket type = " << socket->address_type
- << ", argument type = " << address->type;
- return (socket->error = kSbSocketErrorFailed);
- }
-
- int result =
- connect(socket->socket_handle, sock_addr.sockaddr(), sock_addr.length);
-
- if (result != SOCKET_ERROR) {
- socket->bound_to = SbSocketPrivate::BindTarget::kAny;
- return (socket->error = kSbSocketOk);
- }
-
- const int last_error = WSAGetLastError();
- if (last_error == WSAEWOULDBLOCK) {
- socket->bound_to = SbSocketPrivate::BindTarget::kAny;
- return (socket->error = kSbSocketPending);
- }
-
- SB_DLOG(ERROR) << __FUNCTION__ << ": connect failed: " << last_error;
- return (socket->error = kSbSocketErrorFailed);
-}
diff --git a/src/starboard/shared/win32/socket_create.cc b/src/starboard/shared/win32/socket_create.cc
deleted file mode 100644
index 561967f..0000000
--- a/src/starboard/shared/win32/socket_create.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-#include <mswsock.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/set_non_blocking_internal.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-SbSocket SbSocketCreate(SbSocketAddressType address_type,
- SbSocketProtocol protocol) {
- int socket_domain;
- switch (address_type) {
- case kSbSocketAddressTypeIpv4:
- socket_domain = AF_INET;
- break;
- case kSbSocketAddressTypeIpv6:
- socket_domain = AF_INET6;
- break;
- default:
- SB_NOTREACHED();
- return kSbSocketInvalid;
- }
-
- int socket_type;
- int socket_protocol;
- switch (protocol) {
- case kSbSocketProtocolTcp:
- socket_type = SOCK_STREAM;
- socket_protocol = IPPROTO_TCP;
- break;
- case kSbSocketProtocolUdp:
- socket_type = SOCK_DGRAM;
- socket_protocol = IPPROTO_UDP;
- break;
- default:
- SB_NOTREACHED();
- return kSbSocketInvalid;
- }
-
- // WSASocket with dwFlags=0, instead of socket() creates sockets that do not
- // support overlapped IO.
- SOCKET socket_handle =
- WSASocketW(socket_domain, socket_type, socket_protocol, nullptr, 0, 0);
- if (socket_handle == INVALID_SOCKET) {
- return kSbSocketInvalid;
- }
-
- // From
- // https://msdn.microsoft.com/en-us/library/windows/desktop/cc136103(v=vs.85).aspx:
- // "When the TargetOsVersion member is set to a value for Windows Vista or
- // later, reductions to the TCP receive buffer size on this socket using the
- // SO_RCVBUF socket option are allowed even after a TCP connection has been
- // establishment."
- // "When the TargetOsVersion member is set to a value for Windows Vista or
- // later, receive window auto-tuning is enabled and the TCP window scale
- // factor is reduced to 2 from the default value of 8."
-
- // The main impetus for this change:
-
- // "The WsaBehaviorAutoTuning option is needed on Windows Vista for some
- // Internet gateway devices and firewalls that do not correctly support data
- // flows for TCP connections that use the WSopt extension and a windows scale
- // factor. On Windows Vista, a receiver by default negotiates a window scale
- // factor of 8 for a maximum true window size of 16,776,960 bytes. When data
- // begins to flow on a fast link, Windows initially starts with a 64 Kilobyte
- // true window size by setting the Window field of the TCP header to 256 and
- // setting the window scale factor to 8 in the TCP options (256*2^8=64KB).
- // Some Internet gateway devices and firewalls ignore the window scale factor
- // and only look at the advertised Window field in the TCP header specified as
- // 256, and drop incoming packets for the connection that contain more than
- // 256 bytes of TCP data. To support TCP receive window scaling, a gateway
- // device or firewall must monitor the TCP handshake and track the negotiated
- // window scale factor as part of the TCP connection data. Also some
- // applications and TCP stack implementations on other platforms ignore the
- // TCP WSopt extension and the window scaling factor. So the remote host
- // sending the data may send data at the rate advertised in the Window field
- // of the TCP header (256 bytes). This can result in data being received very
- // slowly by the receiver."
-
- if (protocol == kSbSocketProtocolTcp) {
- WSA_COMPATIBILITY_MODE compatibility_mode = {WsaBehaviorAll, NTDDI_VISTA};
-
- DWORD kZero = 0;
- int return_value = WSAIoctl(
- socket_handle, SIO_SET_COMPATIBILITY_MODE, &compatibility_mode,
- sizeof(WSA_COMPATIBILITY_MODE), nullptr, 0, &kZero, nullptr, nullptr);
- if (return_value == SOCKET_ERROR) {
- closesocket(socket_handle);
- return kSbSocketInvalid;
- }
- }
-
- // All Starboard sockets are non-blocking, so let's ensure it.
- if (!sbwin32::SetNonBlocking(socket_handle)) {
- // Something went wrong, we'll clean up and return failure.
- closesocket(socket_handle);
- return kSbSocketInvalid;
- }
-
- return new SbSocketPrivate(address_type, protocol, socket_handle,
- SbSocketPrivate::BindTarget::kUnbound);
-}
diff --git a/src/starboard/shared/win32/socket_destroy.cc b/src/starboard/shared/win32/socket_destroy.cc
deleted file mode 100644
index df47cae..0000000
--- a/src/starboard/shared/win32/socket_destroy.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-#include "starboard/socket_waiter.h"
-
-bool SbSocketDestroy(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
-
- if (socket->waiter != nullptr) {
- bool result = SbSocketWaiterRemove(socket->waiter, socket);
- SB_DCHECK(result);
- }
-
- bool result = closesocket(socket->socket_handle) != SOCKET_ERROR;
-
- delete socket;
- return result;
-}
diff --git a/src/starboard/shared/win32/socket_free_resolution.cc b/src/starboard/shared/win32/socket_free_resolution.cc
deleted file mode 100644
index 212cde0..0000000
--- a/src/starboard/shared/win32/socket_free_resolution.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include "starboard/log.h"
-
-void SbSocketFreeResolution(SbSocketResolution* resolution) {
- if (!resolution) {
- return;
- }
-
- if (resolution->addresses) {
- delete[] resolution->addresses;
- }
-
- delete resolution;
-}
diff --git a/src/starboard/shared/win32/socket_get_interface_address.cc b/src/starboard/shared/win32/socket_get_interface_address.cc
deleted file mode 100644
index fa0ded7..0000000
--- a/src/starboard/shared/win32/socket_get_interface_address.cc
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include <ifdef.h>
-#include <iphlpapi.h>
-
-#include <algorithm>
-#include <memory>
-
-#include "starboard/byte_swap.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/adapter_utils.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-namespace {
-const ULONG kDefaultAdapterInfoBufferSizeInBytes = 16 * 1024;
-
-bool IsAnyAddress(const SbSocketAddress& address) {
- switch (address.type) {
- case kSbSocketAddressTypeIpv4:
- return (address.address[0] == 0 && address.address[1] == 0 &&
- address.address[2] == 0 && address.address[3] == 0);
- case kSbSocketAddressTypeIpv6: {
- bool found_nonzero = false;
- for (std::size_t i = 0; i != sbwin32::kAddressLengthIpv6; ++i) {
- found_nonzero |= (address.address[i] != 0);
- }
- return !found_nonzero;
- }
- default:
- SB_NOTREACHED() << "Invalid address type " << address.type;
- break;
- }
-
- return false;
-}
-
-void GenerateNetMaskFromPrefixLength(UINT8 prefix_length,
- UINT32* const address_begin,
- UINT32* const address_end) {
- SB_DCHECK(address_end >= address_begin);
- SB_DCHECK((reinterpret_cast<char*>(address_end) -
- reinterpret_cast<char*>(address_begin)) %
- 4 ==
- 0);
- UINT8 ones_left = prefix_length;
- const int kBitsInOneDWORD = sizeof(UINT32) * 8;
- for (UINT32* iterator = address_begin; iterator != address_end; ++iterator) {
- UINT8 ones_in_this_dword = std::min<UINT8>(kBitsInOneDWORD, ones_left);
- UINT64 mask_value =
- kSbUInt64Max - ((1ULL << (kBitsInOneDWORD - ones_in_this_dword)) - 1);
- *iterator =
- SB_HOST_TO_NET_U32(static_cast<UINT32>(mask_value & kSbUInt64Max));
- ones_left -= ones_in_this_dword;
- }
-}
-
-bool PopulateInterfaceAddress(const IP_ADAPTER_UNICAST_ADDRESS& unicast_address,
- SbSocketAddress* out_interface_ip) {
- if (!out_interface_ip) {
- return true;
- }
-
- const SOCKET_ADDRESS& address = unicast_address.Address;
- sbwin32::SockAddr addr;
- return addr.FromSockaddr(address.lpSockaddr) &&
- addr.ToSbSocketAddress(out_interface_ip);
-}
-
-bool PopulateNetmask(const IP_ADAPTER_UNICAST_ADDRESS& unicast_address,
- SbSocketAddress* out_netmask) {
- if (!out_netmask) {
- return true;
- }
-
- const SOCKET_ADDRESS& address = unicast_address.Address;
- if (address.lpSockaddr == nullptr) {
- return false;
- }
- const ADDRESS_FAMILY& family = address.lpSockaddr->sa_family;
-
- switch (family) {
- case AF_INET:
- out_netmask->type = kSbSocketAddressTypeIpv4;
- break;
- case AF_INET6:
- out_netmask->type = kSbSocketAddressTypeIpv6;
- break;
- default:
- SB_NOTREACHED() << "Invalid family " << family;
- return false;
- }
-
- UINT32* const begin_netmask =
- reinterpret_cast<UINT32*>(&(out_netmask->address[0]));
- UINT32* const end_netmask =
- begin_netmask + SB_ARRAY_SIZE(out_netmask->address) / sizeof(UINT32);
-
- GenerateNetMaskFromPrefixLength(unicast_address.OnLinkPrefixLength,
- begin_netmask, end_netmask);
- return true;
-}
-
-bool GetNetmaskForInterfaceAddress(const SbSocketAddress& interface_address,
- SbSocketAddress* out_netmask) {
- std::unique_ptr<char[]> adapter_info_memory_block;
- if (!sbwin32::GetAdapters(
- interface_address.type, &adapter_info_memory_block)) {
- return false;
- }
- const void* const interface_address_buffer =
- reinterpret_cast<const void* const>(interface_address.address);
- for (PIP_ADAPTER_ADDRESSES adapter = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(
- adapter_info_memory_block.get());
- adapter != nullptr; adapter = adapter->Next) {
- if ((adapter->OperStatus != IfOperStatusUp) ||
- !sbwin32::IsIfTypeEthernet(adapter->IfType)) {
- continue;
- }
-
- for (PIP_ADAPTER_UNICAST_ADDRESS unicast_address =
- adapter->FirstUnicastAddress;
- unicast_address != nullptr; unicast_address = unicast_address->Next) {
- sbwin32::SockAddr addr;
- if (!addr.FromSockaddr(unicast_address->Address.lpSockaddr)) {
- continue;
- }
-
- const void* unicast_address_buffer = nullptr;
- int bytes_to_check = 0;
-
- switch (interface_address.type) {
- case kSbSocketAddressTypeIpv4:
- unicast_address_buffer =
- reinterpret_cast<void*>(&(addr.sockaddr_in()->sin_addr));
- bytes_to_check = sbwin32::kAddressLengthIpv4;
- break;
- case kSbSocketAddressTypeIpv6:
- unicast_address_buffer =
- reinterpret_cast<void*>(&(addr.sockaddr_in6()->sin6_addr));
- bytes_to_check = sbwin32::kAddressLengthIpv6;
- break;
- default:
- SB_DLOG(ERROR) << "Invalid interface address type "
- << interface_address.type;
- return false;
- }
-
- if (SbMemoryCompare(unicast_address_buffer, interface_address_buffer,
- bytes_to_check) != 0) {
- continue;
- }
-
- if (PopulateNetmask(*unicast_address, out_netmask)) {
- return true;
- }
- }
- }
-
- return false;
-}
-
-bool IsUniqueLocalAddress(const unsigned char ip[16]) {
- // Unique Local Addresses are in fd08::/8.
- return ip[0] == 0xfd && ip[1] == 0x08;
-}
-
-bool FindInterfaceIP(const SbSocketAddressType address_type,
- SbSocketAddress* out_interface_ip,
- SbSocketAddress* out_netmask) {
- if (out_interface_ip == nullptr) {
- SB_NOTREACHED() << "out_interface_ip must be specified";
- return false;
- }
-
- std::unique_ptr<char[]> adapter_info_memory_block;
- if (!sbwin32::GetAdapters(address_type, &adapter_info_memory_block)) {
- return false;
- }
-
- for (PIP_ADAPTER_ADDRESSES adapter = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(
- adapter_info_memory_block.get());
- adapter != nullptr; adapter = adapter->Next) {
- if ((adapter->OperStatus != IfOperStatusUp) ||
- !sbwin32::IsIfTypeEthernet(adapter->IfType)) {
- continue;
- }
-
- for (PIP_ADAPTER_UNICAST_ADDRESS unicast_address =
- adapter->FirstUnicastAddress;
- unicast_address != nullptr; unicast_address = unicast_address->Next) {
- if (unicast_address->Flags & (IP_ADAPTER_ADDRESS_TRANSIENT)) {
- continue;
- }
- if (!(unicast_address->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE)) {
- continue;
- }
-
- // TODO: For IPv6, Prioritize interface with highest scope.
- // Skip ULAs for now.
- if (address_type == kSbSocketAddressTypeIpv6) {
- // Documentation on MSDN states:
- // "The SOCKADDR structure pointed to by the lpSockaddr member varies
- // depending on the protocol or address family selected. For example,
- // the sockaddr_in6 structure is used for an IPv6 socket address
- // while the sockaddr_in4 structure is used for an IPv4 socket address."
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740507(v=vs.85).aspx
-
- sockaddr_in6* addr = reinterpret_cast<sockaddr_in6*>(
- unicast_address->Address.lpSockaddr);
- SB_DCHECK(addr->sin6_family == AF_INET6);
- if (IsUniqueLocalAddress(addr->sin6_addr.u.Byte)) {
- continue;
- }
- }
-
- if (!PopulateInterfaceAddress(*unicast_address, out_interface_ip)) {
- continue;
- }
- if (!PopulateNetmask(*unicast_address, out_netmask)) {
- continue;
- }
-
- return true;
- }
- }
- return false;
-}
-
-bool FindSourceAddressForDestination(const SbSocketAddress& destination,
- SbSocketAddress* out_source_address) {
- SbSocket socket = SbSocketCreate(destination.type, kSbSocketProtocolUdp);
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- SbSocketError connect_retval = SbSocketConnect(socket, &destination);
- if (connect_retval != kSbSocketOk) {
- bool socket_destroyed = SbSocketDestroy(socket);
- SB_DCHECK(socket_destroyed);
- return false;
- }
-
- bool success = SbSocketGetLocalAddress(socket, out_source_address);
- bool socket_destroyed = SbSocketDestroy(socket);
- SB_DCHECK(socket_destroyed);
- return success;
-}
-
-} // namespace
-
-bool SbSocketGetInterfaceAddress(const SbSocketAddress* const destination,
- SbSocketAddress* out_source_address,
- SbSocketAddress* out_netmask) {
- if (!out_source_address) {
- return false;
- }
-
- if (destination == nullptr) {
- // Return either a v4 or a v6 address. Per spec.
- return (FindInterfaceIP(kSbSocketAddressTypeIpv4, out_source_address,
- out_netmask) ||
- FindInterfaceIP(kSbSocketAddressTypeIpv6, out_source_address,
- out_netmask));
- } else if (IsAnyAddress(*destination)) {
- return FindInterfaceIP(destination->type, out_source_address, out_netmask);
- }
-
- return (FindSourceAddressForDestination(*destination, out_source_address) &&
- GetNetmaskForInterfaceAddress(*out_source_address, out_netmask));
-}
diff --git a/src/starboard/shared/win32/socket_get_last_error.cc b/src/starboard/shared/win32/socket_get_last_error.cc
deleted file mode 100644
index 51d2e9a..0000000
--- a/src/starboard/shared/win32/socket_get_last_error.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include "starboard/shared/win32/socket_internal.h"
-
-SbSocketError SbSocketGetLastError(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- return kSbSocketErrorFailed;
- }
-
- return socket->error;
-}
diff --git a/src/starboard/shared/win32/socket_get_local_address.cc b/src/starboard/shared/win32/socket_get_local_address.cc
deleted file mode 100644
index e4a1bf9..0000000
--- a/src/starboard/shared/win32/socket_get_local_address.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketGetLocalAddress(SbSocket socket, SbSocketAddress* out_address) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- // winsock2 considers calling getsockname() on unbound sockets to be an error.
- // Therefore, SbSocketListen() will call SbSocketBind().
- if (socket->bound_to == SbSocketPrivate::BindTarget::kUnbound) {
- out_address->type = socket->address_type;
- switch (socket->address_type) {
- case kSbSocketAddressTypeIpv4:
- SbMemorySet(out_address->address, 0, sbwin32::kAddressLengthIpv4);
- out_address->port = 0;
- return true;
- case kSbSocketAddressTypeIpv6:
- SbMemorySet(out_address->address, 0, sbwin32::kAddressLengthIpv6);
- out_address->port = 0;
- return true;
- default:
- SB_NOTREACHED() << "Invalid address type: " << socket->address_type;
- return false;
- }
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- sbwin32::SockAddr sock_addr;
- int result = getsockname(socket->socket_handle, sock_addr.sockaddr(),
- &sock_addr.length);
- if (result == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_LOG(ERROR) << "getsockname() failed with last_error = " << last_error;
- socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
- return false;
- }
- if (!sock_addr.ToSbSocketAddress(out_address)) {
- socket->error = kSbSocketErrorFailed;
- return false;
- }
-
- socket->error = kSbSocketOk;
- return true;
-}
diff --git a/src/starboard/shared/win32/socket_internal.cc b/src/starboard/shared/win32/socket_internal.cc
deleted file mode 100644
index 9ae95fd..0000000
--- a/src/starboard/shared/win32/socket_internal.cc
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/socket_internal.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-namespace {
-const socklen_t kAddressStructLengthIpv4 =
- static_cast<socklen_t>(sizeof(struct sockaddr_in));
-const socklen_t kAddressStructLengthIpv6 =
- static_cast<socklen_t>(sizeof(struct sockaddr_in6));
-}
-
-SbSocketError TranslateSocketErrorStatus(int error) {
- switch (error) {
- case 0:
- return kSbSocketOk;
-
- // Microsoft Winsock error codes:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
- case WSAEINPROGRESS:
- case WSAEWOULDBLOCK:
- return kSbSocketPending;
-#if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) || \
- SB_API_VERSION >= 9
- case WSAECONNRESET:
- case WSAENETRESET:
- case WSAECONNABORTED:
- return kSbSocketErrorConnectionReset;
-
- // Microsoft System Error codes:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
- case ERROR_BROKEN_PIPE:
- return kSbSocketErrorConnectionReset;
-#endif // #if SB_HAS(SOCKET_ERROR_CONNECTION_RESET_SUPPORT) ||
- // SB_API_VERSION >= 9
- }
-
- // Here's where we would be more nuanced if we need to be.
- return kSbSocketErrorFailed;
-}
-
-bool SetBooleanSocketOption(SbSocket socket,
- int level,
- int option_code,
- const char* option_name,
- bool value) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- const int on = value ? 1 : 0;
- int result = setsockopt(socket->socket_handle, level, option_code,
- reinterpret_cast<const char*>(&on), sizeof(on));
- if (result == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << "Failed to set " << option_name << " on socket "
- << socket->socket_handle << ", last_error = " << last_error;
- socket->error = TranslateSocketErrorStatus(last_error);
- return false;
- }
-
- socket->error = kSbSocketOk;
- return true;
-}
-
-bool SetIntegerSocketOption(SbSocket socket,
- int level,
- int option_code,
- const char* option_name,
- int value) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- int result = setsockopt(socket->socket_handle, level, option_code,
- reinterpret_cast<const char*>(&value), sizeof(value));
- if (result == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << "Failed to set " << option_name << " on socket "
- << socket->socket_handle << ", last_error = " << last_error;
- socket->error = TranslateSocketErrorStatus(last_error);
- return false;
- }
-
- socket->error = kSbSocketOk;
- return true;
-}
-
-bool SockAddr::FromSbSocketAddress(const SbSocketAddress* address) {
- if (!address) {
- return false;
- }
-
- length = sizeof(storage_);
- switch (address->type) {
- case kSbSocketAddressTypeIpv4: {
- struct sockaddr_in* addr = sockaddr_in();
- length = kAddressStructLengthIpv4;
- SbMemorySet(addr, 0, length);
- addr->sin_family = AF_INET;
- addr->sin_port = htons(static_cast<USHORT>(address->port));
- SbMemoryCopy(&addr->sin_addr, address->address, kAddressLengthIpv4);
- break;
- }
- case kSbSocketAddressTypeIpv6: {
- struct sockaddr_in6* addr6 = sockaddr_in6();
- length = kAddressStructLengthIpv6;
- SbMemorySet(addr6, 0, length);
- addr6->sin6_family = AF_INET6;
- addr6->sin6_port = htons(static_cast<USHORT>(address->port));
- SbMemoryCopy(&addr6->sin6_addr, address->address, kAddressLengthIpv6);
- break;
- }
- default:
- SB_NOTREACHED() << "Unrecognized address type: " << address->type;
- return false;
- }
-
- return true;
-}
-
-bool SockAddr::ToSbSocketAddress(SbSocketAddress* out_address) const {
- if (!out_address) {
- return false;
- }
-
-// Check that we have been properly initialized.
- SB_DCHECK(length == kAddressStructLengthIpv4 ||
- length == kAddressStructLengthIpv6);
-
- if (family() == AF_INET) {
- const struct sockaddr_in* addr = sockaddr_in();
- if (length < kAddressStructLengthIpv4) {
- SB_NOTREACHED() << "Insufficient INET size: " << length;
- return false;
- }
-
- SbMemoryCopy(out_address->address, &addr->sin_addr, kAddressLengthIpv4);
- out_address->port = ntohs(addr->sin_port);
- out_address->type = kSbSocketAddressTypeIpv4;
- return true;
- }
-
- if (family() == AF_INET6) {
- const struct sockaddr_in6* addr6 = sockaddr_in6();
- if (length < kAddressStructLengthIpv6) {
- SB_NOTREACHED() << "Insufficient INET6 size: " << length;
- return false;
- }
-
- SbMemoryCopy(out_address->address, &addr6->sin6_addr, kAddressLengthIpv6);
- out_address->port = ntohs(addr6->sin6_port);
- out_address->type = kSbSocketAddressTypeIpv6;
- return true;
- }
-
- SB_NOTREACHED() << "Unrecognized address family: " << family();
- return false;
-}
-
-bool SockAddr::FromSockaddr(const struct sockaddr* sock_addr) {
- if (!sock_addr) {
- return false;
- }
-
- int family = sock_addr->sa_family;
- if (family == AF_INET) {
- const struct sockaddr_in* addr =
- reinterpret_cast<const struct sockaddr_in*>(sock_addr);
- *sockaddr_in() = *addr;
- length = static_cast<socklen_t>(sizeof(*addr));
- return true;
- } else if (family == AF_INET6) {
- const struct sockaddr_in6* addr =
- reinterpret_cast<const struct sockaddr_in6*>(sock_addr);
- *sockaddr_in6() = *addr;
- length = static_cast<socklen_t>(sizeof(*addr));
- return true;
- }
-
- SB_DLOG(WARNING) << "Unrecognized address family: " << family;
- return false;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/socket_internal.h b/src/starboard/shared/win32/socket_internal.h
deleted file mode 100644
index e4a2b0a..0000000
--- a/src/starboard/shared/win32/socket_internal.h
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_SOCKET_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_SOCKET_INTERNAL_H_
-
-#include <winsock2.h>
-#include <WS2tcpip.h>
-
-#include "starboard/atomic.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/win32/auto_event_handle.h"
-#include "starboard/socket.h"
-#include "starboard/socket_waiter.h"
-#include "starboard/types.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-struct SbSocketPrivate {
- enum struct BindTarget {
- kUnbound = 0,
- kAny = 1,
- kOther = 2,
- kAccepted = 3,
- };
-
- SbSocketPrivate(SbSocketAddressType address_type,
- SbSocketProtocol protocol,
- SOCKET handle,
- BindTarget bound_to)
- : address_type(address_type),
- protocol(protocol),
- socket_handle(handle),
- socket_event(WSA_INVALID_EVENT),
- writable(0),
- error(kSbSocketOk),
- waiter(kSbSocketWaiterInvalid),
- bound_to(bound_to) {}
- ~SbSocketPrivate() {}
-
- // The address domain of this socket, IPv4 or IPv6.
- SbSocketAddressType address_type;
-
- // The protocol of this socket, UDP or TCP.
- SbSocketProtocol protocol;
-
- // The handle for this socket.
- SOCKET socket_handle;
-
- // The event related to the socket_handle. Used for SbSocketWaiter.
- sbwin32::AutoEventHandle socket_event;
-
- // Set to true between when socket is shown as writable via WSAEventSelect/
- // WSAWaitForMultipleEvents and when writing to the socket returns
- // fails with WSAEWOULDBLOCK.
- //
- // Used to work around the fact that WSAEventSelect for FD_WRITE is
- // edge-triggered, unlike other events.
- //
- // See MSDN documentato n for WSAEventSelect FD_WRITE for more info.
- starboard::atomic_bool writable;
-
- // The last error that occurred on this socket, or kSbSocketOk.
- SbSocketError error;
-
- // The waiter this socket is registered with, or kSbSocketWaiterInvalid.
- SbSocketWaiter waiter;
-
- BindTarget bound_to;
-};
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-const socklen_t kAddressLengthIpv4 = 4;
-const socklen_t kAddressLengthIpv6 = 16;
-
-// Translates an last_error from a socket call into an SbSocketError.
-SbSocketError TranslateSocketErrorStatus(int error);
-
-// Sets a boolean socket option, doing all appropriate checks.
-bool SetBooleanSocketOption(SbSocket socket,
- int level,
- int option_code,
- const char* option_name,
- bool value);
-
-// Sets an integer socket option, doing all appropriate checks.
-bool SetIntegerSocketOption(SbSocket socket,
- int level,
- int option_code,
- const char* option_name,
- int value);
-
-// A helper class for converting back and forth from sockaddrs, ugh.
-class SockAddr {
- public:
- SockAddr() : length(sizeof(storage_)) {}
- ~SockAddr() {}
-
- // Initializes this SockAddr with the given SbSocketAddress, overwriting
- // anything any address previously held.
- bool FromSbSocketAddress(const SbSocketAddress* address);
-
- // Initializes the given SbSocketAddress with this SockAddr, which must have
- // been previously initialized.
- bool ToSbSocketAddress(SbSocketAddress* out_address) const;
-
- // Initializes this SockAddr with |sock_addr|, assuming it is appropriately
- // sized for its type.
- bool FromSockaddr(const struct sockaddr* sock_addr);
-
- // The sockaddr family. We only support INET and INET6.
- u_short family() const { return sockaddr()->sa_family; }
-
- struct sockaddr* sockaddr() {
- return reinterpret_cast<struct sockaddr*>(&storage_);
- }
-
- const struct sockaddr* sockaddr() const {
- return reinterpret_cast<const struct sockaddr*>(&storage_);
- }
-
- struct sockaddr_in* sockaddr_in() {
- return reinterpret_cast<struct sockaddr_in*>(&storage_);
- }
-
- const struct sockaddr_in* sockaddr_in() const {
- return reinterpret_cast<const struct sockaddr_in*>(&storage_);
- }
-
- struct sockaddr_in6* sockaddr_in6() {
- return reinterpret_cast<struct sockaddr_in6*>(&storage_);
- }
-
- const struct sockaddr_in6* sockaddr_in6() const {
- return reinterpret_cast<const struct sockaddr_in6*>(&storage_);
- }
-
- // Public on purpose, because it will be handy to be passed directly by
- // reference into other functions.
- socklen_t length;
-
- private:
- sockaddr_storage storage_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_SOCKET_INTERNAL_H_
diff --git a/src/starboard/shared/win32/socket_is_connected.cc b/src/starboard/shared/win32/socket_is_connected.cc
deleted file mode 100644
index 76e982d..0000000
--- a/src/starboard/shared/win32/socket_is_connected.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-bool SbSocketIsConnected(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
-
- // To tell if it is really connected, we peek a byte from the stream. We
- // should get a byte back, or an EAGAIN/EWOULDBLOCK telling us the socket is
- // waiting for data.
- char c;
- const int result = recv(socket->socket_handle, &c, 1, MSG_PEEK);
- if (result == 0) {
- // If the connection is closed, the return value is 0.
- return false;
- }
-
- const int last_error = WSAGetLastError();
- return (result > 0 || last_error == WSAEWOULDBLOCK);
-}
diff --git a/src/starboard/shared/win32/socket_is_connected_and_idle.cc b/src/starboard/shared/win32/socket_is_connected_and_idle.cc
deleted file mode 100644
index d66c508..0000000
--- a/src/starboard/shared/win32/socket_is_connected_and_idle.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-bool SbSocketIsConnectedAndIdle(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
-
- // To tell if it is really connected and idle, we peek a byte from the
- // stream. We should get an EAGAIN/EWOULDBLOCK telling us the socket is
- // waiting for data.
- char c;
- int rv = recv(socket->socket_handle, &c, 1, MSG_PEEK);
- if (rv != SOCKET_ERROR) {
- // Either not connected, or not idle.
- return false;
- }
-
- return (WSAGetLastError() == WSAEWOULDBLOCK);
-}
diff --git a/src/starboard/shared/win32/socket_join_multicast_group.cc b/src/starboard/shared/win32/socket_join_multicast_group.cc
deleted file mode 100644
index 459fcbd..0000000
--- a/src/starboard/shared/win32/socket_join_multicast_group.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketJoinMulticastGroup(SbSocket socket,
- const SbSocketAddress* address) {
- if (!SbSocketIsValid(socket)) {
- return false;
- }
-
- if (!address) {
- return false;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- ip_mreq imreq = {0};
- IN_ADDR addr = *reinterpret_cast<const IN_ADDR*>(address->address);
- imreq.imr_multiaddr.s_addr = addr.S_un.S_addr;
- imreq.imr_interface.s_addr = INADDR_ANY;
-
- int result = setsockopt(socket->socket_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- reinterpret_cast<const char*>(&imreq), sizeof(imreq));
- if (result == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << "Failed to IP_ADD_MEMBERSHIP on socket "
- << socket->socket_handle << ", last_error = " << last_error;
- socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
- return false;
- }
-
- socket->error = kSbSocketOk;
- return true;
-}
diff --git a/src/starboard/shared/win32/socket_listen.cc b/src/starboard/shared/win32/socket_listen.cc
deleted file mode 100644
index 96f28fb..0000000
--- a/src/starboard/shared/win32/socket_listen.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-SbSocketError SbSocketListen(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- return kSbSocketErrorFailed;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
-
- // Under winsock, a socket must be bound before we can listen on it.
- if (socket->bound_to == SbSocketPrivate::BindTarget::kUnbound) {
- // By listening on ::, we can accept both IPv4 and IPv6 connections.
- SbSocketAddress any_address = {0};
- any_address.type = socket->address_type;
- if (SbSocketBind(socket, &any_address) != kSbSocketOk) {
- SB_DLOG(ERROR) << "Unable to bind a socket during SbListen.";
- return (socket->error = kSbSocketErrorFailed);
- }
-
- socket->bound_to = SbSocketPrivate::BindTarget::kAny;
- }
-
- // TODO: Determine if we need to specify a > 0 backlog. It can go up to
- // SOMAXCONN according to the documentation. Several places in chromium
- // specify the literal "10" with the comment "maybe dont allow any backlog?"
- int result = listen(socket->socket_handle, 0);
- if (result == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_LOG(ERROR) << "listen() failed with last_error = " << last_error;
- return (socket->error = sbwin32::TranslateSocketErrorStatus(last_error));
- }
-
- return (socket->error = kSbSocketOk);
-}
diff --git a/src/starboard/shared/win32/socket_receive_from.cc b/src/starboard/shared/win32/socket_receive_from.cc
deleted file mode 100644
index e00f31f..0000000
--- a/src/starboard/shared/win32/socket_receive_from.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-namespace {
-
-bool IsReportableErrno(int code) {
- return (code != WSAEWOULDBLOCK && code != WSAECONNRESET);
-}
-
-} // namespace
-
-int SbSocketReceiveFrom(SbSocket socket,
- char* out_data,
- int data_size,
- SbSocketAddress* out_source) {
- const int kRecvFlags = 0;
-
- if (!SbSocketIsValid(socket)) {
- return -1;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- if (socket->protocol == kSbSocketProtocolTcp) {
- if (out_source) {
- sbwin32::SockAddr sock_addr;
- int result = getpeername(socket->socket_handle, sock_addr.sockaddr(),
- &sock_addr.length);
- if (result == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << __FUNCTION__
- << ": getpeername failed, last_error = " << last_error;
- socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
- return -1;
- }
-
- if (!sock_addr.ToSbSocketAddress(out_source)) {
- SB_DLOG(FATAL) << __FUNCTION__ << ": Bad TCP source address.";
- socket->error = kSbSocketErrorFailed;
- return -1;
- }
- }
-
- int bytes_read =
- recv(socket->socket_handle, out_data, data_size, kRecvFlags);
- if (bytes_read >= 0) {
- socket->error = kSbSocketOk;
- return static_cast<int>(bytes_read);
- }
-
- int last_error = WSAGetLastError();
- if (IsReportableErrno(last_error) &&
- socket->error != sbwin32::TranslateSocketErrorStatus(last_error)) {
- SB_DLOG(ERROR) << "recv failed, last_error = " << last_error;
- }
- socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
- return -1;
- } else if (socket->protocol == kSbSocketProtocolUdp) {
- sbwin32::SockAddr sock_addr;
- ssize_t bytes_read =
- recvfrom(socket->socket_handle, out_data, data_size, kRecvFlags,
- sock_addr.sockaddr(), &sock_addr.length);
-
- if (bytes_read >= 0) {
- if (out_source) {
- if (!sock_addr.ToSbSocketAddress(out_source)) {
- SB_DLOG(FATAL) << __FUNCTION__ << ": Bad UDP source address.";
- socket->error = kSbSocketErrorFailed;
- return -1;
- }
- }
-
- socket->error = kSbSocketOk;
- return static_cast<int>(bytes_read);
- }
-
- int last_error = WSAGetLastError();
- if (last_error != WSAEWOULDBLOCK &&
- socket->error != sbwin32::TranslateSocketErrorStatus(last_error)) {
- SB_DLOG(ERROR) << "recvfrom failed, last_error = " << last_error;
- }
- socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
- return -1;
- }
-
- SB_NOTREACHED() << "Unrecognized protocol: " << socket->protocol;
- return -1;
-}
diff --git a/src/starboard/shared/win32/socket_resolve.cc b/src/starboard/shared/win32/socket_resolve.cc
deleted file mode 100644
index d2fef44..0000000
--- a/src/starboard/shared/win32/socket_resolve.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include <memory>
-#include <vector>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-SbSocketResolution* SbSocketResolve(const char* hostname, int filters) {
- struct addrinfo* ai = nullptr;
- struct addrinfo hints = {0};
-
- if (filters & kSbSocketResolveFilterIpv4) {
- if (filters & kSbSocketResolveFilterIpv6) {
- hints.ai_family = AF_UNSPEC;
- } else {
- hints.ai_family = AF_INET;
- }
- } else if (filters & kSbSocketResolveFilterIpv6) {
- hints.ai_family = AF_INET6;
- } else {
- hints.ai_family = AF_UNSPEC;
- }
-
- hints.ai_flags = AI_ADDRCONFIG;
- hints.ai_socktype = SOCK_STREAM;
-
- // Actually make the call to get the data.
- int err = getaddrinfo(hostname, nullptr, &hints, &ai);
- if (err != 0 || ai == nullptr) {
- SB_DLOG(ERROR) << "getaddrinfo failed. last_error: " << WSAGetLastError();
- return nullptr;
- }
-
- int address_count = 0;
- for (const auto* i = ai; i != nullptr; i = i->ai_next) {
- ++address_count;
- }
-
- SbSocketResolution* result = new SbSocketResolution();
-
- // Translate all the sockaddrs.
- std::vector<sbwin32::SockAddr> sock_addrs;
- sock_addrs.resize(address_count);
-
- std::vector<bool> parsed;
- parsed.resize(address_count);
-
- int index = 0;
- int skip = 0;
- for (const auto *i = ai; i != nullptr; i = i->ai_next, ++index) {
- // Skip over any addresses we can't parse.
- parsed[index] = sock_addrs[index].FromSockaddr(i->ai_addr);
- if (!parsed[index]) {
- ++skip;
- }
- }
-
- result->address_count = address_count - skip;
- result->addresses = new SbSocketAddress[result->address_count];
-
- int result_index = 0;
- for (int i = 0; i < address_count; ++i) {
- if (parsed[i] &&
- sock_addrs[i].ToSbSocketAddress(&result->addresses[result_index])) {
- ++result_index;
- }
- }
-
- freeaddrinfo(ai);
- return result;
-}
diff --git a/src/starboard/shared/win32/socket_send_to.cc b/src/starboard/shared/win32/socket_send_to.cc
deleted file mode 100644
index 156c2c3..0000000
--- a/src/starboard/shared/win32/socket_send_to.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-int SbSocketSendTo(SbSocket socket,
- const char* data,
- int data_size,
- const SbSocketAddress* destination) {
- const int kSendFlags = 0;
- if (!SbSocketIsValid(socket)) {
- return -1;
- }
-
- SB_DCHECK(socket->socket_handle != INVALID_SOCKET);
- if (socket->protocol == kSbSocketProtocolTcp) {
- if (destination) {
- SB_DLOG(FATAL) << "Destination passed to TCP send.";
- socket->error = kSbSocketErrorFailed;
- return -1;
- }
-
- int bytes_written =
- send(socket->socket_handle, data, data_size, kSendFlags);
- if (bytes_written >= 0) {
- socket->error = kSbSocketOk;
- return static_cast<int>(bytes_written);
- }
-
- int last_error = WSAGetLastError();
-
- if ((last_error == WSAEWOULDBLOCK) || (last_error == WSAECONNABORTED)) {
- socket->writable.store(false);
- } else {
- SB_DLOG(ERROR) << "send failed, last_error = " << last_error;
- }
- socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
- return -1;
- } else if (socket->protocol == kSbSocketProtocolUdp) {
- if (!destination) {
- SB_DLOG(FATAL) << "No destination passed to UDP send.";
- socket->error = kSbSocketErrorFailed;
- return -1;
- }
-
- sbwin32::SockAddr sock_addr;
- const sockaddr* sockaddr = nullptr;
- socklen_t sockaddr_length = 0;
- if (destination) {
- if (!sock_addr.FromSbSocketAddress(destination)) {
- SB_DLOG(FATAL) << "Invalid destination passed to UDP send.";
- socket->error = kSbSocketErrorFailed;
- return -1;
- }
- sockaddr = sock_addr.sockaddr();
- sockaddr_length = sock_addr.length;
- }
-
- int bytes_written = sendto(socket->socket_handle, data, data_size,
- kSendFlags, sockaddr, sockaddr_length);
- if (bytes_written >= 0) {
- socket->error = kSbSocketOk;
- return static_cast<int>(bytes_written);
- }
-
- int last_error = WSAGetLastError();
- if (last_error != WSAEWOULDBLOCK) {
- SB_DLOG(ERROR) << "sendto failed, last_error = " << last_error;
- }
- socket->error = sbwin32::TranslateSocketErrorStatus(last_error);
- return -1;
- }
-
- SB_NOTREACHED() << "Unrecognized protocol: " << socket->protocol;
- return -1;
-}
diff --git a/src/starboard/shared/win32/socket_set_broadcast.cc b/src/starboard/shared/win32/socket_set_broadcast.cc
deleted file mode 100644
index b60156b..0000000
--- a/src/starboard/shared/win32/socket_set_broadcast.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketSetBroadcast(SbSocket socket, bool value) {
- return sbwin32::SetBooleanSocketOption(socket, SOL_SOCKET, SO_BROADCAST,
- "SO_BROADCAST", value);
-}
diff --git a/src/starboard/shared/win32/socket_set_receive_buffer_size.cc b/src/starboard/shared/win32/socket_set_receive_buffer_size.cc
deleted file mode 100644
index 61d108d..0000000
--- a/src/starboard/shared/win32/socket_set_receive_buffer_size.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketSetReceiveBufferSize(SbSocket socket, int32_t size) {
- return sbwin32::SetIntegerSocketOption(socket, SOL_SOCKET, SO_RCVBUF,
- "SO_RCVBUF", size);
-}
diff --git a/src/starboard/shared/win32/socket_set_reuse_address.cc b/src/starboard/shared/win32/socket_set_reuse_address.cc
deleted file mode 100644
index 91145e6..0000000
--- a/src/starboard/shared/win32/socket_set_reuse_address.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketSetReuseAddress(SbSocket socket, bool value) {
- return sbwin32::SetBooleanSocketOption(socket, SOL_SOCKET, SO_REUSEADDR,
- "SO_REUSEADDR", value);
-}
diff --git a/src/starboard/shared/win32/socket_set_send_buffer_size.cc b/src/starboard/shared/win32/socket_set_send_buffer_size.cc
deleted file mode 100644
index 2350e95..0000000
--- a/src/starboard/shared/win32/socket_set_send_buffer_size.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketSetSendBufferSize(SbSocket socket, int32_t size) {
- return sbwin32::SetIntegerSocketOption(socket, SOL_SOCKET, SO_SNDBUF,
- "SO_SNDBUF", size);
-}
diff --git a/src/starboard/shared/win32/socket_set_tcp_keep_alive.cc b/src/starboard/shared/win32/socket_set_tcp_keep_alive.cc
deleted file mode 100644
index 82820c9..0000000
--- a/src/starboard/shared/win32/socket_set_tcp_keep_alive.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketSetTcpKeepAlive(SbSocket socket, bool value, SbTime period) {
- SB_UNREFERENCED_PARAMETER(period);
- const DWORD should_set_keepalive = value;
- bool result = sbwin32::SetBooleanSocketOption(
- socket, SOL_SOCKET, SO_KEEPALIVE, "SO_KEEPALIVE", value);
- return result;
-}
diff --git a/src/starboard/shared/win32/socket_set_tcp_no_delay.cc b/src/starboard/shared/win32/socket_set_tcp_no_delay.cc
deleted file mode 100644
index 694d743..0000000
--- a/src/starboard/shared/win32/socket_set_tcp_no_delay.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <winsock2.h>
-
-#include "starboard/shared/win32/socket_internal.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-bool SbSocketSetTcpNoDelay(SbSocket socket, bool value) {
- return sbwin32::SetBooleanSocketOption(socket, IPPROTO_TCP, TCP_NODELAY,
- "TCP_NODELAY", value);
-}
diff --git a/src/starboard/shared/win32/socket_set_tcp_window_scaling.cc b/src/starboard/shared/win32/socket_set_tcp_window_scaling.cc
deleted file mode 100644
index 9a0e29a..0000000
--- a/src/starboard/shared/win32/socket_set_tcp_window_scaling.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket.h"
-
-#include <windows.h>
-#include <winsock2.h>
-
-// Note that windows.h and winsock2.h must be included before these.
-#include <mswsock.h>
-#include <sdkddkver.h>
-
-#include "starboard/shared/win32/socket_internal.h"
-
-bool SbSocketSetTcpWindowScaling(SbSocket socket, bool enable_window_scaling) {
- // From
- // https://msdn.microsoft.com/en-us/library/windows/desktop/cc136103(v=vs.85).aspx:
- // "When the TargetOsVersion member is set to a value for Windows Vista or
- // later, receive window auto-tuning is enabled and the TCP window scale
- // factor is reduced to 2 from the default value of 8."
-
- const ULONG target_os_version =
- enable_window_scaling ? NTDDI_VISTA : NTDDI_WS03;
- WSA_COMPATIBILITY_MODE compatibility_mode = {WsaBehaviorAutoTuning,
- target_os_version};
-
- DWORD kZero = 0;
- int return_value = WSAIoctl(
- socket->socket_handle, SIO_SET_COMPATIBILITY_MODE, &compatibility_mode,
- sizeof(WSA_COMPATIBILITY_MODE), nullptr, 0, &kZero, nullptr, nullptr);
-
- return return_value != SOCKET_ERROR;
-}
diff --git a/src/starboard/shared/win32/socket_waiter_add.cc b/src/starboard/shared/win32/socket_waiter_add.cc
deleted file mode 100644
index 3b5d9dc..0000000
--- a/src/starboard/shared/win32/socket_waiter_add.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket_waiter.h"
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/socket_waiter_internal.h"
-
-bool SbSocketWaiterAdd(SbSocketWaiter waiter,
- SbSocket socket,
- void* context,
- SbSocketWaiterCallback callback,
- int interests,
- bool persistent) {
- if (!SbSocketWaiterIsValid(waiter)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Waiter (" << waiter << ") is invalid.";
- return false;
- }
-
- if (!SbSocketIsValid(socket)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Socket (" << socket << ") is invalid.";
- return false;
- }
-
- if (!callback) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": No callback provided.";
- return false;
- }
-
- if (!interests) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": No interests provided.";
- return false;
- }
-
- return waiter->Add(socket, context, callback, interests, persistent);
-}
diff --git a/src/starboard/shared/win32/socket_waiter_create.cc b/src/starboard/shared/win32/socket_waiter_create.cc
deleted file mode 100644
index 05e8cd0..0000000
--- a/src/starboard/shared/win32/socket_waiter_create.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket_waiter.h"
-
-#include "starboard/shared/win32/socket_waiter_internal.h"
-
-SbSocketWaiter SbSocketWaiterCreate() {
- return new SbSocketWaiterPrivate();
-}
diff --git a/src/starboard/shared/win32/socket_waiter_destroy.cc b/src/starboard/shared/win32/socket_waiter_destroy.cc
deleted file mode 100644
index 69b38e1..0000000
--- a/src/starboard/shared/win32/socket_waiter_destroy.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket_waiter.h"
-
-#include "starboard/shared/win32/socket_waiter_internal.h"
-
-bool SbSocketWaiterDestroy(SbSocketWaiter waiter) {
- if (!SbSocketWaiterIsValid(waiter)) {
- return false;
- }
- delete waiter;
- return true;
-}
diff --git a/src/starboard/shared/win32/socket_waiter_internal.cc b/src/starboard/shared/win32/socket_waiter_internal.cc
deleted file mode 100644
index a5794f1..0000000
--- a/src/starboard/shared/win32/socket_waiter_internal.cc
+++ /dev/null
@@ -1,489 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/socket_waiter_internal.h"
-
-#include <windows.h>
-
-#include <algorithm>
-
-#include "starboard/common/optional.h"
-#include "starboard/log.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/socket_internal.h"
-#include "starboard/shared/win32/thread_private.h"
-#include "starboard/shared/win32/time_utils.h"
-#include "starboard/thread.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-namespace {
-
-// This is a helper function that takes data from |network_events|, and then
-// adds the bitwise ORs |interest_to_add| onto |interest_out|.
-// For more information, please see
-// https://msdn.microsoft.com/en-us/library/windows/desktop/ms741572(v=vs.85).aspx.
-void TranslateNetworkBitIntoInterests(const WSANETWORKEVENTS& network_events,
- int bit_to_check,
- SbSocketWaiterInterest interest_to_add,
- SbSocketWaiterInterest* interests_out) {
- SB_DCHECK(interests_out);
-
- static_assert(
- sizeof(SbSocketWaiterInterest) == sizeof(int),
- "Assuming size of enum is size of int, due to the bitfield logic below.");
-
- if (network_events.lNetworkEvents & (1 << bit_to_check)) {
- *(reinterpret_cast<int*>(interests_out)) |=
- static_cast<int>(interest_to_add);
- const int error_code = network_events.iErrorCode[bit_to_check];
- if (error_code != 0) {
- SB_DLOG(ERROR) << "Error on network event " << bit_to_check << " "
- << sbwin32::Win32ErrorCode(error_code);
- }
- }
-}
-
-SbSocketWaiterInterest DiscoverNetworkEventInterests(SOCKET socket_handle) {
- // Please take note that WSAEnumNetworkEvents below only works with
- // WSAEventSelect.
- SbSocketWaiterInterest interests = kSbSocketWaiterInterestNone;
- WSANETWORKEVENTS network_events = {0};
- int return_code =
- WSAEnumNetworkEvents(socket_handle, nullptr, &network_events);
- if (return_code == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << "WSAEnumNetworkEvents failed with last_error = "
- << sbwin32::Win32ErrorCode(last_error);
- return interests;
- }
-
- // Translate information from WSAEnumNetworkEvents to interests:
- // From the MSDN documentation:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms741572(v=vs.85).aspx
- // The lNetworkEvents member of the WSANETWORKEVENTS structure indicates which
- // of the FD_XXX network events have occurred. The iErrorCode array is used to
- // contain any associated error codes with the array index corresponding to
- // the position of event bits in lNetworkEvents. Identifiers such as
- // FD_READ_BIT and FD_WRITE_BIT can be used to index the iErrorCode array.
- // Note that only those elements of the iErrorCode array are set that
- // correspond to the bits set in lNetworkEvents parameter
- TranslateNetworkBitIntoInterests(network_events, FD_READ_BIT,
- kSbSocketWaiterInterestRead, &interests);
- TranslateNetworkBitIntoInterests(network_events, FD_ACCEPT_BIT,
- kSbSocketWaiterInterestRead, &interests);
- TranslateNetworkBitIntoInterests(network_events, FD_CLOSE_BIT,
- kSbSocketWaiterInterestRead, &interests);
-
- TranslateNetworkBitIntoInterests(network_events, FD_CONNECT_BIT,
- kSbSocketWaiterInterestWrite, &interests);
- TranslateNetworkBitIntoInterests(network_events, FD_WRITE_BIT,
- kSbSocketWaiterInterestWrite, &interests);
-
- return interests;
-}
-
-// The function erases the |index|th element from the collection by swapping
-// it with the last element. This operation leaves all the other elements in
-// place, which is useful for some operations.
-template <typename T>
-void EraseIndexFromVector(T* collection_pointer, std::size_t index) {
- SB_DCHECK(collection_pointer);
- T& collection = *collection_pointer;
- const std::size_t current_size = collection.size();
- if (current_size <= 1) {
- collection.clear();
- return;
- }
- const std::size_t new_size = collection.size() - 1;
- std::swap(collection[index], collection[new_size]);
- collection.resize(new_size);
-}
-
-SbSocketWaiterInterest CombineInterests(
- SbSocketWaiterInterest a, SbSocketWaiterInterest b) {
- int a_int = static_cast<int>(a);
- int b_int = static_cast<int>(b);
- return static_cast<SbSocketWaiterInterest>(a_int | b_int);
-}
-
-} // namespace
-
-SbSocketWaiterPrivate::SbSocketWaiterPrivate()
- : thread_(SbThreadGetCurrent()),
- wakeup_event_token_(-1),
- wakeup_event_(CreateEvent(nullptr, false, false, nullptr)) {
- {
- starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
- unhandled_wakeup_count_ = 0;
- }
- if (wakeup_event_.IsValid() == false) {
- SB_DLOG(ERROR) << "Could not create wakeup event: "
- << starboard::shared::win32::Win32ErrorCode(GetLastError());
- return;
- }
- wakeup_event_token_ =
- waitees_.AddSocketEventAndWaitee(wakeup_event_.GetEvent(), nullptr);
-}
-
-SbSocketWaiterPrivate::~SbSocketWaiterPrivate() {
- for (auto& it : waitees_.GetWaitees()) {
- if (it) {
- SB_DCHECK(CheckSocketWaiterIsThis(it->socket));
- }
- }
-}
-
-bool SbSocketWaiterPrivate::Add(SbSocket socket,
- void* context,
- SbSocketWaiterCallback callback,
- int interests,
- bool persistent) {
- SB_DCHECK(SbThreadIsCurrent(thread_));
-
- if (!SbSocketIsValid(socket)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Socket (" << socket << ") is invalid.";
- return false;
- }
-
- if (!interests) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": No interests provided.";
- return false;
- }
-
- // The policy is not to add a socket to a waiter if it is registered with
- // another waiter.
-
- // TODO: If anyone were to want to add a socket to a different waiter,
- // it would probably be another thread, so doing this check without locking is
- // probably wrong. But, it is also a pain, and, at this precise moment, socket
- // access is all going to come from one I/O thread anyway, and there will only
- // be one waiter.
- if (SbSocketWaiterIsValid(socket->waiter)) {
- if (socket->waiter == this) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Socket already has this waiter ("
- << this << ").";
- } else {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Socket already has waiter ("
- << socket->waiter << ", this=" << this << ").";
- }
- return false;
- }
-
- int network_event_interests = 0;
- if (interests & kSbSocketWaiterInterestRead) {
- network_event_interests |= FD_READ | FD_ACCEPT | FD_CLOSE;
- }
-
- if (interests & kSbSocketWaiterInterestWrite) {
- network_event_interests |= FD_CONNECT | FD_WRITE;
- }
-
- const BOOL manual_reset = !persistent;
-
- SB_DCHECK(!socket->socket_event.IsValid());
-
- socket->socket_event.Reset(
- CreateEvent(nullptr, manual_reset, false, nullptr));
- if (socket->socket_event.GetEvent() == WSA_INVALID_EVENT) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << "Error calling WSACreateEvent() last_error = "
- << sbwin32::Win32ErrorCode(last_error);
- return false;
- }
-
- // Note that WSAEnumNetworkEvents used elsewhere only works with
- // WSAEventSelect.
- // Please consider that before changing this code.
- int return_value =
- WSAEventSelect(socket->socket_handle, socket->socket_event.GetEvent(),
- network_event_interests);
-
- if (return_value == SOCKET_ERROR) {
- int last_error = WSAGetLastError();
- SB_DLOG(ERROR) << "Error calling WSAEventSelect() last_error = "
- << sbwin32::Win32ErrorCode(last_error);
- return false;
- }
-
- if (waitees_.GetHandleArraySize() >= MAXIMUM_WAIT_OBJECTS) {
- SB_DLOG(ERROR) << "Reached maxed number of socket events ("
- << MAXIMUM_WAIT_OBJECTS << ")";
- return false;
- }
-
- std::unique_ptr<Waitee> waitee(
- new Waitee(this, socket, context, callback, interests, persistent));
- waitees_.AddSocketEventAndWaitee(socket->socket_event.GetEvent(),
- std::move(waitee));
- socket->waiter = this;
-
- return true;
-}
-
-bool SbSocketWaiterPrivate::Remove(SbSocket socket) {
- SB_DCHECK(SbThreadIsCurrent(thread_));
-
- if (!CheckSocketWaiterIsThis(socket)) {
- return false;
- }
-
- socket->waiter = kSbSocketWaiterInvalid;
-
- socket->socket_event.Reset(nullptr);
- return waitees_.RemoveSocket(socket);
-}
-
-void SbSocketWaiterPrivate::HandleWakeUpRead() {
- SB_LOG(INFO) << "HandleWakeUpRead incrementing counter..";
- starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
- ++unhandled_wakeup_count_;
-}
-
-void SbSocketWaiterPrivate::SignalWakeupEvent() {
- SB_DCHECK(wakeup_event_.IsValid());
- WSASetEvent(wakeup_event_.GetEvent());
-}
-
-void SbSocketWaiterPrivate::ResetWakeupEvent() {
- SB_DCHECK(wakeup_event_.IsValid());
- WSAResetEvent(wakeup_event_.GetEvent());
-}
-
-bool SbSocketWaiterPrivate::CheckSocketWaiterIsThis(SbSocket socket) {
- if (!SbSocketIsValid(socket)) {
- SB_DLOG(ERROR) << __FUNCTION__ << ": Socket (" << socket << ") is invalid.";
- return false;
- }
-
- if (socket->waiter != this) {
- return false;
- }
-
- return true;
-}
-
-void SbSocketWaiterPrivate::Wait() {
- SB_DCHECK(SbThreadIsCurrent(thread_));
-
- // We basically wait for the largest amount of time to achieve an indefinite
- // block.
- WaitTimed(kSbTimeMax);
-}
-
-SbSocketWaiterResult SbSocketWaiterPrivate::WaitTimed(SbTime duration) {
- SB_DCHECK(SbThreadIsCurrent(thread_));
-
- const SbTimeMonotonic start_time = SbTimeGetMonotonicNow();
- int64_t duration_left = duration;
-
- while (true) {
- // |waitees_| could have been modified in the last loop iteration, so
- // re-read it.
- const DWORD number_events =
- static_cast<DWORD>(waitees_.GetHandleArraySize());
-
- const DWORD millis = sbwin32::ConvertSbTimeToMillisRoundUp(duration_left);
-
- {
- starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
- if (unhandled_wakeup_count_ > 0) {
- --unhandled_wakeup_count_;
- // The signaling thread also set the event, so reset it.
- ResetWakeupEvent();
- return kSbSocketWaiterResultWokenUp;
- }
- }
-
- // There should always be a wakeup event.
- SB_DCHECK(number_events > 0);
-
- SbSocket maybe_writable_socket = kSbSocketInvalid;
- for (auto& it : waitees_.GetWaitees()) {
- if (!it) {
- continue;
- }
- if ((it->interests & kSbSocketWaiterInterestWrite) == 0) {
- continue;
- }
- if (it->socket->writable.load()) {
- maybe_writable_socket = it->socket;
- break;
- }
- }
-
- bool has_writable = (maybe_writable_socket != kSbSocketInvalid);
- DWORD return_value = WSAWaitForMultipleEvents(
- number_events, waitees_.GetHandleArray(),
- false, has_writable ? 0 : millis, false);
-
- if (has_writable || ((return_value >= WSA_WAIT_EVENT_0) &&
- (return_value < (WSA_WAIT_EVENT_0 + number_events)))) {
- int64_t socket_index;
- if (has_writable) {
- socket_index = waitees_.GetIndex(maybe_writable_socket).value();
- } else {
- socket_index = static_cast<int64_t>(return_value) -
- static_cast<int64_t>(WSA_WAIT_EVENT_0);
- }
- SB_DCHECK(socket_index >= 0);
- if (socket_index < 0) {
- SB_NOTREACHED() << "Bad socket_index. " << socket_index;
- return kSbSocketWaiterResultTimedOut;
- }
-
- // Make sure wakeup_event_token_ is initialized.
- SB_DCHECK(wakeup_event_token_ >= 0);
-
- if (socket_index == wakeup_event_token_) {
- starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
- SB_DCHECK(unhandled_wakeup_count_ > 0);
-
- --unhandled_wakeup_count_;
- // This was a dummy event. We were woken up.
- // Note that we do not need to reset the event here,
- // since it was created using an auto-reset flag.
- return kSbSocketWaiterResultWokenUp;
- } else {
- Waitee* waitee = waitees_.GetWaiteeByIndex(socket_index);
-
- // Remove the non-persistent waitee before calling the callback, so
- // that we can add another waitee in the callback if we need to. This
- // is also why we copy all the fields we need out of waitee.
- const SbSocket socket = waitee->socket;
- const SbSocketWaiterCallback callback = waitee->callback;
- void* context = waitee->context;
-
- // Note: this should also go before Remove().
- SbSocketWaiterInterest interests =
- DiscoverNetworkEventInterests(socket->socket_handle);
-
- if ((waitee->interests & kSbSocketWaiterInterestWrite) &&
- socket->writable.load()) {
- interests = CombineInterests(interests, kSbSocketWaiterInterestWrite);
- } else if (interests & kSbSocketWaiterInterestWrite) {
- socket->writable.store(true);
- }
-
- if (!waitee->persistent) {
- Remove(waitee->socket);
- }
-
- callback(this, socket, context, interests);
- }
- } else if (return_value == WSA_WAIT_FAILED) {
- SB_DLOG(ERROR) << "Wait failed -- "
- << sbwin32::Win32ErrorCode(WSAGetLastError());
- return kSbSocketWaiterResultInvalid;
- } else if (return_value == WSA_WAIT_TIMEOUT) {
- // Do nothing, check time ourselves.
- } else {
- SB_NOTREACHED() << "Unhandled case: " << return_value;
- return kSbSocketWaiterResultInvalid;
- }
-
- const int64_t time_elapsed =
- static_cast<std::int64_t>(SbTimeGetMonotonicNow()) -
- static_cast<std::int64_t>(start_time);
- duration_left -= time_elapsed;
- if (duration_left < 0) {
- return kSbSocketWaiterResultTimedOut;
- }
- }
-
- SB_NOTREACHED() << "Invalid state reached";
- return kSbSocketWaiterResultInvalid;
-}
-
-void SbSocketWaiterPrivate::WakeUp() {
- // Increasing unhandled_wakeup_count_mutex_ and calling SignalWakeupEvent
- // atomically helps add additional guarantees of when the waiter can be
- // woken up. While we can code around this easily, having a stronger
- // coupling enables us to add DCHECKs for |unhandled_wakeup_count_| in other
- // parts of the code.
- starboard::ScopedLock lock(unhandled_wakeup_count_mutex_);
- ++unhandled_wakeup_count_;
- SignalWakeupEvent();
-}
-
-SbSocketWaiterPrivate::Waitee* SbSocketWaiterPrivate::WaiteeRegistry::GetWaitee(
- SbSocket socket) {
- starboard::optional<int64_t> token = GetIndex(socket);
- if (!token) {
- return nullptr;
- }
- return waitees_[token.value()].get();
-}
-
-starboard::optional<int64_t>
-SbSocketWaiterPrivate::WaiteeRegistry::GetIndex(SbSocket socket) {
- auto iterator = socket_to_index_map_.find(socket);
- if (iterator == socket_to_index_map_.end()) {
- return starboard::nullopt;
- }
- return iterator->second;
-}
-
-SbSocketWaiterPrivate::WaiteeRegistry::LookupToken
-SbSocketWaiterPrivate::WaiteeRegistry::AddSocketEventAndWaitee(
- WSAEVENT socket_event,
- std::unique_ptr<Waitee> waitee) {
- SB_DCHECK(socket_event != WSA_INVALID_EVENT);
- SB_DCHECK(socket_events_.size() == waitees_.size());
- SbSocket socket = kSbSocketInvalid;
- if (waitee) {
- socket = waitee->socket;
- }
- socket_to_index_map_.emplace(socket, socket_events_.size());
- socket_events_.emplace_back(socket_event);
- waitees_.emplace_back(std::move(waitee));
-
- return socket_events_.size() - 1;
-}
-
-bool SbSocketWaiterPrivate::WaiteeRegistry::RemoveSocket(SbSocket socket) {
- auto iterator = socket_to_index_map_.find(socket);
- if (iterator == socket_to_index_map_.end()) {
- return false;
- }
-
- const std::size_t current_size = socket_events_.size();
- SB_DCHECK(current_size == waitees_.size());
-
- const std::size_t socket_index = iterator->second;
- SbSocket socket_to_swap = waitees_[current_size - 1]->socket;
-
- // Since |EraseIndexFromVector| will swap the last socket and the socket
- // at current index, |socket_to_index_| will need to be updated.
- socket_to_index_map_[socket_to_swap] = socket_index;
- // Note that |EraseIndexFromVector| only touches the last element and the
- // element to remove.
- EraseIndexFromVector(&socket_events_, socket_index);
- EraseIndexFromVector(&waitees_, socket_index);
-
- socket_to_index_map_.erase(socket);
-
- SB_DCHECK(socket_events_.size() == waitees_.size());
- SB_DCHECK(socket_events_.size() == socket_to_index_map_.size());
- return true;
-}
-
-SbSocketWaiterPrivate::Waitee*
-SbSocketWaiterPrivate::WaiteeRegistry::GetWaiteeByIndex(
- const SbSocketWaiterPrivate::WaiteeRegistry::LookupToken socket_index) {
- SB_DCHECK(socket_index >= 0);
-
- SB_DCHECK(static_cast<std::size_t>(socket_index) <= socket_events_.size());
- return waitees_[socket_index].get();
-}
diff --git a/src/starboard/shared/win32/socket_waiter_internal.h b/src/starboard/shared/win32/socket_waiter_internal.h
deleted file mode 100644
index e21d93a..0000000
--- a/src/starboard/shared/win32/socket_waiter_internal.h
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_SOCKET_WAITER_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_SOCKET_WAITER_INTERNAL_H_
-
-#include <windows.h>
-#include <winsock2.h>
-
-#include <deque>
-#include <memory>
-#include <unordered_map>
-#include <vector>
-
-#include "starboard/common/optional.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/win32/auto_event_handle.h"
-#include "starboard/shared/win32/socket_internal.h"
-#include "starboard/socket.h"
-#include "starboard/socket_waiter.h"
-#include "starboard/thread.h"
-#include "starboard/types.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-#pragma warning(push)
-
-// SbSocketWaiterPrivate is defined as a struct, but for windows implementation
-// enough functionality has been added so that it warrants being a class
-// per Google's C++ style guide. This mismatch causes the Microsoft's compiler
-// to generate a warning.
-#pragma warning(disable : 4099)
-class SbSocketWaiterPrivate {
- public:
- SbSocketWaiterPrivate();
- ~SbSocketWaiterPrivate();
-
- // These methods implement the SbSocketWaiter API defined in socket_waiter.h.
- bool Add(SbSocket socket,
- void* context,
- SbSocketWaiterCallback callback,
- int interests,
- bool persistent);
- bool Remove(SbSocket socket);
- void Wait();
- SbSocketWaiterResult WaitTimed(SbTime duration);
- void WakeUp();
- void HandleWakeUpRead();
-
- private:
- // A registration of a socket with a socket waiter.
- struct Waitee {
- Waitee(SbSocketWaiter waiter,
- SbSocket socket,
- void* context,
- SbSocketWaiterCallback callback,
- int interests,
- bool persistent)
- : waiter(waiter),
- socket(socket),
- context(context),
- callback(callback),
- interests(interests),
- persistent(persistent) {}
- // The waiter this event is registered with.
- SbSocketWaiter waiter;
-
- // The socket registered with the waiter.
- SbSocket socket;
-
- // A context value that will be passed to the callback.
- void* context;
-
- // The callback to call when one or more registered interests become ready.
- SbSocketWaiterCallback callback;
-
- // The set of interests registered with the waiter.
- int interests;
-
- // Whether this Waitee should stay registered after the next callback.
- bool persistent;
- };
-
- class WaiteeRegistry {
- public:
- typedef int64_t LookupToken;
- typedef std::deque<std::unique_ptr<Waitee>> Waitees;
- typedef std::unordered_map<SbSocket, std::size_t> SocketToIndex;
-
- WSAEVENT* GetHandleArray() { return socket_events_.data(); }
- std::size_t GetHandleArraySize() { return socket_events_.size(); }
- const Waitees& GetWaitees() const { return waitees_; }
-
- // Gets the Waitee associated with the given socket, or nullptr.
- Waitee* GetWaitee(SbSocket socket);
-
- // Gets the index by socket
- starboard::optional<int64_t> GetIndex(SbSocket socket);
-
- // Gets the Waitee by index.
- Waitee* GetWaiteeByIndex(LookupToken socket_index);
-
- // Returns the index of the event.
- LookupToken AddSocketEventAndWaitee(WSAEVENT socket_event,
- std::unique_ptr<Waitee> waitee);
- // Returns true if socket was found, and removed.
- bool RemoveSocket(SbSocket socket);
-
- private:
- SocketToIndex socket_to_index_map_;
- std::vector<WSAEVENT> socket_events_;
- std::deque<std::unique_ptr<Waitee>> waitees_;
- };
-
- void SignalWakeupEvent();
- void ResetWakeupEvent();
-
- bool CheckSocketWaiterIsThis(SbSocket socket);
-
- // The thread this waiter was created on. Immutable, so accessible from any
- // thread.
- const SbThread thread_;
-
- // The registry of currently registered Waitees.
- WaiteeRegistry waitees_;
- WaiteeRegistry::LookupToken wakeup_event_token_;
-
- // This mutex covers the next two variables.
- starboard::Mutex unhandled_wakeup_count_mutex_;
- // Number of times wake up has been called, and not handled.
- std::int32_t unhandled_wakeup_count_;
- // The WSAEvent that is set by Wakeup();
- sbwin32::AutoEventHandle wakeup_event_;
-};
-#pragma warning(pop)
-
-#endif // STARBOARD_SHARED_WIN32_SOCKET_WAITER_INTERNAL_H_
diff --git a/src/starboard/shared/win32/socket_waiter_remove.cc b/src/starboard/shared/win32/socket_waiter_remove.cc
deleted file mode 100644
index 0f9a96c..0000000
--- a/src/starboard/shared/win32/socket_waiter_remove.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket_waiter.h"
-
-#include "starboard/shared/win32/socket_waiter_internal.h"
-
-bool SbSocketWaiterRemove(SbSocketWaiter waiter, SbSocket socket) {
- if (!SbSocketWaiterIsValid(waiter)) {
- return false;
- }
-
- return waiter->Remove(socket);
-}
diff --git a/src/starboard/shared/win32/socket_waiter_wait.cc b/src/starboard/shared/win32/socket_waiter_wait.cc
deleted file mode 100644
index 798b6d1..0000000
--- a/src/starboard/shared/win32/socket_waiter_wait.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket_waiter.h"
-
-#include "starboard/shared/win32/socket_waiter_internal.h"
-
-void SbSocketWaiterWait(SbSocketWaiter waiter) {
- if (!SbSocketWaiterIsValid(waiter)) {
- return;
- }
-
- waiter->Wait();
-}
diff --git a/src/starboard/shared/win32/socket_waiter_wait_timed.cc b/src/starboard/shared/win32/socket_waiter_wait_timed.cc
deleted file mode 100644
index 6823218..0000000
--- a/src/starboard/shared/win32/socket_waiter_wait_timed.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket_waiter.h"
-
-#include "starboard/shared/win32/socket_waiter_internal.h"
-
-SbSocketWaiterResult SbSocketWaiterWaitTimed(SbSocketWaiter waiter,
- SbTime duration) {
- if (!SbSocketWaiterIsValid(waiter)) {
- return kSbSocketWaiterResultInvalid;
- }
-
- return waiter->WaitTimed(duration);
-}
diff --git a/src/starboard/shared/win32/socket_waiter_wake_up.cc b/src/starboard/shared/win32/socket_waiter_wake_up.cc
deleted file mode 100644
index 9583560..0000000
--- a/src/starboard/shared/win32/socket_waiter_wake_up.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/socket_waiter.h"
-
-#include "starboard/shared/win32/socket_waiter_internal.h"
-
-void SbSocketWaiterWakeUp(SbSocketWaiter waiter) {
- if (!SbSocketWaiterIsValid(waiter)) {
- return;
- }
-
- waiter->WakeUp();
-}
diff --git a/src/starboard/shared/win32/starboard_main.cc b/src/starboard/shared/win32/starboard_main.cc
deleted file mode 100644
index a17ed99..0000000
--- a/src/starboard/shared/win32/starboard_main.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/starboard_main.h"
-
-#include <Objbase.h>
-#include <WinSock2.h>
-#include <mfapi.h>
-#include <windows.h>
-
-#include <cstdio>
-
-#include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
-#include "starboard/shared/starboard/command_line.h"
-#include "starboard/shared/starboard/net_log.h"
-#include "starboard/shared/win32/application_win32.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::starboard::CommandLine;
-using starboard::shared::starboard::kNetLogCommandSwitchWait;
-using starboard::shared::starboard::NetLogFlushThenClose;
-using starboard::shared::starboard::NetLogWaitForClientConnected;
-using starboard::shared::win32::ApplicationWin32;
-
-namespace {
-
-void WaitForNetLogIfNecessary(const CommandLine& cmd_line) {
- if (cmd_line.HasSwitch(kNetLogCommandSwitchWait)) {
- NetLogWaitForClientConnected();
- }
-}
-
-} // namespace.
-
-extern "C" int StarboardMain(int argc, char** argv) {
- WSAData wsaData;
- const int kWinSockVersionMajor = 2;
- const int kWinSockVersionMinor = 2;
- const int init_result = WSAStartup(
- MAKEWORD(kWinSockVersionMajor, kWinSockVersionMajor), &wsaData);
-
- SB_CHECK(init_result == 0);
- // WSAStartup returns the highest version that is supported up to the version
- // we request.
- SB_CHECK(LOBYTE(wsaData.wVersion) == kWinSockVersionMajor &&
- HIBYTE(wsaData.wVersion) == kWinSockVersionMinor);
-
- // Initialize COM for XAudio2 APIs.
- CoInitialize(nullptr);
- SbAudioSinkPrivate::Initialize();
- starboard::shared::win32::RegisterMainThread();
- // TODO: Do this with SbOnce when media is first used instead.
- HRESULT hr = MFStartup(MF_VERSION);
- SB_DCHECK(SUCCEEDED(hr));
-
- WaitForNetLogIfNecessary(CommandLine(argc, argv));
- ApplicationWin32 application;
- // This will run the message loop.
- const int main_return_value = application.Run(argc, argv);
- NetLogFlushThenClose();
-
- MFShutdown();
- WSACleanup();
- return main_return_value;
-}
diff --git a/src/starboard/shared/win32/starboard_main.h b/src/starboard/shared/win32/starboard_main.h
deleted file mode 100644
index 7d19ee0..0000000
--- a/src/starboard/shared/win32/starboard_main.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_STARBOARD_MAIN_H_
-#define STARBOARD_SHARED_WIN32_STARBOARD_MAIN_H_
-
-#include "starboard/export.h"
-
-extern "C" SB_EXPORT_PLATFORM int StarboardMain(int argc, char** argv);
-
-#endif // STARBOARD_SHARED_WIN32_STARBOARD_MAIN_H_
diff --git a/src/starboard/shared/win32/string_compare_no_case.cc b/src/starboard/shared/win32/string_compare_no_case.cc
deleted file mode 100644
index 39a9f2b..0000000
--- a/src/starboard/shared/win32/string_compare_no_case.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/string.h"
-
-#include <string.h>
-
-int SbStringCompareNoCase(const char* string1, const char* string2) {
- return _stricmp(string1, string2);
-}
-
diff --git a/src/starboard/shared/win32/string_compare_no_case_n.cc b/src/starboard/shared/win32/string_compare_no_case_n.cc
deleted file mode 100644
index cca7b9d..0000000
--- a/src/starboard/shared/win32/string_compare_no_case_n.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/string.h"
-
-#include <string.h>
-
-int SbStringCompareNoCaseN(const char* string1,
- const char* string2, size_t count) {
- return _strnicmp(string1, string2, count);
-}
-
diff --git a/src/starboard/shared/win32/string_compare_wide.cc b/src/starboard/shared/win32/string_compare_wide.cc
deleted file mode 100644
index 876d5b7..0000000
--- a/src/starboard/shared/win32/string_compare_wide.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/string.h"
-
-#include <string.h>
-
-int SbStringCompareWide(const wchar_t* string1, const wchar_t* string2,
- size_t count) {
- return wcsncmp(string1, string2, count);
-}
-
diff --git a/src/starboard/shared/win32/string_format.cc b/src/starboard/shared/win32/string_format.cc
deleted file mode 100644
index 1239718..0000000
--- a/src/starboard/shared/win32/string_format.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/string.h"
-
-#include <stdio.h>
-
-int SbStringFormat(char* out_buffer,
- size_t buffer_size,
- const char* format,
- va_list arguments) {
- return vsnprintf(out_buffer, buffer_size, format, arguments);
-}
diff --git a/src/starboard/shared/win32/string_format_wide.cc b/src/starboard/shared/win32/string_format_wide.cc
deleted file mode 100644
index 29258dc..0000000
--- a/src/starboard/shared/win32/string_format_wide.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/string.h"
-
-#include <stdio.h>
-
-int SbStringFormatWide(wchar_t* out_buffer,
- size_t buffer_size,
- const wchar_t* format,
- va_list arguments) {
- return _vsnwprintf(out_buffer, buffer_size, format, arguments);
-}
diff --git a/src/starboard/shared/win32/system_break_into_debugger.cc b/src/starboard/shared/win32/system_break_into_debugger.cc
deleted file mode 100644
index 4bea27f..0000000
--- a/src/starboard/shared/win32/system_break_into_debugger.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <intrin.h>
-
-#include "starboard/shared/starboard/net_log.h"
-
-void SbSystemBreakIntoDebugger() {
- // Note: neither DebugBreak() nor ExitProcess() are valid
- // on some Windows platforms (eg UWP) so we use __debugbreak()
- // instead.
- starboard::shared::starboard::NetLogFlush();
- __debugbreak();
-}
diff --git a/src/starboard/shared/win32/system_clear_last_error.cc b/src/starboard/shared/win32/system_clear_last_error.cc
deleted file mode 100644
index 9b1423d..0000000
--- a/src/starboard/shared/win32/system_clear_last_error.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-
-void SbSystemClearLastError() {
- SetLastError(0);
-}
diff --git a/src/starboard/shared/win32/system_clear_platform_error.cc b/src/starboard/shared/win32/system_clear_platform_error.cc
deleted file mode 100644
index 12521bb..0000000
--- a/src/starboard/shared/win32/system_clear_platform_error.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/application_win32.h"
-#include "starboard/system.h"
-
-using starboard::shared::win32::ApplicationWin32;
-
-void SbSystemClearPlatformError(SbSystemPlatformError handle) {
- ApplicationWin32::Get()->OnSbSystemClearPlatformError(handle);
-}
diff --git a/src/starboard/shared/win32/system_get_connection_type.cc b/src/starboard/shared/win32/system_get_connection_type.cc
deleted file mode 100644
index bced05d..0000000
--- a/src/starboard/shared/win32/system_get_connection_type.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <winsock2.h>
-
-#include <ifdef.h>
-#include <iphlpapi.h>
-
-#include "starboard/shared/win32/adapter_utils.h"
-
-namespace sbwin32 = ::starboard::shared::win32;
-
-namespace {
-// Return the connection type of the first "up" ethernet interface,
-// or unknown if none found.
-SbSystemConnectionType FindConnectionType(PIP_ADAPTER_ADDRESSES adapter) {
- for (; adapter != nullptr; adapter = adapter->Next) {
- if ((adapter->OperStatus != IfOperStatusUp) ||
- !sbwin32::IsIfTypeEthernet(adapter->IfType)) {
- continue;
- }
- // Some devices do not report IfType correctly.
- // So, an extra attempt at determining if an interface is wireless is made.
- if (adapter->IfType == IF_TYPE_IEEE80211 ||
- (wcsstr(adapter->Description, L"WiFi") != nullptr)) {
- return kSbSystemConnectionTypeWireless;
- }
- return kSbSystemConnectionTypeWired;
- }
- return kSbSystemConnectionTypeUnknown;
-}
-} // namespace
-
-SbSystemConnectionType SbSystemGetConnectionType() {
- std::unique_ptr<char[]> buffer;
- if (!sbwin32::GetAdapters(kSbSocketAddressTypeIpv4, &buffer)) {
- return kSbSystemConnectionTypeUnknown;
- }
- SbSystemConnectionType result = FindConnectionType(
- reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.get()));
- if (result != kSbSystemConnectionTypeUnknown) {
- return result;
- }
- if (!sbwin32::GetAdapters(kSbSocketAddressTypeIpv6, &buffer)) {
- return kSbSystemConnectionTypeUnknown;
- }
- return FindConnectionType(
- reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.get()));
-}
diff --git a/src/starboard/shared/win32/system_get_device_type.cc b/src/starboard/shared/win32/system_get_device_type.cc
deleted file mode 100644
index fc51f58..0000000
--- a/src/starboard/shared/win32/system_get_device_type.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <string>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-SbSystemDeviceType SbSystemGetDeviceType() {
- return kSbSystemDeviceTypeDesktopPC;
-}
diff --git a/src/starboard/shared/win32/system_get_error_string.cc b/src/starboard/shared/win32/system_get_error_string.cc
deleted file mode 100644
index 5aa5074..0000000
--- a/src/starboard/shared/win32/system_get_error_string.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <sstream>
-
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/string.h"
-
-int SbSystemGetErrorString(SbSystemError error,
- char* out_string,
- int string_length) {
- std::ostringstream out;
- out << starboard::shared::win32::Win32ErrorCode(error);
- if (out_string != nullptr) {
- SbStringCopy(out_string, out.str().c_str(), string_length);
- }
- return static_cast<int>(out.str().size());
-}
diff --git a/src/starboard/shared/win32/system_get_last_error.cc b/src/starboard/shared/win32/system_get_last_error.cc
deleted file mode 100644
index aa76753..0000000
--- a/src/starboard/shared/win32/system_get_last_error.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-
-SbSystemError SbSystemGetLastError() {
- return GetLastError();
-}
diff --git a/src/starboard/shared/win32/system_get_locale_id.cc b/src/starboard/shared/win32/system_get_locale_id.cc
deleted file mode 100644
index 99dbf34..0000000
--- a/src/starboard/shared/win32/system_get_locale_id.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <string>
-
-#include "starboard/log.h"
-#include "starboard/once.h"
-
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-using starboard::shared::win32::DebugLogWinError;
-using starboard::shared::win32::wchar_tToUTF8;
-
-namespace {
-class LocaleString {
- public:
- static LocaleString* Get();
- const char* value() const { return value_.c_str(); }
-
- private:
- LocaleString() {
- wchar_t name[LOCALE_NAME_MAX_LENGTH];
- int result = GetUserDefaultLocaleName(name, LOCALE_NAME_MAX_LENGTH);
- if (result != 0) {
- value_ = wchar_tToUTF8(name);
- } else {
- SB_LOG(ERROR) << "Error retrieving GetUserDefaultLocaleName";
- DebugLogWinError();
- value_ ="en-US";
- }
- }
- std::string value_;
-};
-
-SB_ONCE_INITIALIZE_FUNCTION(LocaleString, LocaleString::Get);
-} // namespace
-
-const char* SbSystemGetLocaleId() {
- return LocaleString::Get()->value();
-}
diff --git a/src/starboard/shared/win32/system_get_number_of_processors.cc b/src/starboard/shared/win32/system_get_number_of_processors.cc
deleted file mode 100644
index 59ec4e0..0000000
--- a/src/starboard/shared/win32/system_get_number_of_processors.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-
-int SbSystemGetNumberOfProcessors() {
- // Note that this returns the number of logical processors.
- SYSTEM_INFO system_info = {0};
- GetSystemInfo(&system_info);
- return system_info.dwNumberOfProcessors;
-}
diff --git a/src/starboard/shared/win32/system_get_property.cc b/src/starboard/shared/win32/system_get_property.cc
deleted file mode 100644
index 30e9522..0000000
--- a/src/starboard/shared/win32/system_get_property.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-#include "starboard/string.h"
-#include "starboard/system.h"
-
-namespace {
-
-const char* kFriendlyName = "Windows Desktop";
-const char* kPlatformName = "win32; Windows x86_64";
-
-bool CopyStringAndTestIfSuccess(char* out_value,
- int value_length,
- const char* from_value) {
- if (SbStringGetLength(from_value) + 1 > value_length)
- return false;
- SbStringCopy(out_value, from_value, value_length);
- return true;
-}
-
-} // namespace
-
-bool SbSystemGetProperty(SbSystemPropertyId property_id,
- char* out_value,
- int value_length) {
- if (!out_value || !value_length) {
- return false;
- }
-
- switch (property_id) {
- case kSbSystemPropertyBrandName:
- case kSbSystemPropertyChipsetModelNumber:
- case kSbSystemPropertyFirmwareVersion:
- case kSbSystemPropertyModelName:
- case kSbSystemPropertyModelYear:
- case kSbSystemPropertyNetworkOperatorName:
- case kSbSystemPropertySpeechApiKey:
- return false;
-
- case kSbSystemPropertyFriendlyName:
- return CopyStringAndTestIfSuccess(out_value, value_length, kFriendlyName);
-
- case kSbSystemPropertyPlatformName:
- return CopyStringAndTestIfSuccess(out_value, value_length, kPlatformName);
-
- case kSbSystemPropertyPlatformUuid:
- SB_NOTIMPLEMENTED();
- return CopyStringAndTestIfSuccess(out_value, value_length, "N/A");
-
- default:
- SB_DLOG(WARNING) << __FUNCTION__
- << ": Unrecognized property: " << property_id;
- break;
- }
-
- return false;
-}
diff --git a/src/starboard/shared/win32/system_get_random_data.cc b/src/starboard/shared/win32/system_get_random_data.cc
deleted file mode 100644
index a34f696..0000000
--- a/src/starboard/shared/win32/system_get_random_data.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-
-// This must be included after windows.h
-#include <bcrypt.h>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/error_utils.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-void SbSystemGetRandomData(void* out_buffer, int buffer_size) {
- // Note: this might not be secure before Windows 10, as
- // BCRYPT_RNG_DUAL_EC_ALGORITHM might be used. See:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375534%28v=vs.85%29.aspx
- // "Windows 10: Beginning with Windows 10, the dual elliptic curve random
- // number generator algorithm has been removed. Existing uses of this
- // algorithm will continue to work; however, the random number generator is
- // based on the AES counter mode specified in the NIST SP 800-90 standard. New
- // code should use BCRYPT_RNG_ALGORITHM, and it is recommended that existing
- // code be changed to use BCRYPT_RNG_ALGORITHM."
- NTSTATUS status =
- BCryptGenRandom(nullptr, reinterpret_cast<PUCHAR>(out_buffer),
- buffer_size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
- SB_DCHECK(status == 0) << "Error while calling CryptGenRandom: "
- << sbwin32::Win32ErrorCode(GetLastError());
-}
diff --git a/src/starboard/shared/win32/system_get_random_uint64.cc b/src/starboard/shared/win32/system_get_random_uint64.cc
deleted file mode 100644
index 2eb95f7..0000000
--- a/src/starboard/shared/win32/system_get_random_uint64.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-uint64_t SbSystemGetRandomUInt64() {
- uint64_t return_value = 0;
- SbSystemGetRandomData(&return_value, sizeof(return_value));
- return return_value;
-}
diff --git a/src/starboard/shared/win32/system_get_stack.cc b/src/starboard/shared/win32/system_get_stack.cc
deleted file mode 100644
index dc1f918..0000000
--- a/src/starboard/shared/win32/system_get_stack.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2016 Google Inc. 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 "starboard/system.h"
-
-#include <Windows.h>
-
-__declspec(noinline) int SbSystemGetStack(void** out_stack, int stack_size) {
- ULONG frames_to_skip = 1;
- int num_captured = CaptureStackBackTrace(frames_to_skip, stack_size,
- out_stack, nullptr);
- return num_captured;
-}
diff --git a/src/starboard/shared/win32/system_get_total_cpu_memory.cc b/src/starboard/shared/win32/system_get_total_cpu_memory.cc
deleted file mode 100644
index 7c91399..0000000
--- a/src/starboard/shared/win32/system_get_total_cpu_memory.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-
-int64_t SbSystemGetTotalCPUMemory() {
- MEMORYSTATUSEX statex = {0};
- statex.dwLength = sizeof(statex);
- GlobalMemoryStatusEx(&statex);
- return static_cast<int64_t>(statex.ullTotalPhys);
-}
diff --git a/src/starboard/shared/win32/system_get_used_cpu_memory.cc b/src/starboard/shared/win32/system_get_used_cpu_memory.cc
deleted file mode 100644
index 7cddea1..0000000
--- a/src/starboard/shared/win32/system_get_used_cpu_memory.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-
-int64_t SbSystemGetUsedCPUMemory() {
- MEMORYSTATUSEX statex = {0};
- statex.dwLength = sizeof(statex);
- GlobalMemoryStatusEx(&statex);
- int64_t remaining_bytes = static_cast<int64_t>(statex.ullTotalPhys) -
- static_cast<int64_t>(statex.ullAvailPhys);
- SB_DCHECK(remaining_bytes >= 0);
- return remaining_bytes;
-}
diff --git a/src/starboard/shared/win32/system_is_debugger_attached.cc b/src/starboard/shared/win32/system_is_debugger_attached.cc
deleted file mode 100644
index 7fec162..0000000
--- a/src/starboard/shared/win32/system_is_debugger_attached.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-
-bool SbSystemIsDebuggerAttached() {
- return IsDebuggerPresent();
-}
diff --git a/src/starboard/shared/win32/system_raise_platform_error.cc b/src/starboard/shared/win32/system_raise_platform_error.cc
deleted file mode 100644
index e53b245..0000000
--- a/src/starboard/shared/win32/system_raise_platform_error.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/application_win32.h"
-#include "starboard/system.h"
-
-using starboard::shared::win32::ApplicationWin32;
-
-SbSystemPlatformError SbSystemRaisePlatformError(
- SbSystemPlatformErrorType type,
- SbSystemPlatformErrorCallback callback,
- void* user_data) {
- return ApplicationWin32::Get()->OnSbSystemRaisePlatformError(type, callback,
- user_data);
-}
diff --git a/src/starboard/shared/win32/thread_create.cc b/src/starboard/shared/win32/thread_create.cc
deleted file mode 100644
index 527eb10..0000000
--- a/src/starboard/shared/win32/thread_create.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <process.h>
-#include <memory>
-
-#include "starboard/log.h"
-#include "starboard/once.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/thread_private.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-namespace sbwin32 = starboard::shared::win32;
-
-using sbwin32::DebugLogWinError;
-using sbwin32::GetThreadSubsystemSingleton;
-using sbwin32::SbThreadPrivate;
-using sbwin32::ThreadSubsystemSingleton;
-using sbwin32::wchar_tToUTF8;
-
-namespace {
-
-void ResetWinError() {
- SetLastError(0);
-}
-
-class ThreadCreateInfo {
- public:
- SbThreadPrivate thread_private_;
- SbThreadEntryPoint entry_point_;
- void* user_context_;
- std::string name_;
-};
-
-int RunThreadLocalDestructors(ThreadSubsystemSingleton* singleton) {
- int num_destructors_called = 0;
-
- for (auto it = singleton->thread_local_keys_.begin();
- it != singleton->thread_local_keys_.end();) {
- auto curr_it = it++;
-
- if (!curr_it->second->destructor) {
- continue;
- }
- auto key = curr_it->second;
- void* entry = SbThreadGetLocalValue(key);
- if (!entry) {
- continue;
- }
- SbThreadSetLocalValue(key, nullptr);
- ++num_destructors_called;
- curr_it->second->destructor(entry);
- }
- return num_destructors_called;
-}
-
-int CountTlsObjectsRemaining(ThreadSubsystemSingleton* singleton) {
- int num_objects_remain = 0;
- for (auto it = singleton->thread_local_keys_.begin();
- it != singleton->thread_local_keys_.end(); ++it) {
- if (!it->second->destructor) {
- continue;
- }
- auto key = it->second;
- void* entry = SbThreadGetLocalValue(key);
- if (!entry) {
- continue;
- }
- ++num_objects_remain;
- }
- return num_objects_remain;
-}
-
-void CallThreadLocalDestructorsMultipleTimes() {
- // The number of passes conforms to the base_unittests. This is useful for
- // destructors that insert new objects into thread local storage. These
- // objects then need to be destroyed as well in subsequent passes. The total
- // number of passes is 4, which is one more than what base_unittest tests
- // for.
- const int kNumDestructorPasses = 4;
-
- ThreadSubsystemSingleton* singleton = GetThreadSubsystemSingleton();
- int num_tls_objects_remaining = 0;
- // TODO note that the implementation below holds a global lock
- // while processing TLS destructors on thread exit. This could
- // be a bottleneck in some scenarios. A lockless approach may be preferrable.
- SbMutexAcquire(&singleton->mutex_);
-
- for (int i = 0; i < kNumDestructorPasses; ++i) {
- // Run through each destructor and call it.
- const int num_destructors_called = RunThreadLocalDestructors(singleton);
- if (0 == num_destructors_called) {
- break; // No more destructors to call.
- }
- }
- num_tls_objects_remaining = CountTlsObjectsRemaining(singleton);
- SbMutexRelease(&singleton->mutex_);
-
- SB_DCHECK(num_tls_objects_remaining == 0) << "Dangling objects in TLS exist.";
-}
-
-unsigned ThreadTrampoline(void* thread_create_info_context) {
- std::unique_ptr<ThreadCreateInfo> info(
- static_cast<ThreadCreateInfo*>(thread_create_info_context));
-
- ThreadSubsystemSingleton* singleton = GetThreadSubsystemSingleton();
- SbThreadSetLocalValue(singleton->thread_private_key_, &info->thread_private_);
- SbThreadSetName(info->name_.c_str());
-
- void* result = info->entry_point_(info->user_context_);
-
- CallThreadLocalDestructorsMultipleTimes();
-
- SbMutexAcquire(&info->thread_private_.mutex_);
- info->thread_private_.result_ = result;
- info->thread_private_.result_is_valid_ = true;
- SbConditionVariableSignal(&info->thread_private_.condition_);
- while (info->thread_private_.wait_for_join_) {
- SbConditionVariableWait(&info->thread_private_.condition_,
- &info->thread_private_.mutex_);
- }
- SbMutexRelease(&info->thread_private_.mutex_);
-
- return 0;
-}
-
-int SbThreadPriorityToWin32Priority(SbThreadPriority priority) {
- switch (priority) {
- case kSbThreadPriorityLowest:
- return THREAD_PRIORITY_LOWEST;
- case kSbThreadPriorityLow:
- return THREAD_PRIORITY_BELOW_NORMAL;
- case kSbThreadPriorityNormal:
- case kSbThreadNoPriority:
- return THREAD_PRIORITY_NORMAL;
- case kSbThreadPriorityHigh:
- return THREAD_PRIORITY_ABOVE_NORMAL;
- case kSbThreadPriorityHighest:
- return THREAD_PRIORITY_HIGHEST;
- case kSbThreadPriorityRealTime:
- return THREAD_PRIORITY_TIME_CRITICAL;
- }
- SB_NOTREACHED() << "Invalid priority " << priority;
- return 0;
-}
-} // namespace
-
-// Note that SetThreadAffinityMask() is not available on some
-// platforms (eg UWP). If it's necessary for a non-UWP platform,
-// please fork this implementation for UWP.
-SbThread SbThreadCreate(int64_t stack_size,
- SbThreadPriority priority,
- SbThreadAffinity /*affinity*/,
- bool joinable,
- const char* name,
- SbThreadEntryPoint entry_point,
- void* context) {
- if (entry_point == NULL) {
- return kSbThreadInvalid;
- }
- ThreadCreateInfo* info = new ThreadCreateInfo();
-
- info->entry_point_ = entry_point;
- info->user_context_ = context;
- info->thread_private_.wait_for_join_ = joinable;
- if (name) {
- info->name_ = name;
- }
-
- // Create the thread suspended, and then resume once ThreadCreateInfo::handle_
- // has been set, so that it's alway valid in the ThreadCreateInfo
- // destructor.
- uintptr_t handle =
- _beginthreadex(NULL, static_cast<unsigned int>(stack_size),
- ThreadTrampoline, info, CREATE_SUSPENDED, NULL);
- SB_DCHECK(handle);
- info->thread_private_.handle_ = reinterpret_cast<HANDLE>(handle);
- ResetWinError();
- if (priority != kSbThreadNoPriority &&
- !SetThreadPriority(info->thread_private_.handle_,
- SbThreadPriorityToWin32Priority(priority)) &&
- !GetLastError()) {
- SB_LOG(ERROR) << "Failed to set priority for thread " << (name ? name : "")
- << " to " << priority;
- DebugLogWinError();
- }
-
- ResumeThread(info->thread_private_.handle_);
-
- return &info->thread_private_;
-}
diff --git a/src/starboard/shared/win32/thread_create_local_key.cc b/src/starboard/shared/win32/thread_create_local_key.cc
deleted file mode 100644
index 5e7ece6..0000000
--- a/src/starboard/shared/win32/thread_create_local_key.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-#include "starboard/memory.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/win32/thread_local_internal.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::GetThreadSubsystemSingleton;
-using starboard::shared::win32::ThreadSubsystemSingleton;
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-SbThreadLocalKey SbThreadCreateLocalKeyInternal(
- SbThreadLocalDestructor destructor,
- ThreadSubsystemSingleton* singleton) {
- // Note that destructor used here to allow destruction of objects created by
- // TLS access by non-starboard threads.
- // Note that FlsAlloc's destructor mechanism is insufficient to pass
- // base_unittests, which expects that multiple destructor passes for
- // objects which insert destructable TLS pointers as side effects. For
- // starboard threads, the TLS destructors will run first and set the
- // TLS pointers to null, then the destructors will run a second time
- // but this is okay since the pointers will now be nullptrs, which is
- // a no-op. For non starboard threads, only the secondary destructor
- // will run.
- DWORD index = TlsInternalAlloc(destructor);
-
- if (index == TLS_OUT_OF_INDEXES) {
- return kSbThreadLocalKeyInvalid;
- }
-
- SbThreadLocalKeyPrivate* result = static_cast<SbThreadLocalKeyPrivate*>(
- SbMemoryAllocateNoReport(sizeof(SbThreadLocalKeyPrivate)));
-
- if (result == nullptr) {
- return kSbThreadLocalKeyInvalid;
- }
-
- result->tls_index = index;
- result->destructor = destructor;
-
- SbMutexAcquire(&singleton->mutex_);
- singleton->thread_local_keys_.insert(std::make_pair(index, result));
- SbMutexRelease(&singleton->mutex_);
- return result;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-SbThreadLocalKey SbThreadCreateLocalKey(SbThreadLocalDestructor destructor) {
- ThreadSubsystemSingleton* singleton = GetThreadSubsystemSingleton();
- return SbThreadCreateLocalKeyInternal(destructor, singleton);
-}
diff --git a/src/starboard/shared/win32/thread_destroy_local_key.cc b/src/starboard/shared/win32/thread_destroy_local_key.cc
deleted file mode 100644
index 814db1d..0000000
--- a/src/starboard/shared/win32/thread_destroy_local_key.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-#include "starboard/memory.h"
-#include "starboard/shared/win32/thread_local_internal.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::GetThreadSubsystemSingleton;
-using starboard::shared::win32::TlsInternalFree;
-using starboard::shared::win32::ThreadSubsystemSingleton;
-
-void SbThreadDestroyLocalKey(SbThreadLocalKey key) {
- if (!SbThreadIsValidLocalKey(key)) {
- return;
- }
- // To match pthreads, the thread local pointer for the key is set to null
- // so that a supplied destructor doesn't run.
- SbThreadSetLocalValue(key, nullptr);
- DWORD tls_index = static_cast<SbThreadLocalKeyPrivate*>(key)->tls_index;
- ThreadSubsystemSingleton* singleton = GetThreadSubsystemSingleton();
-
- SbMutexAcquire(&singleton->mutex_);
- singleton->thread_local_keys_.erase(tls_index);
- SbMutexRelease(&singleton->mutex_);
-
- TlsInternalFree(tls_index);
- SbMemoryDeallocateNoReport(key);
-}
diff --git a/src/starboard/shared/win32/thread_detach.cc b/src/starboard/shared/win32/thread_detach.cc
deleted file mode 100644
index 422a12d..0000000
--- a/src/starboard/shared/win32/thread_detach.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include "starboard/condition_variable.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::SbThreadPrivate;
-
-void SbThreadDetach(SbThread thread) {
- if (thread == kSbThreadInvalid) {
- return;
- }
- SbThreadPrivate* thread_private = static_cast<SbThreadPrivate*>(thread);
-
- SbMutexAcquire(&thread_private->mutex_);
- thread_private->wait_for_join_ = false;
- SbConditionVariableSignal(&thread_private->condition_);
- SbMutexRelease(&thread_private->mutex_);
-}
diff --git a/src/starboard/shared/win32/thread_get_current.cc b/src/starboard/shared/win32/thread_get_current.cc
deleted file mode 100644
index 12ba544..0000000
--- a/src/starboard/shared/win32/thread_get_current.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::GetCurrentSbThreadPrivate;
-using starboard::shared::win32::SbThreadPrivate;
-
-SbThread SbThreadGetCurrent() {
- return GetCurrentSbThreadPrivate();
-}
diff --git a/src/starboard/shared/win32/thread_get_id.cc b/src/starboard/shared/win32/thread_get_id.cc
deleted file mode 100644
index b6a7293..0000000
--- a/src/starboard/shared/win32/thread_get_id.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-SbThreadId SbThreadGetId() {
- return GetCurrentThreadId();
-}
diff --git a/src/starboard/shared/win32/thread_get_local_value.cc b/src/starboard/shared/win32/thread_get_local_value.cc
deleted file mode 100644
index 4dc175d..0000000
--- a/src/starboard/shared/win32/thread_get_local_value.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/thread_local_internal.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::TlsInternalGetValue;
-
-void* SbThreadGetLocalValue(SbThreadLocalKey key) {
- if (!SbThreadIsValidLocalKey(key)) {
- return NULL;
- }
- DWORD tls_index = static_cast<SbThreadLocalKeyPrivate*>(key)->tls_index;
- return TlsInternalGetValue(tls_index);
-}
diff --git a/src/starboard/shared/win32/thread_get_name.cc b/src/starboard/shared/win32/thread_get_name.cc
deleted file mode 100644
index a3dd3d7..0000000
--- a/src/starboard/shared/win32/thread_get_name.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-#include "starboard/string.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::GetCurrentSbThreadPrivate;
-using starboard::shared::win32::SbThreadPrivate;
-
-void SbThreadGetName(char* buffer, int buffer_size) {
- SbThreadPrivate* thread_private = GetCurrentSbThreadPrivate();
- SbStringCopy(buffer, thread_private->name_.c_str(), buffer_size);
-}
diff --git a/src/starboard/shared/win32/thread_is_equal.cc b/src/starboard/shared/win32/thread_is_equal.cc
deleted file mode 100644
index ccc2d64..0000000
--- a/src/starboard/shared/win32/thread_is_equal.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-bool SbThreadIsEqual(SbThread thread1, SbThread thread2) {
- return thread1 == thread2;
-}
diff --git a/src/starboard/shared/win32/thread_join.cc b/src/starboard/shared/win32/thread_join.cc
deleted file mode 100644
index 44e1589..0000000
--- a/src/starboard/shared/win32/thread_join.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include "starboard/condition_variable.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::SbThreadPrivate;
-
-bool SbThreadJoin(SbThread thread, void** out_return) {
- if (thread == kSbThreadInvalid) {
- return false;
- }
-
- SbThreadPrivate* thread_private = static_cast<SbThreadPrivate*>(thread);
-
- SbMutexAcquire(&thread_private->mutex_);
- if (!thread_private->wait_for_join_) {
- // Thread has already been detached.
- SbMutexRelease(&thread_private->mutex_);
- return false;
- }
- while (!thread_private->result_is_valid_) {
- SbConditionVariableWait(&thread_private->condition_,
- &thread_private->mutex_);
- }
- thread_private->wait_for_join_ = false;
- SbConditionVariableSignal(&thread_private->condition_);
- if (out_return != NULL) {
- *out_return = thread_private->result_;
- }
- SbMutexRelease(&thread_private->mutex_);
- return true;
-}
diff --git a/src/starboard/shared/win32/thread_local_internal.cc b/src/starboard/shared/win32/thread_local_internal.cc
deleted file mode 100644
index 74313d1..0000000
--- a/src/starboard/shared/win32/thread_local_internal.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/thread_local_internal.h"
-
-#include <windows.h>
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// FlsAlloc() is like TlsAlloc(), but allows destructors.
-// The downside is that FlsAlloc() has a small upper limit of
-// about ~100 available keys. To provide extra keys, TlsAlloc()
-// is used whenever a destructor is not necessary.
-DWORD TlsInternalAlloc(SbThreadLocalDestructor destructor_fn) {
- DWORD output = 0;
- if (destructor_fn) {
- output = FlsAlloc(destructor_fn);
- } else {
- output = TlsAlloc();
- }
- if (output == TLS_OUT_OF_INDEXES) {
- return TLS_OUT_OF_INDEXES;
- }
- return (output << 1) | (destructor_fn ? 0x1 : 0x0);
-}
-
-void TlsInternalFree(DWORD key) {
- bool has_destructor = key & 0x1;
- DWORD index = key >> 1;
- if (has_destructor) {
- FlsFree(index);
- } else {
- TlsFree(index);
- }
-}
-
-void* TlsInternalGetValue(DWORD key) {
- bool has_destructor = key & 0x1;
- DWORD index = key >> 1;
- if (has_destructor) {
- return FlsGetValue(index);
- } else {
- return TlsGetValue(index);
- }
-}
-
-bool TlsInternalSetValue(DWORD key, void* value) {
- bool has_destructor = key & 0x1;
- DWORD index = key >> 1;
- if (has_destructor) {
- return FlsSetValue(index, value);
- } else {
- return TlsSetValue(index, value);
- }
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/thread_local_internal.h b/src/starboard/shared/win32/thread_local_internal.h
deleted file mode 100644
index 3e8d2c8..0000000
--- a/src/starboard/shared/win32/thread_local_internal.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_THREAD_LOCAL_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_THREAD_LOCAL_INTERNAL_H_
-
-#include <windows.h>
-
-#include "starboard/thread.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-DWORD TlsInternalAlloc(SbThreadLocalDestructor destructor_fn);
-void TlsInternalFree(DWORD key);
-
-void* TlsInternalGetValue(DWORD key);
-bool TlsInternalSetValue(DWORD key, void* value);
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_THREAD_LOCAL_INTERNAL_H_
diff --git a/src/starboard/shared/win32/thread_private.cc b/src/starboard/shared/win32/thread_private.cc
deleted file mode 100644
index e7e17cb..0000000
--- a/src/starboard/shared/win32/thread_private.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <process.h>
-#include <memory>
-
-#include "starboard/log.h"
-#include "starboard/once.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::GetThreadSubsystemSingleton;
-using starboard::shared::win32::SbThreadPrivate;
-using starboard::shared::win32::ThreadSubsystemSingleton;
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-SB_ONCE_INITIALIZE_FUNCTION(ThreadSubsystemSingleton,
- GetThreadSubsystemSingleton);
-
-void RegisterMainThread() {
- std::unique_ptr<SbThreadPrivate> thread_private(new SbThreadPrivate());
-
- // GetCurrentThread() returns a pseudo-handle that must be duplicated
- // to be used in general cases.
- HANDLE pseudo_handle = GetCurrentThread();
- HANDLE handle;
- BOOL success =
- DuplicateHandle(GetCurrentProcess(), pseudo_handle, GetCurrentProcess(),
- &handle, 0, false, DUPLICATE_SAME_ACCESS);
- SB_DCHECK(success) << "DuplicateHandle failed";
-
- thread_private->handle_ = handle;
- thread_private->wait_for_join_ = false;
-
- SbThreadSetLocalValue(GetThreadSubsystemSingleton()->thread_private_key_,
- thread_private.release());
-}
-
-SbThreadPrivate* GetCurrentSbThreadPrivate() {
- SbThreadPrivate* sb_thread_private =
- static_cast<SbThreadPrivate*>(SbThreadGetLocalValue(
- GetThreadSubsystemSingleton()->thread_private_key_));
- if (sb_thread_private == nullptr) {
- // We are likely on a thread we did not create, so TLS needs to be setup.
- RegisterMainThread();
- sb_thread_private = static_cast<SbThreadPrivate*>(SbThreadGetLocalValue(
- GetThreadSubsystemSingleton()->thread_private_key_));
- // TODO: Clean up TLS storage for threads we do not create.
- }
- return sb_thread_private;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/thread_private.h b/src/starboard/shared/win32/thread_private.h
deleted file mode 100644
index b7b0036..0000000
--- a/src/starboard/shared/win32/thread_private.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_THREAD_PRIVATE_H_
-#define STARBOARD_SHARED_WIN32_THREAD_PRIVATE_H_
-
-#include <windows.h>
-
-#include <map>
-#include <string>
-
-#include "starboard/condition_variable.h"
-#include "starboard/mutex.h"
-#include "starboard/once.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/thread.h"
-
-struct SbThreadLocalKeyPrivate {
- DWORD tls_index;
- SbThreadLocalDestructor destructor;
-};
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class ThreadSubsystemSingleton;
-
-// Creates a SbThreadLocalKey given a ThreadSubsystemSingleton. Used
-// to create the first SbThreadLocalKey.
-SbThreadLocalKey SbThreadCreateLocalKeyInternal(
- SbThreadLocalDestructor destructor,
- ThreadSubsystemSingleton* singleton);
-
-// Singleton state for the thread subsystem.
-class ThreadSubsystemSingleton {
- public:
- ThreadSubsystemSingleton()
- : mutex_(SB_MUTEX_INITIALIZER),
- thread_private_key_(SbThreadCreateLocalKeyInternal(NULL, this)) {}
- // This mutex protects all class members
- SbMutex mutex_;
- // Allocated thread_local_keys. Note that std::map is used
- // so that elements can be deleted without triggering an allocation.
- std::map<DWORD, SbThreadLocalKeyPrivate*> thread_local_keys_;
- // Thread-local key for the thread's SbThreadPrivate
- SbThreadLocalKey thread_private_key_;
-};
-
-// Obtains the ThreadsSubsystemSingleton();
-ThreadSubsystemSingleton* GetThreadSubsystemSingleton();
-
-// Registers the main thread. setting it's SbThreadPrivate*
-void RegisterMainThread();
-
-// Private thread state, stored in thread local storage and
-// cleaned up when thread exits.
-class SbThreadPrivate {
- public:
- SbThreadPrivate()
- : mutex_(SB_MUTEX_INITIALIZER),
- condition_(SB_CONDITION_VARIABLE_INITIALIZER),
- handle_(NULL),
- result_(NULL),
- wait_for_join_(false),
- result_is_valid_(false) {}
-
- ~SbThreadPrivate() {
- if (handle_) {
- CloseHandle(handle_);
- }
-
- SbMutexDestroy(&mutex_);
- SbConditionVariableDestroy(&condition_);
- }
-
- // This mutex protects all class members
- SbMutex mutex_;
- SbConditionVariable condition_;
- std::string name_;
- HANDLE handle_;
- // The result of the thread. The return value of SbThreadEntryPoint
- // to return to SbThreadJoin.
- void* result_;
- // True if a thread must wait to be joined before completely exiting.
- // Changes to this must signal |condition_|.
- bool wait_for_join_;
- // True if |result_| is valid (the thread has completed and is waiting
- // to exit). Changes to this must signal |condition_|.
- bool result_is_valid_;
-};
-
-// Obtains the current thread's SbThreadPrivate* from thread-local storage.
-SbThreadPrivate* GetCurrentSbThreadPrivate();
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_THREAD_PRIVATE_H_
diff --git a/src/starboard/shared/win32/thread_set_local_value.cc b/src/starboard/shared/win32/thread_set_local_value.cc
deleted file mode 100644
index 12dbe24..0000000
--- a/src/starboard/shared/win32/thread_set_local_value.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/thread_local_internal.h"
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::TlsInternalSetValue;
-
-bool SbThreadSetLocalValue(SbThreadLocalKey key, void* value) {
- if (!SbThreadIsValidLocalKey(key)) {
- return false;
- }
- DWORD tls_index = static_cast<SbThreadLocalKeyPrivate*>(key)->tls_index;
- return TlsInternalSetValue(tls_index, value);
-}
diff --git a/src/starboard/shared/win32/thread_set_name.cc b/src/starboard/shared/win32/thread_set_name.cc
deleted file mode 100644
index 1a7db98..0000000
--- a/src/starboard/shared/win32/thread_set_name.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/thread_private.h"
-
-using starboard::shared::win32::GetCurrentSbThreadPrivate;
-using starboard::shared::win32::SbThreadPrivate;
-
-namespace {
-
-// The code below is from
-// https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
-// A |dwThreadID| of -1 means "current thread";
-//
-// Usage: SetThreadName ((DWORD)-1, "MainThread");
-//
-const DWORD MS_VC_EXCEPTION = 0x406D1388;
-#pragma pack(push, 8)
-typedef struct tagTHREADNAME_INF {
- DWORD dwType; // Must be 0x1000.
- LPCSTR szName; // Pointer to name (in user addr space).
- DWORD dwThreadID; // Thread ID (-1=caller thread).
- DWORD dwFlags; // Reserved for future use, must be zero.
-} THREADNAME_INFO;
-#pragma pack(pop)
-void SetThreadName(DWORD dwThreadID, const char* threadName) {
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = threadName;
- info.dwThreadID = dwThreadID;
- info.dwFlags = 0;
-#pragma warning(push)
-#pragma warning(disable : 6320 6322)
- __try {
- RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR),
- reinterpret_cast<ULONG_PTR*>(&info));
- } __except(EXCEPTION_EXECUTE_HANDLER) {
- }
-#pragma warning(pop)
-}
-
-} // namespace
-
-void SbThreadSetName(const char* name) {
- SbThreadPrivate* thread_private = GetCurrentSbThreadPrivate();
-
- // We store the thread name in our own TLS context as well as telling
- // the OS because it's much easier to retrieve from our own TLS context.
- thread_private->name_ = name;
- SetThreadName(static_cast<DWORD>(-1), name);
-}
diff --git a/src/starboard/shared/win32/thread_sleep.cc b/src/starboard/shared/win32/thread_sleep.cc
deleted file mode 100644
index 85c965d..0000000
--- a/src/starboard/shared/win32/thread_sleep.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-#include "starboard/shared/win32/time_utils.h"
-
-using starboard::shared::win32::ConvertSbTimeToMillisRoundUp;
-
-void SbThreadSleep(SbTime duration) {
- if (duration < 0) {
- return;
- }
- Sleep(ConvertSbTimeToMillisRoundUp(duration));
-}
diff --git a/src/starboard/shared/win32/thread_types_public.h b/src/starboard/shared/win32/thread_types_public.h
deleted file mode 100644
index 3443068..0000000
--- a/src/starboard/shared/win32/thread_types_public.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Includes threading primitive types and initializers.
-
-#ifndef STARBOARD_SHARED_WIN32_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_SHARED_WIN32_THREAD_TYPES_PUBLIC_H_
-
-// --- SbConditionVariable ---
-
-// Transparent Condition Variable handle.
-// Note: replica of RTL_CONDITION_VARIABLE in um/winnt.h to avoid include.
-typedef struct WindowsConditionVariable { void* ptr; } SbConditionVariable;
-
-// Condition Variable static initializer.
-// Note: replica of RTL_CONDITION_VARIABILE_INIT in um/winnt.h to avoid include.
-#define SB_CONDITION_VARIABLE_INITIALIZER \
- { 0 }
-
-// --- SbMutex ---
-
-// Transparent Mutex handle.
-// Note SRWLock's are used for SbMutex because:
-// - Normal Windows CreateMutex() mutexes are very heavy-weight
-// - Critical sections do not have a static initializer
-// Note: replica of RTL_SRWLOCK in um/winnt.h to avoid include.
-typedef struct WindowsSrwLock { void* ptr; } SbMutex;
-
-// Mutex static initializer.
-// Note: replica of RTL_SRWLOCK_INIT in um/winnt.h to avoid include.
-#define SB_MUTEX_INITIALIZER \
- { 0 }
-
-// --- SbOnce ---
-
-// Transparent Once control handle.
-typedef union WindowsRunOnce { void* ptr; } SbOnceControl;
-
-// Once static initializer.
-// Note: replica of RTL_RUN_ONCE_INIT in um/winnt.h to avoid include.
-#define SB_ONCE_INITIALIZER \
- { 0 }
-
-// --- SbThread ---
-
-// Transparent pthread handle.
-typedef void* SbThread;
-
-// Well-defined constant value to mean "no thread handle."
-#define kSbThreadInvalid (SbThread) NULL
-
-#endif // STARBOARD_SHARED_WIN32_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/shared/win32/thread_yield.cc b/src/starboard/shared/win32/thread_yield.cc
deleted file mode 100644
index 2687852..0000000
--- a/src/starboard/shared/win32/thread_yield.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/thread.h"
-
-#include <windows.h>
-
-void SbThreadYield() {
- Sleep(0);
-}
diff --git a/src/starboard/shared/win32/time_get_monotonic_now.cc b/src/starboard/shared/win32/time_get_monotonic_now.cc
deleted file mode 100644
index e761368..0000000
--- a/src/starboard/shared/win32/time_get_monotonic_now.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/time.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-
-SbTimeMonotonic SbTimeGetMonotonicNow() {
- LARGE_INTEGER counts;
- bool success;
-
- success = QueryPerformanceCounter(&counts);
-
- // "On systems that run Windows XP or later,
- // the function will always succeed and will thus never return zero."
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
- SB_DCHECK(success);
-
- LARGE_INTEGER countsPerSecond;
- success = QueryPerformanceFrequency(&countsPerSecond);
- // "On systems that run Windows XP or later,
- // the function will always succeed and will thus never return zero."
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx
- SB_DCHECK(success);
-
- // An observed value of countsPerSecond on a desktop x86 machine is
- // ~2.5e6. With this frequency, it will take ~37500 days to exceed
- // 2^53, which is the mantissa precision of a double.
- // Hence, we can safely convert to a double here without losing precision.
- double result = static_cast<double>(counts.QuadPart);
- result *= (1000.0 * 1000.0) / countsPerSecond.QuadPart;
-
- return static_cast<SbTimeMonotonic>(result);
-}
diff --git a/src/starboard/shared/win32/time_get_now.cc b/src/starboard/shared/win32/time_get_now.cc
deleted file mode 100644
index eaa1fbc..0000000
--- a/src/starboard/shared/win32/time_get_now.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/time.h"
-
-#include <windows.h>
-
-SbTime SbTimeGetNow() {
- SYSTEMTIME system_time;
- GetSystemTime(&system_time);
-
- FILETIME file_time;
- SystemTimeToFileTime(&system_time, &file_time);
-
- ULARGE_INTEGER large_int;
- large_int.LowPart = file_time.dwLowDateTime;
- large_int.HighPart = file_time.dwHighDateTime;
- return static_cast<SbTime>(large_int.QuadPart) / 10;
-}
diff --git a/src/starboard/shared/win32/time_utils.h b/src/starboard/shared/win32/time_utils.h
deleted file mode 100644
index fea9e12..0000000
--- a/src/starboard/shared/win32/time_utils.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_TIME_UTILS_H_
-#define STARBOARD_SHARED_WIN32_TIME_UTILS_H_
-
-#include "starboard/log.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-inline SbTime ConvertFileTimeTicksToSbTime(const LARGE_INTEGER ticks) {
- // According to
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
- // FILETIME format is "Contains a 64-bit value representing the number of
- // 100-nanosecond intervals since January 1, 1601 (UTC)"
- const uint64_t kNumber100nanosecondTicksInMicrosecond = 10;
- return ticks.QuadPart / kNumber100nanosecondTicksInMicrosecond;
-}
-
-inline SbTime ConvertFileTimeToSbTime(const FILETIME file_time) {
- // According to
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
- // FILETIME format is "Contains a 64-bit value representing the number of
- // 100-nanosecond intervals since January 1, 1601 (UTC)"
- LARGE_INTEGER ticks;
- ticks.QuadPart = (static_cast<LONGLONG>(file_time.dwHighDateTime) << 32) |
- file_time.dwLowDateTime;
- return ConvertFileTimeTicksToSbTime(ticks);
-}
-
-inline SbTime ConvertSystemTimeToSbTime(const SYSTEMTIME system_time) {
- FILETIME file_time = {0};
- SystemTimeToFileTime(&system_time, &file_time);
- return ConvertFileTimeToSbTime(file_time);
-}
-
-// Many Win32 calls take millis, but SbTime is microseconds.
-// Many nplb tests assume waits are at least as long as requested, so
-// round up.
-inline DWORD ConvertSbTimeToMillisRoundUp(SbTime duration) {
- const int64_t milliseconds_to_sleep =
- (duration + kSbTimeMillisecond - 1) / kSbTimeMillisecond;
- return static_cast<DWORD>(milliseconds_to_sleep);
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_TIME_UTILS_H_
diff --git a/src/starboard/shared/win32/time_zone_get_current.cc b/src/starboard/shared/win32/time_zone_get_current.cc
deleted file mode 100644
index b040059..0000000
--- a/src/starboard/shared/win32/time_zone_get_current.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/time_zone.h"
-
-#include <windows.h>
-
-#include "starboard/log.h"
-SbTimeZone SbTimeZoneGetCurrent() {
- DYNAMIC_TIME_ZONE_INFORMATION time_zone_info;
-
- DWORD zone_id = GetDynamicTimeZoneInformation(&time_zone_info);
-
- switch (zone_id) {
- case TIME_ZONE_ID_UNKNOWN:
- return time_zone_info.Bias;
- case TIME_ZONE_ID_STANDARD:
- return time_zone_info.StandardBias;
- case TIME_ZONE_ID_DAYLIGHT:
- return time_zone_info.DaylightBias;
- default:
- SB_NOTREACHED();
- return 0;
- }
-}
diff --git a/src/starboard/shared/win32/time_zone_get_name.cc b/src/starboard/shared/win32/time_zone_get_name.cc
deleted file mode 100644
index def88fb..0000000
--- a/src/starboard/shared/win32/time_zone_get_name.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/time_zone.h"
-
-#include <string>
-#include <Windows.h>
-
-#include "starboard/once.h"
-#include "starboard/shared/win32/wchar_utils.h"
-
-namespace {
-class TimeZoneString {
- public:
- static TimeZoneString* Get();
- const char* value() const { return value_.c_str(); }
-
- private:
- TimeZoneString() {
- DYNAMIC_TIME_ZONE_INFORMATION time_zone_info;
- GetDynamicTimeZoneInformation(&time_zone_info);
-
- std::wstring wide_string = time_zone_info.TimeZoneKeyName;
- value_ = starboard::shared::win32::wchar_tToUTF8(wide_string.c_str());
- }
- std::string value_;
-};
-
-SB_ONCE_INITIALIZE_FUNCTION(TimeZoneString, TimeZoneString::Get);
-} // namespace.
-
-const char* SbTimeZoneGetName() {
- const char* output = TimeZoneString::Get()->value();
- return output;
-}
diff --git a/src/starboard/shared/win32/video_decoder.cc b/src/starboard/shared/win32/video_decoder.cc
deleted file mode 100644
index 0535f11..0000000
--- a/src/starboard/shared/win32/video_decoder.cc
+++ /dev/null
@@ -1,668 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/video_decoder.h"
-
-#include <functional>
-
-#include "starboard/log.h"
-#include "starboard/shared/win32/decode_target_internal.h"
-#include "starboard/shared/win32/dx_context_video_decoder.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/time.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-namespace {
-
-using Microsoft::WRL::ComPtr;
-using ::starboard::shared::starboard::player::filter::VideoFrame;
-using std::placeholders::_1;
-using std::placeholders::_2;
-
-// Limit the number of active output samples to control memory usage.
-// NOTE: Higher numbers may result in increased dropped frames when the video
-// resolution changes during playback (if the decoder is not forced to use
-// max resolution resources).
-const int kMaxOutputSamples = 10;
-
-// Throttle the number of queued inputs to control memory usage.
-const int kMaxInputSamples = 15;
-
-// Prime the VP9 decoder for a certain number of output frames to reduce
-// hitching at the start of playback.
-const int kVp9PrimingFrameCount = 10;
-
-// Decode targets are cached for reuse. Ensure the cache size is large enough
-// to accommodate the depth of the presentation swap chain, otherwise the
-// video textures may be updated before or while they are actually rendered.
-const int kDecodeTargetCacheSize = 2;
-
-// Allocate decode targets at the maximum size to allow them to be reused for
-// all resolutions. This minimizes hitching during resolution changes.
-#ifdef ENABLE_VP9_8K_SUPPORT
-const int kMaxDecodeTargetWidth = 7680;
-const int kMaxDecodeTargetHeight = 4320;
-#else // ENABLE_VP9_8K_SUPPORT
-const int kMaxDecodeTargetWidth = 3840;
-const int kMaxDecodeTargetHeight = 2160;
-#endif // ENABLE_VP9_8K_SUPPOR
-
-// This structure is used to facilitate creation of decode targets in the
-// appropriate graphics context.
-struct CreateDecodeTargetContext {
- VideoDecoder* video_decoder;
- SbDecodeTarget out_decode_target;
-};
-
-scoped_ptr<MediaTransform> CreateVideoTransform(const GUID& decoder_guid,
- const GUID& input_guid, const GUID& output_guid,
- const IMFDXGIDeviceManager* device_manager) {
- scoped_ptr<MediaTransform> media_transform(new MediaTransform(decoder_guid));
-
- media_transform->EnableInputThrottle(true);
- media_transform->SendMessage(MFT_MESSAGE_SET_D3D_MANAGER,
- ULONG_PTR(device_manager));
-
- // Tell the decoder to allocate resources for the maximum resolution in
- // order to minimize hitching on resolution changes.
- ComPtr<IMFAttributes> attributes = media_transform->GetAttributes();
- CheckResult(attributes->SetUINT32(MF_MT_DECODER_USE_MAX_RESOLUTION, 1));
-
- ComPtr<IMFMediaType> input_type;
- CheckResult(MFCreateMediaType(&input_type));
- CheckResult(input_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
- CheckResult(input_type->SetGUID(MF_MT_SUBTYPE, input_guid));
- CheckResult(input_type->SetUINT32(MF_MT_INTERLACE_MODE,
- MFVideoInterlace_Progressive));
- if (input_guid == MFVideoFormat_VP90) {
- // Set the expected video resolution. Setting the proper resolution can
- // mitigate a format change, but the decoder will adjust to the real
- // resolution regardless.
- CheckResult(MFSetAttributeSize(input_type.Get(), MF_MT_FRAME_SIZE,
- kMaxDecodeTargetWidth,
- kMaxDecodeTargetHeight));
- }
- media_transform->SetInputType(input_type);
-
- media_transform->SetOutputTypeBySubType(output_guid);
-
- return media_transform.Pass();
-}
-
-class VideoFrameImpl : public VideoFrame {
- public:
- VideoFrameImpl(SbTime timestamp, std::function<void(VideoFrame*)> release_cb)
- : VideoFrame(timestamp), release_cb_(release_cb) {
- SB_DCHECK(release_cb_);
- }
- ~VideoFrameImpl() { release_cb_(this); }
-
- private:
- std::function<void(VideoFrame*)> release_cb_;
-};
-
-} // namespace
-
-class VideoDecoder::Sink : public VideoDecoder::VideoRendererSink {
- public:
- void Render() {
- SB_DCHECK(render_cb_);
-
- render_cb_(std::bind(&Sink::DrawFrame, this, _1, _2));
- }
-
- private:
- void SetRenderCB(RenderCB render_cb) override {
- SB_DCHECK(!render_cb_);
- SB_DCHECK(render_cb);
-
- render_cb_ = render_cb;
- }
-
- void SetBounds(int z_index, int x, int y, int width, int height) override {
- SB_UNREFERENCED_PARAMETER(z_index);
- SB_UNREFERENCED_PARAMETER(x);
- SB_UNREFERENCED_PARAMETER(y);
- SB_UNREFERENCED_PARAMETER(width);
- SB_UNREFERENCED_PARAMETER(height);
- }
-
- DrawFrameStatus DrawFrame(const scoped_refptr<VideoFrame>& frame,
- int64_t release_time_in_nanoseconds) {
- SB_UNREFERENCED_PARAMETER(frame);
- SB_DCHECK(release_time_in_nanoseconds == 0);
-
- return kNotReleased;
- }
-
- RenderCB render_cb_;
-};
-
-VideoDecoder::VideoDecoder(
- SbMediaVideoCodec video_codec,
- SbPlayerOutputMode output_mode,
- SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
- SbDrmSystem drm_system)
- : video_codec_(video_codec),
- graphics_context_provider_(graphics_context_provider),
- drm_system_(drm_system),
- decoder_thread_(kSbThreadInvalid),
- decoder_thread_stop_requested_(false),
- decoder_thread_stopped_(false),
- current_decode_target_(kSbDecodeTargetInvalid) {
- SB_DCHECK(output_mode == kSbPlayerOutputModeDecodeToTexture);
-
- HardwareDecoderContext hardware_context = GetDirectXForHardwareDecoding();
- d3d_device_ = hardware_context.dx_device_out;
- device_manager_ = hardware_context.dxgi_device_manager_out;
- CheckResult(d3d_device_.As(&video_device_));
-
- ComPtr<ID3D11DeviceContext> d3d_context;
- d3d_device_->GetImmediateContext(d3d_context.GetAddressOf());
- CheckResult(d3d_context.As(&video_context_));
-}
-
-VideoDecoder::~VideoDecoder() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- Reset();
- ShutdownCodec();
-}
-
-scoped_refptr<VideoDecoder::VideoRendererSink> VideoDecoder::GetSink() {
- if (sink_ == NULL) {
- sink_ = new Sink;
- }
- return sink_;
-}
-
-size_t VideoDecoder::GetPrerollFrameCount() const {
- return kMaxOutputSamples;
-}
-
-size_t VideoDecoder::GetMaxNumberOfCachedFrames() const {
- return kMaxOutputSamples;
-}
-
-void VideoDecoder::Initialize(const DecoderStatusCB& decoder_status_cb,
- const ErrorCB& error_cb) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(!decoder_status_cb_);
- SB_DCHECK(decoder_status_cb);
- SB_DCHECK(!error_cb_);
- SB_DCHECK(error_cb);
- decoder_status_cb_ = decoder_status_cb;
- error_cb_ = error_cb;
- InitializeCodec();
-}
-
-void VideoDecoder::WriteInputBuffer(
- const scoped_refptr<InputBuffer>& input_buffer) {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(input_buffer);
- SB_DCHECK(decoder_status_cb_);
- EnsureDecoderThreadRunning();
-
- ScopedLock lock(thread_lock_);
- thread_events_.emplace_back(
- new Event{ Event::kWriteInputBuffer, input_buffer });
-}
-
-void VideoDecoder::WriteEndOfStream() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(decoder_status_cb_);
- EnsureDecoderThreadRunning();
-
- ScopedLock lock(thread_lock_);
- thread_events_.emplace_back(new Event{ Event::kWriteEndOfStream });
-}
-
-void VideoDecoder::Reset() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- StopDecoderThread();
-
- // Make sure all output samples have been released before flushing the
- // decoder. Be sure to Acquire the mutexes in the same order as
- // CreateDecodeTarget to avoid possible deadlock.
- outputs_reset_lock_.Acquire();
- thread_lock_.Acquire();
- thread_outputs_.clear();
- thread_lock_.Release();
- outputs_reset_lock_.Release();
-
- decoder_status_cb_(kReleaseAllFrames, nullptr);
- decoder_->Reset();
-}
-
-SbDecodeTarget VideoDecoder::GetCurrentDecodeTarget() {
- SB_DCHECK(sink_);
-
- sink_->Render();
-
- // Ensure the decode target is created on the render thread.
- CreateDecodeTargetContext decode_target_context = { this };
- graphics_context_provider_->gles_context_runner(graphics_context_provider_,
- &VideoDecoder::CreateDecodeTargetHelper, &decode_target_context);
-
- ScopedLock lock(decode_target_lock_);
- if (SbDecodeTargetIsValid(decode_target_context.out_decode_target)) {
- if (SbDecodeTargetIsValid(current_decode_target_)) {
- prev_decode_targets_.emplace_back(current_decode_target_);
- }
- current_decode_target_ = decode_target_context.out_decode_target;
- }
- if (SbDecodeTargetIsValid(current_decode_target_)) {
- // Add a reference for the caller.
- current_decode_target_->AddRef();
- }
- return current_decode_target_;
-}
-
-// static
-void VideoDecoder::CreateDecodeTargetHelper(void* context) {
- CreateDecodeTargetContext* target_context =
- static_cast<CreateDecodeTargetContext*>(context);
- target_context->out_decode_target =
- target_context->video_decoder->CreateDecodeTarget();
-}
-
-SbDecodeTarget VideoDecoder::CreateDecodeTarget() {
- RECT video_area;
- ComPtr<IMFSample> video_sample;
-
- // Don't allow a decoder reset (flush) while an IMFSample is
- // alive. However, the decoder thread should be allowed to continue
- // while the SbDecodeTarget is being created.
- ScopedLock reset_lock(outputs_reset_lock_);
-
- // Use the oldest output.
- thread_lock_.Acquire();
- if (!thread_outputs_.empty()) {
- // This function should not remove output frames. However, it's possible
- // for the same frame to be requested multiple times. To avoid re-creating
- // SbDecodeTargets, release the video_sample once it is used to create
- // an output frame. The next call to CreateDecodeTarget for the same frame
- // will return kSbDecodeTargetInvalid, and |current_decode_target_| will
- // be reused.
- Output& output = thread_outputs_.front();
- video_area = output.video_area;
- video_sample.Swap(output.video_sample);
- }
- thread_lock_.Release();
-
- SbDecodeTarget decode_target = kSbDecodeTargetInvalid;
- if (video_sample != nullptr) {
- ScopedLock target_lock(decode_target_lock_);
-
- // Try reusing the previous decode target to avoid the performance hit of
- // creating a new texture.
- SB_DCHECK(prev_decode_targets_.size() <= kDecodeTargetCacheSize + 1);
- if (prev_decode_targets_.size() >= kDecodeTargetCacheSize) {
- decode_target = prev_decode_targets_.front();
- prev_decode_targets_.pop_front();
- if (!decode_target->Update(d3d_device_, video_device_, video_context_,
- video_enumerator_, video_processor_, video_sample, video_area)) {
- // The cached decode target was not compatible; just release it.
- SbDecodeTargetRelease(decode_target);
- decode_target = kSbDecodeTargetInvalid;
- }
- }
-
- if (!SbDecodeTargetIsValid(decode_target)) {
- decode_target = new SbDecodeTargetPrivate(d3d_device_, video_device_,
- video_context_, video_enumerator_, video_processor_,
- video_sample, video_area);
- }
-
- // Release the video_sample before releasing the reset lock.
- video_sample.Reset();
- }
- return decode_target;
-}
-
-void VideoDecoder::InitializeCodec() {
- scoped_ptr<MediaTransform> media_transform;
-
- // If this is updated then media_is_video_supported.cc also needs to be
- // updated.
- switch (video_codec_) {
- case kSbMediaVideoCodecH264: {
- media_transform = CreateVideoTransform(
- CLSID_MSH264DecoderMFT, MFVideoFormat_H264, MFVideoFormat_NV12,
- device_manager_.Get());
- priming_output_count_ = 0;
- break;
- }
- case kSbMediaVideoCodecVp9: {
- media_transform = CreateVideoTransform(
- CLSID_MSVPxDecoder, MFVideoFormat_VP90, MFVideoFormat_NV12,
- device_manager_.Get());
- priming_output_count_ = kVp9PrimingFrameCount;
- break;
- }
- default: { SB_NOTREACHED(); }
- }
-
- decoder_.reset(
- new DecryptingDecoder("video", media_transform.Pass(), drm_system_));
- MediaTransform* transform = decoder_->GetDecoder();
-
- DWORD input_stream_count = 0;
- DWORD output_stream_count = 0;
- transform->GetStreamCount(&input_stream_count, &output_stream_count);
- SB_DCHECK(1 == input_stream_count);
- SB_DCHECK(1 == output_stream_count);
-
- ComPtr<IMFAttributes> attributes = transform->GetAttributes();
- CheckResult(attributes->SetUINT32(MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT,
- kMaxOutputSamples));
-
- UpdateVideoArea(transform->GetCurrentOutputType());
-
- // The transform must output textures that are bound to shader resources,
- // or we can't draw them later via ANGLE.
- ComPtr<IMFAttributes> output_attributes =
- transform->GetOutputStreamAttributes();
- CheckResult(output_attributes->SetUINT32(MF_SA_D3D11_BINDFLAGS,
- D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DECODER));
-
- // The resolution and framerate will adjust to the actual content data.
- D3D11_VIDEO_PROCESSOR_CONTENT_DESC content_desc = {};
- content_desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
- content_desc.InputFrameRate.Numerator = 60;
- content_desc.InputFrameRate.Denominator = 1;
- content_desc.InputWidth = kMaxDecodeTargetWidth;
- content_desc.InputHeight = kMaxDecodeTargetHeight;
- content_desc.OutputFrameRate.Numerator = 60;
- content_desc.OutputFrameRate.Denominator = 1;
- content_desc.OutputWidth = kMaxDecodeTargetWidth;
- content_desc.OutputHeight = kMaxDecodeTargetHeight;
- content_desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
- CheckResult(video_device_->CreateVideoProcessorEnumerator(
- &content_desc, video_enumerator_.GetAddressOf()));
- CheckResult(video_device_->CreateVideoProcessor(
- video_enumerator_.Get(), 0, video_processor_.GetAddressOf()));
- video_context_->VideoProcessorSetStreamFrameFormat(
- video_processor_.Get(), MediaTransform::kStreamId,
- D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE);
- video_context_->VideoProcessorSetStreamAutoProcessingMode(
- video_processor_.Get(), 0, false);
-}
-
-void VideoDecoder::ShutdownCodec() {
- SB_DCHECK(!SbThreadIsValid(decoder_thread_));
- SB_DCHECK(thread_outputs_.empty());
- SB_DCHECK(decoder_ != nullptr);
-
- // Work around a VP9 decoder crash. All IMFSamples and anything that may
- // reference them indirectly (the d3d texture in SbDecodeTarget) must be
- // released before releasing the IMFTransform. Do this on the render thread
- // since graphics resources are being released.
- graphics_context_provider_->gles_context_runner(graphics_context_provider_,
- &VideoDecoder::ReleaseDecodeTargets, this);
-
- // Microsoft recommendeds stalling to let other systems release their
- // references to the IMFSamples.
- if (video_codec_ == kSbMediaVideoCodecVp9) {
- SbThreadSleep(150 * kSbTimeMillisecond);
- }
- decoder_.reset();
- video_processor_.Reset();
- video_enumerator_.Reset();
-}
-
-// static
-void VideoDecoder::ReleaseDecodeTargets(void* context) {
- VideoDecoder* this_ptr = static_cast<VideoDecoder*>(context);
-
- ScopedLock lock(this_ptr->decode_target_lock_);
- while (!this_ptr->prev_decode_targets_.empty()) {
- SbDecodeTargetRelease(this_ptr->prev_decode_targets_.front());
- this_ptr->prev_decode_targets_.pop_front();
- }
- if (SbDecodeTargetIsValid(this_ptr->current_decode_target_)) {
- SbDecodeTargetRelease(this_ptr->current_decode_target_);
- this_ptr->current_decode_target_ = kSbDecodeTargetInvalid;
- }
-}
-
-void VideoDecoder::EnsureDecoderThreadRunning() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- // NOTE: The video decoder thread will exit after processing the
- // kWriteEndOfStream event. In this case, Reset must be called (which will
- // then StopDecoderThread) before WriteInputBuffer or WriteEndOfStream again.
- SB_DCHECK(!decoder_thread_stopped_);
-
- if (!SbThreadIsValid(decoder_thread_)) {
- SB_DCHECK(decoder_ != nullptr);
- SB_DCHECK(thread_events_.empty());
- decoder_thread_stop_requested_ = false;
- decoder_thread_ =
- SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
- "VideoDecoder", &VideoDecoder::DecoderThreadEntry, this);
- SB_DCHECK(SbThreadIsValid(decoder_thread_));
- }
-}
-
-void VideoDecoder::StopDecoderThread() {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
- if (SbThreadIsValid(decoder_thread_)) {
- decoder_thread_stop_requested_ = true;
- SbThreadJoin(decoder_thread_, nullptr);
- SB_DCHECK(decoder_thread_stopped_);
- decoder_thread_stopped_ = false;
- decoder_thread_ = kSbThreadInvalid;
- }
- thread_events_.clear();
-}
-
-void VideoDecoder::UpdateVideoArea(const ComPtr<IMFMediaType>& media) {
- MFVideoArea video_area;
- HRESULT hr = media->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
- reinterpret_cast<UINT8*>(&video_area),
- sizeof(video_area), nullptr);
- if (SUCCEEDED(hr)) {
- video_area_.left = video_area.OffsetX.value;
- video_area_.top = video_area.OffsetY.value;
- video_area_.right = video_area_.left + video_area.Area.cx;
- video_area_.bottom = video_area_.top + video_area.Area.cy;
- return;
- }
-
- UINT32 width;
- UINT32 height;
- hr = MFGetAttributeSize(media.Get(), MF_MT_FRAME_SIZE, &width, &height);
- if (SUCCEEDED(hr)) {
- video_area_.left = 0;
- video_area_.top = 0;
- video_area_.right = width;
- video_area_.bottom = height;
- return;
- }
-
- SB_NOTREACHED() << "Could not determine new video output resolution";
-}
-
-scoped_refptr<VideoFrame> VideoDecoder::CreateVideoFrame(
- const ComPtr<IMFSample>& sample) {
- // NOTE: All samples must be released before flushing the decoder. Since
- // the host may hang onto VideoFrames that are created here, make them
- // weak references to the actual sample.
- LONGLONG win32_sample_time = 0;
- CheckResult(sample->GetSampleTime(&win32_sample_time));
- SbTime sample_time = ConvertToSbTime(win32_sample_time);
-
- thread_lock_.Acquire();
- thread_outputs_.emplace_back(sample_time, video_area_, sample);
- thread_lock_.Release();
-
- // The "native texture" for the VideoFrame is actually just the timestamp
- // for the output sample.
- return new VideoFrameImpl(
- sample_time, std::bind(&VideoDecoder::DeleteVideoFrame, this, _1));
-}
-
-void VideoDecoder::DeleteVideoFrame(VideoFrame* video_frame) {
- ScopedLock lock(thread_lock_);
- for (auto iter = thread_outputs_.begin(); iter != thread_outputs_.end();
- ++iter) {
- if (iter->time == video_frame->timestamp()) {
- thread_outputs_.erase(iter);
- break;
- }
- }
-}
-
-void VideoDecoder::DecoderThreadRun() {
- std::list<std::unique_ptr<Event> > priming_events;
- std::unique_ptr<Event> event;
- bool is_end_of_stream = false;
-
- while (!decoder_thread_stop_requested_) {
- int outputs_to_process = 1;
- bool wrote_input = false;
- bool read_output = false;
-
- // Process a new event or re-try the previous event.
- if (event == nullptr) {
- ScopedLock lock(thread_lock_);
- if (!thread_events_.empty()) {
- event.swap(thread_events_.front());
- thread_events_.pop_front();
- }
- }
-
- if (event == nullptr) {
- SbThreadSleep(kSbTimeMillisecond);
- } else {
- switch (event->type) {
- case Event::kWriteInputBuffer:
- SB_DCHECK(event->input_buffer != nullptr);
- if (decoder_->TryWriteInputBuffer(event->input_buffer, 0)) {
- if (priming_output_count_ > 0) {
- // Save this event for the actual playback.
- priming_events.emplace_back(event.release());
- }
- // The event was successfully processed. Discard it.
- event.reset();
- wrote_input = true;
- } else {
- // The decoder must be full. Re-try the event on the next
- // iteration. Additionally, try reading an extra output frame to
- // start draining the decoder.
- ++outputs_to_process;
- }
- break;
- case Event::kWriteEndOfStream:
- event.reset();
- decoder_->Drain();
- is_end_of_stream = true;
- wrote_input = true;
- break;
- }
- }
-
- // Process output frame(s).
- for (int outputs = 0; outputs < outputs_to_process; ++outputs) {
- // NOTE: IMFTransform::ProcessOutput (called by decoder_->ProcessAndRead)
- // may stall if the number of active IMFSamples would exceed the value of
- // MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT.
- thread_lock_.Acquire();
- bool input_full = thread_events_.size() >= kMaxInputSamples;
- bool output_full = thread_outputs_.size() >= kMaxOutputSamples;
- thread_lock_.Release();
-
- Status status = input_full ? kBufferFull : kNeedMoreInput;
- decoder_status_cb_(status, nullptr);
-
- if (output_full) {
- // Wait for the active samples to be consumed before polling for more.
- break;
- }
-
- ComPtr<IMFSample> sample;
- ComPtr<IMFMediaType> media_type;
- decoder_->ProcessAndRead(&sample, &media_type);
- if (media_type) {
- UpdateVideoArea(media_type);
- }
- if (sample) {
- if (priming_output_count_ > 0) {
- // Ignore the output samples while priming the decoder.
- if (--priming_output_count_ == 0) {
- // Replay all the priming events once priming is finished.
- if (event != nullptr) {
- priming_events.emplace_back(event.release());
- }
- thread_lock_.Acquire();
- while (!priming_events.empty()) {
- thread_events_.emplace_front(priming_events.back().release());
- priming_events.pop_back();
- }
- thread_lock_.Release();
- decoder_->Reset();
- }
- } else {
- decoder_status_cb_(status, CreateVideoFrame(sample));
- }
- read_output = true;
- } else if (is_end_of_stream) {
- decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
- return;
- }
- }
-
- if (!wrote_input && !read_output) {
- // Throttle decode loop since no I/O was possible.
- SbThreadSleep(kSbTimeMillisecond);
- }
- }
-}
-
-// static
-void* VideoDecoder::DecoderThreadEntry(void* context) {
- SB_DCHECK(context);
- VideoDecoder* decoder = static_cast<VideoDecoder*>(context);
- decoder->DecoderThreadRun();
- decoder->decoder_thread_stopped_ = true;
- return nullptr;
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-namespace filter {
-
-// static
-bool VideoDecoder::OutputModeSupported(SbPlayerOutputMode output_mode,
- SbMediaVideoCodec codec,
- SbDrmSystem drm_system) {
- SB_UNREFERENCED_PARAMETER(codec);
- SB_UNREFERENCED_PARAMETER(drm_system);
- return output_mode == kSbPlayerOutputModeDecodeToTexture;
-}
-
-} // namespace filter
-} // namespace player
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/video_decoder.h b/src/starboard/shared/win32/video_decoder.h
deleted file mode 100644
index 402e312..0000000
--- a/src/starboard/shared/win32/video_decoder.h
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_VIDEO_DECODER_H_
-#define STARBOARD_SHARED_WIN32_VIDEO_DECODER_H_
-
-#include <D3d11_1.h>
-#include <wrl/client.h>
-
-#include <list>
-#include <memory>
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/configuration.h"
-#include "starboard/decode_target.h"
-#include "starboard/mutex.h"
-#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
-#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
-#include "starboard/shared/starboard/thread_checker.h"
-#include "starboard/shared/win32/decrypting_decoder.h"
-#include "starboard/thread.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class VideoDecoder
- : public ::starboard::shared::starboard::player::filter::VideoDecoder {
- public:
- typedef ::starboard::shared::starboard::player::filter::VideoRendererSink
- VideoRendererSink;
-
- class Sink;
-
- VideoDecoder(SbMediaVideoCodec video_codec,
- SbPlayerOutputMode output_mode,
- SbDecodeTargetGraphicsContextProvider* graphics_context_provider,
- SbDrmSystem drm_system);
- ~VideoDecoder() override;
-
- scoped_refptr<VideoRendererSink> GetSink();
-
- // Implement VideoDecoder interface.
- void Initialize(const DecoderStatusCB& decoder_status_cb,
- const ErrorCB& error_cb) override;
- size_t GetPrerollFrameCount() const override;
- SbTime GetPrerollTimeout() const override { return kSbTimeMax; }
- size_t GetMaxNumberOfCachedFrames() const override;
-
- void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer)
- override;
- void WriteEndOfStream() override;
- void Reset() override;
- SbDecodeTarget GetCurrentDecodeTarget() override;
-
- private:
- template <typename T>
- using ComPtr = Microsoft::WRL::ComPtr<T>;
-
- struct Event {
- enum Type {
- kWriteInputBuffer,
- kWriteEndOfStream,
- };
- Type type;
- scoped_refptr<InputBuffer> input_buffer;
- };
-
- struct Output {
-#if SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
- Output(SbMediaTime time, const RECT& video_area,
- const ComPtr<IMFSample>& video_sample)
- : time(time), video_area(video_area), video_sample(video_sample) {}
- SbMediaTime time;
-#else // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
- Output(SbTime time,
- const RECT& video_area,
- const ComPtr<IMFSample>& video_sample)
- : time(time), video_area(video_area), video_sample(video_sample) {}
- SbTime time;
-#endif // SB_API_VERSION < SB_DEPRECATE_SB_MEDIA_TIME_API_VERSION
- RECT video_area;
- ComPtr<IMFSample> video_sample;
- };
-
- void InitializeCodec();
- void ShutdownCodec();
- static void ReleaseDecodeTargets(void* context);
-
- void UpdateVideoArea(const ComPtr<IMFMediaType>& media);
- scoped_refptr<VideoFrame> CreateVideoFrame(const ComPtr<IMFSample>& sample);
- void DeleteVideoFrame(VideoFrame* video_frame);
- static void CreateDecodeTargetHelper(void* context);
- SbDecodeTarget CreateDecodeTarget();
-
- void EnsureDecoderThreadRunning();
- void StopDecoderThread();
- void DecoderThreadRun();
- static void* DecoderThreadEntry(void* context);
-
- ::starboard::shared::starboard::ThreadChecker thread_checker_;
-
- // These variables will be initialized inside ctor or SetHost() and will not
- // be changed during the life time of this class.
- const SbMediaVideoCodec video_codec_;
- DecoderStatusCB decoder_status_cb_;
- ErrorCB error_cb_;
- SbDecodeTargetGraphicsContextProvider* graphics_context_provider_;
- SbDrmSystem const drm_system_;
-
- // These are platform-specific objects required to create and use a codec.
- ComPtr<ID3D11Device> d3d_device_;
- ComPtr<IMFDXGIDeviceManager> device_manager_;
- ComPtr<ID3D11VideoDevice1> video_device_;
- ComPtr<ID3D11VideoContext> video_context_;
- ComPtr<ID3D11VideoProcessorEnumerator> video_enumerator_;
- ComPtr<ID3D11VideoProcessor> video_processor_;
-
- scoped_ptr<DecryptingDecoder> decoder_;
- RECT video_area_;
-
- SbThread decoder_thread_;
- volatile bool decoder_thread_stop_requested_;
- bool decoder_thread_stopped_;
- Mutex thread_lock_;
- std::list<std::unique_ptr<Event> > thread_events_;
-
- // This structure shadows the list of outstanding frames held by the host.
- // When a new output is added to this structure, the host should be notified
- // of a new VideoFrame. When the host deletes the VideoFrame, the delete
- // callback is used to update this structure. The VideoDecoder may need to
- // delete outputs without notifying the host. In such a situation, the host's
- // VideoFrames will be invalid if they still require the IMFSample; it's
- // possible that the VideoFrame was converted to a texture already, so it
- // will continue to be valid since the IMFSample is no longer needed.
- Mutex outputs_reset_lock_;
- std::list<Output> thread_outputs_;
-
- // To workaround the startup hitch for VP9, exercise the decoder for a
- // certain number of frames while prerolling the initial playback.
- int priming_output_count_;
-
- Mutex decode_target_lock_;
- SbDecodeTarget current_decode_target_;
- std::list<SbDecodeTarget> prev_decode_targets_;
-
- scoped_refptr<Sink> sink_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_VIDEO_DECODER_H_
diff --git a/src/starboard/shared/win32/wchar_utils.h b/src/starboard/shared/win32/wchar_utils.h
deleted file mode 100644
index 304e7d7..0000000
--- a/src/starboard/shared/win32/wchar_utils.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_WCHAR_UTILS_H_
-#define STARBOARD_SHARED_WIN32_WCHAR_UTILS_H_
-
-#include <codecvt>
-#include <cwchar>
-#include <locale>
-#include <string>
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-inline std::string wchar_tToUTF8(const wchar_t* const str) {
- std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
- return converter.to_bytes(str);
-}
-
-inline std::string wchar_tToUTF8(const wchar_t* const str,
- const std::size_t size) {
- std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
- return converter.to_bytes(str, str + size);
-}
-
-inline std::wstring CStringToWString(const char* str) {
- std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
- return converter.from_bytes(str);
-}
-
-#if defined(__cplusplus_winrt)
-inline std::string platformStringToString(Platform::String^ to_convert) {
- std::wstring ws(to_convert->Begin(), to_convert->End());
- return wchar_tToUTF8(ws.data(), ws.size());
-}
-
-inline Platform::String^ stringToPlatformString(const std::string& to_convert) {
- return ref new Platform::String(CStringToWString(to_convert.c_str()).c_str());
-}
-#endif
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_WCHAR_UTILS_H_
diff --git a/src/starboard/shared/win32/win32_audio_decoder.cc b/src/starboard/shared/win32/win32_audio_decoder.cc
deleted file mode 100644
index eb83a10..0000000
--- a/src/starboard/shared/win32/win32_audio_decoder.cc
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/win32_audio_decoder.h"
-
-#include <algorithm>
-#include <queue>
-
-#include "starboard/atomic.h"
-#include "starboard/shared/starboard/thread_checker.h"
-#include "starboard/shared/win32/atomic_queue.h"
-#include "starboard/shared/win32/audio_decoder.h"
-#include "starboard/shared/win32/audio_transform.h"
-#include "starboard/shared/win32/decrypting_decoder.h"
-#include "starboard/shared/win32/error_utils.h"
-#include "starboard/shared/win32/media_foundation_utils.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-using Microsoft::WRL::ComPtr;
-using ::starboard::shared::starboard::ThreadChecker;
-using ::starboard::shared::win32::CreateAudioTransform;
-
-const size_t kAacSamplesPerFrame = 1024;
-// We are using float samples on Xb1.
-const size_t kBytesPerSample = sizeof(float);
-
-namespace {
-
-class AbstractWin32AudioDecoderImpl : public AbstractWin32AudioDecoder {
- public:
- AbstractWin32AudioDecoderImpl(SbMediaAudioCodec codec,
- SbMediaAudioFrameStorageType audio_frame_fmt,
- SbMediaAudioSampleType sample_type,
- const SbMediaAudioHeader& audio_header,
- SbDrmSystem drm_system)
- : thread_checker_(ThreadChecker::kSetThreadIdOnFirstCheck),
- codec_(codec),
- audio_frame_fmt_(audio_frame_fmt),
- sample_type_(sample_type),
- number_of_channels_(audio_header.number_of_channels),
- heaac_detected_(false),
- samples_per_second_(static_cast<int>(audio_header.samples_per_second)) {
- scoped_ptr<MediaTransform> audio_decoder =
- CreateAudioTransform(audio_header, codec_);
- impl_.reset(
- new DecryptingDecoder("audio", audio_decoder.Pass(), drm_system));
- }
-
- void Consume(ComPtr<IMFSample> sample) {
- DWORD buff_count = 0;
- HRESULT hr = sample->GetBufferCount(&buff_count);
- CheckResult(hr);
- SB_DCHECK(buff_count == 1);
-
- ComPtr<IMFMediaBuffer> media_buffer;
- hr = sample->GetBufferByIndex(0, &media_buffer);
- if (FAILED(hr)) {
- return;
- }
-
- LONGLONG win32_timestamp = 0;
- hr = sample->GetSampleTime(&win32_timestamp);
- CheckResult(hr);
-
- BYTE* buffer;
- DWORD length;
- hr = media_buffer->Lock(&buffer, NULL, &length);
- CheckResult(hr);
- SB_DCHECK(length);
-
- const uint8_t* data = reinterpret_cast<uint8_t*>(buffer);
- const size_t data_size = static_cast<size_t>(length);
-
- const size_t expect_size_in_bytes =
- number_of_channels_ * kAacSamplesPerFrame * kBytesPerSample;
- if (data_size / expect_size_in_bytes == 2) {
- heaac_detected_.store(true);
- }
-
- DecodedAudioPtr data_ptr(
- new DecodedAudio(number_of_channels_, sample_type_, audio_frame_fmt_,
- ConvertToSbTime(win32_timestamp), data_size));
-
- std::copy(data, data + data_size, data_ptr->buffer());
-
- output_queue_.push(data_ptr);
- media_buffer->Unlock();
- }
-
- bool TryWrite(const scoped_refptr<InputBuffer>& buff) override {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- // The incoming audio is in ADTS format which has a 7 bytes header. But
- // the audio decoder is configured to accept raw AAC. So we have to adjust
- // the data, size, and subsample mapping to skip the ADTS header.
- const int kADTSHeaderSize = 7;
- const bool write_ok = impl_->TryWriteInputBuffer(buff, kADTSHeaderSize);
- return write_ok;
- }
-
- void WriteEndOfStream() override {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- impl_->Drain();
- ComPtr<IMFSample> sample;
- ComPtr<IMFMediaType> media_type;
- while (impl_->ProcessAndRead(&sample, &media_type)) {
- if (sample) {
- Consume(sample);
- }
- }
- output_queue_.push(new DecodedAudio);
- }
-
- scoped_refptr<DecodedAudio> ProcessAndRead() override {
- SB_DCHECK(thread_checker_.CalledOnValidThread());
-
- ComPtr<IMFSample> sample;
- ComPtr<IMFMediaType> media_type;
- while (impl_->ProcessAndRead(&sample, &media_type)) {
- if (sample) {
- Consume(sample);
- }
- }
- if (output_queue_.empty()) {
- return NULL;
- }
- scoped_refptr<DecodedAudio> output = output_queue_.front();
- output_queue_.pop();
- return output;
- }
-
- void Reset() override {
- impl_->Reset();
- std::queue<DecodedAudioPtr> empty;
- output_queue_.swap(empty);
- thread_checker_.Detach();
- }
-
- int GetSamplesPerSecond() const override {
- if (heaac_detected_.load()) {
- return samples_per_second_ * 2;
- }
- return samples_per_second_;
- }
-
- // The object is single-threaded and is driven by a dedicated thread.
- // However the thread may gets destroyed and re-created over the life time of
- // this object. We enforce that certain member functions can only called
- // from one thread while still allows this object to be driven by different
- // threads by:
- // 1. The |thread_checker_| is initially created without attaching to any
- // thread.
- // 2. When a thread is destroyed, Reset() will be called which in turn calls
- // Detach() on the |thread_checker_| to allow the object to attach to a
- // new thread.
- ::starboard::shared::starboard::ThreadChecker thread_checker_;
- const SbMediaAudioCodec codec_;
- const SbMediaAudioSampleType sample_type_;
- const SbMediaAudioFrameStorageType audio_frame_fmt_;
- scoped_ptr<DecryptingDecoder> impl_;
- std::queue<DecodedAudioPtr> output_queue_;
- uint16_t number_of_channels_;
- atomic_bool heaac_detected_;
- int samples_per_second_;
-};
-
-} // anonymous namespace.
-
-scoped_ptr<AbstractWin32AudioDecoder> AbstractWin32AudioDecoder::Create(
- SbMediaAudioCodec code,
- SbMediaAudioFrameStorageType audio_frame_fmt,
- SbMediaAudioSampleType sample_type,
- const SbMediaAudioHeader& audio_header,
- SbDrmSystem drm_system) {
- return scoped_ptr<AbstractWin32AudioDecoder>(
- new AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
- audio_header, drm_system));
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/win32_audio_decoder.h b/src/starboard/shared/win32/win32_audio_decoder.h
deleted file mode 100644
index 8a237e9..0000000
--- a/src/starboard/shared/win32/win32_audio_decoder.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_WIN32_AUDIO_DECODER_H_
-#define STARBOARD_SHARED_WIN32_WIN32_AUDIO_DECODER_H_
-
-#include <vector>
-
-#include "starboard/common/ref_counted.h"
-#include "starboard/common/scoped_ptr.h"
-#include "starboard/drm.h"
-#include "starboard/media.h"
-#include "starboard/shared/starboard/player/decoded_audio_internal.h"
-#include "starboard/shared/win32/media_common.h"
-#include "starboard/types.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-// AudioDecoder for Win32.
-class AbstractWin32AudioDecoder {
- public:
- static scoped_ptr<AbstractWin32AudioDecoder> Create(
- SbMediaAudioCodec codec,
- SbMediaAudioFrameStorageType audio_frame_fmt,
- SbMediaAudioSampleType sample_type,
- const SbMediaAudioHeader& audio_header,
- SbDrmSystem drm_system);
- virtual ~AbstractWin32AudioDecoder() {}
-
- // INPUT:
- //
- virtual bool TryWrite(const scoped_refptr<InputBuffer>& buff) = 0;
- virtual void WriteEndOfStream() = 0;
- // OUTPUT
- //
- virtual DecodedAudioPtr ProcessAndRead() = 0;
- // Reset
- virtual void Reset() = 0;
-
- // Dynamically query the audio frequency to support HE-AAC.
- virtual int GetSamplesPerSecond() const = 0;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_WIN32_AUDIO_DECODER_H_
diff --git a/src/starboard/shared/win32/window_create.cc b/src/starboard/shared/win32/window_create.cc
deleted file mode 100644
index 2f0cbd2..0000000
--- a/src/starboard/shared/win32/window_create.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/window.h"
-
-#include "starboard/shared/win32/application_win32.h"
-
-using ::starboard::shared::win32::ApplicationWin32;
-
-SbWindow SbWindowCreate(const SbWindowOptions* options) {
- return ApplicationWin32::Get()->CreateWindowForWin32(options);
-}
diff --git a/src/starboard/shared/win32/window_destroy.cc b/src/starboard/shared/win32/window_destroy.cc
deleted file mode 100644
index 6de112a..0000000
--- a/src/starboard/shared/win32/window_destroy.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/application_win32.h"
-#include "starboard/window.h"
-
-using ::starboard::shared::win32::ApplicationWin32;
-
-bool SbWindowDestroy(SbWindow window) {
- return ApplicationWin32::Get()->DestroyWindow(window);
-}
diff --git a/src/starboard/shared/win32/window_get_platform_handle.cc b/src/starboard/shared/win32/window_get_platform_handle.cc
deleted file mode 100644
index e0f5919..0000000
--- a/src/starboard/shared/win32/window_get_platform_handle.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Google Inc. 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 <EGL/egl.h>
-
-#include "starboard/shared/win32/window_internal.h"
-#include "starboard/window.h"
-
-void* SbWindowGetPlatformHandle(SbWindow window) {
- if (!SbWindowIsValid(window)) {
- return NULL;
- }
-
- return reinterpret_cast<EGLNativeWindowType>(window->GetWindowHandle());
-}
diff --git a/src/starboard/shared/win32/window_get_size.cc b/src/starboard/shared/win32/window_get_size.cc
deleted file mode 100644
index f100240..0000000
--- a/src/starboard/shared/win32/window_get_size.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/log.h"
-#include "starboard/shared/win32/window_internal.h"
-#include "starboard/window.h"
-
-bool SbWindowGetSize(SbWindow window, SbWindowSize* size) {
- if (!SbWindowIsValid(window)) {
- SB_LOG(ERROR) << __FUNCTION__ << ": Invalid window.";
- return false;
- }
-
- size->width = window->width;
- size->height = window->height;
- // The video resolution is the same as the graphics resolution.
- size->video_pixel_ratio = 1.0f;
- return true;
-}
diff --git a/src/starboard/shared/win32/window_internal.cc b/src/starboard/shared/win32/window_internal.cc
deleted file mode 100644
index cab001e..0000000
--- a/src/starboard/shared/win32/window_internal.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/window_internal.h"
-
-// TODO: Make sure the width and height here behave well given that we want
-// 1080 video, but perhaps 4k UI where applicable.
-SbWindowPrivate::SbWindowPrivate(const SbWindowOptions* options,
- HWND window_handle)
- : width(options->size.width),
- height(options->size.height),
- window_handle_(window_handle) {
- RECT window_client_rect;
-
- if (GetClientRect(window_handle_, &window_client_rect)) {
- width = window_client_rect.right - window_client_rect.left;
- height = window_client_rect.bottom - window_client_rect.top;
- }
-}
-
-SbWindowPrivate::~SbWindowPrivate() {}
diff --git a/src/starboard/shared/win32/window_internal.h b/src/starboard/shared/win32/window_internal.h
deleted file mode 100644
index be8a308..0000000
--- a/src/starboard/shared/win32/window_internal.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_WINDOW_INTERNAL_H_
-#define STARBOARD_SHARED_WIN32_WINDOW_INTERNAL_H_
-
-#include <EGL/egl.h>
-
-// Windows headers.
-#include <windows.h>
-
-#include "starboard/atomic.h"
-#include "starboard/time.h"
-#include "starboard/window.h"
-
-struct SbWindowPrivate {
- SbWindowPrivate(const SbWindowOptions* options, HWND window_handle);
- ~SbWindowPrivate();
-
- HWND GetWindowHandle() { return window_handle_; }
-
- // The width of this window.
- int width;
-
- // The height of this window.
- int height;
-
- private:
- HWND window_handle_;
-};
-
-#endif // STARBOARD_SHARED_WIN32_WINDOW_INTERNAL_H_
diff --git a/src/starboard/shared/win32/window_set_default_options.cc b/src/starboard/shared/win32/window_set_default_options.cc
deleted file mode 100644
index d989f52..0000000
--- a/src/starboard/shared/win32/window_set_default_options.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/memory.h"
-#include "starboard/window.h"
-
-void SbWindowSetDefaultOptions(SbWindowOptions* options) {
- SbMemorySet(options, 0, sizeof(*options));
- options->size.width = 1920;
- options->size.height = 1080;
- options->name = "YouTube";
-}
diff --git a/src/starboard/shared/win32/wrm_header.cc b/src/starboard/shared/win32/wrm_header.cc
deleted file mode 100644
index 97106fb..0000000
--- a/src/starboard/shared/win32/wrm_header.cc
+++ /dev/null
@@ -1,395 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/wrm_header.h"
-
-#include <guiddef.h>
-#include <initguid.h>
-
-#include <algorithm>
-
-#include "starboard/byte_swap.h"
-#include "starboard/memory.h"
-#include "starboard/shared/win32/error_utils.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-using Microsoft::WRL::ComPtr;
-
-namespace {
-
-// The playready system id used to create the content header.
-DEFINE_GUID(kPlayreadyContentHeaderCLSID,
- 0xF4637010,
- 0x03C3,
- 0x42CD,
- 0xB9,
- 0x32,
- 0xB4,
- 0x8A,
- 0xDF,
- 0x3A,
- 0x6A,
- 0x54);
-
-// The playready system id used to identify the wrm header in the
-// initialization data.
-DEFINE_GUID(kPlayreadyInitializationDataCLSID,
- 0x79f0049a,
- 0x4098,
- 0x8642,
- 0xab,
- 0x92,
- 0xe6,
- 0x5b,
- 0xe0,
- 0x88,
- 0x5f,
- 0x95);
-
-const uint16_t kPlayreadyWRMTag = 0x0001;
-
-class Reader {
- public:
- Reader(const uint8_t* data, size_t length)
- : start_(data), curr_(data), end_(data + length) {}
-
- size_t GetPosition() const { return curr_ - start_; }
- size_t GetRemaining() const { return end_ - curr_; }
-
- const uint8_t* curr() const { curr_; }
-
- bool Skip(size_t distance) {
- if (distance > GetRemaining())
- return false;
- curr_ += distance;
- return true;
- }
-
- bool ReadGUID(GUID* guid) {
- if (sizeof(*guid) > GetRemaining()) {
- return false;
- }
- SbMemoryCopy(guid, curr_, sizeof(*guid));
- curr_ += sizeof(*guid);
- return true;
- }
-
- bool ReadBigEndianU32(uint32_t* i) {
- if (sizeof(*i) > GetRemaining())
- return false;
- *i =
- curr_[0] * 0x1000000 + curr_[1] * 0x10000 + curr_[2] * 0x100 + curr_[3];
- curr_ += sizeof(*i);
- return true;
- }
-
- bool ReadLittleEndianU16(uint16_t* i) {
- if (sizeof(*i) > GetRemaining())
- return false;
- *i = curr_[0] + curr_[1] * 0x100;
- curr_ += sizeof(*i);
- return true;
- }
-
- private:
- const uint8_t* const start_;
- const uint8_t* curr_;
- const uint8_t* const end_;
-};
-
-std::vector<uint8_t> ParseWrmHeaderFromInitializationData(
- const void* initialization_data,
- int initialization_data_size) {
- if (initialization_data_size == 0) {
- SB_NOTIMPLEMENTED();
- return std::vector<uint8_t>();
- }
-
- std::vector<uint8_t> output;
- const uint8_t* data = static_cast<const uint8_t*>(initialization_data);
-
- Reader reader(data, initialization_data_size);
- while (reader.GetRemaining() > 0) {
- // Parse pssh atom (big endian)
- //
- // 4 bytes -- size
- // 4 bytes -- "pssh"
- // 4 bytes -- flags
- // 16 bytes -- guid
- uint32_t pssh_size;
- if (!reader.ReadBigEndianU32(&pssh_size)) {
- return output;
- }
-
- // Skipping pssh and flags
- if (!reader.Skip(8)) {
- return output;
- }
- GUID system_id;
- if (!reader.ReadGUID(&system_id)) {
- return output;
- }
- if (system_id != kPlayreadyInitializationDataCLSID) {
- // Skip entire pssh atom
- if (!reader.Skip(pssh_size - 28)) {
- return output;
- }
- continue;
- }
-
- // 4 bytes -- size of PlayreadyObject
- // followed by PlayreadyObject
-
- // Skip size, and continue parsing
- if (!reader.Skip(4)) {
- return output;
- }
-
- // Parse Playready object (little endian)
- // 4 bytes -- size
- // 2 bytes -- record count
- //
- // Playready Record
- // 2 bytes -- type
- // 2 bytes -- size of record
- // n bytes -- record
- if (!reader.Skip(4)) {
- return output;
- }
- uint16_t num_records;
- if (!reader.ReadLittleEndianU16(&num_records)) {
- return output;
- }
-
- for (int i = 0; i < num_records; i++) {
- uint16_t record_type;
- if (!reader.ReadLittleEndianU16(&record_type)) {
- return output;
- }
- uint16_t record_size;
- if (!reader.ReadLittleEndianU16(&record_size)) {
- return output;
- }
- if ((record_type & kPlayreadyWRMTag) == kPlayreadyWRMTag) {
- std::copy(data + reader.GetPosition(),
- data + reader.GetPosition() + record_size,
- std::back_inserter(output));
- return output;
- }
- if (!reader.Skip(record_size)) {
- return output;
- }
- }
- }
-
- return output;
-}
-
-uint32_t Base64ToValue(uint8_t byte) {
- if (byte >= 'A' && byte <= 'Z') {
- return byte - 'A';
- }
- if (byte >= 'a' && byte <= 'z') {
- return byte - 'a' + 26;
- }
- if (byte >= '0' && byte <= '9') {
- return byte - '0' + 52;
- }
- if (byte == '+') {
- return 62;
- }
- if (byte == '/') {
- return 63;
- }
- SB_DCHECK(byte == '=');
- return 0;
-}
-
-std::string Base64Decode(const std::wstring& input) {
- SB_DCHECK(input.size() % 4 == 0);
-
- std::string output;
-
- output.reserve(input.size() / 4 * 3);
- for (size_t i = 0; i < input.size() - 3; i += 4) {
- uint32_t decoded =
- Base64ToValue(input[i]) * 4 + Base64ToValue(input[i + 1]) / 16;
- output += static_cast<char>(decoded);
- if (input[i + 2] != '=') {
- decoded = Base64ToValue(input[i + 1]) % 16 * 16 +
- Base64ToValue(input[i + 2]) / 4;
- output += static_cast<char>(decoded);
- if (input[i + 3] != '=') {
- decoded =
- Base64ToValue(input[i + 2]) % 4 * 64 + Base64ToValue(input[i + 3]);
- output += static_cast<char>(decoded);
- }
- }
- }
-
- return output;
-}
-
-GUID ParseKeyIdFromWrmHeader(const std::vector<uint8_t>& wrm_header) {
- // The wrm_header is an XML in wchar_t that contains the base64 encoded key
- // id in <KID> node in base64. The original key id should be 16 characters,
- // so it is 24 characters after based64 encoded.
- const size_t kEncodedKeyIdLength = 24;
-
- SB_DCHECK(wrm_header.size() % 2 == 0);
- std::wstring wrm_header_copy(
- reinterpret_cast<const wchar_t*>(wrm_header.data()),
- wrm_header.size() / 2);
- std::wstring::size_type begin = wrm_header_copy.find(L"<KID>");
- if (begin == wrm_header_copy.npos) {
- return WrmHeader::kInvalidKeyId;
- }
- std::wstring::size_type end = wrm_header_copy.find(L"</KID>", begin);
- if (end == wrm_header_copy.npos) {
- return WrmHeader::kInvalidKeyId;
- }
- begin += 5;
- if (end - begin != kEncodedKeyIdLength) {
- return WrmHeader::kInvalidKeyId;
- }
-
- std::string key_id_in_string =
- Base64Decode(wrm_header_copy.substr(begin, kEncodedKeyIdLength));
- if (key_id_in_string.size() != sizeof(GUID)) {
- return WrmHeader::kInvalidKeyId;
- }
-
- GUID key_id = *reinterpret_cast<const GUID*>(key_id_in_string.data());
-
- key_id.Data1 = SbByteSwap(key_id.Data1);
- key_id.Data2 = SbByteSwap(key_id.Data2);
- key_id.Data3 = SbByteSwap(key_id.Data3);
-
- return key_id;
-}
-
-ComPtr<IStream> CreateWrmHeaderStream(const std::vector<uint8_t>& wrm_header) {
- ComPtr<IStream> stream;
- HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
- if (FAILED(hr)) {
- return NULL;
- }
-
- DWORD wrm_header_size = static_cast<DWORD>(wrm_header.size());
- ULONG bytes_written = 0;
-
- if (wrm_header_size != 0) {
- hr = stream->Write(wrm_header.data(), wrm_header_size, &bytes_written);
- if (FAILED(hr)) {
- return NULL;
- }
- }
-
- return stream;
-}
-
-ComPtr<IStream> CreateContentHeaderFromWrmHeader(
- const std::vector<uint8_t>& wrm_header) {
- ComPtr<IStream> content_header;
- // Assume we use one license for one stream.
- const DWORD kNumStreams = 1;
- const DWORD kNextStreamId = static_cast<DWORD>(-1);
-
- HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &content_header);
- if (FAILED(hr)) {
- return NULL;
- }
-
- // Initialize spInitStm with the required data
- // Format: (All DWORD values are serialized in little-endian order)
- // [GUID (content protection system guid, see msprita.idl)]
- // [DWORD (stream count, use the actual stream count even if all streams are
- // encrypted using the same data, note that zero is invalid)] [DWORD (next
- // stream ID, use -1 if all remaining streams are encrypted using the same
- // data)] [DWORD (next stream's binary data size)] [BYTE* (next stream's
- // binary data)] { Repeat from "next stream ID" above for each stream }
- DWORD wrm_header_size = static_cast<DWORD>(wrm_header.size());
- ULONG bytes_written = 0;
- hr = content_header->Write(&kPlayreadyContentHeaderCLSID,
- sizeof(kPlayreadyContentHeaderCLSID),
- &bytes_written);
- if (FAILED(hr)) {
- return NULL;
- }
-
- hr = content_header->Write(&kNumStreams, sizeof(kNumStreams), &bytes_written);
- if (FAILED(hr)) {
- return NULL;
- }
-
- hr = content_header->Write(&kNextStreamId, sizeof(kNextStreamId),
- &bytes_written);
- if (FAILED(hr)) {
- return NULL;
- }
-
- hr = content_header->Write(&wrm_header_size, sizeof(wrm_header_size),
- &bytes_written);
- if (FAILED(hr)) {
- return NULL;
- }
-
- if (0 != wrm_header_size) {
- hr = content_header->Write(wrm_header.data(), wrm_header_size,
- &bytes_written);
- if (FAILED(hr)) {
- return NULL;
- }
- }
-
- return content_header;
-}
-
-ComPtr<IStream> ResetStreamPosition(const ComPtr<IStream>& stream) {
- if (stream == NULL) {
- return NULL;
- }
- LARGE_INTEGER seek_position = {0};
- HRESULT hr = stream->Seek(seek_position, STREAM_SEEK_SET, NULL);
- CheckResult(hr);
- return stream;
-}
-
-} // namespace.
-
-// static
-GUID WrmHeader::kInvalidKeyId;
-
-WrmHeader::WrmHeader(const void* initialization_data,
- int initialization_data_size) {
- wrm_header_ = ParseWrmHeaderFromInitializationData(initialization_data,
- initialization_data_size);
- key_id_ = ParseKeyIdFromWrmHeader(wrm_header_);
-}
-
-ComPtr<IStream> WrmHeader::content_header() const {
- SB_DCHECK(is_valid());
-
- if (key_id_ == kInvalidKeyId) {
- return NULL;
- }
- return ResetStreamPosition(CreateContentHeaderFromWrmHeader(wrm_header_));
-}
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
diff --git a/src/starboard/shared/win32/wrm_header.h b/src/starboard/shared/win32/wrm_header.h
deleted file mode 100644
index af248d2..0000000
--- a/src/starboard/shared/win32/wrm_header.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN32_WRM_HEADER_H_
-#define STARBOARD_SHARED_WIN32_WRM_HEADER_H_
-
-#include <objidl.h>
-#include <wrl.h>
-#include <wrl/client.h>
-
-#include <string>
-#include <vector>
-
-#include "starboard/log.h"
-#include "starboard/types.h"
-
-namespace starboard {
-namespace shared {
-namespace win32 {
-
-class WrmHeader {
- public:
- WrmHeader(const void* initialization_data, int initialization_data_size);
-
- static GUID kInvalidKeyId;
-
- bool is_valid() const {
- return key_id_ != kInvalidKeyId && !wrm_header_.empty();
- }
- const GUID& key_id() const {
- SB_DCHECK(is_valid());
- return key_id_;
- }
- Microsoft::WRL::ComPtr<IStream> content_header() const;
- const std::vector<uint8_t>& wrm_header() const { return wrm_header_; }
-
- private:
- GUID key_id_;
- std::vector<uint8_t> wrm_header_;
-};
-
-} // namespace win32
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_WIN32_WRM_HEADER_H_
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 9bc5efc..11fa963 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -696,8 +696,6 @@
: wake_up_atom_(None),
wm_delete_atom_(None),
composite_event_id_(kSbEventIdInvalid),
- frame_read_index_(0),
- frames_updated_(false),
display_(NULL),
paste_buffer_key_release_pending_(false) {
SbAudioSinkPrivate::Initialize();
@@ -755,34 +753,27 @@
if (!windows_.empty()) {
SbWindow window = windows_[0];
if (SbWindowIsValid(window)) {
- std::map<int, FrameInfo> frame_infos;
- {
- ScopedLock lock(frame_mutex_);
- if (frames_updated_) {
- // Increment the index to the next frame, which has been written.
- frame_read_index_ = (frame_read_index_ + 1) % kNumFrames;
- frame_infos.swap(frame_infos_[frame_read_index_]);
+ ScopedLock lock(frame_mutex_);
- // Clear the frame written flag, so we will not advance frames until
- // the next frame is written.
- frames_updated_ = false;
- }
- }
window->BeginComposite();
- for (auto& iter : frame_infos) {
- FrameInfo& frame_info = iter.second;
-
- if (frame_info.frame->is_end_of_stream()) {
- continue;
- }
-
+ for (auto& frame_info : current_video_bounds_) {
+ // Get the cached video frame.
+ SbPlayer player = frame_info.player;
scoped_refptr<CpuVideoFrame> cpu_video_frame =
- static_cast<CpuVideoFrame*>(frame_info.frame.get());
-
- if (cpu_video_frame &&
- cpu_video_frame->format() != CpuVideoFrame::kBGRA32) {
- cpu_video_frame = cpu_video_frame->ConvertTo(CpuVideoFrame::kBGRA32);
- frame_info.frame = cpu_video_frame;
+ static_cast<CpuVideoFrame*>(current_video_frames_[player].get());
+ if (!cpu_video_frame) {
+ // Need to generate a new video frame.
+ cpu_video_frame =
+ static_cast<CpuVideoFrame*>(next_video_frames_[player].get());
+ if (!cpu_video_frame) {
+ // The player was already destroyed, so nothing to render.
+ continue;
+ }
+ if (cpu_video_frame->format() != CpuVideoFrame::kBGRA32) {
+ cpu_video_frame = cpu_video_frame->ConvertTo(
+ CpuVideoFrame::kBGRA32);
+ }
+ current_video_frames_[player] = cpu_video_frame;
}
window->CompositeVideoFrame(frame_info.x, frame_info.y,
frame_info.width, frame_info.height,
@@ -803,28 +794,56 @@
int width,
int height) {
ScopedLock lock(frame_mutex_);
- // Always write ahead 1 frame of the current read frame.
- int write_index = (frame_read_index_ + 1) % kNumFrames;
- for (auto iter = frame_infos_[write_index].begin();
- iter != frame_infos_[write_index].end(); ++iter) {
- if (iter->second.player == player) {
- frame_infos_[write_index].erase(iter);
- break;
- }
+ if (frame->is_end_of_stream()) {
+ // Remove all references the the player and its resources.
+ next_video_frames_.erase(player);
+ next_video_bounds_.erase(player);
+ } else {
+ next_video_frames_[player] = frame;
}
- // Copy the frame.
- FrameInfo& frame_info = frame_infos_[write_index][z_index];
+ // Invalidate the cache of this player's current frame.
+ current_video_frames_.erase(player);
+}
+
+void ApplicationX11::SwapBuffersBegin() {
+ // Prevent compositing while the GL layer is changing.
+ frame_mutex_.Acquire();
+}
+
+void ApplicationX11::SwapBuffersEnd() {
+ // Determine the video bounds that should be used with the new GL layer.
+
+ // Sort the video bounds according to their z_index.
+ current_video_bounds_.clear();
+ for (auto& iter : next_video_bounds_) {
+ const FrameInfo& bounds = iter.second;
+ auto position = current_video_bounds_.begin();
+ while (position != current_video_bounds_.end()) {
+ if (bounds.z_index < position->z_index) {
+ break;
+ }
+ ++position;
+ }
+ current_video_bounds_.insert(position, bounds);
+ }
+
+ frame_mutex_.Release();
+}
+
+void ApplicationX11::PlayerSetBounds(SbPlayer player,
+ int z_index, int x, int y, int width, int height) {
+ ScopedLock lock(frame_mutex_);
+
+ // The bounds should only take effect once the UI frame is submitted.
+ FrameInfo& frame_info = next_video_bounds_[player];
frame_info.player = player;
- frame_info.frame = frame;
frame_info.z_index = z_index;
frame_info.x = x;
frame_info.y = y;
frame_info.width = width;
frame_info.height = height;
-
- frames_updated_ = true;
}
void ApplicationX11::Initialize() {
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index 8416705..a2cf693 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -17,8 +17,8 @@
#include <X11/Xlib.h>
-#include <map>
#include <queue>
+#include <unordered_map>
#include <vector>
#include "base/memory/scoped_ptr.h"
@@ -48,8 +48,15 @@
SbWindow CreateWindow(const SbWindowOptions* options);
bool DestroyWindow(SbWindow window);
+ // Make the current GL layer and video layer visible.
void Composite();
+ // Call this function before updating the GL layer.
+ void SwapBuffersBegin();
+
+ // Call this function after the GL layer has been updated.
+ void SwapBuffersEnd();
+
protected:
void AcceptFrame(SbPlayer player,
const scoped_refptr<VideoFrame>& frame,
@@ -64,6 +71,13 @@
bool IsPreloadImmediate() override { return HasPreloadSwitch(); }
#endif // SB_API_VERSION >= 6
+ void PlayerSetBounds(SbPlayer player,
+ int z_index,
+ int x,
+ int y,
+ int width,
+ int height) override;
+
protected:
// --- Application overrides ---
void Initialize() override;
@@ -79,7 +93,6 @@
struct FrameInfo {
SbPlayer player;
- scoped_refptr<VideoFrame> frame;
int z_index;
int x;
int y;
@@ -110,12 +123,19 @@
SbEventId composite_event_id_;
Mutex frame_mutex_;
- int frame_read_index_;
- bool frames_updated_;
- static const int kNumFrames = 2;
- // Video frames from different videos sorted by their z indices.
- std::map<int, FrameInfo> frame_infos_[kNumFrames];
+ // The latest frame for every active player.
+ std::unordered_map<SbPlayer, scoped_refptr<VideoFrame>> next_video_frames_;
+
+ // Raw player frames need to be translated to a new format before compositing.
+ std::unordered_map<SbPlayer, scoped_refptr<VideoFrame>> current_video_frames_;
+
+ // Data for the upcoming render frame's video bounds.
+ std::unordered_map<SbPlayer, FrameInfo> next_video_bounds_;
+
+ // Sorted array (according to the z_index) of the current render frame's
+ // video bounds.
+ std::vector<FrameInfo> current_video_bounds_;
Display* display_;
SbWindowVector windows_;
diff --git a/src/starboard/shared/x11/egl_swap_buffers.cc b/src/starboard/shared/x11/egl_swap_buffers.cc
new file mode 100644
index 0000000..d8c8ce2
--- /dev/null
+++ b/src/starboard/shared/x11/egl_swap_buffers.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 Google Inc. 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 <EGL/egl.h>
+#include "starboard/shared/x11/application_x11.h"
+
+extern "C" {
+EGLBoolean __real_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);
+
+EGLBoolean __wrap_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
+ // This function is a proxy for when the GL layer is updated. Unfortunately,
+ // OpenGL ES 2.0 / X11 do not provide interfaces to know or control exactly
+ // when the GL layer is updated, but with an eglSwapInterval of 0, this is
+ // a good approximation.
+ EGLBoolean return_value;
+ starboard::shared::x11::ApplicationX11* application =
+ static_cast<starboard::shared::x11::ApplicationX11*>(
+ starboard::shared::starboard::Application::Get());
+
+ // Notify the application when the swap happens so that it can synchronize
+ // the bounds for the video layer.
+ application->SwapBuffersBegin();
+ return_value = __real_eglSwapBuffers(dpy, surface);
+ application->SwapBuffersEnd();
+ return return_value;
+}
+}
diff --git a/src/starboard/shared/x11/window_internal.cc b/src/starboard/shared/x11/window_internal.cc
index a0c1540..116a7b1 100644
--- a/src/starboard/shared/x11/window_internal.cc
+++ b/src/starboard/shared/x11/window_internal.cc
@@ -227,30 +227,21 @@
0, 0, image.width, image.height);
// Initially assume we don't have to center or scale.
- int video_width = frame->width();
- int video_height = frame->height();
if (bounds_width != width || bounds_height != height ||
frame->width() != width || frame->height() != height) {
- // Scale to fit the smallest dimension of the frame into the window.
- double scale =
- std::min(bounds_width / static_cast<double>(frame->width()),
- bounds_height / static_cast<double>(frame->height()));
- // Center the scaled frame within the window.
- video_width = scale * frame->width();
- video_height = scale * frame->height();
+ // This transform maps the destination pixel back to the source pixel.
+ double sx = static_cast<double>(frame->width()) / bounds_width;
+ double sy = static_cast<double>(frame->height()) / bounds_height;
XTransform transform = {{
- { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed(0) },
- { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed(0) },
- { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(scale) }
+ { XDoubleToFixed(sx), XDoubleToFixed(0), XDoubleToFixed(0) },
+ { XDoubleToFixed(0), XDoubleToFixed(sy), XDoubleToFixed(0) },
+ { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
}};
XRenderSetPictureTransform(display, video_picture, &transform);
}
-
- int dest_x = bounds_x + (bounds_width - video_width) / 2;
- int dest_y = bounds_y + (bounds_height - video_height) / 2;
XRenderComposite(display, PictOpSrc, video_picture, None,
- composition_picture, 0, 0, 0, 0, dest_x, dest_y,
- video_width, video_height);
+ composition_picture, 0, 0, 0, 0, bounds_x, bounds_y,
+ bounds_width, bounds_height);
}
}
diff --git a/src/starboard/starboard_all.gyp b/src/starboard/starboard_all.gyp
index 68d0488..07c0f68 100644
--- a/src/starboard/starboard_all.gyp
+++ b/src/starboard/starboard_all.gyp
@@ -41,6 +41,11 @@
'<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:*',
],
}],
+ ['sb_filter_based_player==1', {
+ 'dependencies': [
+ '<(DEPTH)/starboard/shared/starboard/player/filter/testing/player_filter_tests.gyp:*',
+ ],
+ }],
],
},
],
diff --git a/src/starboard/stub/BUILD.gn b/src/starboard/stub/BUILD.gn
index 048a1f5..bff83ca 100644
--- a/src/starboard/stub/BUILD.gn
+++ b/src/starboard/stub/BUILD.gn
@@ -212,7 +212,9 @@
"//starboard/shared/stub/drm_create_system.cc",
"//starboard/shared/stub/drm_destroy_system.cc",
"//starboard/shared/stub/drm_generate_session_update_request.cc",
+ "//starboard/shared/stub/drm_is_server_certificate_updatable.cc",
"//starboard/shared/stub/drm_system_internal.h",
+ "//starboard/shared/stub/drm_update_server_certificate.cc",
"//starboard/shared/stub/drm_update_session.cc",
"//starboard/shared/stub/file_can_open.cc",
"//starboard/shared/stub/file_close.cc",
diff --git a/src/starboard/stub/configuration.gni b/src/starboard/stub/configuration.gni
index 55a886e..f418dd6 100644
--- a/src/starboard/stub/configuration.gni
+++ b/src/starboard/stub/configuration.gni
@@ -19,7 +19,7 @@
gl_type = "none"
# Use media source extension implementation that is conformed to the
-# Candidate Recommandation of July 5th 2016.
+# Candidate Recommendation of July 5th 2016.
cobalt_use_media_source_2016 = true
declare_args() {
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index bea5605..4e7743f 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -89,15 +89,6 @@
// Whether the current platform is expected to have exactly 6 cores.
#define SB_HAS_6_CORES 0
-// Whether the current platform supports thread priorities.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
-
-// Whether the current platform implements the on screen keyboard interface.
-#define SB_HAS_ON_SCREEN_KEYBOARD 0
-
-// Whether the current platform uses a media player that relies on a URL.
-#define SB_HAS_PLAYER_WITH_URL 0
-
// Whether the current platform's thread scheduler will automatically balance
// threads between cores, as opposed to systems where threads will only ever run
// on the specifically pinned core.
@@ -144,15 +135,6 @@
// Whether the current platform provides ssize_t.
#define SB_HAS_SSIZE_T 1
-// Whether the current platform has microphone supported.
-#define SB_HAS_MICROPHONE 1
-
-// Whether the current platform has speech recognizer.
-#define SB_HAS_SPEECH_RECOGNIZER 1
-
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 1
-
// Type detection for wchar_t.
#if defined(__WCHAR_MAX__) && \
(__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
@@ -289,54 +271,6 @@
// access time is of 1 day precision.
#undef SB_HAS_QUIRK_FILESYSTEM_COARSE_ACCESS_TIME
-// --- Memory Configuration --------------------------------------------------
-
-// The memory page size, which controls the size of chunks on memory that
-// allocators deal with, and the alignment of those chunks. This doesn't have to
-// be the hardware-defined physical page size, but it should be a multiple of
-// it.
-#define SB_MEMORY_PAGE_SIZE 4096
-
-// Whether this platform has and should use an MMAP function to map physical
-// memory to the virtual address space.
-#define SB_HAS_MMAP 1
-
-// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
-// required for platforms that want to JIT.
-#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
-
-// Whether this platform has and should use an growable heap (e.g. with sbrk())
-// to map physical memory to the virtual address space.
-#define SB_HAS_VIRTUAL_REGIONS 0
-
-// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
-// may require buffers to have a specific alignment, and this is the place to
-// specify that.
-#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
-
-// Determines the alignment that allocations should have on this platform.
-#define SB_MALLOC_ALIGNMENT ((size_t)16U)
-
-// Determines the threshhold of allocation size that should be done with mmap
-// (if available), rather than allocated within the core heap.
-#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
-
-// Defines the path where memory debugging logs should be written to.
-#define SB_MEMORY_LOG_PATH "/tmp/starboard"
-
-// --- Thread Configuration --------------------------------------------------
-
-// Defines the maximum number of simultaneous threads for this platform. Some
-// platforms require sharing thread handles with other kinds of system handles,
-// like mutexes, so we want to keep this managable.
-#define SB_MAX_THREADS 90
-
-// The maximum number of thread local storage keys supported by this platform.
-#define SB_MAX_THREAD_LOCAL_KEYS 512
-
-// The maximum length of the name for a thread, including the NULL-terminator.
-#define SB_MAX_THREAD_NAME_LENGTH 16;
-
// --- Graphics Configuration ------------------------------------------------
// Specifies whether this platform supports a performant accelerated blitter
@@ -365,8 +299,25 @@
#define SB_HAS_VIRTUAL_REALITY 1
+// --- I/O Configuration -----------------------------------------------------
+
+// Whether the current platform has microphone supported.
+#define SB_HAS_MICROPHONE 1
+
+// Whether the current platform implements the on screen keyboard interface.
+#define SB_HAS_ON_SCREEN_KEYBOARD 0
+
+// Whether the current platform has speech recognizer.
+#define SB_HAS_SPEECH_RECOGNIZER 1
+
+// Whether the current platform has speech synthesis.
+#define SB_HAS_SPEECH_SYNTHESIS 1
+
// --- Media Configuration ---------------------------------------------------
+// Whether the current platform uses a media player that relies on a URL.
+#define SB_HAS_PLAYER_WITH_URL 0
+
// After a seek is triggerred, the default behavior is to append video frames
// from the last key frame before the seek time and append audio frames from the
// seek time because usually all audio frames are key frames. On platforms that
@@ -435,6 +386,41 @@
// value leads to more stable fps but also causes the app to use more memory.
#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
+// --- Memory Configuration --------------------------------------------------
+
+// The memory page size, which controls the size of chunks on memory that
+// allocators deal with, and the alignment of those chunks. This doesn't have to
+// be the hardware-defined physical page size, but it should be a multiple of
+// it.
+#define SB_MEMORY_PAGE_SIZE 4096
+
+// Whether this platform has and should use an MMAP function to map physical
+// memory to the virtual address space.
+#define SB_HAS_MMAP 1
+
+// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
+// required for platforms that want to JIT.
+#define SB_CAN_MAP_EXECUTABLE_MEMORY 1
+
+// Whether this platform has and should use an growable heap (e.g. with sbrk())
+// to map physical memory to the virtual address space.
+#define SB_HAS_VIRTUAL_REGIONS 0
+
+// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
+// may require buffers to have a specific alignment, and this is the place to
+// specify that.
+#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
+
+// Determines the alignment that allocations should have on this platform.
+#define SB_MALLOC_ALIGNMENT ((size_t)16U)
+
+// Determines the threshhold of allocation size that should be done with mmap
+// (if available), rather than allocated within the core heap.
+#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
+
+// Defines the path where memory debugging logs should be written to.
+#define SB_MEMORY_LOG_PATH "/tmp/starboard"
+
// --- Network Configuration -------------------------------------------------
// Specifies whether this platform supports IPV6.
@@ -443,6 +429,28 @@
// Specifies whether this platform supports pipe.
#define SB_HAS_PIPE 1
+// --- Thread Configuration --------------------------------------------------
+
+// Whether the current platform supports thread priorities.
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+
+// Defines the maximum number of simultaneous threads for this platform. Some
+// platforms require sharing thread handles with other kinds of system handles,
+// like mutexes, so we want to keep this managable.
+#define SB_MAX_THREADS 90
+
+// The maximum number of thread local storage keys supported by this platform.
+#define SB_MAX_THREAD_LOCAL_KEYS 512
+
+// The maximum length of the name for a thread, including the NULL-terminator.
+#define SB_MAX_THREAD_NAME_LENGTH 16
+
+// --- Timing API ------------------------------------------------------------
+
+// Whether this platform has an API to retrieve how long the current thread
+// has spent in the executing state.
+#define SB_HAS_TIME_THREAD_NOW 1
+
// --- Tuneable Parameters ---------------------------------------------------
// Specifies the network receive buffer size in bytes, set via
@@ -464,12 +472,6 @@
// The maximum number of users that can be signed in at the same time.
#define SB_USER_MAX_SIGNED_IN 1
-// --- Timing API ------------------------------------------------------------
-
-// Whether this platform has an API to retrieve how long the current thread
-// has spent in the executing state.
-#define SB_HAS_TIME_THREAD_NOW 1
-
// --- Platform Specific Audits ----------------------------------------------
#if !defined(__GNUC__)
diff --git a/src/starboard/stub/gyp_configuration.gypi b/src/starboard/stub/gyp_configuration.gypi
index 61fcca4..e2129aa 100644
--- a/src/starboard/stub/gyp_configuration.gypi
+++ b/src/starboard/stub/gyp_configuration.gypi
@@ -13,6 +13,9 @@
# limitations under the License.
{
'variables': {
+ # Stub does not use a filter-based player.
+ 'sb_filter_based_player': 0,
+
'target_arch': 'x64',
'target_os': 'linux',
diff --git a/src/starboard/stub/starboard_platform.gyp b/src/starboard/stub/starboard_platform.gyp
index b03f8ce..b44ec04 100644
--- a/src/starboard/stub/starboard_platform.gyp
+++ b/src/starboard/stub/starboard_platform.gyp
@@ -72,7 +72,9 @@
'<(DEPTH)/starboard/shared/stub/drm_create_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc',
+ '<(DEPTH)/starboard/shared/stub/drm_is_server_certificate_updatable.cc',
'<(DEPTH)/starboard/shared/stub/drm_system_internal.h',
+ '<(DEPTH)/starboard/shared/stub/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/stub/drm_update_session.cc',
'<(DEPTH)/starboard/shared/stub/file_can_open.cc',
'<(DEPTH)/starboard/shared/stub/file_close.cc',
@@ -113,6 +115,7 @@
'<(DEPTH)/starboard/shared/stub/memory_get_stack_bounds.cc',
'<(DEPTH)/starboard/shared/stub/memory_map.cc',
'<(DEPTH)/starboard/shared/stub/memory_move.cc',
+ '<(DEPTH)/starboard/shared/stub/memory_protect.cc',
'<(DEPTH)/starboard/shared/stub/memory_reallocate_unchecked.cc',
'<(DEPTH)/starboard/shared/stub/memory_set.cc',
'<(DEPTH)/starboard/shared/stub/memory_unmap.cc',
diff --git a/src/starboard/system.h b/src/starboard/system.h
index bf2c68e..e2cc7a3 100644
--- a/src/starboard/system.h
+++ b/src/starboard/system.h
@@ -175,10 +175,19 @@
kSbSystemCapabilityReversedEnterAndBack,
// Whether this system has the ability to report on GPU memory usage.
- // If (and only if) a system has this capcability will
+ // If (and only if) a system has this capability will
// SbSystemGetTotalGPUMemory() and SbSystemGetUsedGPUMemory() be valid to
// call.
kSbSystemCapabilityCanQueryGPUMemoryStats,
+
+#if SB_API_VERSION >= SB_INPUT_TIMESTAMP_API_VERSION
+ // Whether this system sets the |timestamp| field of SbInputData. If the
+ // system does not set this field, then it will automatically be set; however,
+ // the relative time between input events likely will not be preserved, so
+ // time-related calculations (e.g. velocity for move events) will be
+ // incorrect.
+ kSbSystemCapabilitySetsInputTimestamp,
+#endif
} SbSystemCapabilityId;
// Enumeration of possible values for the |type| parameter passed to the
diff --git a/src/starboard/tools/build.py b/src/starboard/tools/build.py
index fd2af48..193330a 100644
--- a/src/starboard/tools/build.py
+++ b/src/starboard/tools/build.py
@@ -295,9 +295,9 @@
try:
logging.debug('Loading platform configuration for "%s".', platform_name)
if platform.IsValid(platform_name):
- platform_path = platform.Get(platform_name).path
- module_path = os.path.join(paths.REPOSITORY_ROOT, platform_path,
- 'gyp_configuration.py')
+ platform_path = os.path.join(paths.REPOSITORY_ROOT,
+ platform.Get(platform_name).path)
+ module_path = os.path.join(platform_path, 'gyp_configuration.py')
if not _ModuleLoaded('platform_module', module_path):
platform_module = imp.load_source('platform_module', module_path)
else:
diff --git a/src/starboard/tools/create_derived_build.py b/src/starboard/tools/create_derived_build.py
new file mode 100644
index 0000000..f148190
--- /dev/null
+++ b/src/starboard/tools/create_derived_build.py
@@ -0,0 +1,195 @@
+#!/usr/bin/python
+#
+# Copyright 2018 Google Inc. 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.
+"""Tool to create a new Starboard build deriving from an existing one.
+
+When this tool is provided with input giving the desired existing build path to
+derive from as well as a destination build path to create, it will generate
+the files necessary for a new build to be created in the destination build
+path. The generated build will be identical to the previous build since it
+derives all configuration from it. The intended usage of this script is to
+make it easy to initialize new Starboard builds that are later manually tweaked
+by hand.
+"""
+
+import argparse
+import datetime
+import logging
+import os
+import sys
+import textwrap
+
+import _env # pylint: disable=unused-import
+from starboard.tools import paths
+import starboard.tools.environment as environment
+
+TEMPLATE_DIRECTORY = (
+ os.path.join(os.path.dirname(__file__), 'create_derived_build_templates'))
+
+# The list of files we will generate in the output directory. For each of
+# these files it is expected that there exists a corresponding template file in
+# the directory TEMPLATE_DIRECTORY.
+FILES_TO_GENERATE = [
+ 'atomic_public.h',
+ 'configuration_public.h',
+ 'gyp_configuration.gypi',
+ 'gyp_configuration.py',
+ 'starboard_platform.gyp',
+ 'starboard_platform_tests.gyp',
+ 'thread_types_public.h',
+]
+
+
+def _GenerateFile(output_file_path, template_file_path, template_dictionary):
+ """Applies the dictionary to the template file and outputs to output file."""
+ if not os.path.exists(os.path.dirname(output_file_path)):
+ os.makedirs(os.path.dirname(output_file_path))
+
+ with open(template_file_path, 'r') as f:
+ template_file_contents = f.read()
+
+ with open(output_file_path, 'w') as f:
+ f.write(template_file_contents.format(**template_dictionary))
+
+
+def _GetCopyrightYear():
+ """Returns the year to be used in the copyright header comment."""
+ return datetime.datetime.now().year
+
+
+def _GetAutoGeneratedPreambleComment(language):
+ """Returns the comment about how a current file is auto-generated."""
+ if language == 'cc':
+ comment_start = '//'
+ elif language == 'python':
+ comment_start = '#'
+ script_path_relative_to_repository = (
+ _GetPathRelativeToRepository(os.path.abspath(__file__)))
+ return ('{comment_start} This file was initially generated by {},\n'
+ '{comment_start} though it may have been modified since its '
+ 'creation.'.format(
+ script_path_relative_to_repository, comment_start=comment_start))
+
+
+def _GetPathRelativeToStarboardRoot(path):
+ abspath = os.path.abspath(path)
+ # This is a bit tricky because there can be multiple Starboard roots.
+ for root in environment.GetStarboardPortRoots():
+ absroot = os.path.abspath(root)
+ if os.path.commonprefix([absroot, abspath]) == absroot:
+ return os.path.relpath(abspath, start=absroot)
+
+ raise Exception('Path not relative to any Starboard root.')
+
+
+def _GetPathRelativeToRepository(path):
+ return os.path.relpath(os.path.abspath(path), start=paths.REPOSITORY_ROOT)
+
+
+def _GetIncludeGuard(output_file_path):
+ """Returns the include guard define to be used in generated C++ headers."""
+ repository_relative_path = _GetPathRelativeToRepository(output_file_path)
+ return (repository_relative_path.upper().replace(os.sep, '_').replace(
+ '.', '_') + '_')
+
+
+def _GetBuildName(output_build_path):
+ """Returns the name of the build (e.g. how you identify the build to gyp)."""
+ starboard_relative_path = _GetPathRelativeToStarboardRoot(output_build_path)
+ return starboard_relative_path.lower().replace(os.sep, '-')
+
+
+def _GetParentImportPath(parent_build_path):
+ repository_relative_path = _GetPathRelativeToRepository(parent_build_path)
+ return repository_relative_path.replace(os.sep, '.')
+
+
+def _CreateDerivedBuild(parent_build_path, parent_configuration_class_name,
+ output_build_path):
+ general_template_dictionary = {
+ 'copyright_year':
+ _GetCopyrightYear(),
+ 'auto_generated_cc_preamble_comment':
+ _GetAutoGeneratedPreambleComment('cc'),
+ 'auto_generated_python_preamble_comment':
+ _GetAutoGeneratedPreambleComment('python'),
+ 'parent_build_path':
+ _GetPathRelativeToRepository(parent_build_path),
+ 'build_name':
+ _GetBuildName(output_build_path),
+ 'parent_import_path':
+ _GetParentImportPath(parent_build_path),
+ 'parent_configuration_class_name':
+ parent_configuration_class_name,
+ }
+
+ # Go through each file in the list of files to generate and for each one
+ # apply the dictionary to the template to produce the generated file.
+ for gen_file in FILES_TO_GENERATE:
+ output_file_path = os.path.join(output_build_path, gen_file)
+
+ # Apply per-file customizations to the template dictionary.
+ file_template_dictionary = general_template_dictionary
+ file_template_dictionary['include_guard'] = (
+ _GetIncludeGuard(output_file_path))
+
+ _GenerateFile(output_file_path,
+ os.path.join(TEMPLATE_DIRECTORY, gen_file) + '.template',
+ file_template_dictionary)
+
+
+def main():
+ logging.basicConfig(
+ level=logging.INFO,
+ format=('[%(filename)s:%(lineno)s - %(asctime)s %(levelname)-8s] '
+ '%(message)s'),
+ datefmt='%m-%d %H:%M')
+
+ parser = argparse.ArgumentParser(
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description=textwrap.dedent(__doc__))
+ parser.add_argument(
+ '-p',
+ '--parent_build_path',
+ required=True,
+ type=str,
+ help='Path to the build that the destination build will inherit from.')
+ parser.add_argument(
+ '-c',
+ '--parent_configuration_class_name',
+ required=True,
+ type=str,
+ help='The name of the class in the parent build\'s gyp_configuration.py '
+ 'file that the derived build\'s gyp_configuration.py should '
+ 'derive from. You may have to open the parent build\'s '
+ 'gyp_configuration.py file to know what to pass in for this '
+ 'parameter.')
+ parser.add_argument(
+ '-o',
+ '--output_build_path',
+ required=True,
+ type=str,
+ help='Path to the directory where the destination build\'s files will '
+ 'be created.')
+ arguments = parser.parse_args()
+
+ return _CreateDerivedBuild(
+ os.path.normpath(arguments.parent_build_path),
+ arguments.parent_configuration_class_name,
+ os.path.normpath(arguments.output_build_path))
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/src/starboard/tools/create_derived_build_templates/atomic_public.h.template b/src/starboard/tools/create_derived_build_templates/atomic_public.h.template
new file mode 100644
index 0000000..c86f2f6
--- /dev/null
+++ b/src/starboard/tools/create_derived_build_templates/atomic_public.h.template
@@ -0,0 +1,22 @@
+// Copyright {copyright_year} Google Inc. 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.
+
+{auto_generated_cc_preamble_comment}
+
+#ifndef {include_guard}
+#define {include_guard}
+
+#include "{parent_build_path}/atomic_public.h"
+
+#endif // {include_guard}
diff --git a/src/starboard/tools/create_derived_build_templates/configuration_public.h.template b/src/starboard/tools/create_derived_build_templates/configuration_public.h.template
new file mode 100644
index 0000000..e55c25e
--- /dev/null
+++ b/src/starboard/tools/create_derived_build_templates/configuration_public.h.template
@@ -0,0 +1,22 @@
+// Copyright {copyright_year} Google Inc. 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.
+
+{auto_generated_cc_preamble_comment}
+
+#ifndef {include_guard}
+#define {include_guard}
+
+#include "{parent_build_path}/configuration_public.h"
+
+#endif // {include_guard}
diff --git a/src/starboard/tools/create_derived_build_templates/gyp_configuration.gypi.template b/src/starboard/tools/create_derived_build_templates/gyp_configuration.gypi.template
new file mode 100644
index 0000000..5efa49a
--- /dev/null
+++ b/src/starboard/tools/create_derived_build_templates/gyp_configuration.gypi.template
@@ -0,0 +1,39 @@
+# Copyright {copyright_year} Google Inc. 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.
+
+{auto_generated_python_preamble_comment}
+
+{{
+ 'target_defaults': {{
+ 'default_configuration': '{build_name}_debug',
+ 'configurations': {{
+ '{build_name}_debug': {{
+ 'inherit_from': ['debug_base'],
+ }},
+ '{build_name}_devel': {{
+ 'inherit_from': ['devel_base'],
+ }},
+ '{build_name}_qa': {{
+ 'inherit_from': ['qa_base'],
+ }},
+ '{build_name}_gold': {{
+ 'inherit_from': ['gold_base'],
+ }},
+ }}, # end of configurations
+ }},
+
+ 'includes': [
+ '<(DEPTH)/{parent_build_path}/gyp_configuration.gypi',
+ ],
+}}
diff --git a/src/starboard/tools/create_derived_build_templates/gyp_configuration.py.template b/src/starboard/tools/create_derived_build_templates/gyp_configuration.py.template
new file mode 100644
index 0000000..0419390
--- /dev/null
+++ b/src/starboard/tools/create_derived_build_templates/gyp_configuration.py.template
@@ -0,0 +1,22 @@
+# Copyright {copyright_year} Google Inc. 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.
+
+{auto_generated_python_preamble_comment}
+
+
+from {parent_import_path} import gyp_configuration as parent_configuration
+
+
+def CreatePlatformConfig():
+ return parent_configuration.{parent_configuration_class_name}('{build_name}')
diff --git a/src/starboard/tools/create_derived_build_templates/starboard_platform.gyp.template b/src/starboard/tools/create_derived_build_templates/starboard_platform.gyp.template
new file mode 100644
index 0000000..067897f
--- /dev/null
+++ b/src/starboard/tools/create_derived_build_templates/starboard_platform.gyp.template
@@ -0,0 +1,23 @@
+# Copyright {copyright_year} Google Inc. 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.
+
+{auto_generated_python_preamble_comment}
+
+{{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/{parent_build_path}/starboard_platform.gyp',
+ ],
+}}
diff --git a/src/starboard/tools/create_derived_build_templates/starboard_platform_tests.gyp.template b/src/starboard/tools/create_derived_build_templates/starboard_platform_tests.gyp.template
new file mode 100644
index 0000000..bc89c96
--- /dev/null
+++ b/src/starboard/tools/create_derived_build_templates/starboard_platform_tests.gyp.template
@@ -0,0 +1,23 @@
+# Copyright {copyright_year} Google Inc. 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.
+
+{auto_generated_python_preamble_comment}
+
+{{
+ 'includes': [
+ # Note that we are 'includes'ing a 'gyp' file, not a 'gypi' file. The idea
+ # is that we just want this file to *be* the parent gyp file.
+ '<(DEPTH)/{parent_build_path}/starboard_platform_tests.gyp',
+ ],
+}}
diff --git a/src/starboard/tools/create_derived_build_templates/thread_types_public.h.template b/src/starboard/tools/create_derived_build_templates/thread_types_public.h.template
new file mode 100644
index 0000000..a90c30f
--- /dev/null
+++ b/src/starboard/tools/create_derived_build_templates/thread_types_public.h.template
@@ -0,0 +1,22 @@
+// Copyright {copyright_year} Google Inc. 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.
+
+{auto_generated_cc_preamble_comment}
+
+#ifndef {include_guard}
+#define {include_guard}
+
+#include "{parent_build_path}/thread_types_public.h"
+
+#endif // {include_guard}
diff --git a/src/starboard/tools/example/app_launcher_client.py b/src/starboard/tools/example/app_launcher_client.py
index e6ee49e..641027e 100644
--- a/src/starboard/tools/example/app_launcher_client.py
+++ b/src/starboard/tools/example/app_launcher_client.py
@@ -15,6 +15,7 @@
# limitations under the License.
"""Client to launch executables via the new launcher logic."""
+import signal
import sys
import _env # pylint: disable=unused-import
@@ -39,6 +40,15 @@
args.platform, args.target_name, args.config,
device_id=args.device_id, target_params=target_params,
out_directory=args.out_directory)
+
+ def Abort(signum, frame):
+ del signum, frame # Unused.
+ sys.stderr.write("Killing thread\n")
+ launcher.Kill()
+ sys.exit(1)
+
+ signal.signal(signal.SIGINT, Abort)
+
return launcher.Run()
if __name__ == "__main__":
diff --git a/src/starboard/tools/net_args.py b/src/starboard/tools/net_args.py
new file mode 100644
index 0000000..386c06a
--- /dev/null
+++ b/src/starboard/tools/net_args.py
@@ -0,0 +1,102 @@
+#
+# Copyright 2018 Google Inc. 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.
+
+"""Example script for getting the Net Args."""
+
+from __future__ import print_function
+
+import pprint
+
+import argparse
+import socket
+import sys
+import thread
+import time
+import threading
+
+# Returns |True| if a connection was made and the NetArg payload was delivered.
+# Example:
+# TryConnectAndSendNetArgs('1.2.3.4', '1234', ['--argument', '--switch=value'])
+def TryConnectAndSendNetArgs(host, port, arg_list):
+ arg_string = '\n'.join(arg_list)
+
+ try:
+ server_socket = socket.create_connection((host, port), timeout = .5)
+ result = server_socket.sendall(arg_string)
+ server_socket.close()
+ return True
+ except socket.timeout as err:
+ return False
+ except socket.error as (err_no, err_str):
+ print(err_no, err_str)
+ return False
+
+class NetArgsThread(threading.Thread):
+ """Threaded version of NetArgs"""
+
+ def __init__(self, host, port, arg_list):
+ super(NetArgsThread, self).__init__()
+ assert isinstance(arg_list, list)
+ self.host = host
+ self.port = port
+ self.args_sent = False
+ self.join_called = False
+ self.arg_list = arg_list
+ self.mutex = threading.Lock()
+ self.start()
+
+ def join(self):
+ with self.mutex:
+ self.join_called = True
+ return super(NetArgsThread, self).join()
+
+ def ArgsSent(self):
+ with self.mutex:
+ return self.args_sent
+
+ def run(self):
+ while True:
+ with self.mutex:
+ if self.join_called:
+ break
+ connected_and_sent = TryConnectAndSendNetArgs(
+ self.host, self.port, self.arg_list)
+ if connected_and_sent:
+ with self.mutex:
+ self.args_sent = True
+ break
+
+def main(argv):
+ parser = argparse.ArgumentParser(description = 'Connects to the weblog.')
+ parser.add_argument('--host', type=str, required = False,
+ default = 'localhost',
+ help = "Example localhost or 1.2.3.4")
+ parser.add_argument('--port', type=int, required = False, default = '49355')
+ parser.add_argument('--arg', type=str, required = True)
+ args = parser.parse_args(argv)
+
+ net_args_thread = NetArgsThread(
+ args.host, args.port, [args.arg])
+
+ while not net_args_thread.ArgsSent():
+ print("Waiting to send arg " + args.arg + " to " + str(args.host) +
+ ":" + str(args.port) + "...")
+ time.sleep(.5)
+
+ print("Argument", args.arg, "was sent to", \
+ args.host + ":" + str(args.port))
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/src/starboard/tools/net_log.py b/src/starboard/tools/net_log.py
index f6d4dc5..24d9db9 100644
--- a/src/starboard/tools/net_log.py
+++ b/src/starboard/tools/net_log.py
@@ -20,6 +20,7 @@
import argparse
import pprint
+import select
import socket
import sys
import time
@@ -47,18 +48,21 @@
else:
return log
except socket.error as (err_no, err_str):
- print(err_no, err_str)
+ print(__file__ + ": Socket error while reading log:" \
+ + str(err_no) + " - " + str(err_str))
self.server_socket = None
return None
# Private members
def _TryRead(self):
try:
+ ready_list, _, _ = select.select([self.server_socket], [], [])
+ if not ready_list:
+ return ''
result = self.server_socket.recv(1024)
# An empty string is a flag that the connection has closed.
if len(result) == 0:
return None
-
return result
except socket.error as (err_no, err_str):
if err_no == 10035: # Data not ready yet.
@@ -76,6 +80,7 @@
except socket.timeout as err:
return None
except socket.error as err:
+ print("Error while trying to create socket: " + str(err))
return None
diff --git a/src/starboard/tools/testing/build_tests.py b/src/starboard/tools/testing/build_tests.py
new file mode 100644
index 0000000..b02470e
--- /dev/null
+++ b/src/starboard/tools/testing/build_tests.py
@@ -0,0 +1,36 @@
+import logging
+import os
+import subprocess
+
+def BuildTargets(targets, out_directory, dry_run=False, extra_build_flags=[]):
+ """Builds all specified targets.
+
+ Args:
+ extra_build_flags: Additional command line flags to pass to ninja.
+ """
+ if not targets:
+ logging.info('No targets specified, nothing to build.')
+ return
+
+ args_list = ["ninja", "-C", out_directory]
+ if dry_run:
+ args_list.append("-n")
+
+ args_list.extend(["{}_deploy".format(test_name) for test_name in targets])
+ args_list.extend(extra_build_flags)
+
+ if "TEST_RUNNER_BUILD_FLAGS" in os.environ:
+ args_list.append(os.environ["TEST_RUNNER_BUILD_FLAGS"])
+
+ logging.info('Building targets with command: %s', str(args_list))
+
+ try:
+ # We set shell=True because otherwise Windows doesn't recognize
+ # PATH properly.
+ # https://bugs.python.org/issue15451
+ # We flatten the arguments to a string because with shell=True, Linux
+ # doesn't parse them properly.
+ # https://bugs.python.org/issue6689
+ return subprocess.check_call(" ".join(args_list), shell=True)
+ except KeyboardInterrupt:
+ return 1
diff --git a/src/starboard/tools/testing/test_runner.py b/src/starboard/tools/testing/test_runner.py
index 21bb179..03caaa5 100755
--- a/src/starboard/tools/testing/test_runner.py
+++ b/src/starboard/tools/testing/test_runner.py
@@ -30,7 +30,7 @@
from starboard.tools import build
from starboard.tools import command_line
from starboard.tools.testing import test_filter
-
+from starboard.tools.testing import build_tests
_TOTAL_TESTS_REGEX = (r"\[==========\] (.*) tests? from .*"
r"test cases? ran. \(.* ms total\)")
@@ -171,8 +171,14 @@
class TestRunner(object):
"""Runs unit tests."""
- def __init__(self, platform, config, device_id, single_target,
- target_params, out_directory, application_name=None,
+ def __init__(self,
+ platform,
+ config,
+ device_id,
+ single_target,
+ target_params,
+ out_directory,
+ application_name=None,
dry_run=False):
self.platform = platform
self.config = config
@@ -254,39 +260,6 @@
env_variables[test] = test_env
return env_variables
- def _BuildTests(self, ninja_flags):
- """Builds all specified test binaries.
-
- Args:
- ninja_flags: Command line flags to pass to ninja.
- """
- if not self.test_targets:
- return
-
- if self.out_directory:
- build_dir = self.out_directory
- else:
- build_dir = abstract_launcher.DynamicallyBuildOutDirectory(
- self.platform, self.config)
-
- args_list = ["ninja", "-C", build_dir]
- if self.dry_run:
- args_list.append("-n")
- args_list.extend([
- "{}_deploy".format(test_name) for test_name in self.test_targets])
- if ninja_flags:
- args_list.append(ninja_flags)
- if "TEST_RUNNER_BUILD_FLAGS" in os.environ:
- args_list.append(os.environ["TEST_RUNNER_BUILD_FLAGS"])
- sys.stderr.write("{}\n".format(args_list))
- # We set shell=True because otherwise Windows doesn't recognize
- # PATH properly.
- # https://bugs.python.org/issue15451
- # We flatten the arguments to a string because with shell=True, Linux
- # doesn't parse them properly.
- # https://bugs.python.org/issue6689
- subprocess.check_call(" ".join(args_list), shell=True)
-
def _RunTest(self, target_name):
"""Runs a single unit test binary and collects all of the output.
@@ -313,9 +286,13 @@
test_params.extend(self.target_params)
launcher = abstract_launcher.LauncherFactory(
- self.platform, target_name, self.config,
- device_id=self.device_id, target_params=test_params,
- output_file=write_pipe, out_directory=self.out_directory,
+ self.platform,
+ target_name,
+ self.config,
+ device_id=self.device_id,
+ target_params=test_params,
+ output_file=write_pipe,
+ out_directory=self.out_directory,
env_variables=env)
test_reader = TestLineReader(read_pipe)
@@ -325,9 +302,8 @@
self.threads.append(test_reader)
if self.dry_run:
- sys.stdout.write(
- "{} {}\n".format(target_name, test_params) if test_params
- else "{}\n".format(target_name))
+ sys.stdout.write("{} {}\n".format(target_name, test_params)
+ if test_params else "{}\n".format(target_name))
write_pipe.close()
read_pipe.close()
@@ -404,6 +380,10 @@
failed_tests.append(test_failed_match.group(1))
return failed_tests
+ def _GetFilteredTestList(self, target_name):
+ return _FilterTests([target_name], self._GetTestFilters(), self.config).get(
+ target_name, [])
+
def _ProcessAllTestResults(self, results):
"""Collects and returns output for all selected tests.
@@ -420,6 +400,8 @@
total_run_count = 0
total_passed_count = 0
total_failed_count = 0
+ total_flaky_failed_count = 0
+ total_filtered_count = 0
# If the number of run tests from a test binary cannot be
# determined, assume an error occurred while running it.
@@ -435,6 +417,9 @@
failed_count = result_set[3]
failed_tests = result_set[4]
return_code = result_set[5]
+ flaky_failed_tests = [
+ test_name for test_name in failed_tests if ".FLAKY_" in test_name
+ ]
test_status = "SUCCEEDED"
if return_code != 0:
@@ -443,7 +428,7 @@
print "{}: {}.".format(target_name, test_status)
if run_count == 0:
- print" Results not available. Did the test crash?\n"
+ print " Results not available. Did the test crash?\n"
continue
print " TOTAL TESTS RUN: {}".format(run_count)
@@ -453,16 +438,23 @@
if failed_count > 0:
print " FAILED: {}".format(failed_count)
total_failed_count += failed_count
+ total_flaky_failed_count += len(flaky_failed_tests)
print "\n FAILED TESTS:"
for line in failed_tests:
print " {}".format(line)
+ filtered_count = len(self._GetFilteredTestList(target_name))
+ if filtered_count > 0:
+ print " FILTERED: {}".format(filtered_count)
+ total_filtered_count += filtered_count
# Print a single newline to separate results from each test run
print
overall_status = "SUCCEEDED"
result = True
- if error or total_failed_count > 0:
+ # If we only failed tests that are considered flaky, then count this run
+ # as a pass.
+ if error or total_failed_count - total_flaky_failed_count > 0:
overall_status = "FAILED"
result = False
@@ -470,6 +462,8 @@
print " TOTAL TESTS RUN: {}".format(total_run_count)
print " TOTAL TESTS PASSED: {}".format(total_passed_count)
print " TOTAL TESTS FAILED: {}".format(total_failed_count)
+ print " TOTAL TESTS FILTERED: {}".format(total_filtered_count)
+ print " TOTAL FLAKY TESTS FAILED: {}".format(total_flaky_failed_count)
return result
@@ -485,7 +479,20 @@
result = True
try:
- self._BuildTests(ninja_flags)
+ if self.out_directory:
+ out_directory = self.out_directory
+ else:
+ out_directory = abstract_launcher.DynamicallyBuildOutDirectory(
+ self.platform, self.config)
+
+ if ninja_flags:
+ extra_flags = [ninja_flags]
+ else:
+ extra_flags = []
+
+ build_tests.BuildTargets(self.test_targets, out_directory, self.dry_run,
+ extra_flags)
+
except subprocess.CalledProcessError as e:
result = False
sys.stderr.write("Error occurred during building.\n")
@@ -528,9 +535,7 @@
action="store_true",
help="Specifies to show what would be done without actually doing it.")
arg_parser.add_argument(
- "-t",
- "--target_name",
- help="Name of executable target.")
+ "-t", "--target_name", help="Name of executable target.")
arg_parser.add_argument(
"-a",
"--application_name",
@@ -587,5 +592,6 @@
else:
return 0
+
if __name__ == "__main__":
sys.exit(main())
diff --git a/src/starboard/tools/toolchain/abstract.py b/src/starboard/tools/toolchain/abstract.py
index 254d2e8..dc96503 100644
--- a/src/starboard/tools/toolchain/abstract.py
+++ b/src/starboard/tools/toolchain/abstract.py
@@ -350,6 +350,43 @@
pass
+class SharedLibraryLinker(Tool):
+ """Links shared libraries."""
+
+ def IsPlatformAgnostic(self):
+ return False
+
+ def GetRuleName(self):
+ return 'link_shared'
+
+ def GetHeaderDependenciesFilePath(self):
+ # Only applicable to C family compilers.
+ return None
+
+ def GetHeaderDependenciesFormat(self):
+ # Only applicable to C family compilers.
+ return None
+
+ @abc.abstractmethod
+ def GetFlags(self, ldflags):
+ """Returns tool flags specific to a target.
+
+ This method translates platform-agnostic concepts into a command line
+ arguments understood by a tool.
+
+ Args:
+ ldflags: A list of GCC-style command-line flags. See
+ https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html#Link-Options for
+ details.
+
+ Returns:
+ A list of unquoted strings, one for each flag. It is a responsibility of a
+ caller to quote flags that contain special characters (as determined by a
+ shell) before passing to a tool.
+ """
+ pass
+
+
class Stamp(Tool):
"""Updates the access and modification times of a file to the current time."""
diff --git a/src/starboard/tools/toolchain/clangxx.py b/src/starboard/tools/toolchain/clangxx.py
index 3791a5f..686445d 100644
--- a/src/starboard/tools/toolchain/clangxx.py
+++ b/src/starboard/tools/toolchain/clangxx.py
@@ -17,8 +17,11 @@
from starboard.tools.toolchain import common
-class ExecutableLinker(abstract.ExecutableLinker):
- """Links executables using Clang (invoked as clang++)."""
+class DynamicLinkerBase(object):
+ """A base class for Clang based linking of executables and shared libraries.
+
+ Invoked as clang++.
+ """
def __init__(self, **kwargs):
self._path = common.GetPath('clang++', **kwargs)
@@ -35,10 +38,6 @@
def GetMaxConcurrentProcesses(self):
return self._max_concurrent_processes
- def GetCommand(self, path, extra_flags, flags, shell):
- del shell # Not used.
- return '{0} {1} {2} @$rspfile -o $out'.format(path, extra_flags, flags)
-
def GetDescription(self):
return 'LINK $out'
@@ -50,3 +49,27 @@
def GetFlags(self, ldflags):
return ldflags
+
+
+class ExecutableLinker(DynamicLinkerBase, abstract.ExecutableLinker):
+ """Links executables using Clang (invoked as clang++)."""
+
+ def __init__(self, **kwargs):
+ super(ExecutableLinker, self).__init__(**kwargs)
+
+ def GetCommand(self, path, extra_flags, flags, shell):
+ del shell # Not used.
+ return '{0} {1} {2} @$rspfile -o $out'.format(path, extra_flags, flags)
+
+
+class SharedLibraryLinker(DynamicLinkerBase, abstract.SharedLibraryLinker):
+ """Links shared libraries using Clang (invoked as clang++)."""
+
+ def __init__(self, **kwargs):
+ super(SharedLibraryLinker, self).__init__(**kwargs)
+
+ def GetCommand(self, path, extra_flags, flags, shell):
+ del shell # Not used.
+ return ('{0} -shared {1} -o $out {2} -Wl,-soname=$soname '
+ '-Wl,--whole-archive @$rspfile -Wl,--no-whole-archive').format(
+ path, extra_flags, flags)
diff --git a/src/starboard/win/shared/configuration_public.h b/src/starboard/win/shared/configuration_public.h
deleted file mode 100644
index fc860f0..0000000
--- a/src/starboard/win/shared/configuration_public.h
+++ /dev/null
@@ -1,441 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Other source files should never include this header directly, but should
-// include the generic "starboard/configuration.h" instead.
-
-#ifndef STARBOARD_WIN_SHARED_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_WIN_SHARED_CONFIGURATION_PUBLIC_H_
-
-// The API version implemented by this platform. This will generally be set to
-// the current value of SB_MAXIMUM_API_VERSION at the time of implementation.
-#define SB_API_VERSION 7
-
-// --- Architecture Configuration --------------------------------------------
-
-// Whether the current platform is big endian. SB_IS_LITTLE_ENDIAN will be
-// automatically set based on this.
-#define SB_IS_BIG_ENDIAN 0
-
-// Whether the current platform is an ARM architecture.
-#define SB_IS_ARCH_ARM 0
-
-// Whether the current platform is a MIPS architecture.
-#define SB_IS_ARCH_MIPS 0
-
-// Whether the current platform is a PPC architecture.
-#define SB_IS_ARCH_PPC 0
-
-// Whether the current platform is an x86 architecture.
-#define SB_IS_ARCH_X86 1
-
-#define SB_IS_32_BIT 0
-#define SB_IS_64_BIT 1
-
-#define SB_HAS_32_BIT_LONG 1
-#define SB_HAS_32_BIT_POINTERS 0
-
-#define SB_HAS_64_BIT_LONG 0
-#define SB_HAS_64_BIT_POINTERS 1
-
-#define SB_HAS_SOCKET_ERROR_CONNECTION_RESET_SUPPORT 1
-
-// Configuration parameters that allow the application to make some general
-// compile-time decisions with respect to the the number of cores likely to be
-// available on this platform. For a definitive measure, the application should
-// still call SbSystemGetNumberOfProcessors at runtime.
-
-// Whether the current platform is expected to have many cores (> 6), or a
-// wildly varying number of cores.
-#define SB_HAS_MANY_CORES 1
-
-// Whether the current platform is expected to have exactly 1 core.
-#define SB_HAS_1_CORE 0
-
-// Whether the current platform is expected to have exactly 2 cores.
-#define SB_HAS_2_CORES 0
-
-// Whether the current platform is expected to have exactly 4 cores.
-#define SB_HAS_4_CORES 0
-
-// Whether the current platform is expected to have exactly 6 cores.
-#define SB_HAS_6_CORES 0
-
-// Whether the current platform supports thread priorities.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 1
-
-// Whether the current platform's thread scheduler will automatically balance
-// threads between cores, as opposed to systems where threads will only ever run
-// on the specifically pinned core.
-#define SB_HAS_CROSS_CORE_SCHEDULER 1
-
-// Some platforms will not align variables on the stack with an alignment
-// greater than 16 bytes. Platforms where this is the case should define the
-// following quirk.
-#undef SB_HAS_QUIRK_DOES_NOT_STACK_ALIGN_OVER_16_BYTES
-
-// --- System Header Configuration -------------------------------------------
-
-// Any system headers listed here that are not provided by the platform will be
-// emulated in starboard/types.h.
-
-// Whether the current platform provides the standard header stdarg.h.
-#define SB_HAS_STDARG_H 0
-
-// Whether the current platform provides the standard header stdbool.h.
-#define SB_HAS_STDBOOL_H 0
-
-// Whether the current platform provides the standard header stddef.h.
-#define SB_HAS_STDDEF_H 1
-
-// Whether the current platform provides the standard header stdint.h.
-#define SB_HAS_STDINT_H 1
-
-// Whether the current platform provides the standard header inttypes.h.
-#define SB_HAS_INTTYPES_H 1
-
-// Whether the current platform provides the standard header wchar.h.
-#define SB_HAS_WCHAR_H 1
-
-// Whether the current platform provides the standard header limits.h.
-#define SB_HAS_LIMITS_H 1
-
-// Whether the current platform provides the standard header float.h.
-#define SB_HAS_FLOAT_H 1
-
-// Whether the current platform provides ssize_t.
-#define SB_HAS_SSIZE_T 0
-
-// Whether the current platform has microphone supported.
-#define SB_HAS_MICROPHONE 0
-
-// Whether the current platform has speech synthesis.
-#define SB_HAS_SPEECH_SYNTHESIS 0
-
-// Whether the current platform has speech recognizer.
-#define SB_HAS_SPEECH_RECOGNIZER 0
-
-// Whether the current platform has a DRM session closed callback.
-#define SB_HAS_DRM_SESSION_CLOSED 1
-
-#if !defined(__WCHAR_MAX__)
-#include <wchar.h>
-#define __WCHAR_MAX__ WCHAR_MAX
-#endif
-
-// Type detection for wchar_t.
-#if defined(__WCHAR_MAX__) && \
- (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
-#define SB_IS_WCHAR_T_UTF32 1
-#elif defined(__WCHAR_MAX__) && \
- (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
-#define SB_IS_WCHAR_T_UTF16 1
-#endif
-
-// Chrome only defines these two if ARMEL or MIPSEL are defined.
-#if defined(__ARMEL__)
-// Chrome has an exclusion for iOS here, we should too when we support iOS.
-#define SB_IS_WCHAR_T_UNSIGNED 1
-#elif defined(__MIPSEL__)
-#define SB_IS_WCHAR_T_SIGNED 1
-#endif
-
-// --- Compiler Configuration ------------------------------------------------
-
-// The platform's annotation for forcing a C function to be inlined.
-// https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx#Anchor_1
-#define SB_C_FORCE_INLINE __forceinline
-
-// The platform's annotation for marking a C function as suggested to be
-// inlined.
-#define SB_C_INLINE inline
-
-// The platform's annotation for marking a C function as forcibly not
-// inlined.
-#define SB_C_NOINLINE __declspec(noinline)
-
-// The platform's annotation for marking a symbol as exported outside of the
-// current shared library.
-#define SB_EXPORT_PLATFORM __declspec(dllexport)
-
-// The platform's annotation for marking a symbol as imported from outside of
-// the current linking unit.
-#define SB_IMPORT_PLATFORM
-
-// On some platforms the __GNUC__ is defined even though parts of the
-// functionality are missing. Setting this to non-zero allows disabling missing
-// functionality encountered.
-#undef SB_HAS_QUIRK_COMPILER_SAYS_GNUC_BUT_ISNT
-
-// On some compilers, the frontend has a quirk such that #ifdef cannot
-// correctly detect __has_feature is defined, and an example error you get is:
-#undef SB_HAS_QUIRK_HASFEATURE_NOT_DEFINED_BUT_IT_IS
-
-// --- Extensions Configuration ----------------------------------------------
-
-// GCC/Clang doesn't define a long long hash function, except for Android and
-// Game consoles.
-#define SB_HAS_LONG_LONG_HASH 1
-
-// GCC/Clang doesn't define a string hash function, except for Game Consoles.
-#define SB_HAS_STRING_HASH 1
-
-// Desktop Linux needs a using statement for the hash functions.
-#define SB_HAS_HASH_USING 1
-
-// Set this to 1 if hash functions for custom types can be defined as a
-// hash_value() function. Otherwise, they need to be placed inside a
-// partially-specified hash struct template with an operator().
-#define SB_HAS_HASH_VALUE 1
-
-// Set this to 1 if use of hash_map or hash_set causes a deprecation warning
-// (which then breaks the build).
-#define SB_HAS_HASH_WARNING 1
-#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
-
-// The location to include hash_map on this platform.
-#define SB_HASH_MAP_INCLUDE <hash_map>
-
-// C++'s hash_map and hash_set are often found in different namespaces depending
-// on the compiler.
-#define SB_HASH_NAMESPACE stdext
-
-// The location to include hash_set on this platform.
-#define SB_HASH_SET_INCLUDE <hash_set>
-
-// Define this to how this platform copies varargs blocks.
-#define SB_VA_COPY(dest, source) va_copy(dest, source)
-
-// --- Filesystem Configuration ----------------------------------------------
-
-// The current platform's maximum length of the name of a single directory
-// entry, not including the absolute path.
-#define SB_FILE_MAX_NAME 260
-
-// The current platform's maximum length of an absolute path.
-#define SB_FILE_MAX_PATH 4096
-
-// The current platform's maximum number of files that can be opened at the
-// same time by one process.
-// This is set to MAXIMUM_WAIT_OBJECTS - 1, since we use WaitForMultipleEvents
-// and that only supports MAXIMUM_WAIT_OBJECTS events. The -1 is since we
-// use a dummy event to wake up the socket.
-#define SB_FILE_MAX_OPEN 63
-
-// The current platform's file path component separator character. This is the
-// character that appears after a directory in a file path. For example, the
-// absolute canonical path of the file "/path/to/a/file.txt" uses '/' as a path
-// component separator character.
-#define SB_FILE_SEP_CHAR '\\'
-
-// The current platform's alternate file path component separator character.
-// This is like SB_FILE_SEP_CHAR, except if your platform supports an alternate
-// character, then you can place that here. For example, on windows machines,
-// the primary separator character is probably '\', but the alternate is '/'.
-#define SB_FILE_ALT_SEP_CHAR '/'
-
-// The current platform's search path component separator character. When
-// specifying an ordered list of absolute paths of directories to search for a
-// given reason, this is the character that appears between entries. For
-// example, the search path of "/etc/search/first:/etc/search/second" uses ':'
-// as a search path component separator character.
-#define SB_PATH_SEP_CHAR ';'
-
-// The string form of SB_FILE_SEP_CHAR.
-#define SB_FILE_SEP_STRING "\\"
-
-// The string form of SB_FILE_ALT_SEP_CHAR.
-#define SB_FILE_ALT_SEP_STRING "/"
-
-// The string form of SB_PATH_SEP_CHAR.
-#define SB_PATH_SEP_STRING ";"
-
-// On some platforms the file system stores access times at a coarser
-// granularity than other times. When this quirk is defined, we assume the
-// access time is of 1 day precision.
-#undef SB_HAS_QUIRK_FILESYSTEM_COARSE_ACCESS_TIME
-
-// --- Memory Configuration --------------------------------------------------
-
-// The memory page size, which controls the size of chunks on memory that
-// allocators deal with, and the alignment of those chunks. This doesn't have to
-// be the hardware-defined physical page size, but it should be a multiple of
-// it.
-#define SB_MEMORY_PAGE_SIZE 4096
-
-// Whether this platform has and should use an MMAP function to map physical
-// memory to the virtual address space.
-#define SB_HAS_MMAP 1
-
-// Whether this platform can map executable memory. Implies SB_HAS_MMAP. This is
-// required for platforms that want to JIT.
-#define SB_CAN_MAP_EXECUTABLE_MEMORY 0
-
-// Whether this platform has and should use an growable heap (e.g. with sbrk())
-// to map physical memory to the virtual address space.
-#define SB_HAS_VIRTUAL_REGIONS 0
-
-// Specifies the alignment for IO Buffers, in bytes. Some low-level network APIs
-// may require buffers to have a specific alignment, and this is the place to
-// specify that.
-#define SB_NETWORK_IO_BUFFER_ALIGNMENT 16
-
-// Determines the alignment that allocations should have on this platform.
-#define SB_MALLOC_ALIGNMENT ((size_t)16U)
-
-// Determines the threshhold of allocation size that should be done with mmap
-// (if available), rather than allocated within the core heap.
-#define SB_DEFAULT_MMAP_THRESHOLD ((size_t)(256 * 1024U))
-
-// Defines the path where memory debugging logs should be written to.
-#define SB_MEMORY_LOG_PATH "/tmp/starboard"
-
-// --- Thread Configuration --------------------------------------------------
-
-// Defines the maximum number of simultaneous threads for this platform. Some
-// platforms require sharing thread handles with other kinds of system handles,
-// like mutexes, so we want to keep this managable.
-#define SB_MAX_THREADS 90
-
-// The maximum number of thread local storage keys supported by this platform.
-#define SB_MAX_THREAD_LOCAL_KEYS 512
-
-// The maximum length of the name for a thread, including the NULL-terminator.
-#define SB_MAX_THREAD_NAME_LENGTH 16;
-
-// --- Graphics Configuration ------------------------------------------------
-
-// Specifies whether this platform supports a performant accelerated blitter
-// API. The basic requirement is a scaled, clipped, alpha-blended blit.
-#define SB_HAS_BLITTER 0
-
-// Specifies the preferred byte order of color channels in a pixel. Refer to
-// starboard/configuration.h for the possible values. EGL/GLES platforms should
-// generally prefer a byte order of RGBA, regardless of endianness.
-#define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_RGBA
-
-// 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.
-#define SB_HAS_BILINEAR_FILTERING_SUPPORT 1
-
-// Indicates whether or not the given platform supports rendering of NV12
-// textures. These textures typically originate from video decoders.
-#define SB_HAS_NV12_TEXTURE_SUPPORT 0
-
-// Whether the current platform should frequently flip its display buffer. If
-// this is not required (i.e. SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER is set to
-// 0), then optimizations are enabled so the display buffer is not flipped if
-// the scene hasn't changed.
-#define SB_MUST_FREQUENTLY_FLIP_DISPLAY_BUFFER 0
-
-#define SB_HAS_VIRTUAL_REALITY 0
-
-// --- Media Configuration ---------------------------------------------------
-
-// After a seek is triggerred, the default behavior is to append video frames
-// from the last key frame before the seek time and append audio frames from the
-// seek time because usually all audio frames are key frames. On platforms that
-// cannot decode video frames without displaying them, this will cause the video
-// being played without audio for several seconds after seeking. When the
-// following macro is defined, the app will append audio frames start from the
-// timestamp that is before the timestamp of the video key frame being appended.
-#undef SB_HAS_QUIRK_SEEK_TO_KEYFRAME
-
-// dlmalloc will use the ffs intrinsic if available. Platforms on which this is
-// not available should define the following quirk.
-#undef SB_HAS_QUIRK_NO_FFS
-
-// The maximum audio bitrate the platform can decode. The following value
-// equals to 5M bytes per seconds which is more than enough for compressed
-// audio.
-#define SB_MEDIA_MAX_AUDIO_BITRATE_IN_BITS_PER_SECOND (40 * 1024 * 1024)
-
-// The maximum video bitrate the platform can decode. The following value
-// equals to 25M bytes per seconds which is more than enough for compressed
-// video.
-#define SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND (200 * 1024 * 1024)
-
-// Specifies whether this platform has webm/vp9 support. This should be set to
-// non-zero on platforms with webm/vp9 support.
-#define SB_HAS_MEDIA_WEBM_VP9_SUPPORT 1
-
-// Specifies the stack size for threads created inside media stack. Set to 0 to
-// use the default thread stack size. Set to non-zero to explicitly set the
-// stack size for media stack threads.
-#define SB_MEDIA_THREAD_STACK_SIZE 0U
-
-// --- Decoder-only Params ---
-
-// Specifies how media buffers must be aligned on this platform as some
-// decoders may have special requirement on the alignment of buffers being
-// decoded.
-#define SB_MEDIA_BUFFER_ALIGNMENT 128U
-
-// Specifies how video frame buffers must be aligned on this platform.
-#define SB_MEDIA_VIDEO_FRAME_ALIGNMENT 256U
-
-// The encoded video frames are compressed in different ways, so their decoding
-// time can vary a lot. Occasionally a single frame can take longer time to
-// decode than the average time per frame. The player has to cache some frames
-// to account for such inconsistency. The number of frames being cached are
-// controlled by SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES and
-// SB_MEDIA_MAXIMUM_VIDEO_FRAMES.
-//
-// Specify the number of video frames to be cached before the playback starts.
-// Note that setting this value too large may increase the playback start delay.
-#define SB_MEDIA_MAXIMUM_VIDEO_PREROLL_FRAMES 4
-
-// Specify the number of video frames to be cached during playback. A large
-// value leads to more stable fps but also causes the app to use more memory.
-#define SB_MEDIA_MAXIMUM_VIDEO_FRAMES 12
-
-// --- Network Configuration -------------------------------------------------
-
-// Specifies whether this platform supports IPV6.
-#define SB_HAS_IPV6 1
-
-// Specifies whether this platform supports pipe.
-#define SB_HAS_PIPE 1
-
-// --- Tuneable Parameters ---------------------------------------------------
-
-// Specifies the network receive buffer size in bytes, set via
-// SbSocketSetReceiveBufferSize().
-//
-// Setting this to 0 indicates that SbSocketSetReceiveBufferSize() should
-// not be called. Use this for OSs (such as Linux) where receive buffer
-// auto-tuning is better.
-//
-// On some platforms, this may affect max TCP window size which may
-// dramatically affect throughput in the presence of latency.
-//
-// If your platform does not have a good TCP auto-tuning mechanism,
-// a setting of (128 * 1024) here is recommended.
-#define SB_NETWORK_RECEIVE_BUFFER_SIZE (0)
-
-// --- User Configuration ----------------------------------------------------
-
-// The maximum number of users that can be signed in at the same time.
-#define SB_USER_MAX_SIGNED_IN 1
-
-// --- Timing API ------------------------------------------------------------
-
-// Whether this platform has an API to retrieve how long the current thread
-// has spent in the executing state.
-#define SB_HAS_TIME_THREAD_NOW 0
-
-// --- Platform Specific Audits ----------------------------------------------
-
-#endif // STARBOARD_WIN_SHARED_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/win/shared/gyp_configuration.gypi b/src/starboard/win/shared/gyp_configuration.gypi
deleted file mode 100644
index ca05ba4..0000000
--- a/src/starboard/win/shared/gyp_configuration.gypi
+++ /dev/null
@@ -1,422 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'variables': {
- 'target_arch': 'x64',
- 'target_os': 'win',
-
- # Use a hardware rasterizer and graphical setup.
- 'rasterizer_type': 'hardware',
-
- # Link with angle
- 'gl_type': 'angle',
- 'angle_platform_windows': 1,
-
- # Frame presentation is blocked on vsync, so the render thread will also
- # block on vsync. However, use a non-zero minimum frame time to avoid
- # possible busy-loops on unrendered submissions.
- 'cobalt_minimum_frame_time_in_milliseconds': '1',
-
- 'cobalt_splash_screen_file': '<(DEPTH)/cobalt/browser/splash_screen/youtube_splash_screen.html',
- 'fallback_splash_screen_url': 'file:///cobalt/browser/splash_screen/youtube_splash_screen.html',
-
- # win-win32-lib does not link this binary successfully today.
- 'build_snapshot_app_stats': 0,
-
- # Platform-specific implementations to compile into cobalt.
- 'cobalt_platform_dependencies': [
- '<(DEPTH)/starboard/egl_and_gles/egl_and_gles.gyp:egl_and_gles',
- ],
-
- 'conditions': [
- ['cobalt_fastbuild==0', {
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'DebugInformationFormat': '3',
- },
- 'VCLinkerTool': {
- 'GenerateDebugInformation': 'true',
- },
- }
- }],
- ],
- },
-
- 'target_defaults': {
- 'configurations': {
- 'winrt_base': {
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'AdditionalOptions': [
- '/ZW', # Windows Runtime
- '/ZW:nostdlib', # Windows Runtime, no default #using
- ]
- }
- },
- 'defines': [
- # VS2017 always defines this for UWP apps
- 'WINAPI_FAMILY=WINAPI_FAMILY_APP',
- # VS2017 always defines this for UWP apps
- '__WRL_NO_DEFAULT_LIB__',
- ]
- }, # winrt_base
- 'win32_base': {
- 'abstract': 1,
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'SubSystem': 1, # Build a console application by default.
- 'AdditionalDependencies': [
- 'shell32.lib',
- 'winmm.lib',
- 'gdi32.lib',
- 'dbghelp.lib',
- 'user32.lib',
- 'shlwapi.lib'
- ],
- }
- },
- 'msvs_target_platform': 'x64',
- 'defines': [
- '_WIN32',
- 'WIN32',
- 'WINDOWS',
- '_UNICODE',
- 'UNICODE',
- ],
- }, # win32_base
- 'msvs_base': {
- 'abstract': 1,
- 'msvs_configuration_attributes': {
- 'OutputDirectory': '<(DEPTH)\\build\\<(build_dir_prefix)$(ConfigurationName)',
- 'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)',
- 'CharacterSet': '1',
- },
- 'msvs_system_include_dirs': [
- '<(windows_sdk_path)/include/<(windows_sdk_version)/shared',
- '<(windows_sdk_path)/include/<(windows_sdk_version)/ucrt',
- '<(windows_sdk_path)/include/<(windows_sdk_version)/um',
- '<(windows_sdk_path)/include/<(windows_sdk_version)/winrt',
- '<(windows_sdk_path)/include/<(windows_sdk_version)',
- '<(visual_studio_install_path)/include',
- '<(visual_studio_install_path)/atlmfc/include',
- ],
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'AdditionalDependencies': [
- 'mfplat.lib',
- 'mfuuid.lib',
- 'windowsapp.lib',
- ],
- 'AdditionalLibraryDirectories!': [
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/ucrt/x86',
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/um/x86',
- ],
- 'AdditionalLibraryDirectories': [
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/ucrt/x64',
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/um/x64',
- '<(visual_studio_install_path)/lib/x64',
- ],
- },
- 'VCLibrarianTool': {
- 'AdditionalLibraryDirectories!': [
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/ucrt/x86',
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/um/x86',
- ],
- 'AdditionalLibraryDirectories': [
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/ucrt/x64',
- '<(windows_sdk_path)/lib/<(windows_sdk_version)/um/x64',
- '<(visual_studio_install_path)/lib/x64',
- ],
- },
- },
- 'msvs_target_platform': 'x64',
- # Add the default import libs.
- 'conditions': [
- ['sb_pedantic_warnings==1', {
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- # Enable some warnings, even those that are disabled by default.
- # See https://msdn.microsoft.com/en-us/library/23k5d385.aspx
- 'WarningLevel': '4',
- 'AdditionalOptions': [
- # Warn if an enumeration value is unhandled in switch (C4062).
- # This warning is off by default, so it must be turned on explicitly.
- '/w44062',
- ],
- },
- },
- }],
- ['cobalt_fastbuild==0', {
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'DebugInformationFormat': '3',
- 'AdditionalOptions': [
- '/FS',
- ],
- },
- 'VCLinkerTool': {
- 'GenerateDebugInformation': 'true',
- },
- },
- }],
- ],
- },
- 'msvs_debug': {
- 'inherit_from': ['debug_base', 'msvs_base'],
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'Optimization': '0',
- 'BasicRuntimeChecks': '3', # Check stack frame validity and check for uninitialized variables at run time.
- 'AdditionalOptions': [
- '/MDd', # Use debug multithreaded library.
- '/GS',
- ],
- },
- 'VCLinkerTool': {
- 'AdditionalDependencies': ['dbghelp.lib'],
- 'LinkIncremental': '1', # INCREMENTAL:NO
- },
- },
- },
- 'msvs_devel': {
- 'inherit_from': ['devel_base', 'msvs_base'],
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'Optimization': '2',
- 'AdditionalOptions': [
- '/MDd', # Use debug multithreaded library.
- '/GS',
- ],
- },
- 'VCLinkerTool': {
- 'AdditionalDependencies': ['dbghelp.lib'],
- 'LinkIncremental': '1', # INCREMENTAL:NO
- },
- },
- },
- 'msvs_qa': {
- 'inherit_from': ['qa_base', 'msvs_base'],
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'Optimization': '2',
- 'AdditionalOptions': [
- '/MD', # Use release multithreaded library.
- ],
- },
- 'VCLinkerTool': {
- 'AdditionalDependencies': ['dbghelp.lib'],
- 'LinkIncremental': '1', # INCREMENTAL:NO
- 'OptimizeReferences' : '2', # OPT:REF
- 'EnableCOMDATFolding' : '2', # OPT:ICF
- },
- },
- 'msvs_disabled_warnings': [
- # Unreferenced argument.
- # Often variables are only referenced in DCHECKs.
- 4100,
- # Unreferenced variable.
- # Often variables are only referenced in DCHECKs.
- 4189,
- ],
- },
- 'msvs_gold': {
- 'inherit_from': ['gold_base', 'msvs_base'],
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'Optimization': '2',
- 'AdditionalOptions': [
- '/MD', # Use release multithreaded library.
- ],
- },
- 'VCLinkerTool': {
- 'LinkIncremental': '1', # INCREMENTAL:NO
- 'OptimizeReferences' : '2', # OPT:REF
- 'EnableCOMDATFolding' : '2', # OPT:ICF
- },
- },
- 'msvs_disabled_warnings': [
- # Unreferenced argument.
- # Often variables are only referenced in DCHECKs.
- 4100,
- # Unreferenced variable.
- # Often variables are only referenced in DCHECKs.
- 4189,
- ],
- },
- },
- 'defines': [
- # Disable warnings. These options were inherited from Chromium.
- '_CRT_SECURE_NO_DEPRECATE',
- '_CRT_NONSTDC_NO_WARNINGS',
- '_CRT_NONSTDC_NO_DEPRECATE',
- '_SCL_SECURE_NO_DEPRECATE',
- # Disable suggestions to switch to Microsoft-specific secure CRT.
- '_CRT_SECURE_NO_WARNINGS',
- # Disable support for exceptions in STL in order to detect their use
- # (the use of exceptions is forbidden by Google C++ style guide).
- '_HAS_EXCEPTIONS=0',
- '__STDC_FORMAT_MACROS', # so that we get PRI*
- # Enable GNU extensions to get prototypes like ffsl.
- '_GNU_SOURCE=1',
- 'WIN32_LEAN_AND_MEAN',
- # By defining this, M_PI will get #defined.
- '_USE_MATH_DEFINES',
- # min and max collide with std::min and std::max
- 'NOMINMAX',
- # Conform with C99 spec.
- '_CRT_STDIO_ISO_WIDE_SPECIFIERS',
- ],
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'ForcedIncludeFiles': [],
-
- # Check for buffer overruns.
- 'BufferSecurityCheck': 'true',
-
- 'Conformance': [
- # "for" loop's initializer go out of scope after the for loop.
- 'forScope',
- # wchar_t is treated as a built-in type.
- 'wchar_t',
- ],
-
- # Check for 64-bit portability issues.
- 'Detect64BitPortabilityProblems': 'true',
-
- # Disable Microsoft-specific header dependency tracking.
- # Incremental linker does not support the Windows metadata included
- # in .obj files compiled with C++/CX support (/ZW).
- 'MinimalRebuild': 'false',
-
- # Treat warnings as errors.
- 'WarnAsError': 'true',
-
- # Enable some warnings, even those that are disabled by default.
- # See https://msdn.microsoft.com/en-us/library/23k5d385.aspx
- 'WarningLevel': '2',
-
- 'AdditionalOptions': [
- '/errorReport:none', # Don't send error reports to MS.
- '/FS', # Force sync PDB updates for parallel compile.
- ],
- },
- 'VCLinkerTool': {
- 'RandomizedBaseAddress': '2', # /DYNAMICBASE
- # TODO: SubSystem is hardcoded in
- # win\sources_template.vcxproj. This will have the exe behave in the
- # expected way in MSVS: when it's run without debug (Ctrl+F5), it
- # will pause after finish; when debugging (F5) it will not pause
- # before the cmd window disappears.
- # Currently the value is ignored by msvs_makefile.py which generates
- # the MSVS project files (it's in "data" in GenerateOutput()). Fix
- # it if we ever need to change SubSystem.
- 'SubSystem': '1', # CONSOLE
- 'TargetMachine': '17', # x86 - 64
- 'AdditionalOptions': [
- '/WINMD:NO', # Do not generate a WinMD file.
- '/errorReport:none', # Don't send error reports to MS.
- ],
- },
- 'VCLibrarianTool': {
- 'AdditionalOptions': [
- # Linking statically with C++/CX library is not recommended.
- # TODO: Remove after removing ComponentExtensions
- '/ignore:4264',
- ],
- },
- },
- 'msvs_disabled_warnings': [
- # Conditional expression is constant.
- # Triggers in many legitimate cases, like branching on a constant declared
- # in type traits.
- 4127,
- # 4244 (Level 2) - Implicit conversion from float to int
- # 4244 (Level 3) - Implicit conversion from int to something smaller
- # than int.
- # 4244 (Level 4) - Implicit conversion of types, which may result in
- # data loss.
- 4244,
- # Class has virtual functions, but destructor is not virtual.
- # Far less useful than in GCC because doesn't take into account the fact
- # that destructor is not public.
- 4265,
- # Inconsistent DLL linkage
- 4273,
- # Double -> float truncation. Not enabled on other compilers.
- 4305,
- # cast truncates constant value.
- # We do not care.
- 4310,
- # An rvalue cannot be bound to a non-const reference.
- # In previous versions of Visual C++, it was possible to bind an rvalue
- # to a non-const reference in a direct initialization. This warning
- # is useless as it simply describes proper C++ behavior.
- 4350,
- # layout of class may have changed from a previous version of
- # the compiler due to better packing of member. We don't care about
- # binary compatibility with other compiler versions.
- 4371,
- # relative include path contains '..'.
- # This occurs in a lot third party libraries and we don't care.
- 4464,
- # decorated name length exceeded, name was truncated.
- 4503,
- # assignment operator could not be generated.
- # This is expected for structs with const members.
- 4512,
- # Unreferenced inline function has been removed.
- # While detection of dead code is good, this warning triggers in
- # third-party libraries which renders it useless.
- 4514,
- # Expression before comma has no effect.
- # Cannot be used because Microsoft uses _ASSERTE(("message", 0)) trick
- # in malloc.h which is included pretty much everywhere.
- 4548,
- # Use of noexcept in targets compiled without /EHsc triggers a warning
- # "termination on exception is not guaranteed" which we consider benign
- # because we don't expect exceptions to cross the boundary of modules
- # compiled with /EHsc.
- 4577,
- # Copy constructor could not be generated because a base class copy
- # constructor is inaccessible.
- # This is an expected consequence of using DISALLOW_COPY_AND_ASSIGN().
- 4625,
- # Assignment operator could not be generated because a base class
- # assignment operator is inaccessible.
- # This is an expected consequence of using DISALLOW_COPY_AND_ASSIGN().
- 4626,
- # Digraphs not supported.
- # Thanks god!
- 4628,
- # Symbol is not defined as a preprocessor macro, replacing with '0'.
- # Seems like common practice, used in Windows SDK and gtest.
- 4668,
- # Function not inlined.
- # It's up to the compiler to decide what to inline.
- 4710,
- # Function selected for inline expansion.
- # It's up to the compiler to decide what to inline.
- 4711,
- # The type and order of elements caused the compiler to add padding
- # to the end of a struct.
- # Unsurprisingly, most of the structs become larger because of padding
- # but it's a universally acceptable price for better performance.
- 4820,
- # Disable static analyzer warning for std::min and std::max with
- # objects.
- # https://connect.microsoft.com/VisualStudio/feedback/details/783808/static-analyzer-warning-c28285-for-std-min-and-std-max
- 28285,
- ],
- }, # end of target_defaults
-}
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi
deleted file mode 100644
index 36bc281..0000000
--- a/src/starboard/win/shared/starboard_platform.gypi
+++ /dev/null
@@ -1,445 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-{
- 'variables': {
- 'sb_pedantic_warnings': 1,
- 'winrt%': 1,
-
- # TODO: Move this and the win32 dependencies below to a shared/win32/starboard_platform.gypi?
- 'uwp_incompatible_win32': [
- '<(DEPTH)/starboard/shared/win32/application_win32_key_event.cc',
- '<(DEPTH)/starboard/shared/win32/application_win32.cc',
- '<(DEPTH)/starboard/shared/win32/decode_target_internal.cc',
- '<(DEPTH)/starboard/shared/win32/decode_target_internal.h',
- '<(DEPTH)/starboard/shared/win32/dialog.cc',
- '<(DEPTH)/starboard/shared/win32/get_home_directory.cc',
- '<(DEPTH)/starboard/shared/win32/log_file_impl.cc',
- '<(DEPTH)/starboard/shared/win32/log_file_impl.h',
- '<(DEPTH)/starboard/shared/win32/log_raw.cc',
- '<(DEPTH)/starboard/shared/win32/log_raw_format.cc',
- '<(DEPTH)/starboard/shared/win32/media_is_audio_supported.cc',
- '<(DEPTH)/starboard/shared/win32/media_is_video_supported.cc',
- '<(DEPTH)/starboard/shared/win32/player_components_impl.cc',
- '<(DEPTH)/starboard/shared/win32/playready_license.cc',
- '<(DEPTH)/starboard/shared/win32/starboard_main.cc',
- '<(DEPTH)/starboard/shared/win32/system_clear_platform_error.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_device_type.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_property.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_total_cpu_memory.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_used_cpu_memory.cc',
- '<(DEPTH)/starboard/shared/win32/system_raise_platform_error.cc',
- '<(DEPTH)/starboard/shared/win32/window_create.cc',
- '<(DEPTH)/starboard/shared/win32/window_destroy.cc',
- '<(DEPTH)/starboard/shared/win32/window_get_platform_handle.cc',
- '<(DEPTH)/starboard/shared/win32/window_get_size.cc',
- '<(DEPTH)/starboard/shared/win32/window_internal.cc',
- '<(DEPTH)/starboard/shared/win32/window_intsdfdsfernal.h',
- '<(DEPTH)/starboard/shared/win32/window_set_default_options.cc',
- ],
- 'win32_shared_drm_files': [
- '<(DEPTH)/starboard/shared/starboard/drm/drm_close_session.cc',
- '<(DEPTH)/starboard/shared/starboard/drm/drm_destroy_system.cc',
- '<(DEPTH)/starboard/shared/starboard/drm/drm_generate_session_update_request.cc',
- '<(DEPTH)/starboard/shared/starboard/drm/drm_system_internal.h',
- '<(DEPTH)/starboard/shared/starboard/drm/drm_update_session.cc',
-
- '<(DEPTH)/starboard/shared/win32/drm_create_system.cc',
- '<(DEPTH)/starboard/shared/win32/drm_system_playready.cc',
- '<(DEPTH)/starboard/shared/win32/drm_system_playready.h',
- '<(DEPTH)/starboard/shared/win32/wrm_header.cc',
- '<(DEPTH)/starboard/shared/win32/wrm_header.h',
- ],
-
- # Please keep this in sync with xb1/shared/starboard_platform.gypi.
- 'win32_media_player_files': [
- '<(DEPTH)/starboard/shared/win32/atomic_queue.h',
- '<(DEPTH)/starboard/shared/win32/audio_decoder.cc',
- '<(DEPTH)/starboard/shared/win32/audio_decoder.h',
- '<(DEPTH)/starboard/shared/win32/audio_decoder_thread.cc',
- '<(DEPTH)/starboard/shared/win32/audio_decoder_thread.h',
- '<(DEPTH)/starboard/shared/win32/audio_transform.cc',
- '<(DEPTH)/starboard/shared/win32/audio_transform.h',
- '<(DEPTH)/starboard/shared/win32/decrypting_decoder.cc',
- '<(DEPTH)/starboard/shared/win32/decrypting_decoder.h',
- '<(DEPTH)/starboard/shared/win32/dx_context_video_decoder.cc',
- '<(DEPTH)/starboard/shared/win32/dx_context_video_decoder.h',
- '<(DEPTH)/starboard/shared/win32/media_common.cc',
- '<(DEPTH)/starboard/shared/win32/media_common.h',
- '<(DEPTH)/starboard/shared/win32/media_foundation_utils.cc',
- '<(DEPTH)/starboard/shared/win32/media_foundation_utils.h',
- '<(DEPTH)/starboard/shared/win32/media_is_supported.cc',
- '<(DEPTH)/starboard/shared/win32/media_transform.cc',
- '<(DEPTH)/starboard/shared/win32/media_transform.h',
- '<(DEPTH)/starboard/shared/win32/video_decoder.cc',
- '<(DEPTH)/starboard/shared/win32/video_decoder.h',
- '<(DEPTH)/starboard/shared/win32/win32_audio_decoder.cc',
- '<(DEPTH)/starboard/shared/win32/win32_audio_decoder.h',
- ],
- 'win32_shared_media_player_files': [
- '<(DEPTH)/starboard/shared/starboard/media/codec_util.cc',
- '<(DEPTH)/starboard/shared/starboard/media/codec_util.h',
- '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc',
- '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_output_count_stereo_only.cc',
- '<(DEPTH)/starboard/shared/starboard/media/media_util.cc',
- '<(DEPTH)/starboard/shared/starboard/media/media_util.h',
- '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_frame_tracker.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_resampler_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_time_stretcher.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/decoded_audio_queue.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/media_time_provider_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_frame_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_render_algorithm_impl.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/wsola_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_get_current_frame.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_get_info2.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_output_mode_supported.h',
- '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_seek2.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_set_playback_rate.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc',
- '<(DEPTH)/starboard/shared/starboard/player/player_write_sample2.cc',
- '<(DEPTH)/starboard/shared/stub/media_is_transfer_characteristics_supported.cc',
-
- # Shared renderers
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
- ],
-
- 'win32_shared_misc_files': [
- '<(DEPTH)/starboard/common/thread.cc',
- '<(DEPTH)/starboard/common/thread.h',
- '<(DEPTH)/starboard/shared/starboard/net_log.cc',
- '<(DEPTH)/starboard/shared/starboard/net_log.h',
- ],
-
- 'starboard_platform_dependent_files': [
- '<@(win32_shared_misc_files)',
- '<@(win32_media_player_files)',
- '<@(win32_shared_drm_files)',
- '<@(win32_shared_media_player_files)',
- ]
- },
- 'targets': [
- {
- 'target_name': 'starboard_platform',
- 'type': 'static_library',
- 'hard_dependency': 1,
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'AdditionalIncludeDirectories': [
- '<(DEPTH)/third_party/angle/include',
- '<(DEPTH)/third_party/angle/include/EGL',
- '<(DEPTH)/third_party/angle/include/GLES2',
- '<(DEPTH)/third_party/angle/include/KHR',
- ],
- 'AdditionalOptions': [
- '/EHsc', # C++ exceptions (required with /ZW)
- '/FU"<(visual_studio_install_path)/lib/x86/store/references/platform.winmd"',
- '/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.FoundationContract/3.0.0.0/Windows.Foundation.FoundationContract.winmd"',
- '/FU"<(windows_sdk_path)/References/<(windows_sdk_version)/Windows.Foundation.UniversalApiContract/4.0.0.0/Windows.Foundation.UniversalApiContract.winmd"',
- ]
- }
- },
- 'conditions': [
- ['winrt==1', {
- 'msvs_settings': {
- 'VCCLCompilerTool': {
- 'AdditionalOptions': [
- '/ZW', # Windows Runtime
- '/ZW:nostdlib', # Windows Runtime, no default #using
- ],
- },
- },
- 'defines': [
- # VS2017 always defines this for UWP apps
- 'WINAPI_FAMILY=WINAPI_FAMILY_APP',
- # VS2017 always defines this for UWP apps
- '__WRL_NO_DEFAULT_LIB__',
- ]
- }]
- ],
- 'sources': [
- '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc',
- '<(DEPTH)/starboard/shared/iso/character_is_digit.cc',
- '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc',
- '<(DEPTH)/starboard/shared/iso/character_is_space.cc',
- '<(DEPTH)/starboard/shared/iso/character_is_upper.cc',
- '<(DEPTH)/starboard/shared/iso/character_to_lower.cc',
- '<(DEPTH)/starboard/shared/iso/character_to_upper.cc',
- '<(DEPTH)/starboard/shared/iso/double_absolute.cc',
- '<(DEPTH)/starboard/shared/iso/double_exponent.cc',
- '<(DEPTH)/starboard/shared/iso/double_floor.cc',
- '<(DEPTH)/starboard/shared/iso/double_is_finite.cc',
- '<(DEPTH)/starboard/shared/iso/double_is_nan.cc',
- '<(DEPTH)/starboard/shared/iso/memory_compare.cc',
- '<(DEPTH)/starboard/shared/iso/memory_copy.cc',
- '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc',
- '<(DEPTH)/starboard/shared/iso/memory_move.cc',
- '<(DEPTH)/starboard/shared/iso/memory_set.cc',
- '<(DEPTH)/starboard/shared/iso/string_compare.cc',
- '<(DEPTH)/starboard/shared/iso/string_compare_all.cc',
- '<(DEPTH)/starboard/shared/iso/string_find_character.cc',
- '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc',
- '<(DEPTH)/starboard/shared/iso/string_find_string.cc',
- '<(DEPTH)/starboard/shared/iso/string_get_length.cc',
- '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc',
- '<(DEPTH)/starboard/shared/iso/string_parse_double.cc',
- '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc',
- '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc',
- '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc',
- '<(DEPTH)/starboard/shared/iso/string_scan.cc',
- '<(DEPTH)/starboard/shared/iso/system_binary_search.cc',
- '<(DEPTH)/starboard/shared/iso/system_sort.cc',
- '<(DEPTH)/starboard/shared/nouser/user_get_current.cc',
- '<(DEPTH)/starboard/shared/nouser/user_get_property.cc',
- '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc',
- '<(DEPTH)/starboard/shared/nouser/user_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
- '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
- '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
- '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h',
- '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc',
- '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc',
- '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h',
- '<(DEPTH)/starboard/shared/starboard/application.cc',
- '<(DEPTH)/starboard/shared/starboard/command_line.cc',
- '<(DEPTH)/starboard/shared/starboard/command_line.h',
- '<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
- '<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
- '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc',
- '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc',
- '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc',
- '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc',
- '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc',
- '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc',
- '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc',
- '<(DEPTH)/starboard/shared/starboard/log_message.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
- '<(DEPTH)/starboard/shared/starboard/string_concat.cc',
- '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc',
- '<(DEPTH)/starboard/shared/starboard/string_copy.cc',
- '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc',
- '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc',
- '<(DEPTH)/starboard/shared/stub/accessibility_get_display_settings.cc',
- '<(DEPTH)/starboard/shared/stub/accessibility_get_text_to_speech_settings.cc',
- '<(DEPTH)/starboard/shared/stub/cryptography_create_transformer.cc',
- '<(DEPTH)/starboard/shared/stub/cryptography_destroy_transformer.cc',
- '<(DEPTH)/starboard/shared/stub/cryptography_get_tag.cc',
- '<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
- '<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
- '<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
- '<(DEPTH)/starboard/shared/stub/image_decode.cc',
- '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
- '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc',
- '<(DEPTH)/starboard/shared/stub/system_has_capability.cc',
- '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc',
- '<(DEPTH)/starboard/shared/stub/system_symbolize.cc',
- '<(DEPTH)/starboard/shared/stub/time_zone_get_dst_name.cc',
- '<(DEPTH)/starboard/shared/win32/audio_sink.cc',
- '<(DEPTH)/starboard/shared/win32/adapter_utils.cc',
- '<(DEPTH)/starboard/shared/win32/adapter_utils.h',
- '<(DEPTH)/starboard/shared/win32/atomic_public.h',
- '<(DEPTH)/starboard/shared/win32/audio_sink_get_max_channels.cc',
- '<(DEPTH)/starboard/shared/win32/audio_sink_get_nearest_supported_sample_frequency.cc',
- '<(DEPTH)/starboard/shared/win32/audio_sink_is_audio_frame_storage_type_supported.cc',
- '<(DEPTH)/starboard/shared/win32/audio_sink_is_audio_sample_type_supported.cc',
- '<(DEPTH)/starboard/shared/win32/auto_event_handle.h',
- '<(DEPTH)/starboard/shared/win32/byte_swap.cc',
- '<(DEPTH)/starboard/shared/win32/condition_variable_broadcast.cc',
- '<(DEPTH)/starboard/shared/win32/condition_variable_create.cc',
- '<(DEPTH)/starboard/shared/win32/condition_variable_destroy.cc',
- '<(DEPTH)/starboard/shared/win32/condition_variable_signal.cc',
- '<(DEPTH)/starboard/shared/win32/condition_variable_wait.cc',
- '<(DEPTH)/starboard/shared/win32/condition_variable_wait_timed.cc',
- '<(DEPTH)/starboard/shared/win32/directory_can_open.cc',
- '<(DEPTH)/starboard/shared/win32/directory_close.cc',
- '<(DEPTH)/starboard/shared/win32/directory_create.cc',
- '<(DEPTH)/starboard/shared/win32/directory_get_next.cc',
- '<(DEPTH)/starboard/shared/win32/directory_internal.cc',
- '<(DEPTH)/starboard/shared/win32/directory_internal.h',
- '<(DEPTH)/starboard/shared/win32/directory_open.cc',
- '<(DEPTH)/starboard/shared/win32/error_utils.cc',
- '<(DEPTH)/starboard/shared/win32/error_utils.h',
- '<(DEPTH)/starboard/shared/win32/file_can_open.cc',
- '<(DEPTH)/starboard/shared/win32/file_close.cc',
- '<(DEPTH)/starboard/shared/win32/file_delete.cc',
- '<(DEPTH)/starboard/shared/win32/file_exists.cc',
- '<(DEPTH)/starboard/shared/win32/file_flush.cc',
- '<(DEPTH)/starboard/shared/win32/file_get_info.cc',
- '<(DEPTH)/starboard/shared/win32/file_get_path_info.cc',
- '<(DEPTH)/starboard/shared/win32/file_internal.cc',
- '<(DEPTH)/starboard/shared/win32/file_internal.h',
- '<(DEPTH)/starboard/shared/win32/file_open.cc',
- '<(DEPTH)/starboard/shared/win32/file_read.cc',
- '<(DEPTH)/starboard/shared/win32/file_seek.cc',
- '<(DEPTH)/starboard/shared/win32/file_truncate.cc',
- '<(DEPTH)/starboard/shared/win32/file_write.cc',
- '<(DEPTH)/starboard/shared/win32/log.cc',
- '<(DEPTH)/starboard/shared/win32/log_flush.cc',
- '<(DEPTH)/starboard/shared/win32/log_format.cc',
- '<(DEPTH)/starboard/shared/win32/log_is_tty.cc',
- '<(DEPTH)/starboard/shared/win32/log_raw_dump_stack.cc',
- '<(DEPTH)/starboard/shared/win32/memory_allocate_aligned_unchecked.cc',
- '<(DEPTH)/starboard/shared/win32/memory_allocate_unchecked.cc',
- '<(DEPTH)/starboard/shared/win32/memory_free.cc',
- '<(DEPTH)/starboard/shared/win32/memory_free_aligned.cc',
- '<(DEPTH)/starboard/shared/win32/memory_get_stack_bounds.cc',
- '<(DEPTH)/starboard/shared/win32/memory_map.cc',
- '<(DEPTH)/starboard/shared/win32/memory_reallocate_unchecked.cc',
- '<(DEPTH)/starboard/shared/win32/memory_unmap.cc',
- '<(DEPTH)/starboard/shared/win32/mutex_acquire.cc',
- '<(DEPTH)/starboard/shared/win32/mutex_acquire_try.cc',
- '<(DEPTH)/starboard/shared/win32/mutex_create.cc',
- '<(DEPTH)/starboard/shared/win32/mutex_destroy.cc',
- '<(DEPTH)/starboard/shared/win32/mutex_release.cc',
- '<(DEPTH)/starboard/shared/win32/once.cc',
- '<(DEPTH)/starboard/shared/win32/set_non_blocking_internal.cc',
- '<(DEPTH)/starboard/shared/win32/set_non_blocking_internal.h',
- '<(DEPTH)/starboard/shared/win32/socket_accept.cc',
- '<(DEPTH)/starboard/shared/win32/socket_bind.cc',
- '<(DEPTH)/starboard/shared/win32/socket_clear_last_error.cc',
- '<(DEPTH)/starboard/shared/win32/socket_connect.cc',
- '<(DEPTH)/starboard/shared/win32/socket_create.cc',
- '<(DEPTH)/starboard/shared/win32/socket_destroy.cc',
- '<(DEPTH)/starboard/shared/win32/socket_free_resolution.cc',
- '<(DEPTH)/starboard/shared/win32/socket_get_last_error.cc',
- '<(DEPTH)/starboard/shared/win32/socket_get_local_address.cc',
- '<(DEPTH)/starboard/shared/win32/socket_get_interface_address.cc',
- '<(DEPTH)/starboard/shared/win32/socket_internal.cc',
- '<(DEPTH)/starboard/shared/win32/socket_internal.h',
- '<(DEPTH)/starboard/shared/win32/socket_is_connected.cc',
- '<(DEPTH)/starboard/shared/win32/socket_is_connected_and_idle.cc',
- '<(DEPTH)/starboard/shared/win32/socket_join_multicast_group.cc',
- '<(DEPTH)/starboard/shared/win32/socket_listen.cc',
- '<(DEPTH)/starboard/shared/win32/socket_receive_from.cc',
- '<(DEPTH)/starboard/shared/win32/socket_resolve.cc',
- '<(DEPTH)/starboard/shared/win32/socket_send_to.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_connection_type.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_error_string.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_number_of_processors.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_stack.cc',
- '<(DEPTH)/starboard/shared/win32/socket_set_broadcast.cc',
- '<(DEPTH)/starboard/shared/win32/socket_set_receive_buffer_size.cc',
- '<(DEPTH)/starboard/shared/win32/socket_set_reuse_address.cc',
- '<(DEPTH)/starboard/shared/win32/socket_set_send_buffer_size.cc',
- '<(DEPTH)/starboard/shared/win32/socket_set_tcp_keep_alive.cc',
- '<(DEPTH)/starboard/shared/win32/socket_set_tcp_no_delay.cc',
- '<(DEPTH)/starboard/shared/win32/socket_set_tcp_window_scaling.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_add.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_create.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_destroy.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_internal.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_remove.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_wait.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_wait_timed.cc',
- '<(DEPTH)/starboard/shared/win32/socket_waiter_wake_up.cc',
- '<(DEPTH)/starboard/shared/win32/string_compare_no_case.cc',
- '<(DEPTH)/starboard/shared/win32/string_compare_no_case_n.cc',
- '<(DEPTH)/starboard/shared/win32/string_compare_wide.cc',
- '<(DEPTH)/starboard/shared/win32/string_format.cc',
- '<(DEPTH)/starboard/shared/win32/string_format_wide.cc',
- '<(DEPTH)/starboard/shared/win32/system_break_into_debugger.cc',
- '<(DEPTH)/starboard/shared/win32/system_clear_last_error.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_locale_id.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_random_data.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_random_uint64.cc',
- '<(DEPTH)/starboard/shared/win32/system_get_last_error.cc',
- '<(DEPTH)/starboard/shared/win32/system_is_debugger_attached.cc',
- '<(DEPTH)/starboard/shared/win32/thread_create.cc',
- '<(DEPTH)/starboard/shared/win32/thread_create_local_key.cc',
- '<(DEPTH)/starboard/shared/win32/thread_detach.cc',
- '<(DEPTH)/starboard/shared/win32/thread_destroy_local_key.cc',
- '<(DEPTH)/starboard/shared/win32/thread_get_current.cc',
- '<(DEPTH)/starboard/shared/win32/thread_get_id.cc',
- '<(DEPTH)/starboard/shared/win32/thread_get_local_value.cc',
- '<(DEPTH)/starboard/shared/win32/thread_get_name.cc',
- '<(DEPTH)/starboard/shared/win32/thread_is_equal.cc',
- '<(DEPTH)/starboard/shared/win32/thread_join.cc',
- '<(DEPTH)/starboard/shared/win32/thread_local_internal.cc',
- '<(DEPTH)/starboard/shared/win32/thread_local_internal.h',
- '<(DEPTH)/starboard/shared/win32/thread_private.cc',
- '<(DEPTH)/starboard/shared/win32/thread_private.h',
- '<(DEPTH)/starboard/shared/win32/thread_set_local_value.cc',
- '<(DEPTH)/starboard/shared/win32/thread_set_name.cc',
- '<(DEPTH)/starboard/shared/win32/thread_sleep.cc',
- '<(DEPTH)/starboard/shared/win32/thread_types_public.h',
- '<(DEPTH)/starboard/shared/win32/thread_yield.cc',
- '<(DEPTH)/starboard/shared/win32/thread_yield.h',
- '<(DEPTH)/starboard/shared/win32/time_get_monotonic_now.cc',
- '<(DEPTH)/starboard/shared/win32/time_get_now.cc',
- '<(DEPTH)/starboard/shared/win32/time_zone_get_current.cc',
- '<(DEPTH)/starboard/shared/win32/time_zone_get_name.cc',
- '<(DEPTH)/starboard/shared/win32/time_utils.h',
- '<(DEPTH)/starboard/shared/win32/wchar_utils.h',
- # Include private stubs, if present.
- '<!@(python "<(DEPTH)/starboard/tools/find_private_files.py" "<(DEPTH)" "shared/stub/*.cc")',
- '<@(starboard_platform_dependent_files)',
- ],
- 'defines': [
- # This must be defined when building Starboard, and must not when
- # building Starboard client code.
- 'STARBOARD_IMPLEMENTATION',
- # We assume most modern Windows PCs can handle 4k H264 and 8k VP9.
- # The latter is additionally gated on Media Foundation acceleration
- # support during runtime anyway.
- 'ENABLE_H264_4K_SUPPORT',
- 'ENABLE_VP9_8K_SUPPORT',
- ],
- 'dependencies': [
- 'convert_i18n_data',
- ],
- },
- {
- 'target_name': 'convert_i18n_data',
- 'type': 'none',
- 'actions': [
- {
- 'action_name': 'convert_i18n_data',
- 'variables': {
- 'input_files':
- '<!(find <(DEPTH)/cobalt/content/i18n/platform/xb1/*.xlb)',
- },
- 'includes': [ '../../build/convert_i18n_data.gypi' ],
- },
- ],
- }
- ],
-}
diff --git a/src/starboard/win/shared/system_get_path.cc b/src/starboard/win/shared/system_get_path.cc
deleted file mode 100644
index b9c491d..0000000
--- a/src/starboard/win/shared/system_get_path.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/system.h"
-
-#include <windows.h>
-// pathcch.h must come after windows.h.
-#include <pathcch.h>
-
-#include <codecvt>
-#include <cstring>
-#include <locale>
-
-#include "starboard/directory.h"
-#include "starboard/log.h"
-#include "starboard/shared/win32/directory_internal.h"
-#include "starboard/shared/win32/file_internal.h"
-#include "starboard/shared/win32/wchar_utils.h"
-#include "starboard/string.h"
-
-namespace {
-
-using starboard::shared::win32::CreateDirectoryHiearchy;
-using starboard::shared::win32::NormalizeWin32Path;
-
-// Places up to |path_size| - 1 characters of the path to the current
-// executable in |out_path|, ensuring it is NULL-terminated. Returns success
-// status. The result being greater than |path_size| - 1 characters is a
-// failure. |out_path| may be written to in unsuccessful cases.
-bool GetExecutablePath(char* out_path, int path_size) {
- if (!out_path || (path_size <= 0)) {
- return false;
- }
-
- wchar_t w_file_path[SB_FILE_MAX_PATH];
- DWORD characters_written =
- GetModuleFileName(NULL, w_file_path, SB_FILE_MAX_PATH);
- if (characters_written < 1) {
- return false;
- }
- std::string utf8_string =
- starboard::shared::win32::wchar_tToUTF8(w_file_path);
- if (utf8_string.length() >= path_size) {
- return false;
- }
- return SbStringCopy(out_path, utf8_string.c_str(), path_size);
-}
-
-// Places up to |path_size| - 1 characters of the path to the directory
-// containing the current executable in |out_path|, ensuring it is
-// NULL-terminated. Returns success status. The result being greater than
-// |path_size| - 1 characters is a failure. |out_path| may be written to in
-// unsuccessful cases.
-bool GetExecutableDirectory(char* out_path, int path_size) {
- if (!out_path || (path_size <= 0)) {
- return false;
- }
-
- wchar_t w_file_path[SB_FILE_MAX_PATH];
- DWORD characters_written =
- GetModuleFileName(NULL, w_file_path, SB_FILE_MAX_PATH);
- if (characters_written < 1) {
- return false;
- }
- PathCchRemoveFileSpec(w_file_path, SB_FILE_MAX_PATH);
- std::string utf8_string =
- starboard::shared::win32::wchar_tToUTF8(w_file_path);
- if (utf8_string.length() >= path_size) {
- return false;
- }
- return SbStringCopy(out_path, utf8_string.c_str(), path_size);
-}
-
-bool GetRelativeDirectory(const char* relative_path,
- char* out_path, int path_size) {
- if (!out_path || (path_size <= 0)) {
- return false;
- }
- char file_path[SB_FILE_MAX_PATH];
- file_path[0] = '\0';
- if (!GetExecutableDirectory(file_path, path_size)) {
- return false;
- }
- if (SbStringConcat(file_path, relative_path, SB_FILE_MAX_PATH) >=
- path_size) {
- return false;
- }
-
- if (!CreateDirectoryHiearchy(NormalizeWin32Path(file_path))) {
- return false;
- }
- return SbStringCopy(out_path, file_path, path_size);
-}
-
-// Places up to |path_size| - 1 characters of the path to the content directory
-// in |out_path|, ensuring it is NULL-terminated. Returns success
-// status. The result being greater than |path_size| - 1 characters is a
-// failure. |out_path| may be written to in unsuccessful cases.
-bool GetContentPath(char* out_path, int path_size) {
- return GetRelativeDirectory("\\content\\data", out_path, path_size);
-}
-
-bool GetCachePath(char* out_path, int path_size) {
- return GetRelativeDirectory("\\content\\cache",
- out_path, path_size);
-}
-
-bool CreateAndGetTempPath(char* out_path, int path_size) {
- if (!out_path || (path_size <= 0)) {
- return false;
- }
- wchar_t w_file_path[SB_FILE_MAX_PATH];
- w_file_path[0] = L'\0';
-
- int64_t characters_written =
- static_cast<int>(GetTempPathW(SB_FILE_MAX_PATH, w_file_path));
- if (characters_written >= (path_size + 1) || characters_written < 1) {
- return false;
- }
- // Remove the last slash, to match other Starboard implementations.
- w_file_path[characters_written - 1] = L'\0';
-
- std::string utf8_string =
- starboard::shared::win32::wchar_tToUTF8(w_file_path);
-
- if (SbStringCopy(out_path, utf8_string.c_str(), path_size) >= path_size) {
- return false;
- }
- SbDirectoryCreate(out_path);
-
- size_t length = SbStringGetLength(out_path);
- if (length < 1 || length > path_size) {
- return false;
- }
- return true;
-}
-
-} // namespace
-
-// Note: This function is only minimally implemented to allow tests to run.
-bool SbSystemGetPath(SbSystemPathId path_id, char* out_path, int path_size) {
- if (!out_path || (path_size <= 0)) {
- return false;
- }
-
- switch (path_id) {
- case kSbSystemPathContentDirectory:
- return GetContentPath(out_path, path_size);
- case kSbSystemPathDebugOutputDirectory:
- return SbSystemGetPath(kSbSystemPathTempDirectory, out_path, path_size);
- case kSbSystemPathExecutableFile:
- return GetExecutablePath(out_path, path_size);
- case kSbSystemPathTempDirectory:
- return CreateAndGetTempPath(out_path, path_size);
- case kSbSystemPathCacheDirectory:
- return GetCachePath(out_path, path_size);
- case kSbSystemPathFontConfigurationDirectory:
- return false;
- case kSbSystemPathFontDirectory:
- return false;
- // TODO: implement all the other cases.
- default:
- SB_NOTIMPLEMENTED();
- return false;
- }
-}
diff --git a/src/starboard/win/win32/atomic_public.h b/src/starboard/win/win32/atomic_public.h
deleted file mode 100644
index f7eaedf..0000000
--- a/src/starboard/win/win32/atomic_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN_WIN32_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_WIN32_ATOMIC_PUBLIC_H_
-
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif // STARBOARD_WIN_WIN32_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/win/win32/configuration_public.h b/src/starboard/win/win32/configuration_public.h
deleted file mode 100644
index 0eed9c1..0000000
--- a/src/starboard/win/win32/configuration_public.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Other source files should never include this header directly, but should
-// include the generic "starboard/configuration.h" instead.
-
-#ifndef STARBOARD_WIN_WIN32_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_WIN_WIN32_CONFIGURATION_PUBLIC_H_
-
-#include "starboard/win/shared/configuration_public.h"
-
-#endif // STARBOARD_WIN_WIN32_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/win/win32/gyp_configuration.gypi b/src/starboard/win/win32/gyp_configuration.gypi
deleted file mode 100644
index 6a89095..0000000
--- a/src/starboard/win/win32/gyp_configuration.gypi
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-
-{
- 'variables': {
- 'angle_build_winrt': 0,
- 'winrt': 0,
- 'enable_d3d11_feature_level_11': 1,
- 'angle_platform_windows': 1,
- },
- 'includes': [
- '../shared/gyp_configuration.gypi',
- ],
- 'target_defaults': {
- 'default_configuration': 'win32_debug',
- 'configurations': {
- 'win-win32_debug': {
- 'inherit_from': ['win32_base', 'msvs_debug'],
- },
- 'win-win32_devel': {
- 'inherit_from': ['win32_base', 'msvs_devel'],
- },
- 'win-win32_qa': {
- 'inherit_from': ['win32_base', 'msvs_qa'],
- },
- 'win-win32_gold': {
- 'inherit_from': ['win32_base', 'msvs_gold'],
- },
- }, # end of configurations
- },
-}
diff --git a/src/starboard/win/win32/gyp_configuration.py b/src/starboard/win/win32/gyp_configuration.py
deleted file mode 100644
index 8e3f89d..0000000
--- a/src/starboard/win/win32/gyp_configuration.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-"""Starboard win-win32 platform configuration for gyp_cobalt."""
-
-from __future__ import print_function
-
-import imp
-import logging
-import os
-import sys
-
-# Import the shared win platform configuration.
-sys.path.append(
- os.path.realpath(
- os.path.join(
- os.path.dirname(__file__), os.pardir, os.pardir, 'shared',
- 'win32')))
-import gyp_configuration
-
-
-def CreatePlatformConfig():
- try:
- win_lib_config = WinWin32PlatformConfig('win-win32')
- return win_lib_config
- except RuntimeError as e:
- logging.critical(e)
- return None
-
-
-class WinWin32PlatformConfig(gyp_configuration.Win32Configuration):
- """Starboard win-32 platform configuration."""
-
- def __init__(self, platform):
- super(WinWin32PlatformConfig, self).__init__(platform)
-
- def GetLauncher(self):
- """Gets the module used to launch applications on this platform."""
- module_path = os.path.abspath(
- os.path.join(os.path.dirname(__file__), 'launcher.py'))
-
- launcher_module = imp.load_source('launcher', module_path)
- return launcher_module
diff --git a/src/starboard/win/win32/launcher.py b/src/starboard/win/win32/launcher.py
deleted file mode 100644
index 128284e..0000000
--- a/src/starboard/win/win32/launcher.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/python
-# Copyright 2017 Google Inc. 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.
-"""Starboard win-win32 platform configuration for gyp_cobalt."""
-
-
-from __future__ import print_function
-
-import os
-import subprocess
-import sys
-import traceback
-
-import starboard.tools.abstract_launcher as abstract_launcher
-
-class Launcher(abstract_launcher.AbstractLauncher):
-
- def __init__(self, platform, target_name, config, device_id, **kwargs):
- super(Launcher, self).__init__(platform, target_name, config, device_id,
- **kwargs)
- self.executable = self.GetTargetPath()
-
- def Run(self):
- sys.stderr.write('\n***Running Launcher***\n')
- """Runs launcher's executable."""
- self.proc = subprocess.Popen(
- [self.executable] + self.target_command_line_params,
- stdout=self.output_file,
- stderr=self.output_file)
- self.pid = self.proc.pid
- self.proc.communicate()
- self.proc.poll()
- return self.proc.returncode
-
- def Kill(self):
- sys.stderr.write("\n***Killing Launcher***\n")
- if self.pid:
- try:
- self.proc.kill()
- except OSError:
- sys.stderr.write("Error killing launcher with SIGKILL:\n")
- traceback.print_exc(file=sys.stderr)
- # If for some reason Kill() fails then os_.exit(1) will kill the
- # child process without cleanup. Otherwise the process will hang.
- os._exit(1)
- else:
- sys.stderr.write("Kill() called before Run(), cannot kill.\n")
-
- return
diff --git a/src/starboard/win/win32/lib/atomic_public.h b/src/starboard/win/win32/lib/atomic_public.h
deleted file mode 100644
index b85fb12..0000000
--- a/src/starboard/win/win32/lib/atomic_public.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2017 Google Inc. 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_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
-#define STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
-
-#include "starboard/shared/win32/atomic_public.h"
-
-#endif // STARBOARD_WIN_WIN32_LIB_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/win/win32/lib/configuration_public.h b/src/starboard/win/win32/lib/configuration_public.h
deleted file mode 100644
index 8b5f8b1..0000000
--- a/src/starboard/win/win32/lib/configuration_public.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Other source files should never include this header directly, but should
-// include the generic "starboard/configuration.h" instead.
-
-#ifndef STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
-#define STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
-
-#include "starboard/win/shared/configuration_public.h"
-
-#endif // STARBOARD_WIN_WIN32_LIB_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/win/win32/lib/gyp_configuration.gypi b/src/starboard/win/win32/lib/gyp_configuration.gypi
deleted file mode 100644
index b5dbd3b..0000000
--- a/src/starboard/win/win32/lib/gyp_configuration.gypi
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-
-{
- 'variables': {
- # TODO: In theory, there are tools that can combine static libraries into
- # thick static libraries with all their transitive dependencies. Using
- # shared_library here can have unexpected consequences. Explore building
- # this into a thick static library instead.
- 'final_executable_type': 'shared_library',
- 'default_renderer_options_dependency': '<(DEPTH)/cobalt/renderer/rasterizer/lib/lib.gyp:external_rasterizer',
- 'sb_enable_lib': 1,
-
- # We turn off V-Sync and throttling in Cobalt so we can let the client
- # decide if they want those features.
- 'cobalt_egl_swap_interval': 0,
- 'cobalt_minimum_frame_time_in_milliseconds': 0,
-
- 'enable_map_to_mesh': 1,
- 'angle_build_winrt': 0,
- 'winrt': 0,
- 'enable_d3d11_feature_level_11': 1,
- 'cobalt_user_on_exit_strategy': 'noexit',
- },
- 'includes': [
- '../../shared/gyp_configuration.gypi',
- ],
- 'target_defaults': {
- 'default_configuration': 'win-win32-lib_debug',
- 'configurations': {
-
- 'win-win32-lib_debug': {
- 'inherit_from': ['win32_base', 'msvs_debug'],
- },
- 'win-win32-lib_devel': {
- 'inherit_from': ['win32_base', 'msvs_devel'],
- },
- 'win-win32-lib_qa': {
- 'inherit_from': ['win32_base', 'msvs_qa'],
- },
- 'win-win32-lib_gold': {
- 'inherit_from': ['win32_base', 'msvs_gold'],
- },
- }, # end of configurations
- },
-}
diff --git a/src/starboard/win/win32/lib/gyp_configuration.py b/src/starboard/win/win32/lib/gyp_configuration.py
deleted file mode 100644
index efcffaa..0000000
--- a/src/starboard/win/win32/lib/gyp_configuration.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2016 Google Inc. 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.
-"""Starboard win-win32-lib platform configuration for gyp_cobalt."""
-
-import logging
-import os
-import sys
-
-# Import the shared win platform configuration.
-sys.path.append(
- os.path.realpath(
- os.path.join(
- os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
- 'shared', 'win32')))
-import gyp_configuration
-
-
-def CreatePlatformConfig():
- try:
- return gyp_configuration.Win32Configuration('win-win32-lib')
- except RuntimeError as e:
- logging.critical(e)
- return None
diff --git a/src/starboard/win/win32/lib/starboard_platform.gyp b/src/starboard/win/win32/lib/starboard_platform.gyp
deleted file mode 100644
index 52063bb..0000000
--- a/src/starboard/win/win32/lib/starboard_platform.gyp
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'includes': [
- '../starboard_platform.gypi',
- ],
- 'variables': {
- 'starboard_platform_dependent_files': [
- '<@(base_win32_starboard_platform_dependent_files)',
- '<@(win32_media_player_files)',
- '<@(win32_shared_drm_files)',
- '<@(win32_shared_media_player_files)',
- ]
- },
-}
diff --git a/src/starboard/win/win32/lib/starboard_platform_tests.gyp b/src/starboard/win/win32/lib/starboard_platform_tests.gyp
deleted file mode 100644
index 66fc40e..0000000
--- a/src/starboard/win/win32/lib/starboard_platform_tests.gyp
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'variables': {
- 'sb_pedantic_warnings': 1,
- },
- 'targets': [
- {
- 'target_name': 'starboard_platform_tests',
- 'type': '<(gtest_target_type)',
- 'sources': [
- '<(DEPTH)/starboard/common/test_main.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_type_test.cc',
- ],
- 'dependencies': [
- '<(DEPTH)/starboard/starboard.gyp:starboard',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- ],
- },
- {
- 'target_name': 'starboard_platform_tests_deploy',
- 'type': 'none',
- 'dependencies': [
- '<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:starboard_platform_tests',
- ],
- 'variables': {
- 'executable_name': 'starboard_platform_tests',
- },
- 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
- },
- ],
-}
diff --git a/src/starboard/win/win32/lib/thread_types_public.h b/src/starboard/win/win32/lib/thread_types_public.h
deleted file mode 100644
index adc6221..0000000
--- a/src/starboard/win/win32/lib/thread_types_public.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Includes threading primitive types and initializers.
-
-#ifndef STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
-
-#include "starboard/shared/win32/thread_types_public.h"
-
-#endif // STARBOARD_WIN_WIN32_LIB_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/win/win32/main.cc b/src/starboard/win/win32/main.cc
deleted file mode 100644
index a43ef4d..0000000
--- a/src/starboard/win/win32/main.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2017 Google Inc. 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 "starboard/shared/win32/starboard_main.h"
-
-int main(int argc, char** argv) {
- StarboardMain(argc, argv);
-}
diff --git a/src/starboard/win/win32/starboard_platform.gyp b/src/starboard/win/win32/starboard_platform.gyp
deleted file mode 100644
index c2fda5c..0000000
--- a/src/starboard/win/win32/starboard_platform.gyp
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'includes': [
- 'starboard_platform.gypi',
- ],
- 'variables': {
- 'starboard_platform_dependent_files': [
- 'main.cc',
- '<@(base_win32_starboard_platform_dependent_files)',
- ]
- },
-}
diff --git a/src/starboard/win/win32/starboard_platform.gypi b/src/starboard/win/win32/starboard_platform.gypi
deleted file mode 100644
index 26bbd6b..0000000
--- a/src/starboard/win/win32/starboard_platform.gypi
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'includes': [
- '../shared/starboard_platform.gypi',
- ],
- 'variables': {
- 'base_win32_starboard_platform_dependent_files': [
- 'atomic_public.h',
- 'configuration_public.h',
- 'thread_types_public.h',
- '<(DEPTH)/starboard/shared/starboard/localized_strings.cc',
- '<(DEPTH)/starboard/shared/starboard/media/media_get_audio_configuration_stereo_only.cc',
- '<(DEPTH)/starboard/shared/starboard/queue_application.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_pause.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_suspend.cc',
- '<(DEPTH)/starboard/shared/starboard/system_request_unpause.cc',
- '<(DEPTH)/starboard/shared/starboard/system_supports_resume.cc',
- '<(DEPTH)/starboard/shared/stub/media_is_output_protected.cc',
- '<(DEPTH)/starboard/shared/stub/media_set_output_protection.cc',
- '<(DEPTH)/starboard/win/shared/system_get_path.cc',
- '<@(uwp_incompatible_win32)',
- ],
- },
-}
diff --git a/src/starboard/win/win32/starboard_platform_tests.gyp b/src/starboard/win/win32/starboard_platform_tests.gyp
deleted file mode 100644
index 66fc40e..0000000
--- a/src/starboard/win/win32/starboard_platform_tests.gyp
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2017 Google Inc. 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.
-{
- 'variables': {
- 'sb_pedantic_warnings': 1,
- },
- 'targets': [
- {
- 'target_name': 'starboard_platform_tests',
- 'type': '<(gtest_target_type)',
- 'sources': [
- '<(DEPTH)/starboard/common/test_main.cc',
- '<(DEPTH)/starboard/shared/starboard/media/mime_type_test.cc',
- ],
- 'dependencies': [
- '<(DEPTH)/starboard/starboard.gyp:starboard',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- ],
- },
- {
- 'target_name': 'starboard_platform_tests_deploy',
- 'type': 'none',
- 'dependencies': [
- '<(DEPTH)/<(starboard_path)/starboard_platform_tests.gyp:starboard_platform_tests',
- ],
- 'variables': {
- 'executable_name': 'starboard_platform_tests',
- },
- 'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
- },
- ],
-}
diff --git a/src/starboard/win/win32/thread_types_public.h b/src/starboard/win/win32/thread_types_public.h
deleted file mode 100644
index 6ee053b..0000000
--- a/src/starboard/win/win32/thread_types_public.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2017 Google Inc. 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.
-
-// Includes threading primitive types and initializers.
-
-#ifndef STARBOARD_WIN_WIN32_THREAD_TYPES_PUBLIC_H_
-#define STARBOARD_WIN_WIN32_THREAD_TYPES_PUBLIC_H_
-
-#include "starboard/shared/win32/thread_types_public.h"
-
-#endif // STARBOARD_WIN_WIN32_THREAD_TYPES_PUBLIC_H_
diff --git a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
index 32d5676..5d7beff 100644
--- a/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
+++ b/src/third_party/angle/src/libANGLE/renderer/d3d/d3d11/winrt/CoreWindowNativeWindow.cpp
@@ -191,6 +191,13 @@
}
}
+ ComPtr<IDXGIDevice1> pDXGIDevice;
+ result = device->QueryInterface(__uuidof(IDXGIDevice1), (void **)pDXGIDevice.GetAddressOf());
+ if (SUCCEEDED(result))
+ {
+ pDXGIDevice->SetMaximumFrameLatency(swapChainDesc.BufferCount);
+ }
+
return result;
}
diff --git a/src/third_party/blink/Tools/Scripts/webkitpy/bindings/bindings_tests.py b/src/third_party/blink/Tools/Scripts/webkitpy/bindings/bindings_tests.py
index 1c72e85..14e57aa 100644
--- a/src/third_party/blink/Tools/Scripts/webkitpy/bindings/bindings_tests.py
+++ b/src/third_party/blink/Tools/Scripts/webkitpy/bindings/bindings_tests.py
@@ -53,8 +53,8 @@
PASS_MESSAGE = 'All tests PASS!'
FAIL_MESSAGE = """Some tests FAIL!
-To update the reference files, execute:
- run-bindings-tests --reset-results
+To update the reference files, execute (from cobalt/bindings):
+ ./run_cobalt_bindings_tests.sh --reset-results
If the failures are not due to your changes, test results may be out of sync;
please rebaseline them in a separate CL, after checking that tests fail in ToT.
diff --git a/src/third_party/icu/icu.gyp b/src/third_party/icu/icu.gyp
index 39892f7..a741957 100644
--- a/src/third_party/icu/icu.gyp
+++ b/src/third_party/icu/icu.gyp
@@ -301,6 +301,12 @@
'<(DEPTH)/starboard/starboard_headers_only.gyp:starboard_headers_only',
],
}],
+ ['(OS=="starboard") and (target_os=="android")', {
+ 'cflags_cc': [
+ # reldtfmt.cpp compares 'UDateFormatStyle' and 'EStyle'.
+ '-Wno-enum-compare-switch',
+ ],
+ }],
['(OS=="lb_shell" or OS=="starboard") and (target_os=="android" or target_os=="linux" or clang==1)', {
'cflags_cc': [
'-frtti',
diff --git a/src/third_party/libwebp/.mailmap b/src/third_party/libwebp/.mailmap
new file mode 100644
index 0000000..b4242a8
--- /dev/null
+++ b/src/third_party/libwebp/.mailmap
@@ -0,0 +1,12 @@
+<johann.koenig@duck.com> <johannkoenig@google.com>
+Mikołaj Zalewski <mikolajz@google.com>
+Pascal Massimino <pascal.massimino@gmail.com>
+<pascal.massimino@gmail.com> <skal@google.com>
+Vikas Arora <vikasa@google.com>
+<vikasa@google.com> <vikasa@gmail.com>
+<vikasa@google.com> <vikaas.arora@gmail.com>
+<slobodan.prijic@imgtec.com> <Slobodan.Prijic@imgtec.com>
+<vrabaud@google.com> <vincent.rabaud@gmail.com>
+Tamar Levy <tamar.levy@intel.com>
+<qrczak@google.com> <qrczak>
+Hui Su <huisu@google.com>
diff --git a/src/third_party/libwebp/AUTHORS b/src/third_party/libwebp/AUTHORS
new file mode 100644
index 0000000..83c7b9c
--- /dev/null
+++ b/src/third_party/libwebp/AUTHORS
@@ -0,0 +1,39 @@
+Contributors:
+- Charles Munger (clm at google dot com)
+- Christian Duvivier (cduvivier at google dot com)
+- Djordje Pesut (djordje dot pesut at imgtec dot com)
+- Hui Su (huisu 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)
+- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
+- Jyrki Alakuijala (jyrki at google 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)
+- 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)
+- 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)
+- Sam Clegg (sbc at chromium dot org)
+- Scott Hancher (seh at google dot com)
+- Scott LaVarnway (slavarnway at google dot com)
+- Scott Talbot (s at chikachow dot org)
+- Slobodan Prijic (slobodan dot prijic at imgtec dot com)
+- 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)
+- 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)
+- Yang Zhang (yang dot zhang at arm dot com)
diff --git a/src/third_party/libwebp/Android.mk b/src/third_party/libwebp/Android.mk
new file mode 100644
index 0000000..6752f77
--- /dev/null
+++ b/src/third_party/libwebp/Android.mk
@@ -0,0 +1,280 @@
+LOCAL_PATH := $(call my-dir)
+
+WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD
+WEBP_CFLAGS += -fvisibility=hidden
+
+ifeq ($(APP_OPTIM),release)
+ WEBP_CFLAGS += -finline-functions -ffast-math \
+ -ffunction-sections -fdata-sections
+ ifeq ($(findstring clang,$(NDK_TOOLCHAIN_VERSION)),)
+ WEBP_CFLAGS += -frename-registers -s
+ endif
+endif
+
+# mips32 fails to build with clang from r14b
+# https://bugs.chromium.org/p/webp/issues/detail?id=343
+ifeq ($(findstring clang,$(NDK_TOOLCHAIN_VERSION)),clang)
+ ifeq ($(TARGET_ARCH),mips)
+ clang_version := $(shell $(TARGET_CC) --version)
+ ifneq ($(findstring clang version 3,$(clang_version)),)
+ WEBP_CFLAGS += -no-integrated-as
+ endif
+ endif
+endif
+
+ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
+ # Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
+ # instructions to be generated for armv7a code. Instead target the neon code
+ # specifically.
+ NEON := c.neon
+ USE_CPUFEATURES := yes
+ WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
+else
+ NEON := c
+endif
+
+dec_srcs := \
+ src/dec/alpha_dec.c \
+ src/dec/buffer_dec.c \
+ src/dec/frame_dec.c \
+ src/dec/idec_dec.c \
+ src/dec/io_dec.c \
+ src/dec/quant_dec.c \
+ src/dec/tree_dec.c \
+ src/dec/vp8_dec.c \
+ src/dec/vp8l_dec.c \
+ src/dec/webp_dec.c \
+
+demux_srcs := \
+ src/demux/anim_decode.c \
+ src/demux/demux.c \
+
+dsp_dec_srcs := \
+ src/dsp/alpha_processing.c \
+ src/dsp/alpha_processing_mips_dsp_r2.c \
+ src/dsp/alpha_processing_neon.$(NEON) \
+ src/dsp/alpha_processing_sse2.c \
+ src/dsp/alpha_processing_sse41.c \
+ src/dsp/cpu.c \
+ src/dsp/dec.c \
+ src/dsp/dec_clip_tables.c \
+ src/dsp/dec_mips32.c \
+ src/dsp/dec_mips_dsp_r2.c \
+ src/dsp/dec_msa.c \
+ src/dsp/dec_neon.$(NEON) \
+ src/dsp/dec_sse2.c \
+ src/dsp/dec_sse41.c \
+ src/dsp/filters.c \
+ src/dsp/filters_mips_dsp_r2.c \
+ src/dsp/filters_msa.c \
+ src/dsp/filters_neon.$(NEON) \
+ src/dsp/filters_sse2.c \
+ src/dsp/lossless.c \
+ src/dsp/lossless_mips_dsp_r2.c \
+ src/dsp/lossless_msa.c \
+ src/dsp/lossless_neon.$(NEON) \
+ src/dsp/lossless_sse2.c \
+ src/dsp/rescaler.c \
+ src/dsp/rescaler_mips32.c \
+ src/dsp/rescaler_mips_dsp_r2.c \
+ src/dsp/rescaler_msa.c \
+ src/dsp/rescaler_neon.$(NEON) \
+ src/dsp/rescaler_sse2.c \
+ src/dsp/upsampling.c \
+ src/dsp/upsampling_mips_dsp_r2.c \
+ src/dsp/upsampling_msa.c \
+ src/dsp/upsampling_neon.$(NEON) \
+ src/dsp/upsampling_sse2.c \
+ src/dsp/upsampling_sse41.c \
+ src/dsp/yuv.c \
+ src/dsp/yuv_mips32.c \
+ src/dsp/yuv_mips_dsp_r2.c \
+ src/dsp/yuv_neon.$(NEON) \
+ src/dsp/yuv_sse2.c \
+ src/dsp/yuv_sse41.c \
+
+dsp_enc_srcs := \
+ src/dsp/cost.c \
+ src/dsp/cost_mips32.c \
+ src/dsp/cost_mips_dsp_r2.c \
+ 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 \
+ src/dsp/enc_neon.$(NEON) \
+ src/dsp/enc_sse2.c \
+ src/dsp/enc_sse41.c \
+ src/dsp/lossless_enc.c \
+ src/dsp/lossless_enc_mips32.c \
+ src/dsp/lossless_enc_mips_dsp_r2.c \
+ src/dsp/lossless_enc_msa.c \
+ src/dsp/lossless_enc_neon.$(NEON) \
+ src/dsp/lossless_enc_sse2.c \
+ src/dsp/lossless_enc_sse41.c \
+ src/dsp/ssim.c \
+ src/dsp/ssim_sse2.c \
+
+enc_srcs := \
+ src/enc/alpha_enc.c \
+ src/enc/analysis_enc.c \
+ src/enc/backward_references_cost_enc.c \
+ src/enc/backward_references_enc.c \
+ src/enc/config_enc.c \
+ src/enc/cost_enc.c \
+ src/enc/filter_enc.c \
+ src/enc/frame_enc.c \
+ src/enc/histogram_enc.c \
+ src/enc/iterator_enc.c \
+ src/enc/near_lossless_enc.c \
+ src/enc/picture_enc.c \
+ src/enc/picture_csp_enc.c \
+ src/enc/picture_psnr_enc.c \
+ src/enc/picture_rescale_enc.c \
+ src/enc/picture_tools_enc.c \
+ src/enc/predictor_enc.c \
+ src/enc/quant_enc.c \
+ src/enc/syntax_enc.c \
+ src/enc/token_enc.c \
+ src/enc/tree_enc.c \
+ src/enc/vp8l_enc.c \
+ src/enc/webp_enc.c \
+
+mux_srcs := \
+ src/mux/anim_encode.c \
+ src/mux/muxedit.c \
+ src/mux/muxinternal.c \
+ src/mux/muxread.c \
+
+utils_dec_srcs := \
+ src/utils/bit_reader_utils.c \
+ src/utils/color_cache_utils.c \
+ src/utils/filters_utils.c \
+ src/utils/huffman_utils.c \
+ src/utils/quant_levels_dec_utils.c \
+ src/utils/random_utils.c \
+ src/utils/rescaler_utils.c \
+ src/utils/thread_utils.c \
+ src/utils/utils.c \
+
+utils_enc_srcs := \
+ src/utils/bit_writer_utils.c \
+ src/utils/huffman_encode_utils.c \
+ src/utils/quant_levels_utils.c \
+
+################################################################################
+# libwebpdecoder
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(dec_srcs) \
+ $(dsp_dec_srcs) \
+ $(utils_dec_srcs) \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+ifeq ($(USE_CPUFEATURES),yes)
+ LOCAL_STATIC_LIBRARIES := cpufeatures
+endif
+
+LOCAL_MODULE := webpdecoder_static
+
+include $(BUILD_STATIC_LIBRARY)
+
+ifeq ($(ENABLE_SHARED),1)
+include $(CLEAR_VARS)
+
+LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
+
+LOCAL_MODULE := webpdecoder
+
+include $(BUILD_SHARED_LIBRARY)
+endif # ENABLE_SHARED=1
+
+################################################################################
+# libwebp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(dsp_enc_srcs) \
+ $(enc_srcs) \
+ $(utils_enc_srcs) \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
+
+LOCAL_MODULE := webp
+
+ifeq ($(ENABLE_SHARED),1)
+ include $(BUILD_SHARED_LIBRARY)
+else
+ include $(BUILD_STATIC_LIBRARY)
+endif
+
+################################################################################
+# libwebpdemux
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(demux_srcs)
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+LOCAL_MODULE := webpdemux
+
+ifeq ($(ENABLE_SHARED),1)
+ LOCAL_SHARED_LIBRARIES := webp
+ include $(BUILD_SHARED_LIBRARY)
+else
+ LOCAL_STATIC_LIBRARIES := webp
+ include $(BUILD_STATIC_LIBRARY)
+endif
+
+################################################################################
+# libwebpmux
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(mux_srcs)
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+LOCAL_MODULE := webpmux
+
+ifeq ($(ENABLE_SHARED),1)
+ LOCAL_SHARED_LIBRARIES := webp
+ include $(BUILD_SHARED_LIBRARY)
+else
+ LOCAL_STATIC_LIBRARIES := webp
+ include $(BUILD_STATIC_LIBRARY)
+endif
+
+################################################################################
+
+WEBP_SRC_PATH := $(LOCAL_PATH)
+include $(WEBP_SRC_PATH)/imageio/Android.mk
+include $(WEBP_SRC_PATH)/examples/Android.mk
+
+ifeq ($(USE_CPUFEATURES),yes)
+ $(call import-module,android/cpufeatures)
+endif
diff --git a/src/third_party/libwebp/CMakeLists.txt b/src/third_party/libwebp/CMakeLists.txt
new file mode 100644
index 0000000..ea263b3
--- /dev/null
+++ b/src/third_party/libwebp/CMakeLists.txt
@@ -0,0 +1,371 @@
+cmake_minimum_required(VERSION 2.8.7)
+
+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)
+endif()
+
+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
+ )
+endif()
+
+# Include dependencies.
+include(cmake/deps.cmake)
+
+################################################################################
+# Options.
+if(WEBP_ENABLE_SWAP_16BIT_CSP)
+ add_definitions(-DWEBP_SWAP_16BIT_CSP=1)
+endif()
+
+################################################################################
+# 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_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.
+
+# 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}
+ )
+ 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}
+ )
+ foreach(FILE ${FILES})
+ list(APPEND SRCS ${FOLDER}/${FILE})
+ endforeach()
+ endif()
+ endforeach()
+ set(${VAR} ${SRCS} PARENT_SCOPE)
+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_[^ ]*")
+
+# Remove the files specific to SIMD we don't use.
+foreach(FILE ${WEBP_SIMD_FILES_NOT_TO_INCLUDE})
+ list(REMOVE_ITEM WEBP_DSP_ENC_SRCS ${FILE})
+ list(REMOVE_ITEM WEBP_DSP_DEC_SRCS ${FILE})
+endforeach()
+
+### Define the mandatory libraries.
+# Build the webpdecoder library.
+if(MSVC)
+ # avoid security warnings for e.g., fopen() used in the examples.
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+else()
+ add_definitions(-Wall)
+endif()
+include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${WEBP_DEP_INCLUDE_DIRS})
+add_library(webpdecode OBJECT ${WEBP_DEC_SRCS})
+add_library(webpdspdecode OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS})
+add_library(webputilsdecode OBJECT ${WEBP_UTILS_COMMON_SRCS}
+ ${WEBP_UTILS_DEC_SRCS})
+add_library(webpdecoder $<TARGET_OBJECTS:webpdecode>
+ $<TARGET_OBJECTS:webpdspdecode> $<TARGET_OBJECTS:webputilsdecode>)
+target_link_libraries(webpdecoder ${WEBP_DEP_LIBRARIES})
+
+# Build the webp library.
+add_library(webpencode OBJECT ${WEBP_ENC_SRCS})
+add_library(webpdsp OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS}
+ ${WEBP_DSP_ENC_SRCS})
+add_library(webputils OBJECT ${WEBP_UTILS_COMMON_SRCS} ${WEBP_UTILS_DEC_SRCS}
+ ${WEBP_UTILS_ENC_SRCS})
+add_library(webp $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdsp>
+ $<TARGET_OBJECTS:webpencode> $<TARGET_OBJECTS:webputils>)
+target_link_libraries(webp ${WEBP_DEP_LIBRARIES})
+
+# 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)
+
+# Build the webp demux library.
+add_library(webpdemux ${WEBP_DEMUX_SRCS})
+target_link_libraries(webpdemux webp)
+
+# 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})
+
+# Define the libraries to install.
+set(INSTALLED_LIBRARIES webpdecoder webp webpdemux)
+
+### 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"
+)
+
+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}
+ )
+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})
+
+ parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEIOUTILS_SRCS"
+ "imageio_util_[^ ]*")
+ add_library(imageioutil ${IMAGEIOUTILS_SRCS})
+ target_link_libraries(imageioutil webp)
+
+ # Image-decoding utility library.
+ parse_Makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEDEC_SRCS"
+ "imagedec_[^ ]*")
+ add_library(imagedec ${IMAGEDEC_SRCS})
+ target_link_libraries(imagedec imageioutil webpdemux webp
+ ${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)
+
+ 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")
+ 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)
+endif()
+
+if(WEBP_BUILD_CWEBP)
+ # cwebp
+ include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+ 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)
+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)
+endif()
+
+if(WEBP_BUILD_GIF2WEBP)
+ # gif2webp
+ include_directories(${WEBP_DEP_GIF_INCLUDE_DIRS})
+ 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)
+endif()
+
+if(WEBP_BUILD_IMG2WEBP)
+ # img2webp
+ include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+ 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)
+endif()
+
+if (WEBP_BUILD_WEBPINFO)
+ # webpinfo
+ include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+ 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)
+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)
+
+ # WASM version
+ 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_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
+
+ target_compile_definitions(webpdecoder 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})
+
+# 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)
+
+# Create the CMake version file.
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
+ VERSION ${PACKAGE_VERSION}
+ COMPATIBILITY AnyNewerVersion
+)
+
+# Create the Config file.
+include(CMakePackageConfigHelpers)
+set(ConfigPackageLocation share/WebP/cmake/)
+configure_package_config_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/WebPConfig.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake
+ INSTALL_DESTINATION ${ConfigPackageLocation}
+)
+
+# Install the generated CMake files.
+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")
+list(LENGTH MAN_PAGES MAN_PAGES_LENGTH)
+math(EXPR MAN_PAGES_RANGE "${MAN_PAGES_LENGTH} - 1")
+
+foreach(I_MAN RANGE ${MAN_PAGES_RANGE})
+ list(GET EXEC_BUILDS ${I_MAN} EXEC_BUILD)
+ 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
+ )
+ endif()
+endforeach()
diff --git a/src/third_party/libwebp/COPYING b/src/third_party/libwebp/COPYING
new file mode 100644
index 0000000..7a6f995
--- /dev/null
+++ b/src/third_party/libwebp/COPYING
@@ -0,0 +1,30 @@
+Copyright (c) 2010, 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.
+
diff --git a/src/third_party/libwebp/ChangeLog b/src/third_party/libwebp/ChangeLog
new file mode 100644
index 0000000..9fd9acf
--- /dev/null
+++ b/src/third_party/libwebp/ChangeLog
@@ -0,0 +1,3852 @@
+8d510751 webp-container-spec: correct frame duration=0 note
+e6b2164e vwebp: Copy Chrome's behavior w/frame duration == 0
+d20b7707 update ChangeLog (tag: v1.0.0-rc3)
+0d5fad46 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
+f5565ca8 cmake: Make sure we use near-lossless by default.
+d898dc14 fix bug in WebPImport565: alpha value was not set
+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.
+cd758a17 {de,}mux/Makefile.am: add missing headers
+b892b8ba makefile.unix,dist: use ascii for text output
+64a57d05 add -version option to anim_dump,anim_diff and img2webp
+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
+c57b2736 bump version to 1.0.0
+cba28853 update NEWS
+c909d531 Merge "remove some deprecation warning on MacOSX"
+217443c7 remove some deprecation warning on MacOSX
+b672bdfa configure: quiet glut deprecation warnings on OS X
+daa9fcaf configure: use sdl-config if available
+dd174cae Merge "imagedec: support metadata reading for WebP image decoding"
+641cedcc imagedec: support metadata reading for WebP image decoding
+065b2ce1 anim_diff: add a couple missing newlines in Help()
+c4cc1147 Merge "gif2webp: force low duration frames to 100ms"
+09333097 gif2webp: force low duration frames to 100ms
+e03f0ec3 sharp_yuv: use 14b fixed-point precision for gamma
+b2db361c image_enc,WebPWritePNG: move locals after setjmp
+74e82ec6 Merge "WebPPictureDistortion: fix big-endian results order"
+645d04ca Merge "cwebp,get_disto: report bpp"
+120f58c3 Merge "lossless*sse2: improve non-const 16-bit vector creation"
+a7fe9412 WebPPictureDistortion: fix big-endian results order
+e26fe066 cwebp,get_disto: report bpp
+9df64e28 Merge changes Id5b4a1a4,Ia20ce844
+8043504f lossless*sse2: improve non-const 16-bit vector creation
+1e3dfc48 Import: extract condition from loop
+3b07d327 Import,RGBA: fix for BigEndian import
+551948e4 Remove unused argument in VP8LBitsEntropy.
+3005237a ReadWebP: fix for big-endian
+499c395a Merge "anim_diff: expose the -max_diff option"
+f69dcd69 Merge "remove WEBP_EXPERIMENTAL_FEATURES"
+07d884d5 anim_diff: expose the -max_diff option
+f4dd9256 remove WEBP_EXPERIMENTAL_FEATURES
+94a8377b extract the command-line parsing helpers to example_util
+fc09e6e2 PNM decoder: prevent unsupported depth=2 PAM case.
+6de58603 MIPS64: Fix defined-but-not-used errors with WEBP_REDUCE_CSP
+cbde5728 gif2webp: add support for reading from stdin
+cf1c5054 Add an SSE4 version of some lossless color transforms.
+45a8b5eb Fix lint error with man page.
+cff38e8f Merge "PNG decoder: handle gAMA chunk"
+59cb1a48 Merge "enable dc error-diffusion always"
+78318b30 PNG decoder: handle gAMA chunk
+664c21dd Merge "remove some TODOs"
+815652de enable dc error-diffusion always
+aec45cec remove some TODOs
+5715dfce fix block-count[] increment in case of large image
+c2d04f3e enable DC error-diffusion always for multi-pass
+96bf07c5 use DC error diffusion for U/V at low-quality
+1c59020b fix missing sse41 targets in makefile.unix
+7a8e814b cosmetics: s/color_space/colorspace/
+05f6fe24 upsampling: rm asserts w/REDUCE_CSP+OMIT_C_CODE
+b4cf5597 Merge "Upsampling SSE2/SSE4 speedup."
+ccbeb32c Makefile.vc: add missing sse41 files
+55403a9a Upsampling SSE2/SSE4 speedup.
+807b53c4 Implement the upsampling/yuv functions in SSE41
+84101a81 Fix wasm WebP compilation
+8bebd2a3 fix warning on MSVC
+a7f93fe3 webpmux: allow reading argument from a file
+b69f18a7 gif2webp.1: fix -loop_compatibility layout
+72d530c0 Merge "fix lossless decoding w/WEBP_REDUCE_SIZE"
+296c7dc4 fix lossless decoding w/WEBP_REDUCE_SIZE
+0d5d029c Merge "ImgIoUtilReadFile: fix file leak upon error"
+ae568ce7 ImgIoUtilReadFile: fix file leak upon error
+796b5a8a Merge tag 'v0.6.1'
+6b7a95fd update ChangeLog (tag: v0.6.1)
+f66955de WEBP_REDUCE_CSP: restrict colorspace support
+1af0df76 Merge "WEBP_REDUCE_CSP: restrict colorspace support"
+6de20df0 WEBP_REDUCE_CSP: restrict colorspace support
+a289d8e7 update ChangeLog (tag: v0.6.1-rc2)
+c10a493c vwebp: disable double buffering on windows & mac
+0d4466c2 webp_to_sdl.c: fix file mode
+1b27bf8b WEBP_REDUCE_SIZE: disable all rescaler code
+126be109 webpinfo: add -version option
+0df22b9e WEBP_REDUCE_SIZE: disable all rescaler code
+9add62b5 bump version to 0.6.1
+d3e26144 update NEWS
+2edda639 README: add webpinfo section
+9ca568ef Merge "right-size some tables"
+31f1995c Merge "SSE2 implementation of HasAlphaXXX"
+a80c46bd SSE2 implementation of HasAlphaXXX
+083507f2 right-size some tables
+2e5785b2 anim_utils.c: remove warning when !defined(WEBP_HAVE_GIF)
+b299c47e add WEBP_REDUCE_SIZE
+f593d71a enc: disable pic->stats/extra_info w/WEBP_DISABLE_STATS
+541179a9 Merge "predictor_enc: fix build w/--disable-near-lossless"
+5755a7ec predictor_enc: fix build w/--disable-near-lossless
+eab5bab7 add WEBP_DISABLE_STATS
+8052c585 remove some petty TODOs from vwebp.
+c245343d move LOAD8x4 and STORE8x2 closer to their use location
+b9e734fd dec,cosmetics: normalize function naming style
+c188d546 dec: harmonize function suffixes
+28c5ac81 dec_sse41: harmonize function suffixes
+e65b72a3 Merge "introduce WebPHasAlpha8b and WebPHasAlpha32b"
+b94cee98 dec_sse2: remove HE8uv_SSE2
+44a0ee3f introduce WebPHasAlpha8b and WebPHasAlpha32b
+aebf59ac Merge "WebPPictureAllocARGB: align argb allocation"
+c184665e WebPPictureAllocARGB: align argb allocation
+3daf7509 WebPParseHeaders: remove obsolete animation TODO
+80285d97 cmake: avoid security warnings under msvc
+650eac55 cmake: don't set -Wall with MSVC
+c462cd00 Remove useless code.
+01a98217 Merge "remove WebPWorkerImpl declaration from the header"
+3c49fc47 Merge "thread_utils: fix potentially bad call to Execute"
+fde2782e thread_utils: fix potentially bad call to Execute
+2a270c1d remove WebPWorkerImpl declaration from the header
+f1f437cc remove mention of 'lossy-only parameters' from the doc
+3879074d Merge "WebPMemToUint32: remove ptr cast to int"
+04b029d2 WebPMemToUint32: remove ptr cast to int
+b7971d0e dsp: avoid defining _C functions w/NEON builds
+6ba98764 webpdec: correct alloc size check w/use_argb
+5cfb3b0f normalize include guards
+f433205e Merge changes Ia17c7dfc,I75423abb,Ia2f716b4,I161caa14,I4210081a, ...
+8d033b14 {dec,enc}_neon: harmonize function suffixes x2
+0295e981 upsampling_neon: harmonize function suffixes
+d572c4e5 yuv_neon: harmonize function suffixes
+ab9c2500 rescaler_neon: harmonize function suffixes
+93e0ce27 lossless_neon: harmonize function suffixes
+22fbc50e lossless_enc_neon: harmonize function suffixes
+447875b4 filters_neon,cosmetics: fix indent
+e51bdd43 remove unused VP8TokenToStats() function
+785da7ea enc_neon: harmonize function suffixes
+bc1a251f dec_neon: harmonize function suffixes
+61e535f1 dsp/lossless: workaround gcc-4.8 bug on arm
+68b2eab7 cwebp: fix alpha reporting w/lossless & metadata
+30042faa WebPDemuxGetI: add doc details around WebPFormatFeature
+0a17f471 Merge "WIP: list includes as descendants of the project dir"
+a4399721 WIP: list includes as descendants of the project dir
+08275708 Merge "Make sure we reach the full range for alpha blending."
+d361a6a7 yuv_sse2: harmonize function suffixes
+6921aa6f upsampling_sse2: harmonize function suffixes
+08c67d3e ssim_sse2: harmonize function suffixes
+582a1b57 rescaler_sse2: harmonize function suffixes
+2c1b18ba lossless_sse2: harmonize function suffixes
+0ac46e81 lossless_enc_sse2: harmonize function suffixes
+bc634d57 enc_sse2: harmonize function suffixes
+bcb7347c dec_sse2: harmonize function suffixes
+e14ad93c Make sure we reach the full range for alpha blending.
+7038ca8d demux,StoreFrame: restore hdr size check to min req
+fb3daad6 cpu: fix ssse3 check
+be590e06 Merge "Fix CMake redefinition for HAVE_CPU_FEATURES_H"
+35f736e1 Fix CMake redefinition for HAVE_CPU_FEATURES_H
+a5216efc Fix integer overflow warning.
+a9c8916b decode.h,WebPIDecGetRGB: clarify output ptr validity
+3c74c645 gif2webp: handle 1-frame case properly + fix anim_diff
+c7f295d3 Merge "gif2webp: introduce -loop_compatibility option"
+b4e04677 gif2webp: introduce -loop_compatibility option
+f78da3de add LOCAL_CLANG_PREREQ and avoid WORK_AROUND_GCC w/3.8+
+01c426f1 define WEBP_USE_INTRINSICS w/gcc-4.9+
+8635973d use sdl-config (if available) to determine the link flags
+e9459382 use CPPFLAGS before CFLAGS
+4a9d788e Merge "Android.mk,mips: fix clang build with r15"
+4fbdc9fb Android.mk,mips: fix clang build with r15
+a80fcc4a ifdef code not used by Chrome/Android.
+3993af12 Fix signed integer overflows.
+f66f94ef anim_dump: small tool to dump frames from animated WebP
+6eba857b Merge "rationalize the Makefile.am"
+c5e34fba function definition cleanup
+3822762a rationalize the Makefile.am
+501ef6e4 configure style fix: animdiff -> anim_diff
+f8bdc268 Merge "protect against NULL dump_folder[] value in ReadAnimatedImage()"
+23bfc652 protect against NULL dump_folder[] value in ReadAnimatedImage()
+8dc3d71b cosmetics,ReadAnimatedWebP: correct function comment
+5bd40066 Merge changes I66a64a0a,I4d2e520f
+7945575c cosmetics,webpinfo: remove an else after a return
+8729fa11 cosmetics,cwebp: remove an else after a return
+f324b7f9 cosmetics: normalize fn proto & decl param names
+869eb369 CMake cleanups.
+289e62a3 Remove declaration of unimplemented VP8ApplyNearLosslessPredict
+20a94186 pnmdec,PAM: validate depth before calculating bytes_per_px
+34130afe anim_encode: fix integer overflow
+42c79aa6 Merge "Encoder: harmonize function suffixes"
+b09307dc Encoder: harmonize function suffixes
+bed0456d Merge "SSIM: harmonize the function suffix"
+54f6a3cf lossless_sse2.c: fix some missed suffix changes
+088f1dcc SSIM: harmonize the function suffix
+86fc4dd9 webpdec: use ImgIoUtilCheckSizeArgumentsOverflow
+08ea9ecd imageio: add ability restrict max image size
+6f9daa4a jpegdec,ReadError: fix leaks on error
+a0f72a4f VP8LTransformColorFunc: drop an non-respected 'const' from the signature.
+8c934902 Merge "Lossess dec: harmonize the function suffixes"
+622242aa Lossess dec: harmonize the function suffixes
+1411f027 Lossless Enc: harmonize the function suffixes
+24ad2e3c add const to two variables
+46efe062 Merge "Allow the lossless cruncher to work for alpha."
+8c3f9a47 Speed-up LZ77.
+1aef4c71 Allow the lossless cruncher to work for alpha.
+b8821dbd Improve the box LZ77 speed.
+7beed280 add missing ()s to macro parameters
+6473d20b Merge "fix Android standalone toolchain build"
+dcefed95 Merge "build.gradle: fix arm64 build"
+0c83a8bc Merge "yuv: harmonize suffix naming"
+c6d1db4b fix Android standalone toolchain build
+663a6d9d unify the ALTERNATE_CODE flag usage
+73ea9f27 yuv: harmonize suffix naming
+c71b68ac build.gradle: fix arm64 build
+c4568b47 Rescaler: harmonize the suffix naming
+6cb13b05 Merge "alpha_processing: harmonize the naming suffixes to be _C()"
+83a3e69a Merge "simplify WEBP_EXTERN macro"
+7295fde2 Merge "filters: harmonize the suffixes naming to _SSE2(), _C(), etc."
+8e42ba4c simplify WEBP_EXTERN macro
+331ab34b cost*.c: harmonize the suffix namings
+b161f670 filters: harmonize the suffixes naming to _SSE2(), _C(), etc.
+dec5e4d3 alpha_processing: harmonize the naming suffixes to be _C()
+6878d427 fix memory leak in SDL_Init()
+461ae555 Merge "configure: fix warnings in sdl check"
+62486a22 configure: test for -Wundef
+92982609 dsp.h: fix -Wundef w/__mips_dsp_rev
+0265cede configure: fix warnings in sdl check
+88c73d8a backward_references_enc.h: fix WINDOW_SIZE_BITS check
+4ea49f6b rescaler_sse2.c: fix WEBP_RESCALER_FIX -> _RFIX typo
+1b526638 Clean-up some CMake
+87f57a4b Merge "cmake: fix gif lib detection when cross compiling"
+b34a9db1 cosmetics,dec_sse2: remove some redundant comments
+471c5755 cmake: fix gif lib detection when cross compiling
+c793417a cmake: disable gif2webp if gif lib isn't found
+dcbc1c88 cmake: split gif detection from IMG deps
+66ad84f0 Merge "muxread: remove unreachable code"
+50ec3ab7 muxread: remove unreachable code
+7d67a164 Lossy encoding: smoothen transparent areas to improve compression
+e50650c7 Merge "fix signature for DISABLE_TOKEN_BUFFER compilation"
+671d2567 fix signature for DISABLE_TOKEN_BUFFER compilation
+d6755580 cpu.cmake: use unique flag to test simd disable flags
+28914528 Merge "Remove the argb* files."
+8acb4942 Remove the argb* files.
+3b62347b README: correct cmake invocation note
+7ca0df13 Have the SSE2 version of PackARGB use common code.
+7b250459 Merge "Re-use the transformed image when trying several LZ77 in lossless."
+e132072f Re-use the transformed image when trying several LZ77 in lossless.
+5d7a50ef Get code to compile in C++.
+7b012987 configure: test for -Wparentheses-equality
+f0569adb Fix man pages for multi-threading.
+f1d5a397 multithread cruncher: only copy stats when picture->stats != NULL
+f8c2ac15 Multi-thread the lossless cruncher.
+a88c6522 Merge "Integrate a new LZ77 looking for matches in the neighborhood of a pixel only."
+8f6df1d0 Unroll Predictors 10, 11 and 12.
+355c3d1b Integrate a new LZ77 looking for matches in the neighborhood of a pixel only.
+a1779a01 Refactor LZ77 handling in preparation for a new method.
+67de68b5 Android.mk/build.gradle: fix mips build with clang from r14b
+f209a548 Use the plane code and not the distance when computing statistics.
+b903b80c Split cost-based backward references in its own file.
+498cad34 Cosmetic changes in backward reference.
+e4eb4587 lossless, VP8LTransformColor_C: make sure no overflow happens with colors.
+af6deaff webpinfo: handle alpha flag mismatch
+7caef29b Fix typo that creeped in.
+39e19f92 Merge "near lossless: fix unsigned int overflow warnings."
+9bbc0891 near lossless: fix unsigned int overflow warnings.
+e1118d62 Merge "cosmetics,FindClosestDiscretized: use uint in mask creation"
+186bc9b7 Merge "webpinfo: tolerate ALPH+VP8L"
+b5887297 cosmetics,FindClosestDiscretized: use uint in mask creation
+f1784aee near_lossless,FindClosestDiscretized: use unsigned ops
+0d20abb3 webpinfo: tolerate ALPH+VP8L
+972104b3 webpmux: tolerate false positive Alpha flag
+dd7e83cc tiffdec,ReadTIFF: ensure data_size is < tsize_t max
+d988eb7b tiffdec,MyRead: quiet -Wshorten-64-to-32 warning
+dabda707 webpinfo: add support to parse Alpha bitstream
+4c117643 webpinfo: correct background color output, BGRA->ARGB
+defc98d7 Doc: clarify the role of quality in WebPConfig.
+d78ff780 Merge "Fix code to compile with C++."
+c8f14093 Fix code to compile with C++.
+497dc6a7 pnmdec: sanitize invalid header output
+d78e5867 Merge "configure: test for -Wconstant-conversion"
+481e91eb Merge "pnmdec,PAM: set bytes_per_px based on depth when missing"
+93b12753 configure: test for -Wconstant-conversion
+645f0c53 pnmdec,PAM: set bytes_per_px based on depth when missing
+e9154605 Merge "vwebp: activate GLUT double-buffering"
+818d795b vwebp: activate GLUT double-buffering
+d63e6f4b Add a man page for webpinfo
+4d708435 Merge "NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV"
+faf42213 NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV
+b4d576fa Install man pages with CMake.
+cbc1b921 webpinfo: add features to parse bitstream header
+e644c556 Fix bad bit writer initialization.
+b62cdad2 Merge "Implement a cruncher for lossless at method 6."
+da3e4dfb use the exact constant for the gamma transfer function
+a9c701e0 Merge "tiffdec: fix EXTRASAMPLES check"
+adab8ce0 Implement a cruncher for lossless at method 6.
+1b92b237 Merge "Fix VP8ApplyNearLossless to respect const and stride."
+1923ff02 tiffdec: fix EXTRASAMPLES check
+97cce5ba tiffdec: only request EXTRASAMPLES w/> 3 samples/px
+0dcd85b6 Fix VP8ApplyNearLossless to respect const and stride.
+f7682189 yuv: rationalize the C/SSE2 function naming
+52245424 NEON implementation of some Sharp-YUV420 functions
+690efd82 Avoid several backward reference copies.
+4bb1f607 src/dec/vp8_dec.h, cosmetics: fix comments
+285748be cmake: build/install webpinfo
+78fd199c backward_references_enc.c: clear -Wshadow warnings
+ae836410 WebPLog2FloorC: clear -Wshadow warning
+d0b7404e Merge "WASM support"
+134e314f WASM support
+c08adb6f Merge "VP8LEnc: remove use of BitsLog2Ceiling()"
+28c37ebd VP8LEnc: remove use of BitsLog2Ceiling()
+2cb58ab2 webpinfo: output format as a human readable string
+bb175a93 Merge "rename some symbols clashing with MSVC headers"
+39eda658 Remove a duplicated pixel hash implementation.
+36b8274d rename some symbols clashing with MSVC headers
+274daf54 Add webpinfo tool.
+ec5036e4 add explicit reference to /usr/local/{lib,inc}
+18f0dfac Merge "fix TIFF encoder regarding rgbA/RGBA"
+4e2b0b50 Merge "webpdec.h: fix a doc typo"
+e2eeabff Merge "Install binaries, libraries and headers in CMake."
+836607e6 webpdec.h: fix a doc typo
+9273e441 fix TIFF encoder regarding rgbA/RGBA
+17e3c11f Add limited PAM decoding support
+5f624871 Install binaries, libraries and headers in CMake.
+976adac1 Merge "lossless incremental decoding: fix missing eos_ test"
+f8fad4fa lossless incremental decoding: fix missing eos_ test
+27415d41 Merge "vwebp_sdl: fix the makefile.unix"
+49566182 Merge "ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode"
+6f75a51b Analyze the transform entropy on the whole image.
+a5e4e3af Use palette only if we can in entropy analysis.
+75a9c3c4 Improve compression by better entropy analysis.
+39cf6f4f vwebp_sdl: fix the makefile.unix
+699b0416 ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode
+7d985bd1 Fix small entropy analysis bug.
+6e7caf06 Optimize the color cache size.
+833c9219 More efficient stochastic histogram merge.
+5183326b Refactor the greedy histogram merge.
+99f6f462 Merge "histogram_enc.c,MyRand: s/ul/u/ for unsigned constants"
+80a22186 ssim.c: remove dead include
+a128dfff histogram_enc.c,MyRand: s/ul/u/ for unsigned constants
+693bf74e move the SSIM calculation code in ssim.c / ssim_sse2.c
+10d791ca Merge "Fix the random generator in HistogramCombineStochastic."
+fa63a966 Fix the random generator in HistogramCombineStochastic.
+16be192f VP8LSetBitPos: remove the eos_ setting
+027151ca don't erase the surface before blitting.
+4105d565 disable WEBP_USE_XXX optimisations when EMSCRIPTEN is defined
+9ee32a75 Merge "WebP-JS: emscripten-based Javascript decoder"
+ca9f7b7d WebP-JS: emscripten-based Javascript decoder
+868aa690 Perform greedy histogram merge in a unified way.
+5b393f2d Merge "fix path typo for vwebp_sdl in Makefile.vc"
+e0012bea CMake: only use libwebpdecoder for building dwebp
+84c2a7b0 fix path typo for vwebp_sdl in Makefile.vc
+1b0e4abf Merge "Add a flag to disable SIMD optimizations."
+32263250 Add a flag to disable SIMD optimizations.
+b494fdec optimize the ARGB->ARGB Import to use memcpy
+f1536039 Merge "ReadWebP: decode directly into a pre-allocated buffer"
+e69ed291 ReadWebP: decode directly into a pre-allocated buffer
+57d8de8a Merge "vwebp_sdl: simple viewer based on SDL"
+5cfd4ebc LZ77 interval speedups. Faster, smaller, simpler.
+1e7ad88b PNM header decoder: add some basic numerical validation
+17c7890c Merge "Add a decoder only library for WebP in CMake."
+be733786 Merge "Add clang build fix for MSA"
+03cda0e4 Add a decoder only library for WebP in CMake.
+aa893914 Add clang build fix for MSA
+31a92e97 Merge "imageio: add limited PNM support for reading"
+dcf9d82a imageio: add limited PNM support for reading
+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)
+20a7fea0 extras/Makefile.am: fix libwebpextras.la reference
+415f3ffe update ChangeLog (tag: v0.6.0-rc3)
+3c6d1224 update NEWS
+ee4a4141 update AUTHORS
+32ed856f Fix "all|no frames are keyframes" settings.
+1c3190b6 Merge "Fix "all|no frames are keyframes" settings."
+f4dc56fd disable GradientUnfilter_NEON
+4f3e3bbd disable GradientUnfilter_NEON
+2dc0bdca Fix "all|no frames are keyframes" settings.
+0d8e0588 img2webp: treat -loop as a no-op w/single images
+b0450139 ReadImage(): restore size reporting
+0ad3b4ef update ChangeLog (tag: v0.6.0-rc2)
+6451709e img2webp,get_disto: fix image decode w/WIC builds
+92504d21 get_disto: make ReadPicture() return a bool
+c3e4b3a9 update NEWS
+3363eb6d man/img2webp.1: fix formatting warning
+4d1312f2 update NEWS
+36c42ea4 bump version to 0.6.0
+bb498a51 update AUTHORS
+84cef16f Makefile.vc: fix CFG=debug-dynamic build
+919f9e2f Merge "add .rc files for windows dll versioning"
+f1ae8af4 Merge ".gitignore: add img2webp"
+4689ce16 cwebp: add a -sharp_yuv option for 'sharp' RGB->YUV conversion
+79bf46f1 rename the pretentious SmartYUV into SharpYUV
+eb1dc89a silently expose use_delta_palette in the WebPConfig API
+c85b0dde .gitignore: add img2webp
+43d3f01a add .rc files for windows dll versioning
+668e1dd4 src/{dec,enc,utils}: give filenames a unique suffix
+0e6b7f33 Merge "iosbuild.sh: only add required headers to framework"
+29ed6f9a iosbuild.sh: only add required headers to framework
+71c53f1a NEON: speed-up strong filtering
+73f567ea Merge "get_disto: remove redundant reader check"
+9e14276f Merge "makefiles: prune get_disto & webp_quality deps"
+99965bac Merge "Makefile.vc: add get_disto.exe, webp_quality.exe"
+d4912238 get_disto: remove redundant reader check
+ea482409 makefiles: prune get_disto & webp_quality deps
+2ede5a19 Makefile.vc: add get_disto.exe, webp_quality.exe
+a345068a ARM: speed up bitreader by avoiding tables
+1dc82a6b Merge "introduce a generic GetCoeffs() function pointer"
+8074b89e introduce a generic GetCoeffs() function pointer
+749a45a5 Merge "NEON: implement alpha-filters (horizontal/vertical/gradient)"
+74c053b5 Merge "NEON: fix overflow in SSE NxN calculation"
+0a3aeff7 Merge "dsp: WebPExtractGreen function for alpha decompression"
+1de931c6 NEON: implement alpha-filters (horizontal/vertical/gradient)
+9b3aca40 NEON: fix overflow in SSE NxN calculation
+1c07a3c6 dsp: WebPExtractGreen function for alpha decompression
+9ed5e3e5 use pointers for WebPRescaler's in WebPDecParams
+db013a8d Merge "ARM: don't use USE_GENERIC_TREE"
+fcd4784d use a 8b table for C-version for clz()
+fbb5c473 ARM: don't use USE_GENERIC_TREE
+8fda5612 Merge "add a kSlowSSSE3 feature for CPUInfo"
+86bbd245 add a kSlowSSSE3 feature for CPUInfo
+7c2779e9 Get code to fully compile in C++.
+250c3586 Merge "When compiling as C++, avoid narrowing warnings."
+c0648ac2 When compiling as C++, avoid narrowing warnings.
+0d55f60c 40% faster ApplyAlphaMultiply_SSE2
+49d0280d NEON: implement several alpha-processing functions
+48b1e85f SSE2: 15% faster alpha-processing functions
+e3b8abbc fix warning from static analysis.
+28fe054e SSE2: 30% faster ApplyAlphaMultiply()
+f44acd25 Merge "Properly compute the optimal color cache size."
+527844fe Properly compute the optimal color cache size.
+be0ef639 fix a comment typo
+8874b162 Fix a non-deterministic color cache size computation.
+d712e20d Do not allow a color cache size bigger than the number of colors.
+ecff04f6 re-introduce some comments in Huffman Cost.
+259e9828 replace 'ptr + y * stride' by 'ptr += stride'
+00b08c88 Merge "NEON: 5% faster conversion to RGB565 and RGBA4444"
+0e7f4447 Merge "NEON: faster fancy upsampling"
+b016cb91 NEON: faster fancy upsampling
+1cb63801 Call the C function to finish off lossless SSE loops only when necessary.
+875fafc1 Implement BundleColorMap in SSE2.
+3674d49e Merge "remove Clang warnings with unused arch arguments."
+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)
+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++.
+a4a8e5f3 vwebp: clear canvas during resize w/o animation
+67c25ad5 vwebp: clear canvas during resize w/o animation
+a4bbe4b3 fix indentation
+31ca2a80 tiffdec: restore libtiff 3.9.x compatibility
+b2f77b57 update NEWS
+5ab6d9de AnimEncoder: avoid freeing uninitialized memory pointer.
+f29bf582 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless.
+3ebe1c00 AnimEncoder: avoid freeing uninitialized memory pointer.
+df780e0e fix a potential overflow with MALLOC_LIMIT
+58fc5078 Merge "PredictorSub: implement fully-SSE2 version"
+9cc42167 PredictorSub: implement fully-SSE2 version
+0aa1f35c remove dependency of imageio/ to stopwatch.h
+cb9ec84b Merge "remove the dependency to stop_watch.[ch] in imageio"
+dc0c01fb Merge "anim_util: quiet implicit conv warnings in 32-bit"
+827d3c50 Merge "fix a potential overflow with MALLOC_LIMIT"
+1e2e25b0 anim_util: quiet implicit conv warnings in 32-bit
+218460cd bump version to 0.5.2
+de7d654d update AUTHORS & .mailmap
+273367c1 Merge "dsp/lossless.c,cosmetics: fix indent"
+76bbcf2e fix a potential overflow with MALLOC_LIMIT
+8ac1abfe Merge "jpegdec: correct ContextFill signature"
+cb215aed remove the dependency to stop_watch.[ch] in imageio
+2423017a dsp/lossless.c,cosmetics: fix indent
+74a12b10 iosbuild.sh: add WebPDecoder.framework + encoder
+a9cc7621 Merge "iosbuild.sh: add WebPDecoder.framework + encoder"
+fbba5bc2 optimize predictor #1 in plain-C For some reason, gcc has hard time inlining this one...
+9ae0b3f6 Merge "SSE2: slightly (~2%) faster Predictor #1"
+c1f97bd7 SSE2: slightly (~2%) faster Predictor #1
+ea664b89 SSE2: 10% faster Predictor #11
+be7dcc08 AnimEncoder: Correctly skip a frame when sub-rectangle is empty.
+40885830 Fix assertions in WebPRescalerExportRow()
+1d5046d1 iosbuild.sh: add WebPDecoder.framework + encoder
+cec72014 jpegdec: correct ContextFill signature
+8f38c72e fix a typo in WebPPictureYUVAToARGB's doc
+33ca93f9 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_
+76e19073 doc: use two's complement explicitly for uint8->int8 conversion
+f91ba963 Anim_encoder: correctly handle enc->prev_candidate_undecided_
+25d74e65 WebPPictureDistortion(): free() -> WebPSafeFree()
+03f1c008 mux/Makefile.am: add missing -lm
+58410cd6 fix bug in RefineUsingDistortion()
+e168af8c fix filtering auto-adjustment
+ed9dec41 fix doc and code snippet for WebPINewDecoder() doc
+3c49178f prevent 32b overflow for very large canvas_width / height
+9595f290 fix anim_util.c compilation when HAVE_GIF is not defined.
+7ec9552c Make gif transparent color to be transparent black
+b3fb8bb6 slightly faster Predictor #11 in NEON
+9871335f Add a CMake option for WEBP_SWAP_16BIT_CSP.
+0ae32226 Fix missing cpu-features for Android.
+ab4c8056 cpu.cmake: improve webp_check_compiler_flag output
+eec5fa3a Provide support for CMake on Android studio 2.2.
+004d5690 Split the main CMake file.
+4fe5d588 Android.mk: use -fvisibility=hidden
+bd63a31a vwebp: ensure setenv() is available in stdlib.h
+363a5681 vwebp: handle window resizing properly
+a0d2753f lower WEBP_MAX_ALLOCABLE_MEMORY default
+31fe11a5 fix infinite loop in case of PARTITION0 overflow
+532215dd Change the rule of picking UV mode in MBAnalyzeBestUVMode()
+9c75dbd3 cwebp.1: improve some grammar
+af2e05cb vwebp: Clear previous frame when a key triggers a redraw
+26ffa296 Add descriptions of default configuration in help info.
+7416280d Fix an unsigned integer overflow error in enc/cost.h
+13cf1d2e Do token recording and counting in a single loop
+eb9a4b97 Reset segment id if we decide not to update segment map
+42ebe3b7 configure: fix NEON flag detection under gcc 6
+76ebbfff NEON: implement predictor #13
+95b12a08 Merge "Revert Average3 and Average4"
+54ab2e75 Revert Average3 and Average4
+fe12330c 3-5% faster Predictor #5, #6, #7 and #10 for NEON
+fbfb3bef ~2% faster predictor #10 for NEON
+d4b7d801 lossless_sse2: use the local functions
+a5e3b225 Lossless decoder SSE2 improvements.
+58a1f124 ~2% faster predictor #12 in NEON.
+906c3b63 Merge "Implement lossless transforms in NEON."
+d23abe4e Implement lossless transforms in NEON.
+2e6cb6f3 Give more flexibility to the predictor generating macro.
+28e0bb70 Merge "Fix race condition in multi-threading initialization."
+64704530 Fix race condition in multi-threading initialization.
+bded7848 img2webp: fix default -lossless value and use pic.argb=1
+0e61a513 Merge "img2webp: convert a sequence of images to an animated webp"
+1cc79e92 AnimEncoder: Correctly skip a frame when sub-rectangle is empty.
+03f40955 img2webp: convert a sequence of images to an animated webp
+ea72cd60 add missing 'extern' keyword for predictor dcl
+67879e6d SSE implementation of decoding predictors.
+34aee990 Merge "vwebp: make 'd' key toggle the debugging of fragments"
+a41296ae Fix potentially uninitialized value.
+c85adb33 vwebp: make 'd' key toggle the debugging of fragments
+4239a148 Make the lossless predictors work on a batch of pixels.
+bc18ebad fix extra 'const's in signatures
+71e2f5ca Remove memcpy in lossless decoding.
+7474d46e Do not use a register array in SSE.
+67748b41 Improve latency of FTransform2.
+16951b19 Merge "Provide an SSE implementation of ConvertBGRAToRGB"
+6540cd0e Provide an SSE implementation of ConvertBGRAToRGB
+de568abf Android.mk: use -fvisibility=hidden
+3c2a61b0 remove some unneeded casts
+9ac063c3 add dsp functions for SmartYUV
+22efabdd Merge "smart_yuv: switch to planar instead of packed r/g/b processing"
+1d6e7bf3 smart_yuv: switch to planar instead of packed r/g/b processing
+0a3838ca fix bug in RefineUsingDistortion()
+c0699515 webpmux -duration: set default 'end' value equal to 'start'
+83cbfa09 Import: use relative pointer offsets
+a1ade40e PreprocessARGB: use relative pointer offsets
+fd4d090f ConvertWRGBToYUV: use relative pointer offsets
+9daad459 ImportYUVAFromRGBA: use relative pointer offsets
+f90c60d1 Merge "add a "-duration duration,start,end" option to webpmux"
+3f182d36 add a "-duration duration,start,end" option to webpmux
+342e15f0 Import: use relative pointer offsets
+1147ab4e PreprocessARGB: use relative pointer offsets
+e4cd4daf fix filtering auto-adjustment
+e7152856 fix doc and code snippet for WebPINewDecoder() doc
+de9fa507 ConvertWRGBToYUV: use relative pointer offsets
+deb1b831 ImportYUVAFromRGBA: use relative pointer offsets
+c284780f imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow
+e375080d gifdec,Remap: avoid out of bounds colormap read
+c222a053 additional fix for stride type as size_t
+bb233617 fix potential overflow when width * height * 4 >= (1<<32)
+883d41fb gif2webp: fix crash with NULL extension data
+cac9a36a gifdec,Remap: avoid out of bounds colormap read
+4595e01f Revert "gifdec,Remap: avoid out of bounds colormap read"
+fb52d443 gifdec: make some constants unsigned
+f048d38d gifdec,Remap: avoid out of bounds colormap read
+31b1e343 fix SSIM metric ... by ignoring too-dark area
+2f51b614 introduce WebPPlaneDistortion to compute plane distortion
+0104d730 configure: fix NEON flag detection under gcc 6
+265abbe9 Merge "additional fix for stride type as size_t"
+f7601aa6 Merge "Introduce a generic WebPGetImageReader(type) function"
+ce873320 Introduce a generic WebPGetImageReader(type) function
+2a2773ea imageio/*dec,Read*: add input parameter checks
+9f5c8eca additional fix for stride type as size_t
+4eb5df28 remove unused stride fields from VP8Iterator
+11bc423a MIN_LENGTH cleanups.
+273d035a Merge "fix a typo in WebPPictureYUVAToARGB's doc"
+4db82a17 Merge "fix potential overflow when width * height * 4 >= (1<<32)"
+e2affacc fix potential overflow when width * height * 4 >= (1<<32)
+dc789ada fix a typo in WebPPictureYUVAToARGB's doc
+539f5a68 Fix non-included header in config.c.
+aaf2a6a6 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_
+20ef9915 Merge "imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow"
+bc86b7a8 imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow
+806f6279 gif2webp: fix crash with NULL extension data
+68ae5b67 Add libwebp/src/mux/animi.h
+28ce3043 Remove some errors when compiling the code as C++.
+b34abcb8 Favor keeping the areas locally similar in spatial prediction mode selection
+ba843a92 fix some SSIM calculations
+51b71fd2 Merge "vwebp: ensure setenv() is available in stdlib.h"
+fb01743a get_disto: fix the r/g/b order for luma calculation
+bfab8947 vwebp: ensure setenv() is available in stdlib.h
+9310d192 vwebp: handle window resizing properly
+f79450ca Speedup ApplyMap.
+cfdda7c6 Merge "prevent 32b overflow for very large canvas_width / height"
+e36396ba Merge "get_disto: new option to compute SSIM map and convert to gray"
+18a9a0ab Add an API to import a color-mapped image.
+30d43706 Speed-up Combined entropy for palettized histograms.
+36aa087b get_disto: new option to compute SSIM map and convert to gray
+86a84b35 2x faster SSE2 implementation of SSIMGet
+b8384b53 lower WEBP_MAX_ALLOCABLE_MEMORY default
+1c364400 prevent 32b overflow for very large canvas_width / height
+eee0cce1 Merge "Small LZ77 speedups."
+5f1caf29 Small LZ77 speedups.
+1effde7b fix anim_util.c compilation when HAVE_GIF is not defined.
+a2fe9bf4 Speedup TrellisQuantizeBlock().
+573cce27 smartYUV improvements
+21e7537a fix infinite loop in case of PARTITION0 overflow
+053a1565 Merge "Change the rule of picking UV mode in MBAnalyzeBestUVMode()"
+1377ac2e Change the rule of picking UV mode in MBAnalyzeBestUVMode()
+7c1fb7d0 fix uint32_t initialization (0. -> 0)
+bfff0bf3 speed-up SSIM calculation
+64577de8 De-VP8L-ize GetEntropUnrefinedHelper.
+a7be7328 Merge "refactor the PSNR / SSIM calculation code"
+50c3d7da refactor the PSNR / SSIM calculation code
+d6228aed indentation fix after I7055d3ee3bd7ed5e78e94ae82cb858fa7db3ddc0
+dd538b19 Remove unused declaration.
+6cc48b17 Move some lossless logic out of dsp.
+78363e9e Merge "Remove a redundant call to InitLeft() in VP8IteratorReset()"
+ffd01929 Refactor VP8IteratorNext().
+c4f6d9c9 Remove a redundant call to InitLeft() in VP8IteratorReset()
+c27d8210 Merge "smartYUV: simplify main loop"
+07795296 smartYUV: simplify main loop
+c9b45863 Split off common lossless dsp inline functions.
+490ae5b1 smartYUV: improve initial state for faster convergence
+894232be smartYUV: fix and simplify the over-zealous stop criterion
+8de08483 Remove unused code in webpi.h
+41cab7fe imageio/Android.mk: correct imagedec dependencies
+82c91c70 Merge "libimageenc.a: extract image-saving code from dwebp"
+af1ad3e2 libimageenc.a: extract image-saving code from dwebp
+dd7309e3 Merge "doc: use two's complement explicitly for uint8->int8 conversion"
+6105777e Merge "add gif2webp to CMake"
+13ae011e doc: use two's complement explicitly for uint8->int8 conversion
+4bda0cfb add gif2webp to CMake
+6029c7fe Merge "remove mention of fragment, frgm, FRGM, etc."
+545c147f remove mention of fragment, frgm, FRGM, etc.
+5b46f7fc cwebp.1: improve some grammar
+9e478f80 dec/vp8l.c: add assertions in EmitRescaledRowsRGBA/YUVA
+43bd8958 Make gif transparent color to be transparent black
+0887fc2d Merge "get_disto: add a '-o file' option to save a diff map"
+0de48e18 get_disto: add a '-o file' option to save a diff map
+0a57ad0d cosmetics: WebPSafeAlloc -> WebPSafeMalloc
+0a4699bc Merge "WebPPictureDistortion(): free() -> WebPSafeFree()"
+29fedbf5 Anim_encoder: correctly handle enc->prev_candidate_undecided_
+32dead4e WebPPictureDistortion(): free() -> WebPSafeFree()
+85cd5d06 Smarter LZ77 for uniform regions.
+6585075f Change PixelsAreSimilar() to handle black pixels correctly.
+c0a27fd2 vwebp: Clear previous frame when a key triggers a redraw
+57a5e3b6 webp_quality should return '0' in case of success.
+7f1b897b Faster stochastic histogram merging.
+48c810b8 Merge "remove WEBP_FORCE_ALIGNED and use memcpy() instead."
+3884972e remove WEBP_FORCE_ALIGNED and use memcpy() instead.
+485cac1a switch libimagedec.a and libimageio_util.a to avoid undefined symbol
+005e15b1 Merge "{extras,mux}/Makefile.am: add missing -lm"
+6ab496ed fix some 'unsigned integer overflow' warnings in ubsan
+8a4ebc6a Revert "fix 'unsigned integer overflow' warnings in ubsan"
+9d4f209f Merge changes I25711dd5,I43188fab
+e44f5248 fix 'unsigned integer overflow' warnings in ubsan
+27b5d991 Fix assertions in WebPRescalerExportRow()
+74f6f9e7 Add descriptions of default configuration in help info.
+aaf2530c {extras,mux}/Makefile.am: add missing -lm
+1269dc7c Refactor VP8LColorCacheContains()
+40872fb2 dec_neon,NeedsHev: micro optimization
+7b54e26b Add a CMake option for WEBP_SWAP_16BIT_CSP.
+d2223d8d Fix missing cpu-features for Android.
+bf16a4b4 Merge "cpu.cmake: improve webp_check_compiler_flag output"
+ee1057e3 cpu.cmake: improve webp_check_compiler_flag output
+b551e587 cosmetics: add {}s on continued control statements
+d2e4484e dsp/Makefile.am: put msa source in correct lib
+c7f66c82 Merge "utils/thread.c,cosmetics: join a few lines"
+98d8f295 Merge "examples/Makefile.am,cosmetics: sort binary targets"
+39f4ffbc utils/thread.c,cosmetics: join a few lines
+a86ce2b1 Merge "extras/Makefile.am: don't install libwebpextras"
+6fa9fe24 extras/Makefile.am: don't install libwebpextras
+0b2c58a9 Fix an unsigned integer overflow error in enc/cost.h
+d7ce4a2e examples/Makefile.am,cosmetics: sort binary targets
+386e4ba2 Reset segment id if we decide not to update segment map
+7b87e848 Merge "Add MSA optimized YUV to RGB upsampling functions"
+d3ddacb6 Add MSA optimized YUV to RGB upsampling functions
+eb98d8d8 webp_quality: detect lossless format and features
+ebee57f4 move imageio/example_util.[hc] (back to) examples/
+99542bbf webpdec: s/ExUtil//
+da573cf4 imageio_util: s/ExUtil/ImgIoUtil/
+bdda5bd4 split example_util.h
+15ed462b .gitignore: add extras/{get_disto,webp_quality}
+7be57489 Merge "VP8EstimateQuality(): roughty estimate webp bitstream quality factor"
+57020525 Makefile.vc: add missing imageio target
+e8ab6a82 VP8EstimateQuality(): roughty estimate webp bitstream quality factor
+fee7b3d6 Merge "'extras/get_disto' example: compute PSNR between two files"
+1e7d4401 'extras/get_disto' example: compute PSNR between two files
+4cecab63 pngdec.c,jpegdec.[hc]: remove unnecessary includes
+259f0434 makefile.unix: normalize image decode lib name
+ed34c39b fix: examples/libexample_dec.a => imageio/libexample_dec.a
+33d8d0d4 Merge "move examples/{example_util,image_dec} to imageio/"
+c960b82e Merge "extras.h: correct include guard"
+fe3cd28a Merge ".gitignore: add .gradle, /build"
+45fbeba5 Merge "Do token recording and counting in a single loop"
+4f33c820 .gitignore: add .gradle, /build
+c379b55a move examples/{example_util,image_dec} to imageio/
+5108d9aa extras.h: correct include guard
+ad497fbc move src/extras to the top-level
+0c0fb832 Do token recording and counting in a single loop
+9ac74f92 Add MSA optimized rescaling functions
+cb19dbc1 Add MSA optimized color transform functions
+3f4042b5 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless.
+5e2eb89e cosmetics,dsp/*msa.c: associate '*' with the type
+5b60db5c FastMBAnalyze() for quick i16/i4 decision
+567e6977 Add MSA optimized CollectHistogram function
+c54ab8dd Add MSA optimized quantization functions
+ec6f68c5 Merge "Remove QuantizeBlockWHT() in enc.c"
+2a5c417c Apply the RLE heuristic to LZ77.
+91b59e88 Remove QuantizeBlockWHT() in enc.c
+fe572737 Add MSA optimized SSE functions
+6b53ca87 cosmetics,(dec|enc)_sse2.c: fix indent
+b15d00d9 Merge "Add MSA optimized encoder IntraChromaPreds function"
+afe3cec8 Add MSA optimized encoder IntraChromaPreds function
+fc8cad9f reduce the number of malloc/free cycles in huffman.c
+7b4b05e0 Add MSA optimized encoder Intra16Preds function
+c18787a0 Add MSA optimized encoder Intra4Preds function
+479d1908 webpmux: Also print compression info per frame.
+a80e8cfd Provide support for CMake on Android studio 2.2.
+6c628410 Split the main CMake file.
+bbb6ecd9 Merge "Add MSA optimized distortion functions"
+7915396f Add MSA optimized distortion functions
+652e944f Merge "build.gradle: remove tab"
+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)
+deb54d91 Clarify the expected 'config' lifespan in WebPIDecode()
+435308e0 Add MSA optimized encoder transform functions
+dce64bfa Add MSA optimized alpha filter functions
+429120d0 Add MSA optimized color transform functions
+c7e2d245 update ChangeLog (tag: v0.5.1-rc5)
+55b2fede normalize the macros' "do {...} while (0)" constructs
+701c772e Add MSA optimized colorspace conversion functions
+c7eb06f7 Fix corner case in CostManagerInit.
+f918cb10 fix rescaling bug: alpha plane wasn't filled with 0xff
+ab7937a5 gif2webp: normalize the number of .'s in the help message
+3cdec847 vwebp: normalize the number of .'s in the help message
+bdf6241e cwebp: normalize the number of .'s in the help message
+06a38c7b fix rescaling bug: alpha plane wasn't filled with 0xff
+319e37be Improve lossless compression.
+6a197937 Add MSA optimized intra pred chroma functions
+447adbce 'our bug tracker' -> 'the bug tracker'
+97b9e644 normalize the number of .'s in the help message
+293d786f Added MSA optimized intra prediction 16x16 functions
+0afa0ce2 Added MSA optimized intra prediction 4x4 functions
+a6621bac Added MSA optimized simple edge filtering functions
+bb50bf42 pngdec,ReadFunc: throw an error on invalid read
+38063af1 decode.h,WebPGetInfo: normalize function comment
+1ebf193c Added MSA optimized chroma edge filtering functions
+9ad2352d Merge "Added MSA optimized edge filtering functions"
+60751096 Added MSA optimized edge filtering functions
+9e8e1b7b Inline GetResidual for speed.
+7d58d1b7 Speed-up uniform-region processing.
+8ec7032b simplify HistogramCombineEntropyBin()
+23e29cb1 Merge "Fix a boundary case in BackwardReferencesHashChainDistanceOnly." into 0.5.1
+472a049b remove bin_map[] allocation altogether
+0bb23b2c free -> WebPSafeFree()
+a977b4b5 Merge "rewrite the bin_map clustering to use less memory"
+3591ba66 rewrite the bin_map clustering to use less memory
+e6ac450c utils.[hc]: s/MAX_COLOR_COUNT/MAX_PALETTE_SIZE/
+e7b91772 Merge "DecodeImageData(): change the incorrect assert" into 0.5.1
+2abfa54f DecodeImageData(): change the incorrect assert
+5a48fcd8 Merge "configure: test for -Wfloat-conversion"
+0174d18d Fix a boundary case in BackwardReferencesHashChainDistanceOnly.
+6a9c262a Merge "Added MSA optimized transform functions"
+cfbcc5ec Make sure to consider small distances in LZ77.
+5e60c42a Added MSA optimized transform functions
+3dc28d76 configure: test for -Wfloat-conversion
+f2a0946a add some asserts to delimit the perimeter of CostManager's operation
+9a583c66 fix invalid-write bug for alpha-decoding
+f66512db make gradlew executable
+6fda58f1 backward_references: quiet double->int warning
+a48cc9d2 Merge "Fix a compression regression for images with long uniform regions." into 0.5.1
+cc2720c1 Merge "Revert an LZ77 boundary constant." into 0.5.1
+059aab4f Fix a compression regression for images with long uniform regions.
+b0c7e49e Check more backward matches with higher quality.
+a3611513 Revert an LZ77 boundary constant.
+8190374c README: fix typo
+7551db44 update NEWS
+0fb2269c bump version to 0.5.1
+f4537610 update AUTHORS & .mailmap
+3259571e Refactor GetColorPalette method.
+1df5e260 avoid using tmp histogram in PreparePair()
+7685123a fix comment typos
+a246b921 Speedup backward references.
+76d73f18 Merge "CostManager: introduce a free-list of ~10 intervals"
+eab39d81 CostManager: introduce a free-list of ~10 intervals
+4c59aac0 Merge "mips msa webp configuration"
+043c33f1 Merge "Improve speed and compression in backward reference for lossless."
+71be9b8c Merge "clarify variable names in HistogramRemap()"
+0ba7fd70 Improve speed and compression in backward reference for lossless.
+0481d42a CostManager: cache one interval and re-use it when possible
+41b7e6b5 Merge "histogram: fix bin calculation"
+96c3d624 histogram: fix bin calculation
+fe9e31ef clarify variable names in HistogramRemap()
+ce3c8247 disable near-lossless quantization if palette is used
+e11da081 mips msa webp configuration
+5f8f998d mux: Presence of unknown chunks should trigger VP8X chunk output.
+cadec0b1 Merge "Sync mips32 and dsp_r2 YUV->RGB code with C verison"
+d9637758 Compute the hash chain once and for all for lossless compression.
+50a48665 Sync mips32 and dsp_r2 YUV->RGB code with C verison
+eee788e2 Merge "introduce a common signature for all image reader function"
+d77b877c introduce a common signature for all image reader function
+ca8d9519 remove some obsolete TODOs
+ae2a7222 collect all decoding utilities from examples/ in libexampledec.a
+0b8ae852 Merge "Move DitherCombine8x8 to dsp/dec.c"
+77cad885 Merge "ReadWebP: avoid conversion to ARGB if final format is YUVA"
+ab8d6698 ReadWebP: avoid conversion to ARGB if final format is YUVA
+f8b7ce9e Merge "test pointer to NULL explicitly"
+5df6f214 test pointer to NULL explicitly
+77f21c9c Move DitherCombine8x8 to dsp/dec.c
+c9e6d865 Add gradle support
+c65f41e8 Revert "Add gradle support"
+bf731ede Add gradle support
+08333b85 WebPAnimEncoder: Detect when canvas is modified, restore only when needed.
+0209d7e6 Merge "speed-up MapToPalette() with binary search"
+fdd29a3d speed-up MapToPalette() with binary search
+cf4a651b Revert "Refactor GetColorPalette method."
+0a27aca3 Merge changes Idfa8ce83,I19adc9c4
+f25c4406 WebPAnimEncoder: Restore original canvas between multiple encodes.
+169004b1 Refactor GetColorPalette method.
+576362ab VP8LDoFillBitWindow: support big-endian in fast path
+ac49e4e4 bit_reader.c: s/VP8L_USE_UNALIGNED_LOAD/VP8L_USE_FAST_LOAD/
+d39ceb58 VP8LDoFillBitWindow: remove stale TODO
+2ec2de14 Merge "Speed-up BackwardReferencesHashChainDistanceOnly."
+3e023c17 Speed-up BackwardReferencesHashChainDistanceOnly.
+f2e1efbe Improve near lossless compression when a prediction filter is used.
+e15afbce dsp.h: fix ubsan macro name
+e53c9ccb dsp.h: add WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+af81fdb7 utils.h: quiet -fsanitize=undefined warnings
+ea0be354 dsp.h: remove utils.h include
+cd276aec utils/*.c: ../utils/utils.h -> ./utils.h
+c8927131 utils/Makefile.am: add some missing headers
+ea24e026 Merge "dsp.h: add WEBP_UBSAN_IGNORE_UNDEF"
+369e264e dsp.h: add WEBP_UBSAN_IGNORE_UNDEF
+0d020a78 Merge "add runtime NEON detection"
+5ee2136a Merge "add VP8LAddPixels() to lossless.h"
+47435a61 add VP8LAddPixels() to lossless.h
+8fa6ac68 remove two ubsan warnings
+74fb56fb add runtime NEON detection
+4154a839 MIPS update to new Unfilter API
+c80b9fc8 Merge "cherry-pick decoder fix for 64-bit android devices"
+6235147e cherry-pick decoder fix for 64-bit android devices
+d41b8c43 configure: test for -Wformat-* w/-Wformat present
+5f95589f Fix WEBP_ALIGN in case the argument is a pointer to a type larger than a byte.
+2309fd5c replace num_parts_ by num_parts_minus_one_ (unsigned)
+9629f4bc SimplifySegments: quiet -Warray-bounds warning
+de47492e Merge "update the Unfilter API in dsp to process one row independently"
+2102ccd0 update the Unfilter API in dsp to process one row independently
+e3912d56 WebPAnimEncoder: Restore canvas before evaluating blending possibility.
+6e12e1e3 WebPAnimEncoder: Fix for single-frame optimization.
+602f344a Merge changes I1d03acac,Ifcb64219
+95ecccf6 only apply color-mapping for alpha on the cropped area
+47dd0708 anim_diff: Add an experimental option for max inter-frame diff.
+aa809cfe only allocate alpha_plane_ up to crop_bottom row
+31f2b8d8 WebPAnimEncoder: FlattenSimilarPixels(): look for similar
+774dfbdc perform alpha filtering within the decoding loop
+a4cae68d lossless decoding: only process decoded row up to last_row
+238cdcdb Only call WebPDequantizeLevels() on cropped area
+cf6c713a alpha: preparatory cleanup
+b95ac0a2 Merge "VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions"
+89231394 VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions
+5828e199 use_8b_decode -> use_8b_decode_
+8dca0247 fix bug in alpha.c that was triggering a memory error in incremental mode
+9a950c53 WebPAnimEncoder: Disable filtering when blending is used with lossy encoding.
+eb423903 WebPAnimEncoder: choose max diff for framerect based on quality.
+ff0a94be WebPAnimEncoder lossy: ignore small pixel differences for frame rectangles.
+f8040084 gif2webp: Remove the 'prev_to_prev_canvas' buffer.
+6d8c07d3 Merge "WebPDequantizeLevels(): use stride in CountLevels()"
+d96fe5e0 WebPDequantizeLevels(): use stride in CountLevels()
+ec1b2407 WebPPictureImport*: check output pointer
+c0768769 Merge "Revert "Re-enable encoding of alpha plane with color cache for next release.""
+41f14bcb WebPPictureImport*: check src pointer
+64eed387 Pass stride parameter to WebPDequantizeLevels()
+97934e24 Revert "Re-enable encoding of alpha plane with color cache for next release."
+e88c4ca0 fix -m 2 mode-cost evaluation (causing partition0 overflow)
+4562e83d Merge "add extra meaning to WebPDecBuffer::is_external_memory"
+abdb109f add extra meaning to WebPDecBuffer::is_external_memory
+875aec70 enc_neon,cosmetics: break long comment
+71e856cf GetMBSSIM,cosmetics: fix alignment
+a90edffb fix missing 'extern' for SSIM function in dsp/
+423ecaf4 move some SSIM-accumulation function for dsp/
+f08e6624 Merge "Fix FindClosestDiscretized in near lossless:"
+0d40cc5e enc_neon,Disto4x4: remove an unnecessary transpose
+e8feb20e Fix FindClosestDiscretized in near lossless:
+82006430 anim_util: quiet static analysis warning
+a6f23c49 Merge "AnimEncoder: Support progress hook and user data."
+a5193774 Merge "Near lossless feature: fix some comments."
+da98d31c AnimEncoder: Support progress hook and user data.
+33357131 Near lossless feature: fix some comments.
+0beed01a cosmetics: fix indent after 2f5e898
+6753f35c Merge "FTransformWHT optimization."
+6583bb1a Improve SSE4.1 implementation of TTransform.
+7561d0c3 FTransformWHT optimization.
+7ccdb734 fix indentation after patch #328220
+6ec0d2a9 clarify the logic of the error path when decoding fails.
+8aa352b2 Merge "Remove an unnecessary transposition in TTransform."
+db860884 Merge "remove useless #include"
+9960c316 Remove an unnecessary transposition in TTransform.
+6e36b511 Small speedup in FTransform.
+9dbd4aad Merge "fix C and SIMD flags completion."
+e60853ea Add missing common_sse2.h file to makefile.unix
+696eb2b0 fix C and SIMD flags completion.
+2b4fe33e Merge "fix multiple allocation for transform buffer"
+2f5e8986 fix multiple allocation for transform buffer
+bf2b4f11 Regroup common SSE code + optimization.
+4ed650a1 force "-pass 6" if -psnr or -size is used but -pass isn't.
+3ef1ce98 yuv_sse2: fix -Wconstant-conversion warning
+a7a03e9f Merge changes I4852d18f,I51ccb85d
+5e122bd6 gif2webp: set enc_options.verbose = 0 w/-quiet
+ab3c2583 anim_encode,DefaultEncoderOptions: init verbose
+8f0dee77 Merge "configure: fix builtin detection w/-Werror"
+4a7b85a9 cmake: fix builtin detection w/-Werror
+b74657fb configure: fix builtin detection w/-Werror
+3661b980 Add a CMakeLists.txt
+75f4af4d remove useless #include
+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)
+7e7b6ccc faster rgb565/rgb4444/argb output
+4c7f565f update NEWS
+1f62b6b2 update AUTHORS
+e224fdc8 update mailmap
+71100500 bump version to 0.5.0
+230a685e README: update help text, repo link
+d48e427b Merge "demux: accept raw bitstreams"
+99a01f4f Merge "Unify some entropy functions."
+4b025f10 Merge "configure: disable asserts by default"
+92cbddf8 Merge "fix PrintBlockInfo()"
+ca509a33 Unify some entropy functions.
+367bf903 fix PrintBlockInfo()
+b0547ff0 move back common constants for lossless_enc*.c into the .h
+fb4c7832 lossless: simpler alpha cleanup preprocessing
+ba7f4b68 Merge "anim_diff: add brief description of options"
+47ddd5a4 Move some codec logic out of ./dsp .
+b4106c44 anim_diff: add brief description of options
+357f455d yuv_sse2: fix 32-bit visual studio build
+b9d80fa4 configure: disable asserts by default
+7badd3da cosmetic fix: sizeof(type) -> sizeof(*var)
+80ce27d3 Speed up 24-bit packing / unpacking in YUV / RGB conversions.
+68eebcb0 remove a TODO about rotation
+2dee2966 remove few obsolete TODO about aligned loads in SSE2
+e0c0bb34 remove TODO about unused ref_lf_delta[]
+9cf1cc2b remove few TODO: * 256 -> RD_DISTO_MULT * don't use TDisto for UV mode picking
+79189645 Merge changes from topic 'demux-fragment-cleanup'
+47399f92 demux: remove GetFragment()
+d3cfb79a demux: remove dead fragment related TODO
+ab714b8a demux, Frame: remove is_fragment_ field
+b105921c yuv_sse2, cosmetics: fix indent
+466c92e8 demux,WebPIterator: remove fragment_num/num_fragments
+11714ff1 demux: remove WebPDemuxSelectFragment
+c0f7cc47 fix for bug #280: UMR in next->bits
+578beeb8 Merge "enc/Makefile.am: add missing headers"
+1a819f00 makefile.unix: make visibility=hidden the default
+d4f9c2ef enc/Makefile.am: add missing headers
+846caff4 configure: check for -fvisibility=hidden
+3f3ea2c5 demux: accept raw bitstreams
+d6dad5d0 man cwebp: add precision about exactness of the 'lossless' mode
+46bb1e34 Merge "gifdec: remove utils.h include"
+2b882e94 Merge "Makefile.vc: define WEBP_HAVE_GIF for gifdec.c"
+892b9238 Merge "man/*, AUTHORS: clarify origin of the tool"
+e5687a18 Merge "fix optimized build with -mcmodel=medium"
+e56e6859 Makefile.vc: define WEBP_HAVE_GIF for gifdec.c
+4077d944 gifdec: remove utils.h include
+b5e30dac man/*, AUTHORS: clarify origin of the tool
+b275e598 fix optimized build with -mcmodel=medium
+64da45a9 cosmetics, cwebp: fix indent
+038a060d Merge "add disto-based refinement for UV mode (if method = 1 or 2)"
+2835089d Provide an SSE2 implementation of CombinedShannonEntropy.
+e6c93519 add disto-based refinement for UV mode (if method = 1 or 2)
+04507dc9 Merge "fix undefined behaviour during shift, using a cast"
+793c5261 Merge "wicdec: add support for reading from stdin"
+d3d16397 Optimize the heap usage in HistogramCombineGreedy.
+202a710b fix undefined behaviour during shift, using a cast
+14d27a46 improve method #2 by merging DistoRefine() and SimpleQuantize()
+cb1ce996 Merge "10% faster table-less SSE2/NEON version of YUV->RGB conversion"
+ac761a37 10% faster table-less SSE2/NEON version of YUV->RGB conversion
+79fcf29a wicdec: add support for reading from stdin
+015f173f Merge "cwebp: add support for stdin input"
+a9947c32 cwebp: add support for stdin input
+7eb01ff3 Merge "Improved alpha cleanup for the webp encoder when prediction transform is used."
+fb8c9106 Merge "introduce WebPMemToUint32 and WebPUint32ToMem for memory access"
+bd91af20 Merge "bit_reader: remove aarch64 BITS TODO"
+6c702b81 Speed up hash chain initialization using memset.
+4c60f63c make ReadPNG and ReadJPEG take a filename instead of a FILE
+464ed10f bit_reader: remove aarch64 BITS TODO
+d478e589 Merge "configure: update issue tracker"
+69381113 Improved alpha cleanup for the webp encoder when prediction transform is used.
+2c08aac8 introduce WebPMemToUint32 and WebPUint32ToMem for memory access
+010ca3d1 Fix FindMatchLength with non-aligned buffers.
+a90e1e3f README: add prerequisites for an autoconf build
+458f0866 configure: update issue tracker
+33914595 vwebp: work around the transparent background with GLUT bug
+e4a7eed4 cosmetics: fix indent
+08375129 Merge "Make a separate case for low_effort in CopyImageWithPrediction"
+aa2eb2d4 Merge "cosmetics: fix indent"
+b7551e90 cosmetics: fix indent
+5bda52d4 Make a separate case for low_effort in CopyImageWithPrediction
+66fa598a Merge "configure: fix intrinsics build w/older gcc"
+5ae220be backward_references.c: Fixed compiler warning
+1556da09 Merge "configure: restore 2 warnings"
+71a17e58 configure: restore 2 warnings
+9eeabc07 configure: fix intrinsics build w/older gcc
+363babe2 Merge "fix some warning about unaligned 32b reads"
+a1411782 Optimization in hash chain comparison for 64 bit Arrays were compared 32 bits at a time, it is now done 64 bits at a time. Overall encoding speed-up is only of 0.2% on @skal's small PNG corpus. It is of 3% on my initial 1.3 Mp desktop screenshot image.
+829bd141 Combine Huffman cost and bit entropy into one loop
+a7a954c8 Merge "lossless: make prediction in encoder work per scanline"
+61b605b4 Merge "fix of undefined multiply (int32 overflow)"
+239421c5 lossless: make prediction in encoder work per scanline
+f5ca40e0 fix of undefined multiply (int32 overflow)
+5cd2ef4c Merge changes from topic 'win-threading-compat'
+76ce9187 Makefile.vc: enable WEBP_USE_THREAD for windows phone
+d2afe974 thread: use CreateThread for windows phone
+0fd0e12b thread: use WaitForSingleObjectEx if available
+63fadc9f thread: use InitializeCriticalSectionEx if available
+110ad583 thread: use native windows cond var if available
+912c9fdf dec/webp: use GetLE(24|32) from utils
+f1694481 utils/GetLE32: correct uint32 promotion
+158763de Merge "always call WebPInitSamplers(), don't try to be smart"
+3770f3bb Merge "cleanup the YFIX/TFIX difference by removing some code and #define"
+a40f60a9 Merge "3% speed improvement for lossless webp encoder for low effort mode:"
+ed1c2bc6 always call WebPInitSamplers(), don't try to be smart
+b8c44f1a 3% speed improvement for lossless webp encoder for low effort mode:
+997e1038 cleanup the YFIX/TFIX difference by removing some code and #define
+d73d1c8b Merge "Make discarding invisible RGB values (cleanup alpha) the default."
+1f9be97c Make discarding invisible RGB values (cleanup alpha) the default.
+f240117b Make dwebp listen more to the -quiet flag
+b37b0179 fix for issue #275: don't compare to out-of-bound pointers
+21735e06 speed-up trivial one-symbol decoding case for lossless
+397863bd Refactor CopyPlane() and CopyPixels() methods: put them in utils.
+6ecd72f8 Re-enable encoding of alpha plane with color cache for next release.
+1f7148a4 Merge "remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures"
+6ae395fa Merge "use ExReadFile() for ReadYUV()"
+8076a00e gitignore list: add anim_diff.
+1c1702d8 use ExReadFile() for ReadYUV()
+775d3a37 remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures
+c13245c7 AnimEncoder: Add a GetError() method.
+688b265d AnimDecoder API: Add a GetDemuxer() method.
+1aa4e3d6 WebPAnimDecoder: add an option to enable multi-threaded decoding.
+3584abca AnimDecoder: option to decode to common color modes.
+afd5a62c Merge "mux.h does NOT need to include encode.h"
+8550d443 Merge "migrate anim_diff tool from C++ to C89"
+96201e50 migrate anim_diff tool from C++ to C89
+945cfa3b mux.h does NOT need to include encode.h
+8da07e8d Merge "~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV"
+bfd3fc02 ~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV
+02432427 man/cwebp.1, cosmetics: escape '-'s
+96f5b423 man/cwebp: group lossy-only options
+52fdbdfe extract some RGB24 to Luma conversion function from enc/ to dsp/
+ab8c2300 add missing \n
+8304179a sync NEWS with 0.4.4
+5bd04a08 sync versions with 0.4.4
+8f1fcc15 Merge "Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c"
+25bf2ce5 fix some warning about unaligned 32b reads
+922268fd s/TIFF/WebP
+fa8927ef Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c
+9b373598 Merge "for ReadXXXX() image-readers, use the value of pic->use_argb"
+f7c507a5 Merge "remove unnecessary #include "yuv.h""
+7861578b for ReadXXXX() image-readers, use the value of pic->use_argb
+14e4043b remove unnecessary #include "yuv.h"
+469ba2cd vwebp: fix incorrect clipping w/NO_BLEND
+4b9186b2 update issue tracker url
+d64d376c change WEBP_ALIGN_CST value to 31
+f717b828 vp8l.c, cosmetics: fix indent after 95509f9
+927ccdc4 Merge "fix alignment of allocated memory in AllocateTransformBuffer"
+fea94b2b fix alignment of allocated memory in AllocateTransformBuffer
+5aa8d61f Merge "MIPS: rescaler code synced with C implementation"
+e7fb267d MIPS: rescaler code synced with C implementation
+93c86ed5 Merge "format_constants.h: MKFOURCC, correct cast"
+5d791d26 format_constants.h: MKFOURCC, correct cast
+65726cd3 dsp/lossless: Average2, make a constant unsigned
+d26d9def Use __has_builtin to check clang support
+12ec204e moved ALIGN_CST into util/utils.h and renamed WEBP_ALIGN_xxx
+a2640838 Merge "rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand"
+3fb600d5 Merge "wicdec: fix alpha detection w/64bpp BGRA/RGBA"
+67c547fd rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand
+99e3f812 Merge "large re-organization of the delta-palettization code"
+95509f99 large re-organization of the delta-palettization code
+74fb458b fix for weird msvc warning message
+ae49ad86 Merge "SSE2 implementation of ImportRowShrink"
+932fd4df SSE2 implementation of ImportRowShrink
+badfcbaa wicdec: fix alpha detection w/64bpp BGRA/RGBA
+35cafa6c Merge "iosbuild: fix linking with Xcode 7 / iOS SDK 9"
+b0c9d8af label rename: NO_CHANGE -> NoChange
+b4e731cd neon-implementation for rescaler code
+db1321a6 iosbuild: fix linking with Xcode 7 / iOS SDK 9
+6dfa5e3e rescaler: better handling of the fxy_scale=0 special case.
+55c05293 Revert "rescaler: better handling of the fxy_scale=0 special case."
+9f226bf8 rescaler: better handling of the fxy_scale=0 special case.
+f7b8f907 delta_palettization.*: add copyright
+c1e1b710 Changed delta palette to compress better
+0dd28267 Merge "Add delta_palettization feature to WebP"
+48f66b66 Add delta_palettization feature to WebP
+27933e2a anim_encoder: drop a frame if it has same pixels as the prev frame.
+df9f6ec8 Merge "webpmux/DisplayInfo: send non-error output to stdout"
+8af4993b Merge "rescaler_mips_dsp_r2: cosmetics, fix indent"
+2b9d2495 Merge "rescaler: cosmetics, join two lines"
+cc020a8c webpmux/DisplayInfo: send non-error output to stdout
+a288e746 configure: add -Wshorten-64-to-32
+c4c3cf2d pngdec: fix type conversion warnings
+bef8e97d webpmux: fix type conversion warning
+5a84460d rescaler_mips_dsp_r2: cosmetics, fix indent
+acde0aae rescaler: cosmetics, join two lines
+306ce4fd rescaler: move the 1x1 or 2x1 handling one level up
+cced974b remove _mm_set_epi64x(), which is too specific
+56668c9f fix warnings about uint64_t -> uint32_t conversion
+76a7dc39 rescaler: add some SSE2 code
+1df1d0ee rescaler: harmonize function protos
+9ba1894b rescaler: simplify ImportRow logic
+5ff0079e fix rescaler vertical interpolation
+cd82440e VP8LAllocateHistogramSet: align histogram[] entries
+a406b1dd Merge "fix memory over-allocation in lossless rescaler init"
+0fde33e3 add missing const in VP8InitFrame signature
+ac7d5e8d fix memory over-allocation in lossless rescaler init
+017f8ccc Loosen the buffer size checks for Y/U/V/A too.
+15ca5014 loosen the padding check on buffer size
+d623a870 dec_neon: add whitespace around stringizing operator
+29377d55 dsp/mips: cosmetics: add whitespace around XSTR macro
+eebaf97f dsp/mips: add whitespace around stringizing operator
+d39dc8f3 Create a WebPAnimDecoder API.
+03fb7522 gif2webp: print output file size
+14efabbf Android: limit use of cpufeatures
+7b83adbe preparatory cosmetics for Rescaler code fix and clean-up
+77fb41c2 dec/vp8l/DecodeAlphaData: remove redundant cast
+90fcfcd9 Insert less hash chain entries from the beginnings of long copies.
+bd55604d SSE2: add yuv444 converters, re-using yuv_sse2.c
+41a5d99d add a -quiet option to 'dwebp'
+80ab3edb Merge "README: update dwebp help output after 1e595fe"
+32b71b2e README: update dwebp help output after 1e595fe
+3ec11827 use the DispatchAlpha() call from dsp
+c5f00621 incorporate bzero() into WebPRescalerInit() instead of call site
+3ebcdd41 remove duplicate "#include <stdlib.h>"
+1e595fe1 dwebp: add -resize as a synonym for -scale
+24a96932 dec: allow 0 as a scaling dimension
+b9187242 utils/rescaler: add WebPRescalerGetScaledDimensions
+923e8eda Merge "update NEWS"
+020fd099 Merge "WebPPictureDistortion: support ARGB format for 'pic' when computing distortion."
+6a5292f6 update NEWS
+56a2e9f5 WebPPictureDistortion: support ARGB format for 'pic' when computing distortion.
+0ae582e4 configure: test and add -Wunreachable-code
+c2f9dc06 bit_writer: convert VP8L macro values to immediates
+b969f888 Reduce magic in palette reordering
+acb297e9 anim_diff: add a -raw_comparison flag
+155c1b22 Merge changes I76f4d6fe,I45434639
+717e4d5a mips32/mipsDSPr2: function ImportRow rebased
+7df93893 fix rescaling bug (uninitialized read, see bug #254).
+5cdcd561 lossless_enc_neon: add VP8LTransformColor
+a53c3369 lossless_neon: add VP8LTransformColorInverse
+99131e7f Merge changes I9fb25a89,Ibc648e9e
+c4556766 simplify the main loop for downscaling
+2a010f99 lossless_neon: remove predictors 5-13
+ca221bbc ll_enc_neon: enable VP8LSubtractGreenFromBlueAndRed
+585d93db Container spec: clarify ordering of ALPH chunk.
+01d61fd9 lossless: ~20 % speedup
+f722c8f0 lossless: Speed up ComputeCacheEntropy by 40 %
+1ceecdc8 add a VP8LColorCacheSet() method for color cache
+17eb6099 lossless: Allow copying from prev row in rle-mode.
+f3a7a5bf lossless: bit writer optimization
+d97b9ff7 Merge changes from topic 'lossless-enc-improvements'
+0250dfcc msvc: fix pointer type warning in BitsLog2Floor
+52931fd5 lossless: combine the Huffman code with extra bits
+c4855ca2 lossless: Inlining add literal
+8e9c94de lossless: simplify HashChainFindCopy heuristics
+888429f4 lossless: 0.5 % compression density improvement
+7b23b198 lossless: Add zeroes into the predicted histograms.
+85b44d8a lossless: encoding, don't compute unnecessary histo
+d92453f3 lossless: Remove about 25 % of the speed degradation
+2cce0317 Faster alpha coding for webp
+5e75642e lossless: rle mode not to accept lengths smaller than 4.
+84326e4a lossless: Less code for the entropy selection
+16ab951a lossless: 0.37 % compression density improvement
+822f113e add WebPFree() to the API
+0ae2c2e4 SSE2/SSE41: optimize SSE_16xN loops
+39216e59 cosmetics: fix indent after 32462a07
+559e54ca Merge "SSE2: slightly faster FTransformWHT"
+8ef9a63b SSE2: slightly faster FTransformWHT
+f27f7735 lossless_neon: enable VP8LAddGreenToBlueAndRed
+36e9c4bc SSE2: minor cosmetrics on in-loop filter code
+4741fac4 dsp/lossless_*sse2: remove some unnecessary inlines
+1819965e fix warning ("left shift of negative value") using a cast
+70170014 SSE2: speed-up some lossless-encoding functions
+abcb0128 Merge "SSE2: slightly faster (~5%) AddGreenToBlueAndRed()"
+2df5bd30 Merge "Speedup to HuffmanCostCombinedCount"
+9e356d6b SSE2: slightly faster (~5%) AddGreenToBlueAndRed()
+fc6c75a2 SSE2: 53% faster TransformColor[Inverse]
+49073da6 SSE2: 46% speed-up of TransformColor[Inverse]
+32462a07 Speedup to HuffmanCostCombinedCount
+f3d687e3 SSE4.1 implementation of some lossless encoding functions
+bfc300c7 SSE4.1 implementation of some alpha-processing functions
+7f9c98f2 Merge "sse2 in-loop: simplify SignedShift8b() a bit"
+ef314a5d dec_sse2/GetNotHEV: micro optimization
+a729cff9 sse2 in-loop: simplify SignedShift8b() a bit
+422ec9fb simplify Load8x4() a bit
+8df238ec Merge "remove some duplicate FlipSign()"
+751506c4 remove some duplicate FlipSign()
+65ef5afc Merge "lossless: 0.13% compression density gain"
+2beef2f2 lossless: 0.13% compression density gain
+3033f24c lossless: 0.06 % compression density improvement
+64960da9 dec_neon: add VE8uv / VE16
+14dbd87b dec_neon: add HE8uv / HE16
+ac768011 introduce FTransform2 to perform two transforms at a time.
+aa6065ae dec_neon: use vld1_dup(mem) rather than vdup(mem[0])
+8b63ac78 Merge "dec_neon: add TM16"
+f51be09e Merge "dec_neon/TrueMotion: simply left border load"
+dc48196b dec_neon: add TM16
+ea95b305 dec_neon/TrueMotion: simply left border load
+f262d612 speed-up SetResidualSSE2
+bf46d0ac fix mips2 build target
+929a0fdc enc_sse2/TTransform: simplify abs calculation
+17dbd058 enc_sse2/CollectHistogram: simplify abs calculation
+a6c15936 dec_neon: add DC16 intra predictors
+03b4f50d Makefile.vc: add anim_diff build support.
+1b989874 Merge changes I9cd84125,Iee7e387f,I7548be72
+acd7b5af Introduce a test tool anim_diff.
+f274a96c dsp/enc_sse2: add luma4 intra predictors
+040b11bd dsp/enc_sse2: add chroma intra predictors
+aee021bb dsp/enc_sse2: add luma16 intra predictors
+9e00a499 makefile.unix: remove superclean target
+cefc9c09 makefile.unix: clean up after extras target
+4c9af023 dec_neon: add DC8uvNoTopLeft
+dd55b873 Merge "doc/webp-container-spec: update repo browser link"
+f0486968 doc/webp-container-spec: update repo browser link
+9287761d Merge "GetResidualCostSSE2: simplify abs calculation"
+0e009366 dsp/cpu.c(x86): check maximum supported cpuid feature
+b243a4bc GetResidualCostSSE2: simplify abs calculation
+6d4602b8 Merge "fix typo: constitutes -> constitute"
+5fe1fe37 fix typo: constitutes -> constitute
+b83bd7c4 Merge "populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions"
+b0114a32 Merge "histogram.h: cosmetics: remove unnecessary includes"
+feab45ef gifdec: Move inclusion of webp/config.h to header.
+dbba67d1 histogram.h: cosmetics: remove unnecessary includes
+e978fec6 Merge "VP8LBitReader: fix remaining ubsan error with large shifts"
+d6fe5884 Merge "ReconstructRow: move some one-time inits out of the main loop"
+a21d647c ReconstructRow: move some one-time inits out of the main loop
+7a01c3c3 VP8LBitReader: fix remaining ubsan error with large shifts
+7fa67c9b change GetPixPairHash64() return type to uint32_t
+ec1fb9f8 Merge "dsp/enc.c: cosmetics: move DST() def closer to use"
+7073bfb3 Merge "split 64-mult hashing into two 32-bit multiplies"
+0768b252 dsp/enc.c: cosmetics: move DST() def closer to use
+6a48b8f0 Merge "fix MSVC size_t->int conversion warning"
+1db07cde Merge "anim_encode: cosmetics: fix alignment"
+e28271a3 anim_encode: cosmetics: fix alignment
+7fe357b8 split 64-mult hashing into two 32-bit multiplies
+af74c145 populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions
+61214134 remove VP8Residual::cost unused field
+e2544823 fix MSVC size_t->int conversion warning
+b69a6c35 vwebp: don't redefine snprintf with VS2015+
+0ac29c51 AnimEncoder API: Consistent use of trailing underscores in struct.
+d4845550 AnimEncoder API: Use timestamp instead of duration as input to Add().
+9904e365 dsp/dec_sse2: DC8uv / DC8uvNoLeft speedup
+7df20497 dsp/dec_sse2: DC16 / DC16NoLeft speedup
+8e515dfe Merge "makefile.unix: add some missing headers"
+db12250f cosmetics: vp8enci.h: break long line
+bf516a87 makefile.unix: add some missing headers
+b44eda3f dsp: add DSP_INIT_STUB
+03e76e96 clarify the comment about double-setting the status in SetError()
+9fecdd71 remove unused EmitRGB()
+43f010dd move ReconstructRow to top
+82d98020 add a dec/common.h header to collect common enc/dec #defines
+5d4744a2 Merge "enc_sse41: add Disto4x4 / Disto16x16"
+e38886a7 mux.h: Bump up ABI version
+46305ca6 configure: add --disable-<avx2|sse4.1|sse2>
+2fc8b658 CPPFLAGS->CFLAGS for detecting sse4.1 in preprocessor
+1a338fb3 enc_sse41: add Disto4x4 / Disto16x16
+94055503 encoding SSE4.1 stub for StoreHistogram + Quantize + SSE_16xN
+c64659e1 remove duplicate variables after the lossless{_enc}.c split
+67ba7c7a enc_sse2: call local FTransform in CollectHistogram
+18249799 dsp: s/VP8LSetHistogramData/VP8SetHistogramData/
+ede5e158 cosmetics: dsp/lossless.h: reorder prototypes
+553051f7 dsp/lossless: split enc/dec functions
+9064adc8 Merge "conditionally add -msse4.1 in Makefile.unix"
+cecf5096 dsp/yuv*.c: rework WEBP_USE_<arch> ifdef
+6584d398 dsp/upsampling*.c: rework WEBP_USE_<arch> ifdef
+80809422 dsp/rescaler*.c: rework WEBP_USE_<arch> ifdef
+1d93ddec dsp/lossless*.c: rework WEBP_USE_<arch> ifdef
+73805ff2 dsp/filters*.c: rework WEBP_USE_<arch> ifdef
+fbdcef24 dsp/enc*.c: rework WEBP_USE_<arch> ifdef
+66de69c1 dsp/dec*.c: rework WEBP_USE_<arch> ifdef
+48e4ffd1 dsp/cost*.c: rework WEBP_USE_<arch> ifdef
+29fd6f90 dsp/argb*.c: rework WEBP_USE_<arch> ifdef
+80ff3813 dsp/alpha*.c: rework WEBP_USE_<arch> ifdef
+bf09cf1e conditionally add -msse4.1 in Makefile.unix
+e9570dd9 stub for SSE4.1 support.
+4a95384b Merge "dsp: add sse4.1 detection"
+cabf4bd2 dsp: add sse4.1 detection
+4ecba1ab thread.h: rename interface param
+b8d706c8 Merge "sync versions with 0.4.3"
+ae64a711 Merge "add shell for libwebpextras"
+92a5da9c sync versions with 0.4.3
+9d4e2d16 Merge "~30% faster smart-yuv (-pre 4) with early-out criterion"
+b1bdbbab ~30% faster smart-yuv (-pre 4) with early-out criterion
+7efb9748 Merge "Disable NEON code on Native Client"
+ac4f5784 Disable NEON code on Native Client
+0873f85b AnimEncoder API: Support input frames in YUV(A) format.
+5c176d2d add shell for libwebpextras
+44bd9561 fix signature for VP8RecordCoeffTokens()
+c9b8ea0e small cosmetics on TokenBuffer.
+76394c09 Merge "MIPS: dspr2: added optimization for TrueMotion"
+0f773693 WebPPictureRescale: add a note about 0 width/height
+241bb5d9 MIPS: dspr2: added optimization for TrueMotion
+6cef0e4f examples/Android.mk: add webpmux_example target
+53c16ff0 Android.mk: add webpmux target
+21852a00 Android.mk: add webpdemux target
+8697a3bc Android.mk: add webpdecoder{,_static} targets
+4a670491 Android.mk: split source lists per-directory
+b5e79422 MIPS: dspr2: Added optimization for some convert functions
+0f595db6 MIPS: dspr2: Added optimization for some convert functions
+8a218b4a MIPS: [mips32|dspr2]: GetResidualCost rebased
+ef987500 Speedup method StoreImageToBitMask by 5%.
+602a00f9 fix iOS arm64 build with Xcode 6.3
+23820507 1-2% faster encoding by removing an indirection in GetResidualCost()
+eddb7e70 MIPS: dspr2: added otpimization for DC8uv, DC8uvNoTop and DC8uvNoLeft
+73ba2915 MIPS: dspr2: added optimization for functions RD4 and LD4
+c7129da5 Merge "4-5% faster encoding using SSE2 for GetResidualCost"
+94380d00 MIPS: dspr2: added optimizaton for functions VE4 and DC4
+2a407092 4-5% faster encoding using SSE2 for GetResidualCost
+17e19862 Merge "MIPS: dspr2: added optimization for simple filtering functions"
+3ec404c4 Merge "dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage"
+b969f5df dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage
+d7b8e711 MIPS: dspr2: added optimization for simple filtering functions
+235f774e Merge "MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C"
+42a8a628 MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C
+b442bef3 Merge "ApplyFiltersAndEncode: only copy lossless stats"
+b510fbfe doc/webp-container-spec: note MSB order for chunk diagrams
+9bc0f922 ApplyFiltersAndEncode: only copy lossless stats
+3030f115 Merge "dsp/mips: add some missing TSan annotations"
+dfcf4593 Merge "MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C"
+55c75a25 dsp/mips: add some missing TSan annotations
+2cb879f0 MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C
+e1556010 move some cost tables from enc/ to dsp/
+c3a03168 Merge "picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined"
+39537d7c Merge "VP8LDspInitMIPSdspR2: add missing TSan annotation"
+1dd419ce picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined
+43fd3543 VP8LDspInitMIPSdspR2: add missing TSan annotation
+c7233dfc Merge "VP8LDspInit: remove memcpy"
+0ec4da96 picture_csp::InitGammaTables*: add missing TSan annotations
+35579a49 VP8LDspInit: remove memcpy
+97f6aff8 VP8YUVInit: add missing TSan annotation
+f9016d66 dsp/enc::InitTables: add missing TSan annotation
+e3d9771a VP8EncDspCostInit*: add missing TSan annotations
+d97c143d Merge "doc/webp-container-spec: cosmetics"
+309b7908 MIPS: mips32: Added optimization for function SetResidualCoeffs
+a987faed MIPS: dspr2: added optimization for function GetResidualCost
+e7d3df23 doc/webp-container-spec: cosmetics
+be6635e9 Merge "VP8TBufferClear: remove some misleading const's"
+02971e72 Merge "VP8EmitTokens: remove unnecessary param void cast"
+3b77e5a7 VP8TBufferClear: remove some misleading const's
+aa139c8f VP8EmitTokens: remove unnecessary param void cast
+c24d8f14 cosmetics: upsampling_sse2: add const to some casts
+1829c42c cosmetics: lossless_sse2: add const to some casts
+183168f3 cosmetics: enc_sse2: add const to some casts
+860badca cosmetics: dec_sse2: add const to some casts
+0254db97 cosmetics: argb_sse2: add const to some casts
+1aadf856 cosmetics: alpha_processing_sse2: add const to some casts
+1579de3c vwebp: clear canvas at the beginning of each loop
+4b9fa5d0 Merge "webp-container-spec: clarify background clear on loop"
+4c82284d Updated the near-lossless level mapping.
+56039479 webp-container-spec: clarify background clear on loop
+19f0ba0e Implement true-motion prediction in SSE2
+774d4cb7 make VP8PredLuma16[] array non-const
+d7eabb80 Merge "MIPS: dspr2: Added optimization for function CollectHistogram"
+fe42739c Use integers for kmin/kmax for simplicity.
+b9df35f7 AnimEncode API: kmax=0 should imply all keyframes.
+6ce296da MIPS: dspr2: Added optimization for function CollectHistogram
+2c906c40 vwebp: remove unnecessary static Help() prototype
+be0fd1d5 Merge "dec/vp8: clear 'dither_' on skipped blocks"
+e96170fe Merge "vwebp/animation: display last frame on end-of-loop"
+0f017b56 vwebp/animation: display last frame on end-of-loop
+c86b40cc enc/near_lossless.c: fix alignment
+66935fb9 dec/vp8: clear 'dither_' on skipped blocks
+b7de7946 Merge "lossless_neon: enable subtract green for aarch64"
+77724f70 SSE2 version of GradientUnfilter
+416e1cea lossless_neon: enable subtract green for aarch64
+72831f6b Speedup AnalyzeAndInit for low effort compression.
+a6597483 Speedup Analyze methods for lossless compression.
+98c81386 Enable Near-lossless feature.
+c6b24543 AnimEncoder API: Fix for kmax=1 and default kmin case.
+022d2f88 add SSE2 variants for alpha filtering functions
+2db15a95 Temporarily disable encoding of alpha plane with color cache.
+1d575ccd Merge "Lossless decoding: Remove an unnecessary if condition."
+cafa1d88 Merge "Simplify backward refs calculation for low-effort."
+7afdaf84 Alpha coding: reorganize the filter/unfiltering code
+4d6d7285 Simplify backward refs calculation for low-effort.
+ec0d1be5 Cleaup Near-lossless code.
+9814ddb6 Remove the post-transform near-lossless heuristic.
+4509e32e Lossless decoding: Remove an unnecessary if condition.
+f2ebc4a8 Merge "Regression fix for lossless decoding"
+783a8cda Regression fix for lossless decoding
+9a062b8e AnimEncoder: Bugfix for kmin = 1 and kmax = 2.
+0f027a72 simplify smart RGB->YUV conversion code
+0d5b334e BackwardReferencesHashChainFollowChosenPath: remove unused variable
+f480d1a7 Fix to near lossless artefacts on palettized images.
+d4615d08 Merge changes Ia1686828,I399fda40
+cb4a18a7 rename HashChainInit into HashChainReset
+f079e487 use uint16_t for chosen_path[]
+da091212 MIPS: dspr2: Added optimization for function FTransformWHT
+b8c20135 Merge "wicdec: (msvs) quiet some /analyze warnings"
+9b228b54 wicdec: (msvs) quiet some /analyze warnings
+daeb276a Merge "MIPS: dspr2: Added optimization for MultARGBRow function"
+cc087424 Merge "dsp/cpu: (msvs) add include for __cpuidex"
+4a82aab5 Merge changes I87544e92,I0bb6cda5
+7a191398 dwebp/WritePNG: mark png variables volatile
+775dfad2 dwebp: include setjmp.h w/WEBP_HAVE_PNG
+47d26be7 dwebp: correct sign in format strings
+f0e0677b VP8LEncodeStream: add an assert
+c5f7747f VP8LColorCacheCopy: promote an int before shifting
+0de5f33e dsp/cpu: (msvs) add include for __cpuidex
+7d850f7b MIPS: dspr2: Added optimization for MultARGBRow function
+54875293 MIPS: dspr2: added optimization for function QuantizeBlock
+4fbe9cf2 dsp/cpu: (msvs) avoid immintrin.h on _M_ARM
+3fd59039 simplify/reorganize arguments for CollectColorBlueTransforms
+b9e356b9 Disable costly TraceBackwards for method=0.
+a7e7caa4 MIPS: dspr2: added optimization for function TransformColorRed
+2cb39180 Merge "MIPS: dspr2: added optimization for function TransformColorBlue"
+279e6613 Merge "dsp/cpu: add include for _xgetbv() w/MSVS"
+b6c0428e dsp/cpu: add include for _xgetbv() w/MSVS
+d1c4ffae gif2webp: Move GIF decoding related code to a support library.
+07c39559 Merge "AnimEncoder API: Add info in README.mux"
+7b161973 MIPS: dspr2: added optimization for function TransformColorBlue
+d7c4b02a cpu: fix AVX2 detection for gcc/clang targets
+9d299469 AnimEncoder API: Add info in README.mux
+d581ba40 follow-up: clean up WebPRescalerXXX dsp function
+f8740f0d dsp: s/USE_INTRINSICS/WEBP_USE_INTRINSICS/
+ce73abe0 Merge "introduce a separate WebPRescalerDspInit to initialize pointers"
+ab66beca introduce a separate WebPRescalerDspInit to initialize pointers
+205c7f26 fix handling of zero-sized partition #0 corner case
+cbcdd5ff Merge "move rescaler functions to rescaler* files in src/dsp/"
+bf586e88 Merge changes I230b3532,Idf3057a7
+6dc79dc2 Merge "anim_encode: fix type conversion warnings"
+11fce25a Merge "dec_neon: remove returns from void functions"
+c4e63f99 Makefile.vc: add gif2webp target
+4f43d38c enable NEON for Windows ARM builds
+3f6615ac Makefile.vc: add rudimentary Windows ARM support
+e7c5954c dec_neon: remove returns from void functions
+f79c163b anim_encode: fix type conversion warnings
+0f54f1ec Remove gif2webp_util which is no longer needed.
+cbcbedd0 move rescaler functions to rescaler* files in src/dsp/
+ac79ed19 webpmux: remove experimental fragment handling
+e8694d4d mux: remove experimental FRGM parsing
+9e92b6ea AnimEncoder API: Optimize single-frame animated images
+abbae279 Merge "Move over gif2webp to the new AnimEncoder API."
+a28c4b36 MIPS: move WORK_AROUND_GCC define to appropriate place
+012d2c60 MIPS: dspr2: added optimization for functions SSEAxB
+67720c8b Move over gif2webp to the new AnimEncoder API.
+9241ecf4 MIPS: dspr2: added optimization for function Average
+9422211d Merge "Tune BackwardReferencesLz77 for low_effort (m=0)."
+df40057b Merge "Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode."
+ea08466d Tune BackwardReferencesLz77 for low_effort (m=0).
+b0b973c3 Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode.
+c6d32927 argb_sse2: cosmetics
+67f601cd make the 'last_cpuinfo_used' variable names unique
+b9489861 AnimEncoder API: Init method for default options.
+856f8ec1 Merge "AnimEncoder API: Remove AnimEncoderFrameOptions."
+c537514d Merge "AnimEncoder API: GenerateCandidates bugfix."
+dc0ce039 Merge "AnimEncoder API: Compute change rectangle for first frame too."
+f00b639b Merge "AnimEncoder API: In Assemble(), always set animation parameters."
+29ed796c Merge "AnimEncoder lib cleanup: prev to prev canvas not needed."
+9f0dd6e5 Merge "WebPAnimEncoder API: Header and implementation"
+5e56bbe0 AnimEncoder API: Remove AnimEncoderFrameOptions.
+b902c3ea AnimEncoder API: GenerateCandidates bugfix.
+ef3c39bb AnimEncoder API: Compute change rectangle for first frame too.
+eec423ab AnimEncoder API: In Assemble(), always set animation parameters.
+ae1c046e AnimEncoder lib cleanup: prev to prev canvas not needed.
+4b997ae4 WebPAnimEncoder API: Header and implementation
+72208bec move argb_*.o build target to encoder list
+95920538 Merge "multi-thread fix: lock each entry points with a static var"
+4c1b300a Merge "SSE2 implementation of VP8PackARGB"
+fbcc2004 Merge "add -Wformat-nonliteral and -Wformat-security"
+80d950d9 add -Wformat-nonliteral and -Wformat-security
+04c20e75 Merge "MIPS: dspr2: added optimization for function Intra4Preds"
+a437694a multi-thread fix: lock each entry points with a static var
+ca7f60db SSE2 implementation of VP8PackARGB
+72d573f6 simplify the PackARGB signature
+4e2589ff demux: restore strict fragment flag check
+4ba8e074 Merge "webp-container-spec: remove references to fragments"
+e752f0a6 Merge "demux: remove experimental FRGM parsing"
+f8abb112 Merge changes I109ec4d9,I73fe7743
+ae2188a4 MIPS: dspr2: added optimization for function Intra4Preds
+1f4b8642 move VP8EncDspARGBInit() call closer to where it's needed
+14108d78 dec_neon: add DC8uvNoTop / DC8uvNoLeft
+d8340da7 dec_neon: add DC8uv
+a66e66c7 webp-container-spec: remove references to fragments
+7ce8788b MIPS: dspr2: added optimization for function MakeARGB32
+012e623d demux: remove experimental FRGM parsing
+87c3d531 method=0: Don't evaluate any predictor
+6f4fcb98 Merge "MIPS: dspr2: added optimization for function ImportRow"
+24284459 replace unneeded calls to HistogramCopy() by swaps
+bdf7b40c MIPS: dspr2: added optimization for function ImportRow
+e66a9225 Merge "MIPS: dspr2: added optimization for function ExportRowC"
+c279fec1 MIPS: dspr2: added optimization for function ExportRowC
+31a9cf64 Speedup WebP lossless compression for low effort (m=0) mode with following: - Disable Cross-Color transform. - Evaluate predictors #11 (paeth), #12 and #13 only.
+9275d91c MIPS: dspr2: added optimization for function TrueMotion
+26106d66 Merge "enc_neon: fix building with non-Xcode clang (iOS)"
+1c4e3efe unroll the kBands[] indirection to remove a dereference in GetCoeffs()
+a3946b89 enc_neon: fix building with non-Xcode clang (iOS)
+8ed9c00d Merge "simplify the Histogram struct, to only store max_value and last_nz"
+bad77571 simplify the Histogram struct, to only store max_value and last_nz
+3cca0dc7 MIPS: dspr2: Added optimization for DCMode function
+37e395fd MIPS: fix functions to use generic BPS istead of hardcoded value
+9475bef4 PickBestUV: fix VP8Copy16x8 invocation
+441f273f Merge changes I55f8da52,Id73a1e96
+4a279a68 cosmetics: add some missing != NULL comparisons
+66ad3725 factorize BPS definition in dsp.h and add VP8Copy16x8
+432e5b55 make ALIGN_xxx naming consistent
+57606047 encoder: switch BPS to 32 instead of 16
+1b66bbe9 MIPS: dspr2: added optimization for function TransformColor_C
+c6d0f9e7 histogram: cosmetics
+f399d307 Merge changes I6eac17e5,I32d2b514
+9de9074c dec_neon: add TM8uv
+8e517eca bit_reader/kVP8NewRange: range_t -> uint8_t
+e1857139 dsp: initialize VP8PredChroma8 in VP8DspInit()
+e0c809ad Move Entropy methods to lossless.c
+a96ccf8f iosbuild: add x64_64 simulator support
+a0df5510 Remove handling for WEBP_HINT_GRAPH
+413dfc0c Move static method definition before its usage.
+0f235665 Update BackwardRefsWithLocalCache.
+d69e36ec Remove TODOs from lossless encoder code.
+fdaac8e0 Optmize VP8LGetBackwardReferences LZ77 references.
+2f0e2ba8 MIPS: dspr2: added optimization for function Select
+a3e79a46 Merge "WebPEncode: Support encoding same pic twice (even if modified)"
+e4f4dddb WebPEncode: Support encoding same pic twice (even if modified)
+cbc3fbb4 Merge "Updated VP8LGetBackwardReferences and color cache."
+95a9bd85 Updated VP8LGetBackwardReferences and color cache.
+54f2c14c MIPS: dspr2: added optimization for function FTransform
+aa42f423 MIPS: dspr2: Added optimization for function VP8LSubtractGreenFromBlueAndRed
+11a25f75 Merge "FlattenSimilarBlocks should only be tried when blending is possible."
+5cccdadf FlattenSimilarBlocks should only be tried when blending is possible.
+95ca44a7 MIPS: dspr2: added optimization for Disto4x4
+4171b672 backward_references.c: reindent after c8581b0
+c8581b06 Optimize BackwardReferences for RLE encoding.
+5798eee6 MIPS: dspr2: unfilters bugfix (Ie7b7387478a6b5c3f08691628ae00f059cf6d899)
+4167a3f5 Optimize backwardreferences
+d18554c3 Merge "webp/types.h: use inline for clang++/-std=c++11"
+7489b0e7 gif2webp: Add '-min-size' option to get best compression.
+77bdddf0 Speed up BackwardReferences
+6638710b webp/types.h: use inline for clang++/-std=c++11
+abf04205 Enable entropy based merge histo for (q<100)
+572022a3 filters_mips_dsp_r2.c: disable unfilters
+a28e21b1 MIPS: dspr2: Added optimization for function ClampedAddSubtractFull
+18d5a1ef MIPS: dspr2: added optimization for function ClampedAddSubtractHalf
+829a8c19 MIPS: dspr2: added optimization for ITransform
+c94ed49e gif2webp: Use the default hint instead of WEBP_HINT_GRAPH.
+653ace55 Increase the MAX_COLOR_CACHE_BITS from 9 to 10.
+919220c7 Change the logic adjusting the Histogram bits.
+53b096c0 Merge "Fix bug in VP8LCalculateEstimateForCacheSize."
+e912bd55 Fix bug in VP8LCalculateEstimateForCacheSize.
+541d7839 Merge "dec_neon: add RD4 intra predictor"
+f8cd0672 Merge "Makefile.vc: add a 'legacy' RTLIBCFG option"
+22881c99 dec_neon: add RD4 intra predictor
+613d281e update NEWS
+1304eb34 Merge "dec_neon: DC4: use pair-wise adds for top row"
+34c20c06 Makefile.vc: add a 'legacy' RTLIBCFG option
+7083006b Merge "dsp/dec_{neon,sse2}: VE4: normalize variable names"
+0db9031c dsp/dec_{neon,sse2}: VE4: normalize variable names
+b5bc1530 dec_neon: DC4: use pair-wise adds for top row
+5b90d8fe Unify the API between VP8BitWriter and VP8LBitWriter
+f7ada560 Merge changes I2e06907b,Ia9ed4ca6,I782282ff
+5beb6bf0 Merge "dec_neon: add VE4 intra predictor"
+eba6ce06 dec_neon: add DC4 intra predictor
+79abfbd9 dec_neon: add TM4 intra predictor
+fe395f0e dec_neon: add LD4 intra predictor
+32de385e dec_neon: add VE4 intra predictor
+72395ba9 Merge "Modify CostModel to allocate optimal memory."
+65e5eb8a gif2webp: Support GIF_DISPOSE_RESTORE_PREVIOUS
+e4c829ef gif2webp: Handle frames with odd offsets + disposal to background.
+c2b5a039 Modify CostModel to allocate optimal memory.
+b7a33d7e implement VE4/HE4/RD4/... in SSE2
+97c76f1f make VP8PredLuma4[] non-const and initialize array in VP8DspInit()
+0ea8c6c2 Merge "PrintReg: output to stderr"
+d7ff2f97 Merge "stopwatch.h: fix includes"
+f85ec712 PrintReg: output to stderr
+54edbf65 stopwatch.h: fix includes
+139142e4 Optimize BackwardReferenceHashChainFollowPath.
+5f36b68d enc/backward_references.c: fix indent
+e0e9960d Merge "sync version numbers to 0.4.2 release"
+64ac5144 sync version numbers to 0.4.2 release
+c24f8954 Simplify and speedup Backward refs computation.
+d1c359ef fix shared object build with -fvisibility=hidden
+a4c3a31b WEBP_TSAN_IGNORE_FUNCTION: fix gcc compat warning
+f358eeb8 add code for testing random incremental decoding in dwebp
+80247291 mark some init function as being safe for thread_sanitizer.
+79b5bdbf bit_reader.h: cosmetics: fix a typo
+6c673681 Improved near-lossless mode.
+0ce27e71 enc_mips32: workaround gcc-4.9 bug
+aca1b98f enc/vp8l.c: fix indent
+ca005027 Evaluate non-palette compression for palette image
+c8a87bb6 AssignSegments: quiet -Warray-bounds warning
+32f67e30 Merge "enc_neon: initialize vectors w/vdup_n_u32"
+fabc65da 1-3% faster encoding optimizing SSE_NxN functions
+7534d716 enc_neon: initialize vectors w/vdup_n_u32
+5f813912 Merge "Fix return code of EncodeImageInternal()"
+e321abe4 Fix return code of EncodeImageInternal()
+f82cb06a optimize palette ordering
+f545feee don't set the alpha value for histogram index image
+2d9b0a44 add WebPDispatchAlphaToGreen() to dsp
+1bd4c2ad Merge "Change Entropy based Histogram Combine heuristic."
+e295b8f1 Merge "iosbuild: cleanup"
+1be4e760 Merge "iosbuild: output autoconf req. on failure"
+d5e498d4 Change Entropy based Histogram Combine heuristic.
+47a2d8e1 fix MSVC float->int conversion warning
+041956f6 iosbuild: cleanup
+767eb402 iosbuild: output autoconf req. on failure
+35ad48b8 HistoHeapInit: correct positions allocation size
+45d9635f lossless: entropy clustering for high qualities.
+dc37df8c fix type warning for VS9_x64
+9f7d9e6d iosbuild: make iOS 6 the minimum requirement
+fdd6528b Remove unused VP8LDecoder member variable
+ea3bba5a Merge "rewrite Disto4x4 in enc_neon.c with intrinsic"
+f060dfc4 add lossless incremental decoding support
+ab70794d rewrite Disto4x4 in enc_neon.c with intrinsic
+d4471637 MIPS: dspr2: added optimization for function FilterLoop24
+2aef54d4 Merge "prepare VP8LDecodeImage for incremental decode"
+aed0f5a2 Merge "MIPS: dspr2: added optimization for function FilterLoop26"
+28630685 prepare VP8LDecodeImage for incremental decode
+248f3aed remove br->error_ field
+49e15044 MIPS: dspr2: added optimization for function FilterLoop26
+38128cb9 iobuild.sh: only install .h files in Headers
+c792d412 Premultiply with alpha during U/V downsampling
+0cc811d7 gif2webp: Background color correction
+d7167ff7 Amend the lossless spec according to issue #205, #206 and #224
+b901416b Record the lossless size stats.
+cddd3340 Add a WebPExtractAlpha function to dsp
+0716a98e fix indent after I0204949917836f74c0eb4ba5a7f4052a4797833b
+f9ced95a Optimize lossless decoding for trivial(ARB) codes.
+924fcfd9 Merge "webpmux: simplify InitializeConfig()"
+c0a462ca webpmux: simplify InitializeConfig()
+6986bb5e webpmux: fix indent
+f89e1690 webpmux: fix exit status on numeric value parse error
+2172cb62 Merge "webpmux: fix loop_count range check"
+e3b343ec Merge "examples: warn on invalid numeric parameters"
+0e23c487 webpmux: fix loop_count range check
+6208338a Merge "fix loop bug in DispatchAlpha()"
+d51f3e40 gif2webp: Handle frames with missing graphic control extension
+690b491a fix loop bug in DispatchAlpha()
+96d43a87 examples: warn on invalid numeric parameters
+3101f537 MIPS: dspr2: added optimization for TransformOne
+a6bb9b17 SSE2 for inverse Mult(ARGB)Row and ApplyAlphaMultiply
+d84a8ffd Remove default initialization of decoder status.
+be70b86c configure: simplify libpng-config invocation
+e0a99321 Rectify bug in lossless incremental decoding.
+e2502a97 MIPS: dspr2: added optimization for TransformAC3
+24e1072a MIPS: dspr2: added optimization for TransformDC
+c0e84df8 Merge "Slightly faster lossless decoding (1%)"
+8dd28bb5 Slightly faster lossless decoding (1%)
+f0103595 MIPS: dspr2: added optimization for ColorIndexInverseTransforms
+d3242aee make VP8LSetBitPos() set br->eos_ flag
+a9decb55 Lossless decoding: fix eos_ flag condition
+3fea6a28 fix erroneous dec->status_ setting
+80b8099f MIPS: dspr2: add some specific mips code to commit I2c3f2b12f8df15b785fad5a9c56316e954ae0c53
+e5640625 Merge "further refine the COPY_PATTERN optim for DecodeAlpha"
+854509fe enc/histogram.c: reindent after f4059d0
+34421964 Merge "~3-5% faster encoding optimizing PickBestIntra*()"
+865069c1 further refine the COPY_PATTERN optim for DecodeAlpha
+a5956228 added C-level optimization for DecodeAlphaData function
+187d379d add a fallback to ALPHA_NO_COMPRESSION
+a48a2d76 ~3-5% faster encoding optimizing PickBestIntra*()
+a6140194 ExUtilReadFromStdin: (windows) open stdin in bin mode
+e80eab1f webpmux: (windows) open stdout in binary mode
+e9bfb116 cwebp: (windows) open stdout in binary mode
+5927e15b example_util: add ExUtilSetBinaryMode
+30f3b75b webpmux man page: Clarify some title, descriptions and examples
+77d4c7e3 address cosmetic comments from patch #71380
+f75dfbf2 Speed up Huffman decoding for lossless
+637b3888 dsp/lossless: workaround gcc-4.9 bug on arm
+8323a903 dsp.h: collect gcc/clang version test macros
+e6c4b52f move static initialization of WebPYUV444Converters[] to the Init function.
+49911d4d Merge "fix indentation"
+f4059d0c Code cleanup for HistogramRemap.
+e632b092 fix indentation
+f5c04d64 Merge "add a DispatchAlpha() for SSE2 that handles 8 pixels at a time"
+fc98edd9 add a DispatchAlpha() for SSE2 that handles 8 pixels at a time
+73d361dd introduce VP8EncQuantize2Blocks to quantize two blocks at a time
+0b21c30b MIPS: dspr2: added optimization for EmitAlphaRGB
+953acd56 enc_neon: enable QuantizeBlock for aarch64
+f4ae1437 MIPS: mips32: code rebase
+56977154 MIPS: dspr2: added optimizations for VP8YuvTo*
+2523aa73 SmartRGBYUV: fix odd-width problem with pixel replication
+ee52dc4e fix some MSVC64 warning about float conversion
+3fca851a cpu: check for _MSC_VER before using msvc inline asm
+e2a83d71 faster RGB->YUV conversion function (~7% speedup)
+de2d03e1 Merge "Add smart RGB->YUV conversion option -pre 4"
+3fc4c539 Add smart RGB->YUV conversion option -pre 4
+b4dc4069 MIPS: dspr2: added optimization for (un)filters
+137e6090 Merge "configure: add work around for gcc-4.9 aarch64 bug"
+b61c9cec MIPS: dspr2: Optimization of some simple point-sampling functions
+e2b8cec0 configure: add work around for gcc-4.9 aarch64 bug
+98c54107 MIPS: mips32r2: added optimization for BSwap32
+dab702b3 Update PATENTS to reflect s/VP8/WebM/g
+b564f7c7 Merge "MIPS: detect mips32r6 and disable mips32r1 code"
+b7e5a5c4 MIPS: detect mips32r6 and disable mips32r1 code
+63c2fc02 Correctly use the AC_CANONICAL_* macros
+bb07022b Merge "cosmetics"
+e300c9d8 cosmetics
+0e519eea Merge "cosmetics: remove some extraneous 'extern's"
+3ef0f08a Merge "vp8enci.h: cosmetics: fix '*' placement"
+4c6dde37 bit_writer: cosmetics: rename kFlush() -> Flush()
+f7b4c48b cosmetics: remove some extraneous 'extern's
+b47fb00a vp8enci.h: cosmetics: fix '*' placement
+b5a36cc9 add -near_lossless [0..100] experimental option
+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)
+e09e9ff6 Record & log the image pre-processing time.
+f59c0b4b iosbuild.sh: specify optimization flags
+8d34ea3e update ChangeLog (tag: v0.4.1-rc1)
+dbc3da66 makefile.unix: add vwebp.1 to the dist target
+89a7c83c update ChangeLog
+ffe67ee9 Merge "update NEWS for the next release" into 0.4.1
+2def1fe6 gif2webp: dust up the help message
+fb668d78 remove -noalphadither option from README/vwebp.1
+e49f693b update NEWS for the next release
+cd013580 Merge "update AUTHORS" into 0.4.1
+268d01eb update AUTHORS
+85213b9b bump version to 0.4.1
+695f80ae Merge "restore mux API compatibility" into 0.4.1
+862d296c restore mux API compatibility
+8f6f8c5d remove the !WEBP_REFERENCE_IMPLEMENTATION tweak in Put8x8uv
+d713a696 Merge changes If4debc15,I437a5d5f into 0.4.1
+c2fc52e4 restore encode API compatibility
+793368e8 restore decode API compatibility
+b8984f31 gif2webp: fix compile with giflib 5.1.0
+222f9b1a gif2webp: simplify giflib version checking
+d2cc61b7 Extend MakeARGB32() to accept Alpha channel.
+4595b62b Merge "use explicit size of kErrorMessages[] arrays"
+157de015 Merge "Actuate memory stats for PRINT_MEMORY_INFO"
+fbda2f49 JPEG decoder: delay conversion to YUV to WebPEncode() call
+0b747b1b use explicit size of kErrorMessages[] arrays
+3398d81a Actuate memory stats for PRINT_MEMORY_INFO
+6f3202be Merge "move WebPPictureInit to picture.c"
+6c347bbb move WebPPictureInit to picture.c
+fb3acf19 fix configure message for multi-thread
+40b086f7 configure: check for _beginthreadex
+1549d620 reorder the YUVA->ARGB and ARGB->YUVA functions correctly
+c6461bfd Merge "extract colorspace code from picture.c into picture_csp.c"
+736f2a17 extract colorspace code from picture.c into picture_csp.c
+645daa03 Merge "configure: check for -Wformat-security"
+abafed86 configure: check for -Wformat-security
+fbadb480 split monolithic picture.c into picture_{tools,psnr,rescale}.c
+c76f07ec dec_neon/TransformAC3: initialize vector w/vcreate
+bb4fc051 gif2webp: Allow single-frame animations
+46fd44c1 thread: remove harmless race on status_ in End()
+5a1a7264 Merge "configure: check for __builtin_bswapXX()"
+6781423b configure: check for __builtin_bswapXX()
+6450c48d configure: fix iOS builds
+6422e683 VP8LFillBitWindow: enable fast path for 32-bit builds
+4f7f52b2 VP8LFillBitWindow: respect WEBP_FORCE_ALIGNED
+e458badc endian_inl.h: implement htoleXX with BSwapXX
+f2664d1a endian_inl.h: add BSwap16
+6fbf5345 Merge "configure: add --enable-aligned"
+dc0f479d configure: add --enable-aligned
+9cc69e2b Merge "configure: support WIC + OpenGL under mingw64"
+257adfb0 remove experimental YUV444 YUV422 and YUV400 code
+10f4257c configure: support WIC + OpenGL under mingw64
+380cca4f configure.ac: add AC_C_BIGENDIAN
+ee70a901 endian_inl.h: add BSwap64
+47779d46 endian_inl.h: add BSwap32
+d5104b1f utils: add endian_inl.h
+58ab6224 Merge "make alpha-detection loop in IsKeyFrame() in good x/y order"
+9d562902 make alpha-detection loop in IsKeyFrame() in good x/y order
+516971b1 lossless: Remove unaligned read warning
+b8b596f6 Merge "configure.ac: add an autoconf version prerequisite"
+34b02f8c configure.ac: add an autoconf version prerequisite
+e59f5360 neon: normalize vdup_n_* usage
+6ee7160d Merge changes I0da7b3d3,Idad2f278,I4accc305
+abc02f24 Merge "fix (uncompiled) typo"
+bc03670f neon: add INIT_VECTOR4
+6c1c632b neon: add INIT_VECTOR3
+dc7687e5 neon: add INIT_VECTOR2
+4536e7c4 add WebPMuxSetCanvasSize() to the mux API
+824eab10 fix (uncompiled) typo
+1f3e5f1e remove unused 'shift' argument and QFIX2 define
+8e867051 Merge "VP8LoadNewBytes: use __builtin_bswap32 if available"
+1b6a2635 Merge "Fix handling of weird GIF with canvas dimension 0x0"
+1da3d461 VP8LoadNewBytes: use __builtin_bswap32 if available
+1582e402 Fix handling of weird GIF with canvas dimension 0x0
+b8811dac Merge "rename interface -> winterface"
+db8b8b5f Fix logic in the GIF LOOP-detection parsing
+25aaddc8 rename interface -> winterface
+5584d9d2 make WebPSetWorkerInterface() check its arguments
+a9ef7ef9 Merge "cosmetics: update thread.h comments"
+c6af9991 Merge "dust up the help message"
+0a8b8863 dust up the help message
+a9cf3191 cosmetics: update thread.h comments
+27bfeee4 QuantizeBlock SSE2 Optimization:
+2bc0dc3e Merge "webpmux: warn when odd frame offsets are used"
+3114ebe4 Merge changes Id8edd3c1,Id418eb96,Ide05e3be
+c0726634 webpmux: warn when odd frame offsets are used
+c5c6b408 Merge "add alpha dithering for lossy"
+d5146784 examples/Android.mk: add cwebp
+ca0fa7c7 Android.mk: move dwebp to examples/Android.mk
+73d8fca0 Android.mk: add ENABLE_SHARED flag
+6e93317f muxread: fix out of bounds read
+8b0f6a48 Makefile.vc: fix CFLAGS assignment w/HAVE_AVX2=1
+bbe32df1 add alpha dithering for lossy
+79020767 Merge "make error-code reporting consistent upon malloc failure"
+77bf4410 make error-code reporting consistent upon malloc failure
+7a93c000 **/Makefile.am: remove unused AM_CPPFLAGS
+24e30805 Add an interface abstraction to the WebP worker thread implementation
+d6cd6358 Merge "fix orig_rect==NULL case"
+2bfd1ffa fix orig_rect==NULL case
+059e21c1 Merge "configure: move config.h to src/webp/config.h"
+f05fe006 properly report back encoding error code in WebPFrameCacheAddFrame()
+32b31379 configure: move config.h to src/webp/config.h
+90090d99 Merge changes I7c675e51,I84f7d785
+ae7661b3 makefiles: define WEBP_HAVE_AVX2 when appropriate
+69fce2ea remove the special casing for res->first in VP8SetResidualCoeffs
+6e61a3a9 configure: test for -msse2
+b9d2efc6 rename upsampling_mips32.c to yuv_mips32.c
+bdfeebaa dsp/yuv: move sse2 functions to yuv_sse2.c
+46b32e86 Merge "configure: set WEBP_HAVE_AVX2 when available"
+88305db4 Merge "VP8RandomBits2: prevent signed int overflow"
+73fee88c VP8RandomBits2: prevent signed int overflow
+db4860b3 enc_sse2: prevent signed int overflow
+3fdaf4d2 Merge "real fix for longjmp warning"
+385e3340 real fix for longjmp warning
+230a0555 configure: set WEBP_HAVE_AVX2 when available
+a2ac8a42 restore original value_/range_ field order
+5e2ee56f Merge "remove libwebpdspdecode dep on libwebpdsp_avx2"
+61362db5 remove libwebpdspdecode dep on libwebpdsp_avx2
+42c447ae Merge "lossy bit-reader clean-up:"
+479ffd8b Merge "remove unused #include's"
+9754d39a Merge "strong filtering speed-up (~2-3% x86, ~1-2% for NEON)"
+158aff9b remove unused #include's
+09545eea lossy bit-reader clean-up:
+ea8b0a17 strong filtering speed-up (~2-3% x86, ~1-2% for NEON)
+6679f899 Optimize VP8SetResidualCoeffs.
+ac591cf2 fix for gcc-4.9 warnings about longjmp + local variables
+4dfa86b2 dsp/cpu: NaCl has no support for xgetbv
+4c398699 Merge "cwebp: fallback to native webp decode in WIC builds"
+33aa497e Merge "cwebp: add some missing newlines in longhelp output"
+c9b340a2 fix missing WebPInitAlphaProcessing call for premultiplied colorspace output
+57897bae Merge "lossless_neon: use vcreate_*() where appropriate"
+6aa4777b Merge "(enc|dec)_neon: use vcreate_*() where appropriate"
+0d346e41 Always reinit VP8TransformWHT instead of hard-coding
+7d039fc3 cwebp: fallback to native webp decode in WIC builds
+d471f424 cwebp: add some missing newlines in longhelp output
+bf0e0030 lossless_neon: use vcreate_*() where appropriate
+9251c2f6 (enc|dec)_neon: use vcreate_*() where appropriate
+399b916d lossy decoding: correct alpha-rescaling for YUVA format
+78c12ed8 Merge "Makefile.vc: add rudimentary avx2 support"
+dc5b122f try to remove the spurious warning for static analysis
+ddfefd62 Makefile.vc: add rudimentary avx2 support
+a8911643 Merge "simplify VP8LInitBitReader()"
+fdbcd44d simplify VP8LInitBitReader()
+7c004287 makefile.unix: add rudimentary avx2 support
+515e35cf Merge "add stub dsp/enc_avx2.c"
+a05dc140 SSE2: yuv->rgb speed-up for point-sampling
+178e9a69 add stub dsp/enc_avx2.c
+1b99c09c Merge "configure: add a test for -mavx2"
+fe728071 configure: add a test for -mavx2
+e46a247c cpu: fix check for __cpuidex availability
+176fda26 fix the bit-writer for lossless in 32bit mode
+541784c7 dsp.h: add a check for AVX2 / define WEBP_USE_AVX2
+bdb151ee dsp/cpu: add AVX2 detection
+ab9f2f86 Merge "revamp the point-sampling functions by processing a full plane"
+a2f8b289 revamp the point-sampling functions by processing a full plane
+ef076026 use decoder's DSP functions for autofilter
+2b5cb326 Merge "dsp/cpu: add AVX detection"
+df08e67e dsp/cpu: add AVX detection
+e2f405c9 Merge "clean-up and slight speed-up in-loop filtering SSE2"
+f60957bf clean-up and slight speed-up in-loop filtering SSE2
+9fc3ae46 .gitattributes: treat .ppm as binary
+3da924b5 Merge "dsp/WEBP_USE_NEON: test for __aarch64__"
+c7164490 Android.mk: always include *_neon.c in the build
+a577b23a dsp/WEBP_USE_NEON: test for __aarch64__
+54bfffca move RemapBitReader() from idec.c to bit_reader code
+34168ecb Merge "remove all unused layer code"
+f1e77173 remove all unused layer code
+b0757db7 Code cleanup for VP8LGetHistoImageSymbols.
+5fe628d3 make the token page size be variable instead of fixed 8192
+f948d08c memory debug: allow setting pre-defined malloc failure points
+ca3d746e use block-based allocation for backward refs storage, and free-lists
+1ba61b09 enable NEON intrinsics in aarch64 builds
+b9d2bb67 dsp/neon.h: coalesce intrinsics-related defines
+b5c75258 iosbuild: add support for iOSv7/aarch64
+9383afd5 Reduce number of memory allocations while decoding lossless.
+888e63ed Merge "dsp/lossless: prevent signed int overflow in left shift ops"
+8137f3ed Merge "instrument memory allocation routines for debugging"
+2aa18736 instrument memory allocation routines for debugging
+d3bcf72b Don't allocate VP8LHashChain, but treat like automatic object
+bd6b8619 dsp/lossless: prevent signed int overflow in left shift ops
+b7f19b83 Merge "dec/vp8l: prevent signed int overflow in left shift ops"
+29059d51 Merge "remove some uint64_t casts and use."
+e69a1df4 dec/vp8l: prevent signed int overflow in left shift ops
+cf5eb8ad remove some uint64_t casts and use.
+38e2db3e MIPS: MIPS32r1: Added optimization for HistogramAdd.
+e0609ade dwebp: fix exit code on webp load failure
+bbd358a8 Merge "example_util.h: avoid forward declaring enums"
+8955da21 example_util.h: avoid forward declaring enums
+6d6865f0 Added SSE2 variants for Average2/3/4
+b3a616b3 make HistogramAdd() a pointer in dsp
+c8bbb636 dec_neon: relocate some inline-asm defines
+4e393bb9 dec_neon: enable intrinsics-only functions
+ba99a922 dec_neon: use positive tests for USE_INTRINSICS
+69058ff8 Merge "example_util: add ExUtilDecodeWebPIncremental"
+a7828e8b dec_neon: make WORK_AROUND_GCC conditional on version
+3f3d717a Merge "enc_neon: enable intrinsics-only functions"
+de3cb6c8 Merge "move LOCAL_GCC_VERSION def to dsp.h"
+1b2fe14d example_util: add ExUtilDecodeWebPIncremental
+ca49e7ad Merge "enc_neon: move Transpose4x4 to dsp/neon.h"
+ad900abd Merge "fix warning about size_t -> int conversion"
+4825b436 fix warning about size_t -> int conversion
+42b35e08 enc_neon: enable intrinsics-only functions
+f937e012 move LOCAL_GCC_VERSION def to dsp.h
+5e1a17ef enc_neon: move Transpose4x4 to dsp/neon.h
+c7b92a5a dec_neon: (WORK_AROUND_GCC) delete unused Load4x8
+8e5f90b0 Merge "make ExUtilLoadWebP() accept NULL bitstream param."
+05d4c1b7 Merge "cwebp: add webpdec"
+ddeb6ac8 cwebp: add webpdec
+35d7d095 Merge "Reduce memory footprint for encoding WebP lossless."
+0b896101 Reduce memory footprint for encoding WebP lossless.
+f0b65c9a make ExUtilLoadWebP() accept NULL bitstream param.
+9c0a60cc Merge "dwebp: move webp decoding to example_util"
+1d62acf6 MIPS: MIPS32r1: Added optimization for HuffmanCost functions.
+4a0e7390 dwebp: move webp decoding to example_util
+c0220460 Merge "Bugfix: Incremental decode of lossy-alpha"
+8c7cd722 Bugfix: Incremental decode of lossy-alpha
+7955152d MIPS: fix error with number of registers.
+b1dabe37 Merge "Move the HuffmanCost() function to dsp lib"
+75b12006 Move the HuffmanCost() function to dsp lib
+2772b8bd MIPS: fix assembler error revealed by clang's debug build
+6653b601 enc_mips32: fix unused symbol warning in debug
+8dec1209 enc_mips32: disable ITransform(One) in debug builds
+98519dd5 enc_neon: convert Disto4x4 to intrinsics
+fe9317c9 cosmetics:
+953b0746 enc_neon: cosmetics
+a9fc697c Merge "WIP: extract the float-calculation of HuffmanCost from loop"
+3f84b521 Merge "replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8)"
+4ae0533f MIPS: MIPS32r1: Added optimizations for ExtraCost functions.
+b30a04cf WIP: extract the float-calculation of HuffmanCost from loop
+a8fe8ce2 Merge "NEON intrinsics version of CollectHistogram"
+95203d2d NEON intrinsics version of CollectHistogram
+7ca2e74b replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8)
+41c6efbd fix lossless_neon.c
+8ff96a02 NEON intrinsics version of FTransform
+0214f4a9 Merge "MIPS: MIPS32r1: Added optimizations for FastLog2"
+baabf1ea MIPS: MIPS32r1: Added optimizations for FastLog2
+3d49871d NEON functions for lossless coding
+3fe02915 MIPS: MIPS32r1: Added optimizations for SSE functions.
+c503b485 Merge "fix the gcc-4.6.0 bug by implementing alternative method"
+abe6f487 fix the gcc-4.6.0 bug by implementing alternative method
+5598bdec enc_mips32.c: fix file mode
+2b1b4d5a MIPS: MIPS32r1: Add optimization for GetResidualCost
+f0a1f3cd Merge "MIPS: MIPS32r1: Added optimization for FTransform"
+7231f610 MIPS: MIPS32r1: Added optimization for FTransform
+869eaf6c ~30% encoding speedup: use NEON for QuantizeBlock()
+f758af6b enc_neon: convert FTransformWHT to intrinsics
+7dad095b MIPS: MIPS32r1: Added optimization for Disto4x4 (TTransform)
+2298d5f3 MIPS: MIPS32r1: Added optimization for QuantizeBlock
+e88150c9 Merge "MIPS: MIPS32r1: Add optimization for ITransform"
+de693f25 lossless_neon: disable VP8LConvert* functions
+4143332b NEON intrinsics for encoding
+0ca2914b MIPS: MIPS32r1: Add optimization for ITransform
+71bca5ec dec_neon: use vst_lane instead of vget_lane
+bf061052 Intrinsics NEON version of TransformOne
+19c6f1ba Merge "dec_neon: use vld?_lane instead of vset?_lane"
+7a94c0cf upsampling_neon: drop NEON suffix from local functions
+d14669c8 upsampling_sse2: drop SSE2 suffix from local functions
+2ca42a4f enc_sse2: drop SSE2 suffix from local functions
+d038e619 dec_sse2: drop SSE2 suffix from local functions
+fa52d752 dec_neon: use vld?_lane instead of vset?_lane
+c520e77d cosmetic: fix long line
+4b0f2dae Merge "add intrinsics NEON code for chroma strong-filtering"
+e351ec07 add intrinsics NEON code for chroma strong-filtering
+aaf734b8 Merge "Add SSE2 version of forward cross-color transform"
+c90a902e Add SSE2 version of forward cross-color transform
+bc374ff3 Use histogram_bits to initalize transform_bits.
+2132992d Merge "Add strong filtering intrinsics (inner and outer edges)"
+5fbff3a6 Add strong filtering intrinsics (inner and outer edges)
+d4813f0c Add SSE2 function for Inverse Cross-color Transform
+26029568 dec_neon: add strong loopfilter intrinsics
+cca7d7ef Merge "add intrinsics version of SimpleHFilter16NEON()"
+1a05dfa7 windows: fix dll builds
+d6c50d8a Merge "add some colorspace conversion functions in NEON"
+4fd7c82e SSE2 variants of Subtract-Green: Rectify loop condition
+97e5fac3 add some colorspace conversion functions in NEON
+b9a7a45f add intrinsics version of SimpleHFilter16NEON()
+daccbf40 add light filtering NEON intrinsics
+af444608 fix typo in STORE_WHT
+6af6b8e1 Tune HistogramCombineBin for large images.
+af93bdd6 use WebPSafe[CM]alloc/WebPSafeFree instead of [cm]alloc/free
+51f406a5 lossless_sse2: relocate VP8LDspInitSSE2 proto
+0f4f721b separate SSE2 lossless functions into its own file
+514fc251 VP8LConvertFromBGRA: use conversion function pointers
+6d2f3527 dsp/dec: TransformDCUV: use VP8TransformDC
+defc8e1b Merge "fix out-of-bound read during alpha-plane decoding"
+fbed3643 Merge "dsp: reuse wht transform from dec in encoder"
+d8467084 Merge "Add SSE2 version of ARGB -> BGR/RGB/... conversion functions"
+207d03b4 fix out-of-bound read during alpha-plane decoding
+d1b33ad5 2-5% faster trellis with clang/MacOS (and ~2-3% on ARM)
+369c26dd Add SSE2 version of ARGB -> BGR/RGB/... conversion functions
+df230f27 dsp: reuse wht transform from dec in encoder
+80e218d4 Android.mk: fix build with APP_ABI=armeabi-v7a-hard
+59daf083 Merge "cosmetics:"
+53622008 cosmetics:
+3e7f34a3 AssignSegments: quiet array-bounds warning
+3c2ebf58 Merge "UpdateHistogramCost: avoid implicit double->float"
+cf821c82 UpdateHistogramCost: avoid implicit double->float
+312e638f Extend the search space for GetBestGreenRedToBlue
+1c58526f Fix few nits
+fef22704 Optimize and re-structure VP8LGetHistoImageSymbols
+068b14ac Optimize lossless decoding.
+5f0cfa80 Do a binary search to get the optimum cache bits.
+24ca3678 Merge "allow 'cwebp -o -' to emit output to stdout"
+e12f874e allow 'cwebp -o -' to emit output to stdout
+2bcad89b allow some more stdin/stout I/O
+84ed4b3a fix cwebp.1 typos after patch #69199
+65b99f1c add a -z option to cwebp, and WebPConfigLosslessPreset() function
+30176619 4-5% faster trellis by removing some unneeded calculations.
+687a58ec histogram.c: reindent after b33e8a0
+06d456f6 Merge "~3-4% faster lossless encoding"
+c60de260 ~3-4% faster lossless encoding
+42eb06fc Merge "few cosmetics after patch #69079"
+82af8264 few cosmetics after patch #69079
+b33e8a05 Refactor code for HistogramCombine.
+ca1bfff5 Merge "5-10% encoding speedup with faster trellis (-m 6)"
+5aeeb087 5-10% encoding speedup with faster trellis (-m 6)
+82ae1bf2 cosmetics: normalize VP8GetCPUInfo checks
+e3dd9243 Merge "Refactor GetBestPredictorForTile for future tuning."
+206cc1be Refactor GetBestPredictorForTile for future tuning.
+3cb84062 Merge "speed-up trellis quant (~5-10% overall speed-up)"
+b66f2227 Merge "lossy encoding: ~3% speed-up"
+4287d0d4 speed-up trellis quant (~5-10% overall speed-up)
+390c8b31 lossy encoding: ~3% speed-up
+9a463c4a Merge "dec_neon: convert TransformWHT to intrinsics"
+e8605e96 Merge "dec_neon: add ConvertU8ToS16"
+4aa3e412 MIPS: MIPS32r1: rescaler bugfix
+c16cd99a Speed up lossless encoder.
+9d6b5ff1 dec_neon: convert TransformWHT to intrinsics
+2ff0aae2 dec_neon: add ConvertU8ToS16
+77a8f919 fix compilation with USE_YUVj flag
+4acbec1b Merge changes I3b240ffb,Ia9370283,Ia2d28728
+2719bb7e dec_neon: TransformAC3: work on packed vectors
+b7b60ca1 dec_neon: add SaturateAndStore4x4
+b7685d73 Rescale: let ImportRow / ExportRow be pointer-to-function
+e02f16ef dec_neon.c: convert TransformDC to intrinsics
+9cba963f add missing file
+8992ddb7 use static clipping tables
+0235d5e4 1-2% faster quantization in SSE2
+b2fbc36c fix VC12-x64 warning
+6e37cb94 Merge "cosmetics: backward_references.c: reindent after a7d2ee3"
+a42ea974 cosmetics: backward_references.c: reindent after a7d2ee3
+6c327442 Merge "fix missing __BIG_ENDIAN__ definition on some platform"
+a8b6aad1 fix missing __BIG_ENDIAN__ definition on some platform
+fde2904b Increase initial buffer size for VP8L Bit Writer.
+a7d2ee39 Optimize cache estimate logic.
+7fb6095b Merge "dec_neon.c: add TransformAC3"
+bf182e83 VP8LBitWriter: use a bit-accumulator
+3f40b4a5 Merge "MIPS: MIPS32r1: clang macro warning resolved"
+1684f4ee WebP Decoder: Mark some truncated bitstreams as invalid
+acbedac4 MIPS: MIPS32r1: clang macro warning resolved
+228e4877 dec_neon.c: add TransformAC3
+393f89b7 Android.mk: avoid gcc-specific flags with clang
+32aeaf11 revamp VP8LColorSpaceTransform() a bit
+0c7cc4ca Merge "Don't dereference NULL, ensure HashChain fully initialized"
+391316fe Don't dereference NULL, ensure HashChain fully initialized
+926ff402 WEBP_SWAP_16BIT_CSP: remove code dup
+1d1cd3bb Fix decode bug for rgbA_4444/RGBA_4444 color-modes.
+939e70e7 update AUTHORS file
+8934a622 cosmetics: *_mips32.c
+dd438c9a MIPS: MIPS32r1: Optimization of some simple point-sampling functions. PATCH [6/6]
+53520911 Added support for calling sampling functions via pointers.
+d16c6974 MIPS: MIPS32r1: Optimization of filter functions. PATCH [5/6]
+04336fc7 MIPS: MIPS32r1: Optimization of function TransformOne. PATCH [4/6]
+92d8fc7d MIPS: MIPS32r1: Optimization of function WebPRescalerImportRow. PATCH [3/6]
+bbc23ff3 parse one row of intra modes altogether
+a2f608f9 Merge "MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6]"
+88230854 MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6]
+c5a5b028 decode mt+incremental: fix segfault in debug builds
+9882b2f9 always use fast-analysis for all methods.
+000adac0 Merge "autoconf: update ax_pthread.m4"
+2d2fc37d update .gitignore
+5bf4255a Merge "Make it possible to avoid automagic dependencies"
+c1cb1933 disable NEON for arm64 platform
+73a304e9 Make it possible to avoid automagic dependencies
+4d493f8d MIPS: MIPS32r1: Decoder bit reader function optimized. PATCH [1/6]
+c741183c make WebPCleanupTransparentArea work with argb picture
+5da18552 add a decoding option to flip image vertically
+00c3c4e1 Merge "add man/vwebp.1"
+2c6bb428 add man/vwebp.1
+ea59a8e9 Merge "Merge tag 'v0.4.0'"
+7574bed4 fix comments related to array sizes
+0b5a90fd dwebp.1: fix option formatting
+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)
+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
+b5b2e3c7 cwebp: fix metadata output w/lossy+alpha
+ad26df1a makefile.unix: clean up libgif2webp_util.a
+c3b45570 update Changelog
+ca841121 Merge "bump version to 0.4.0" into 0.4.0
+8c524db8 bump version to 0.4.0
+eec2398c update AUTHORS & .mailmap
+b9bbf6a1 update NEWS for 0.4.0
+c72e0811 Merge "dec/webp.c: don't wait for data before reporting w/h"
+5ad65314 dec/frame.c: fix formatting
+f7fc4bc8 dec/webp.c: don't wait for data before reporting w/h
+66a32af5 Merge "NEON speed up"
+26d842eb NEON speed up
+f307f98b Merge "webpmux: let -- stop parameter parsing"
+fe051da7 Merge "README: add a section on gif2webp"
+6fd2bd62 Merge "manpage pedantry"
+4af19007 README: add a section on gif2webp
+6f36ade9 manpage pedantry
+f9016cb9 README: update dwebp options
+b4fa0a47 webpmux: let -- stop parameter parsing
+a9a20acf gif2webp: Add a multi-threaded encode option
+495bef41 fix bug in TrellisQuantize
+605a7127 simplify __cplusplus ifdef
+33109f99 Merge "drop: ifdef __cplusplus checks from C files"
+7f9de0b9 Merge changes I994a5587,I8467bb71,I13b50688,I1e2c9c7b
+5459030b gif2webp: let -- stop parameter parsing
+a4b0aa06 vwebp: let -- stop parameter parsing
+98af68fe cwebp: let -- stop parameter parsing
+a33831e2 dwebp: let -- stop parameter parsing
+36301249 add some checks on error paths
+ce4c7139 Merge "autoconf: add --disable-wic"
+5227d991 drop: ifdef __cplusplus checks from C files
+f6453559 dwebp.1: fix typo
+f91034f2 Merge "cwebp: print metadata stats when no output file is given"
+d4934553 gif2webp: Backward compatibility for giflib version <= 4.1.3
+4c617d32 gif2webp: Disable output of ICC profile by default
+73b731fb introduce a special quantization function for WHT
+41c0cc4b Make Forward WHT transform use 32bit fixed-point calculation
+a3359f5d Only compute quantization params once
+70490437 cwebp: print metadata stats when no output file is given
+d513bb62 * fix off-by-one zthresh calculation * remove the sharpening for non luma-AC coeffs * adjust the bias a little bit to compensate for this
+ad9dec0c Merge "cosmetics: dwebp: fix local function name format"
+f737f037 Merge "dwebp: remove a dead store"
+3c3a70da Merge "makefile.unix: install binaries in $(DESTDIR)/bin/"
+150b655f Merge "Android.mk: add some release compile flags"
+dbebd33b cosmetics: dwebp: fix local function name format
+27749951 dwebp: remove a dead store
+a01e04fe autoconf: add --disable-wic
+5009b227 makefile.unix: install binaries in $(DESTDIR)/bin/
+bab30fca Merge "fix -print_psnr / ssim options"
+ebef7fb3 fix -print_psnr / ssim options
+cb637855 Merge "fix bug due to overzealous check in WebPPictureYUVAToARGB()"
+8189885b Merge "EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE"
+4ad7d335 Android.mk: add some release compile flags
+c12e2369 cosmetics: fix a few typos
+6f104034 fix bug due to overzealous check in WebPPictureYUVAToARGB()
+3f6c35c6 EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE
+cc55790e Merge changes I8bb7a4dc,I2c180051,I021a014f,I8a224a62
+c536afb5 Merge "cosmetics: fix some typos"
+cbdd3e6e add a -dither dithering option to the decoder
+e8124012 Updated iosbuild.sh for XCode 5.x
+4931c329 cosmetics: fix some typos
+05aacf77 mux: add some missing casts
+617d9348 enc/vp8l: add a missing cast
+46db2865 idec: add some missing casts
+b524e336 ErrorStatusLossless: correct return type
+cb261f79 fix a descaling bug for vertical/horizontal U/V interpolation
+bcb3955c Merge changes I48968468,I181bc736
+73f52133 gif2webp: Add a mixed compression mode
+6198715e demux: split chunk parsing from ParseVP8X
+d2e3f4e6 demux: add a tail pointer for chunks
+87cffcc3 demux: cosmetics: s/has_frames/is_animation/
+e18e6677 demux: strictly enforce the animation flag
+c4f39f4a demux: cosmetics: remove a useless break
+61cb884d demux: (non-exp) fail if the fragmented flag is set
+ff379db3 few % speedup of lossless encoding
+df3649a2 remove all disabled code related to P-frames
+6d0cb3de Merge "gif2webp: kmin = 0 should suppress key-frame addition."
+36555983 gif2webp: kmin = 0 should suppress key-frame addition.
+7708e609 Merge "detect flatness in blocks and favor DC prediction"
+06b1503e Merge "add comment about the kLevelsFromDelta[][] LUT generation"
+5935259c add comment about the kLevelsFromDelta[][] LUT generation
+e3312ea6 detect flatness in blocks and favor DC prediction
+ebc9b1ee Merge "VPLBitReader bugfix: Catch error if bit_pos > LBITS too."
+96ad0e0a VPLBitReader bugfix: Catch error if bit_pos > LBITS too.
+a014e9c9 tune quantization biases toward higher precision
+1e898619 add helpful PrintBlockInfo() function
+596a6d73 make use of 'extern' consistent in function declarations
+c8d48c6e Merge "extract random utils to their own file util/random.[ch]"
+98aa33cf extract random utils to their own file util/random.[ch]
+432a723e Merge "swig: add basic go bindings"
+fab618b5 Merge "rename libwebp.i -> libwebp.swig"
+e4e7fcd6 swig: add basic go bindings
+d3408720 Merge "fast auto-determined filtering strength"
+f8bfd5cd fast auto-determined filtering strength
+ac0bf951 small clean-up in ExpandMatrix()
+1939607e rename libwebp.i -> libwebp.swig
+43148b6c filtering: precompute ilimit and hev_threshold
+18f992ec simplify f_inner calculation a little
+241d11f1 add missing const
+86c0031e add a 'format' field to WebPBitstreamFeatures
+dde91fde Demux: Correct the extended format validation
+5d6c5bd2 add entry for '-resize' option in cwebp's man
+7c098d18 Use some gamma-curve range compression when computing U/V average
+0b2b0504 Use deterministic random-dithering during RGB->YUV conversion
+8a2fa099 Add a second multi-thread method
+7d6f2da0 Merge "up to 20% faster multi-threaded decoding"
+266f63ea Merge "libwebp.jar: build w/Java 1.6 for Android compat"
+0532149c up to 20% faster multi-threaded decoding
+38efdc2e Simplify the gif2webp tool: move the optimization details to util
+de899516 libwebp.jar: build w/Java 1.6 for Android compat
+cb221552 Decode a full row of bitstream before reconstructing
+dca8a4d3 Merge "NEON/simple loopfilter: avoid q4-q7 registers"
+9e84d901 Merge "NEON/TransformWHT: avoid q4-q7 registers"
+fc10249b NEON/simple loopfilter: avoid q4-q7 registers
+2f09d63e NEON/TransformWHT: avoid q4-q7 registers
+77585a2b Merge "use a macrofunc for setting NzCoeffs bits"
+d155507c Merge "use HINT_GRAPH as image_hint for gif source"
+9c561646 Merge "only print GIF_DISPOSE_WARNING once"
+05879865 use HINT_GRAPH as image_hint for gif source
+0b28d7ab use a macrofunc for setting NzCoeffs bits
+f9bbc2a0 Special-case sparse transform
+00125196 gif2webp: detect and flatten uniformly similar blocks
+0deaf0fa only print GIF_DISPOSE_WARNING once
+6a8c0eb7 Merge "small optimization in segment-smoothing loop"
+f7146bc1 small optimization in segment-smoothing loop
+5a7533ce small gif2webp fix
+4df0c89e Merge changes Ic697660c,I27285521
+5b2e6bd3 Android.mk: add a dwebp target
+f910a84e Android.mk: update build flags
+63f9aba4 special-case WHT transform when there's only DC
+80911aef Merge "7-8% faster decoding by rewriting GetCoeffs()"
+606c4304 gif2webp: Improved compression for lossy animated WebP
+fb887f7f gif2webp: Different kmin/kmax defaults for lossy and lossless
+2a981366 7-8% faster decoding by rewriting GetCoeffs()
+92d47e4c improve VP8L signature detection by checking the version bits too
+5cd43e43 Add -incremental option to dwebp
+54b8e3f6 webpmux: DisplayInfo(): remove unnecessary error checks.
+40ae3520 fix memleak in WebPIDelete()
+d9662658 mux.h doc: WebPMuxGetFrame() can return WEBP_MUX_MEMORY_ERROR too.
+0e6747f8 webpmux -info: display dimensions and has_alpha per frame
+d78a82c4 Sanity check for underflow
+8498f4bf Merge "remove -Wshadow warnings"
+e89c6fc8 Avoid a potential memleak
+3ebe1757 Merge "break down the proba 4D-array into some handy structs"
+6a44550a break down the proba 4D-array into some handy structs
+2f5e8934 remove -Wshadow warnings
+bf3a29b3 Merge "add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags"
+2b0a7593 Merge "fix some warnings from static analysis"
+22dd07ce mux.h: Some doc corrections
+79ff0346 add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags
+d51f45f0 fix some warnings from static analysis
+d134307b fix conversion warning on MSVC
+d538cea8 gif2webp: Support a 'min' and 'max' key frame interval
+80b54e1c allow search with token buffer loop and fix PARTITION0 problem
+b7d4e042 add VP8EstimateTokenSize()
+10fddf53 enc/quant.c: silence a warning
+399cd456 Merge "fix compile error on ARM/gcc"
+9f24519e encoder: misc rate-related fixes
+c663bb21 Merge "simplify VP8IteratorSaveBoundary() arg passing"
+fa46b312 Demux.h: Correct a method name reference
+f8398c9d fix compile error on ARM/gcc
+f691f0e4 simplify VP8IteratorSaveBoundary() arg passing
+42542be8 up to 6% faster encoding with clang compiler
+93402f02 multi-threaded segment analysis
+7e2d6595 Merge "remove the PACK() bit-packing tricks"
+c13fecf9 remove the PACK() bit-packing tricks
+2fd091c9 Merge "use NULL for lf_stats_ testing, not bool"
+b11c9d62 dwebp: use default dct_method
+4bb8465f Merge "(de)mux.h: wrap pseudo-code in /* */"
+cfb56b17 make -pass option work with token buffers
+5416aab4 (de)mux.h: wrap pseudo-code in /* */
+35dba337 use NULL for lf_stats_ testing, not bool
+733a7faa enc->Iterator memory cleanup
+e81fac86 Add support for "no blend" in webpmux binary
+3b80bc48 gif2webp: Separate out each step into a method
+bef7e9cc Add doc precision about demux object keeping pointers to data.
+61405a14 dwebp: enable stdout output with WIC
+6eabb886 Merge "Animated WebP: add "do no blend" option to spec"
+be20decb fix compilation for BITS 24
+e58cc137 Merge "dwebp: s/unsigned char/uint8_t/"
+72501d43 dwebp: s/unsigned char/uint8_t/
+2c9633e8 Merge "gif2webp: Insert independent frames at regular intervals."
+f0d6a14b gif2webp: Insert independent frames at regular intervals.
+b25a6fbf yuv.h: fix indent
+ede3602e Merge "cosmetics: fix indent"
+3a65122a dwebp: fix stdout related output
+388a7249 cosmetics: fix indent
+4c7322c8 Merge "dsp: msvc compatibility"
+d50c7e32 Merge "5-7% faster SSE2 versions of YUV->RGB conversion functions"
+b8ab7847 Merge "simplify upsampler calls: only allow 'bottom' to be NULL"
+df6cebfa 5-7% faster SSE2 versions of YUV->RGB conversion functions
+ad6ac32d simplify upsampler calls: only allow 'bottom' to be NULL
+a5e8afaf output to stdout if file name is "-"
+f358450f dsp: msvc compatibility
+43a7c8eb Merge "cosmetics"
+4c5f19c1 Merge "bit_reader.h: cosmetics"
+f72fab70 cosmetics
+14dd5e78 fix const-ness
+b20aec49 Merge "Support for 'do not blend' option in vwebp"
+dcf65222 Support for 'do not blend' option in vwebp
+d5bad033 Animated WebP: add "do no blend" option to spec
+a2f5f73d Merge "Support for "Do not blend" in mux and demux libraries"
+e081f2f3 Pack code & extra_bits to Struct (VP8LPrefixCode).
+6284854b Support for "Do not blend" in mux and demux libraries
+f486aaa9 Merge "slightly faster ParseIntraMode"
+d1718632 slightly faster ParseIntraMode
+3ceca8ad bit_reader.h: cosmetics
+69257f70 Create LUT for PrefixEncode.
+988b7084 add WebPWorkerExecute() for convenient bypass
+06e24987 Merge "VP8EncIterator clean-up"
+de4d4ad5 VP8EncIterator clean-up
+7bbe9529 Merge "cosmetics: thread.c: drop a redundant comment"
+da411485 cosmetics: thread.c: drop a redundant comment
+feb4b6e6 thread.h: #ifdef when checking WEBP_USE_THREAD
+8924a3a7 thread.c: drop WebPWorker prefix from static funcs
+1aed8f2a Merge "fix indent"
+4038ed15 fix indent
+1693fd9b Demux: A new state WEBP_DEMUX_PARSE_ERROR
+8dcae8b3 fix rescaling-with-alpha inaccuracy
+11249abf Merge changes I9b4dc36c,I4e0eef4d
+52508a1f Mux: support parsing unknown chunks within a frame/fragment.
+05db0572 WebPMuxSetChunk: remove unused variable
+8ba1bf61 Stricter check for presence of alpha when writing lossless images
+a03c3516 Demux: WebPIterator now also denotes if the frame has alpha.
+6df743a3 Decoder: handle fragments case correctly too.
+faa4b07e Support for unknown chunks in mux library
+7d60bbc6 Speed up HashChainFindCopy function.
+66740140 Speedup Alpha plane encoding.
+b7346a1e 0.1 % speedup to decoding
+c606182e webp-container-spec: Tighten language added by last
+a34a5029 pngdec: output error messages from libpng
+e84c625d Merge "Detect canvas and image size mismatch in decoder."
+f626fe2e Detect canvas and image size mismatch in decoder.
+f5fbdee0 demux: stricter image bounds check
+30c8158a add extra assert in Huffman decode code
+8967b9f3 SSE2 for lossless decoding (critical) functions.
+699d80ea Jump-lookup for Huffman coding
+c34307ab fix some VS9 warnings about type conversion
+eeada35c pngdec: add missing include
+54b65108 gif2webp: If aligning to even offsets, extra pixels should be transparent
+0bcf5ce3 Merge "remove a malloc() in case we're using only FILTER_NONE for alpha"
+2c07143b remove a malloc() in case we're using only FILTER_NONE for alpha
+a4d5f59d Faster lossless decoding
+fd53bb75 Merge "alternate LUT-base reverse-bits code"
+d1c166ef Merge "Container spec: a clarification on background color."
+fdb91779 Rename a method
+5e967532 Container spec: a clarification on background color.
+30e77d0f Merge branch '0.3.0'
+1b631e29 alternate LUT-base reverse-bits code
+24cc307a ~20% faster lossless decoding
+313d853f Speedup for decoding lossless WebP photographs:
+24ee098a change the bytes_per_pixels_ field into more evocative use_8b_decode
+2a04b034 update ChangeLog (tag: v0.3.1-rc2, tag: v0.3.1)
+7288950b Regression fix for alpha channels using color cache:
+2e377b53 wicdec: silence a format warning
+ad9e42a6 muxedit: silence some uninitialized warnings
+3307c163 Don't set alpha-channel to 0xff for alpha->green uplift
+5130770c Merge "wicdec: silence a format warning"
+a37eff47 Regression fix for alpha channels using color cache:
+241cf99b Merge "muxedit: silence some uninitialized warnings"
+c8f9c84d Regression fix for alpha unfiltering:
+14cd5c6c muxedit: silence some uninitialized warnings
+a368db81 dec/vp8l: quiet vs9 x64 type conversion warning
+ffae9f31 wicdec: silence a format warning
+8cf0701e Alpha encoding: never filter in case of NO_COMPRESSION
+825e73b1 update ChangeLog (tag: v0.3.1-rc1)
+abf6f691 update NEWS
+5a92c1a5 bump version to 0.3.1
+86daf77c store top Y/U/V samples in packed fashion
+67bc353e Revert "add WebPBlendAlpha() function to blend colors against background"
+068db59e Intertwined decoding of alpha and RGB
+38cc0114 Simplify forward-WHT + SSE2 version
+3fa595a5 Support decoding upto given row in DECODE_DATA_FUNC
+520f005f DequantizeLevels(): Add 'row' and 'num_rows' args
+47374b82 Alpha unfilter for given set of rows
+f32097e0 probe input file and quick-check for WebP format.
+a2aed1d0 configure: improve gl/glut library test
+c7e89cbb update copyright text
+a00380d2 configure: remove use of AS_VAR_APPEND
+a94a88dd fix EXIF parsing in PNG
+a71e5d84 add doc precision for WebPPictureCopy() and WebPPictureView()
+8287012e remove datatype qualifier for vmnv
+e1908430 fix a memory leak in gif2webp
+0b18b9ee fix two minor memory leaks in webpmux
+db5095d5 remove some cruft from swig/libwebp.jar
+850e956f README: update swig notes
+bddd9b0a swig/python: add minimal documentation
+d573a8d5 swig: add python encode support
+6b931875 swig/java: reduce wrapper function code duplication
+6fe536f4 swig/java: rework uint8_t typemap
+a2ea4643 Fix the bug in ApplyPalette.
+7bb28d2a webp/lossless: fix big endian BGRA output
+f036d4bf Speed up ApplyPalette for ARGB pixels.
+8112c8cf remove some warnings:
+cc128e0b Further reduce memory to decode lossy+alpha images
+07db70d2 fix for big-endian
+eda8a7de gif2webp: Fix signed/unsigned comparison mismatch
+31f346fe Makefile.vc: fix libwebpdemux dll variable typo
+6c76d28e swig: add python (decode) support
+b4f5bb6c swig: cosmetics
+498d4dd6 WebP-Lossless encoding improvements.
+26e72442 swig: ifdef some Java specific code
+8ecec686 configure: add warning related flags
+e676b043 configure: add GLUT detection; build vwebp
+b0ffc437 Alpha decoding: significantly reduce memory usage
+20aa7a8d configure: add --enable-everything
+b8307cc0 configure.ac: add some helper macros
+980e7ae9 Remove the gcc compilation comments
+7f25ff99 gif2webp: Fix ICC and XMP support
+d8e53211 Add missing name to AUTHORS
+11edf5e2 Demux: Fix a potential memleak
+c7b92184 don't forward declare enums
+7a650c6a prevent signed int overflow in left shift ops
+31bea324 add precision about dynamic output reallocation with IDecoder
+c22877f7 Add incremental support for extended format files
+5051245f Makefile.vc: have 'all' target build everything
+8191deca Makefile.vc: flags cleanup
+b9d74735 Makefile.vc: drop /FD flag
+5568dbcf update gitignore
+f4c7b654 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded.
+1fb04bec pngdec: Avoid a double-free.
+dcbb1ca5 add WebPBlendAlpha() function to blend colors against background
+bc9f5fbe configure.ac: add AM_PROG_AR for automake >= 1.12
+bf867bf2 Tuned cross_color parameter (step) for lower qual
+90e2ec5a Merge "probe input file and quick-check for WebP format."
+7180d7ff Merge "update copyright text"
+830f72b7 probe input file and quick-check for WebP format.
+2ccf58d6 configure: improve gl/glut library test
+d640614d update copyright text
+c2113ad4 Merge "configure: remove use of AS_VAR_APPEND"
+9326a56f configure: remove use of AS_VAR_APPEND
+ea63d619 fix a type warning on VS9 x86
+bec11092 fix EXIF parsing in PNG
+b6e65f3d Merge "fix warnings for vs9 x64"
+438946dc fix warnings for vs9 x64
+f4710e3b collect macroblock reconstruction data in VP8MBData struct
+23d28e21 add doc precision for WebPPictureCopy() and WebPPictureView()
+518f2cd7 cosmetics: gif2webp: fix indent
+af358e68 Merge "remove datatype qualifier for vmnv"
+3fe91635 remove datatype qualifier for vmnv
+764fdffa fix a memory leak in gif2webp
+3e59a74d fix two minor memory leaks in webpmux
+47b9862f Merge "README: update swig notes"
+325d15ff remove some cruft from swig/libwebp.jar
+4a7627c2 README: update swig notes
+5da81e33 Merge "swig/python: add minimal documentation"
+f39e08f2 Merge "swig: add python encode support"
+6ca4a3e3 Merge "swig/java: reduce wrapper function code duplication"
+8f8702b0 Merge "swig/java: rework uint8_t typemap"
+91413be2 reduce memory for VP8MB and remove bitfields use
+7413394e Fix the memory leak in ApplyFilters.
+2053c2cf simplify the alpha-filter testing loop
+825b64db swig/python: add minimal documentation
+14677e11 swig: add python encode support
+a5c297c8 swig/java: reduce wrapper function code duplication
+ad4a367d swig/java: rework uint8_t typemap
+0d25876b use uint8_t for inv_palette[]
+afa3450c Fix the bug in ApplyPalette.
+2d6ac422 Merge "webp/lossless: fix big endian BGRA output"
+2ca83968 webp/lossless: fix big endian BGRA output
+742110cc Speed up ApplyPalette for ARGB pixels.
+2451e47d misc code cleanup
+83db4043 Merge "swig: add python (decode) support"
+eeeea8b5 Merge "swig: cosmetics"
+d5f9b8f3 Merge "libwebp: fix vp8 encoder mem alloc offsetting"
+d8edd835 libwebp: fix vp8 encoder mem alloc offsetting
+8983b83e remove use of bit-fields in VP8FInfo
+87a4fca2 remove some warnings:
+ba8f74e2 Merge "fix for big-endian"
+a65067fa Merge "Further reduce memory to decode lossy+alpha images"
+64c84486 Further reduce memory to decode lossy+alpha images
+332130b9 Mux: make a few methods static
+44370617 fix for big-endian
+5199eab5 Merge "add uncompressed TIFF output support"
+a3aede97 add uncompressed TIFF output support
+f975b67f Merge "gif2webp: Fix signed/unsigned comparison mismatch"
+5fbc734b Merge "GetFeatures: Detect invalid VP8X/VP8/VP8L data"
+d5060c87 Merge "mux.h: A comment fix + some consistency fixes"
+352d0dee GetFeatures: Detect invalid VP8X/VP8/VP8L data
+3ef79fef Cosmetic: "width * height"
+043e1ae4 gif2webp: Fix signed/unsigned comparison mismatch
+5818cff7 mux.h: A comment fix + some consistency fixes
+1153f888 Merge "swig: ifdef some Java specific code"
+3eeedae1 Makefile.vc: fix libwebpdemux dll variable typo
+f980faf4 swig: add python (decode) support
+7f5f42bb swig: cosmetics
+8eae188a WebP-Lossless encoding improvements.
+c7247c4c swig: ifdef some Java specific code
+4cb234d5 Merge "Mux: make ValidateForSingleImage() method static"
+ed6f5308 Merge "Add GetCanvasSize() method to mux"
+1d530c9a Mux: make ValidateForSingleImage() method static
+bba4c2b2 configure: add warning related flags
+fffefd18 Add GetCanvasSize() method to mux
+732da8d0 Merge "configure: add GLUT detection; build vwebp"
+0e513f7a configure: add GLUT detection; build vwebp
+55d1c150 Merge "Alpha decoding: significantly reduce memory usage"
+13d99fb5 Merge "configure: add --enable-everything"
+2bf698fe Merge "configure.ac: add some helper macros"
+edccd194 Alpha decoding: significantly reduce memory usage
+3cafcc9a configure: add --enable-everything
+4ef14477 configure.ac: add some helper macros
+a4e1cdbb Remove the gcc compilation comments
+6393fe4b Cosmetic fixes
+9c4ce971 Simplify forward-WHT + SSE2 version
+878b9da5 fix missed optim
+00046171 VP8GetInfo(): Check for zero width or height.
+9bf31293 align VP8Encoder::nz_ allocation
+5da165cf fix CheckMode() signature
+0ece07dc Merge "explicitly pad bitfields to 32-bits"
+9dbc9d19 explicitly pad bitfields to 32-bits
+5369a80f Merge "prevent signed int overflow in left shift ops"
+70e39712 Merge "cosmetics: remove unnecessary ';'s"
+d3136ce2 Merge "don't forward declare enums"
+b26e5ad5 gif2webp: Fix ICC and XMP support
+46089b20 Add missing name to AUTHORS
+94328d64 Demux: Fix a potential memleak
+96e948d7 don't forward declare enums
+f4f90880 prevent signed int overflow in left shift ops
+0261545e cosmetics: remove unnecessary ';'s
+7ebdf110 Merge "Fix few missing comparisons to NULL"
+1579989e Fix few missing comparisons to NULL
+ea1b21cf Cleaned up VP8GetHeaders() so that it parses only frame header
+b66caee4 dwebp: add support for BMP output
+ff885bfe add precision about dynamic output reallocation with IDecoder
+79241d5a Merge "Makefile.vc: have 'all' target build everything"
+ac1c729b Merge "Makefile.vc: flags cleanup"
+118a055c Merge "Makefile.vc: drop /FD flag"
+ecad0109 Merge "update gitignore"
+a681b4f4 Rename PRE_VP8 state to WEBP_HEADER
+ead4d478 Add incremental support for extended format files
+69d0f926 Makefile.vc: have 'all' target build everything
+52967498 Makefile.vc: flags cleanup
+c61baf0c Makefile.vc: drop /FD flag
+3a15125d update gitignore
+5167ca47 Merge "WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded."
+67708d67 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded.
+b68912af pngdec: Avoid a double-free.
+82abbe12 Merge "configure.ac: add AM_PROG_AR for automake >= 1.12"
+e7d9548c add WebPBlendAlpha() function to blend colors against background
+ed4dc717 configure.ac: add AM_PROG_AR for automake >= 1.12
+df4a406d Merge branch '0.3.0'
+1e0d4b8c Update ChangeLog (tag: v0.3.0-rc7, tag: v0.3.0)
+d52b405d Cosmetic fixes
+6cb4a618 misc style fix
+68111ab0 add missing YUVA->ARGB automatic conversion in WebPEncode()
+e9a7990b Cosmetic fixes
+403bfe82 Container spec: Clarify frame disposal
+2aaa423b Merge "add missing YUVA->ARGB automatic conversion in WebPEncode()"
+07d87bda add missing YUVA->ARGB automatic conversion in WebPEncode()
+142c4629 misc style fix
+3e7a13a0 Merge "Container spec: clarify the background color field" into 0.3.0
+14af7745 container doc: add a note about the 'ANMF' payload
+cc635efa Container spec: clarify the background color field
+e3e33949 container doc: move RIFF description to own section
+4299f398 libwebp/mux: fix double free
+33f9a692 Merge "demux: keep a frame tail pointer; used in AddFrame" into 0.3.0
+a2a7b959 use WebPDataCopy() instead of re-coding it.
+6f18f12f demux: keep a frame tail pointer; used in AddFrame
+e5af49e9 add doc precision about WebPParseHeaders() return codes
+db46daab Merge "Makefile.vc: fix dynamic builds" into 0.3.0
+53c77afc Merge "gif2webp: Bgcolor fix for a special case" into 0.3.0
+a5ebd143 gif2webp: Bgcolor fix for a special case
+6378f238 Merge "vwebp/animation: fix background dispose" into 0.3.0
+3c8eb9a8 fix bad saturation order in QuantizeBlock
+04c7a2ec vwebp/animation: fix background dispose
+81a50695 Makefile.vc: fix dynamic builds
+5f25c396 update ChangeLog (tag: v0.3.0-rc6)
+14d42af2 examples: don't use C99 %zu
+5ccf1fe5 update ChangeLog
+2560c243 update NEWS
+f43bafc3 Merge changes Iecccb09c,If5ee9fd2,I3e181ce4 into 0.3.0
+a788644f dwebp: warn when decoding animated webp's
+302efcdb Decode: return more meaningful error for animation
+ad452735 WebPBitstreamFeatures: add has_animation field
+783dfa49 disable FRGM decoding for good in libwebpmux
+4b956be0 Update ChangeLog
+ad8b86d7 update NEWS
+3e084f63 Merge "demux cosmetics: comments/rename internal function" into 0.3.0
+d3f8c621 Merge "move WebPFeatureFlags declaration" into 0.3.0
+7386fe50 Merge "libwebp{demux,mux}: install mux_types.h" into 0.3.0
+d6cd4e90 Merge "bump decode abi" into 0.3.0
+17f8da5c bump decode abi
+97684ae2 Merge "add doc precision about WebPDemuxPartial()" into 0.3.0
+f933fd2a move WebPFeatureFlags declaration
+289bc47b libwebp{demux,mux}: install mux_types.h
+224e8d46 add doc precision about WebPDemuxPartial()
+4c18e80c demux cosmetics: comments/rename internal function
+7cfd1bf1 update AUTHORS
+401f7b85 Merge "speed-up lossless (~3%) with ad-hoc histogram cost evaluation" into 0.3.0
+1fc8ffca Merge "makefile.unix: dist related changes" into 0.3.0
+8a89c6ed Merge changes I466c377f,Ib761ebd3,I694857fc into 0.3.0
+f4ffb2d5 speed-up lossless (~3%) with ad-hoc histogram cost evaluation
+723847d5 gif2webp: only write error messages to stderr
+701b9e2a makefile.unix: dist related changes
+bb85b437 Merge "update NEWS" into 0.3.0
+59423a24 gif2webp: fix crash on open failure with libgif5
+9acb17de gif2webp: silence a unused param warning
+7d9fdc23 Merge "README updates" into 0.3.0
+5621934e Merge "build: fix install race on shared headers" into 0.3.0
+70809d89 Merge "bump version to 0.3.0" into 0.3.0
+d851cd1d demux: make the parse a bit more strict
+28bb4107 update NEWS
+cef93882 bump version to 0.3.0
+9048494d build: fix install race on shared headers
+1e67e8ef README updates
+42b611a4 Merge "configure: drop experimental from mux/demux" into 0.3.0
+096a8e32 Merge "vwebp: add color profile support" into 0.3.0
+ddfee5dc vwebp: add color profile support
+0d6927d3 Merge "Mark fragment options as experimental in webpmux" into 0.3.0
+5dbd4030 Mark fragment options as experimental in webpmux
+a0a6648c configure: drop experimental from mux/demux
+ee65bad8 Merge "add support for BITS > 32" into 0.3.0
+744930db add support for BITS > 32
+7dd288f0 cwebp: fix build
+19a8dd01 Merge "Makefile.vc: add vwebp.exe target" into 0.3.0
+50eeddad Merge "examples: normalize icc related program arguments" into 0.3.0
+757f637f Merge "Makefile.vc: add libwebpdecoder target" into 0.3.0
+b65c4b7c Makefile.vc: add libwebpdecoder target
+f8db7b4a Merge "vwebp: replace doubles w/floats where appropriate" into 0.3.0
+d99aa56f Makefile.vc: add vwebp.exe target
+013023e7 vwebp: replace doubles w/floats where appropriate
+9b3db894 README.mux: add version reference
+7b6a26cf Merge "cwebp: output metadata statistics" into 0.3.0
+d8dc72a0 examples: normalize icc related program arguments
+7bfc9056 Merge "make alpha unfilter work in-place" into 0.3.0
+0037b2d2 Merge "add LUT-free reference code for YUV->RGB conversion." into 0.3.0
+166bf744 Merge "demux: disable fragment parsing" into 0.3.0
+126974b4 add LUT-free reference code for YUV->RGB conversion.
+0aef3ebd make alpha unfilter work in-place
+14ef5005 Merge "Remove 'status: experimental' from container spec" into 0.3.0
+d40c98e1 Merge "webpmux binary: tiny style fix" into 0.3.0
+0bc42689 cwebp: output metadata statistics
+bc039803 Merge "autoconf: normalize experimental define" into 0.3.0
+d1e21b13 Remove 'status: experimental' from container spec
+7681bb96 webpmux binary: tiny style fix
+a3dd3d0f avoid installing example_util.h
+252320e2 demux: disable fragment parsing
+537bde05 autoconf: normalize experimental define
+5e338e0b Merge changes I33e8a613,I8e8a7b44 into 0.3.0
+d9d0ea1b Merge changes If21e3ec7,I991fc30b into 0.3.0
+627f5ca6 automake: add reference to libwebp for mux/demux
+eef73d07 don't consolidate proba stats too often
+05ec4cc2 libwebp{,decoder}.pc: add pthread flags
+1bfcf5bf add libwebpmux.pc
+26ca843d add libwebpdemux.pc
+69e25906 Merge "Tune Lossless compression for lower qualities."
+0478b5d2 Tune Lossless compression for lower qualities.
+39f7586f add a mention of parallel alpha encoding in the NEWS
+5a21d967 Merge "1.5x-2x faster encoding for method 3 and up"
+9bfbdd14 1.5x-2x faster encoding for method 3 and up
+27dc741b Correct frame options order in README.mux
+be2fd173 Mux: fix a scenario with bad ANMF/FRGM size
+19eb012c Merge "Demux: Add option to get frame count using GetI()"
+7368b8cb Merge "WebPGetFeatures() out of if condition for clarity."
+f604c9a4 Merge "fix windows build"
+153f94e8 fix windows build
+847b4924 Merge "vwebp: use magenta for 'i'nfo display"
+25ea46bd Merge "vwebp: add keyboard shortcuts to help output"
+bea7ccaf vwebp: use magenta for 'i'nfo display
+8fab161a webpmux: correct -frame param order in help output
+03cc23d6 vwebp: add keyboard shortcuts to help output
+068eba8d Demux: Add option to get frame count using GetI()
+988b8f56 WebPGetFeatures() out of if condition for clarity.
+6933d910 Merge "gif2webp: Be lenient about background color index."
+4d0f7c55 Merge "WebPGetFeatures() behavior change:"
+fdeeb01d gif2webp: Be lenient about background color index.
+ad250320 Merge "multi-threaded alpha encoding for lossy"
+4e32d3e1 Merge "fix compilation of token.c"
+f817930a multi-threaded alpha encoding for lossy
+88050351 fix compilation of token.c
+fc816219 code using the actual values for num_parts_, not the ones from config
+72655350 Merge "move the config check from .c to .h"
+dd9e76f7 move the config check from .c to .h
+956b217a WebPGetFeatures() behavior change:
+df02e4ce WebPDemuxGetI behavior change:
+633c004d Merge "rebalance method tools (-m) for methods [0..4]"
+58ca6f65 rebalance method tools (-m) for methods [0..4]
+7648c3cc Merge "describe rd-opt levels introduce VP8RDLevel enum"
+67fb1003 Merge "autoconf: enable silent-rules by default"
+a5042a32 GetVersion() methods for mux and demux
+5189957e describe rd-opt levels introduce VP8RDLevel enum
+4e094ace autoconf: enable silent-rules by default
+b7eaa85d inline VP8LFastLog2() and VP8LFastSLog2 for small values
+5cf7792e split quant_levels.c into decoder and encoder version
+e5d3ffe2 Merge "Update code example in README.mux"
+ac5a9156 Update code example in README.mux
+38a91e99 Add example code snippet for demux API
+5f557f3c README.mux: add info about Demux API and vwebp
+c0ba0903 backward_references: avoid signed integer overflow
+943386db disable SSE2 for now
+9479fb7d lossless encoding speedup
+ec2030a8 merge two lines together
+b67956c0 Merge "Remove ReadOneBit() and ReadSymbolUnsafe()"
+1667bded Remove ReadOneBit() and ReadSymbolUnsafe()
+3151669b wicdec + dwebp cosmetics: normalize formatting
+92668da6 change default filtering parameters: * type is now 'strong' * strength is now '60'
+b7490f85 introduce WEBP_REFERENCE_IMPLEMENTATION compile option
+33838857 faster decoding (3%-6%)
+5c3e381b Merge "add a -jpeg_like option"
+c2311046 remove unused declaration of VP8Zigzag
+36152957 Merge "wicdec: add alpha support for paletted formats"
+c9f16490 wicdec: add alpha support for paletted formats
+1262f81e Merge "wicdec: silence some warnings"
+e7ea61eb wicdec: silence some warnings
+23c0f354 fix missing intptr_t->int cast for MSVC
+e895059a add a -jpeg_like option
+1f803f64 Merge "Tune alpha quality mapping to more reasonable values."
+1267d498 Tune alpha quality mapping to more reasonable values.
+043076e2 Merge "speed-up lossless in BackwardTrace"
+f3a44dcd remove one malloc from TraceBackwards()
+0fc1a3a0 speed-up lossless in BackwardTrace
+7c732e59 cwebp: centralize WebPCleanupTransparentArea()
+7381254e Merge "wicdec: add ICC profile extraction"
+e83ff7de wicdec: add ICC profile extraction
+146c6e3b Merge "cosmetics: pngdec: normalize default label location"
+a8f549d7 Merge "manpages: italicize option parameters"
+e118db83 Merge "encode.h: note the need to free() WebPMemoryWriter"
+1dfee6db cosmetics: pngdec: normalize default label location
+14c38200 manpages: italicize option parameters
+7defbfad encode.h: note the need to free() WebPMemoryWriter
+88d382a0 cwebp: cleanup after memory_writer
+12d6cecf fix extra space in dwebp.1 man
+b01681a9 Fix for demuxer frame iteration:
+56c12aa6 Demuxer creation fix:
+66c810bc add a -yuv option to dwebp (very similar to -pgm)
+841a3ba5 Merge "Remove -Wshadow warnings."
+8fd02527 Merge "upsampling_neon.c: fix build"
+6efed268 Remove -Wshadow warnings.
+60904aa6 Merge "allow WebPINewRGB/YUVA to be passed a NULL output buffer."
+b7adf376 allow WebPINewRGB/YUVA to be passed a NULL output buffer.
+27f8f742 upsampling_neon.c: fix build
+06b9cdf1 gitignore: add IOS related directories
+f112221e Merge "Fix more comments for iobuild.sh"
+fe4d25dd Fix more comments for iobuild.sh
+1de3e252 Merge "NEON optimised yuv to rgb conversion"
+090b708a NEON optimised yuv to rgb conversion
+daa06476 Merge "Add ios build script for building iOS library."
+79fe39e2 Add ios build script for building iOS library.
+126c035f remove some more -Wshadow warnings
+522e9d61 Merge "cwebp: enable '-metadata'"
+76ec5fa1 cwebp: enable '-metadata'
+aeb91a9d Merge "cosmetics: break a few long lines"
+be7c96b0 cosmetics: break a few long lines
+cff8ddb6 Merge "add libwebpdecoder.pc"
+93148ab8 Merge "libwebp.pc.in: detab"
+6477f955 Merge "Makefile.vc: normalize path separator"
+bed1ed7c add libwebpdecoder.pc
+46168b2d libwebp.pc.in: detab
+a941a346 Fixed few nits in the build files.
+dd7a49b2 Makefile.vc: normalize path separator
+9161be86 Merge "cwebp: extract WIC decoding to its own module"
+08e7c58e Merge "Provide an option to build decoder library."
+0aeba528 Provide an option to build decoder library.
+757ebcb1 catch malloc(0)/calloc(0) with an assert
+152ec3d2 Merge "handle malloc(0) and calloc(0) uniformly on all platforms"
+a452a555 cwebp: extract WIC decoding to its own module
+2b252a53 Merge "Provide option to swap bytes for 16 bit colormodes"
+94a48b4b Provide option to swap bytes for 16 bit colormodes
+42f8f934 handle malloc(0) and calloc(0) uniformly on all platforms
+8b2152c5 Merge "add an extra assert to check memory bounds"
+0d19fbff remove some -Wshadow warnings
+cd22f655 add an extra assert to check memory bounds
+8189feda Merge "Add details and reference about the YUV->RGB conversion"
+1d2702b1 Merge "Formatting fixes in lossless bitstream spec"
+8425aaee Formatting fixes in lossless bitstream spec
+a556cb1a Add details and reference about the YUV->RGB conversion
+d8f21e0b add link to SSIM description on Wikipedia
+18e9167e Merge "WebP-lossless spec clarifications:"
+98e25b9b Merge "cwebp: add -metadata option"
+f01c2a53 WebP-lossless spec clarifications:
+f4a97970 Merge "Disto4x4 and Disto16x16 in NEON"
+47b7b0ba Disto4x4 and Disto16x16 in NEON
+7eaee9f1 cwebp: add -metadata option
+36c52c2c tiffdec: use toff_t for exif ifd offset
+7c8111e4 Merge "cwebp/tiffdec: add TIFF metadata extraction"
+e6409adc Remove redundant include from dsp/lossless code.
+1ab5b3a7 Merge "configure: fix --with-gifincludedir"
+03c749eb configure: fix --with-gifincludedir
+8b650635 multiple libgif versions support for gif2webp
+476e293f gif2webp: Use DGifOpenFileName()
+b50f277b tiffdec: correct format string
+2b9048e3 Merge "tiffdec: check error returns for width/height"
+a1b5a9a3 Merge "cwebp/tiff: use the first image directory"
+079423f5 tiffdec: check error returns for width/height
+d62824af Merge "cwebp/jpegdec: add JPEG metadata extraction"
+03afaca4 Merge "cwebp: add PNG metadata extraction"
+2c724968 cwebp/jpegdec: add JPEG metadata extraction
+dba64d91 cwebp: add PNG metadata extraction
+1f075f89 Lossless spec corrections/rewording/clarifications
+2914ecfd cwebp/tiffdec: add TIFF metadata extraction
+d82a3e33 More corrections/clarifications in lossless spec:
+bd002557 cwebp/tiff: use the first image directory
+df7aa076 Merge "Cleanup around jpegdec"
+0f57dcc3 decoding speed-up (~1%)
+bcec339b Lossless bitstream clarification:
+6bf20874 add examples/metadata.c
+207f89c0 Merge "configure: add libwebpdemux status to summary"
+1bd287a6 Cleanup around jpegdec
+91455679 Merge "cosmetics: use '== 0' in size checks"
+d6b88b76 cosmetics: use '== 0' in size checks
+d3dace2f cosmetics: jpegdec
+2f69af73 configure: add libwebpdemux status to summary
+1c1c5646 cwebp: extract tiff decoding to its own module
+6a871d66 cwebp: extract jpeg decoding to its own module
+2ee228f9 cwebp: extract png decoding to its own module
+4679db00 Merge "cwebp: add metadata framework"
+63aba3ae cwebp: add metadata framework
+931bd516 lossless bitstream: block size bits correction
+e4fc4c1c lossless bitstream: block size bits correction
+d65ec678 fix build, move token.c to src/enc/
+657f5c91 move token buffer to its own file (token.c)
+c34a3758 introduce GetLargeValue() to slim-fast GetCoeffs().
+d5838cd5 faster non-transposing SSE2 4x4 FTransform
+f76191f9 speed up GetResidualCost()
+ba2aa0fd Add support for BITS=24 case
+2e7f6e8e makefile.unix: Dependency on libraries
+dca84219 Merge "Separate out mux and demux code and libraries:"
+23782f95 Separate out mux and demux code and libraries:
+bd56a01f configure: add summary output
+90e5e319 dwebp manual: point to webpmux, gif2webp.
+540790ca gif2webp.c: add a note about prerequisites
+d1edf697 cwebp man page: meaning of '-q' for lossy/lossless
+79efa1d0 Add man page for gif2webp utility
+2243e40c Merge "gif2webp build support with autoconf tools"
+c40efca1 gif2webp build support with autoconf tools
+6523e2d4 WebP Container:
+4da788da Merge "simplify the fwd transform"
+42c3b550 simplify the fwd transform
+41a6ced9 user GLfloat instead of float
+b5426119 fix indentation
+68f282f7 * handle offset in anim viewer 'vwebp' * fix gif2webp to handle disposal method and odd offset correctly
+118cb312 Merge "add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case"
+8a7c3cc8 Merge "Change the order of -frame argument to be more natural"
+99e0a707 Merge "Simplify the texture evaluation Disto4x4()"
+0f923c3f make the bundling work in a tmp buffer
+e5c3b3f5 Simplify the texture evaluation Disto4x4()
+48600084 Change the order of -frame argument to be more natural
+35bfd4c0 add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case
+a7305c2e Clarification for unknown chunks
+4c4398e2 Refine WebP Container Spec wrt unknown chunks.
+2ca642e0 Rectify WebPMuxGetFeatures:
+7caab1d8 Some cosmetic/comment fixes.
+60b2651a Merge "Write a GIF to WebP converter based on libgif."
+c7127a4d Merge "Add NEON version of FTransformWHT"
+11b27212 Write a GIF to WebP converter based on libgif.
+e9a15a37 ExUtilWriteFile() to write memory segment to file
+74356eb5 Add a simple cleanup step in mux assembly:
+51bb1e5d mux.h: correct WebPDemuxSelectFragment() prototype
+22a0fd9d Add NEON version of FTransformWHT
+fa30c863 Update mux code to match the spec wrt animation
+d9c5fbef by-pass Analysis pass in case segments=1
+d2ad4450 Merge changes Ibeccffc3,Id1585b16
+5c8be251 Merge "Chunk fourCCs for XMP/EXIF"
+a00a3daf Use 'frgm' instead of 'tile' in webpmux parameters
+81b8a741 Design change in ANMF and FRGM chunks:
+f903cbab Chunk fourCCs for XMP/EXIF
+812933d6 Tune performance of HistogramCombine
+52ad1979 Animation specification in container spec
+001b9302 Image fragment specification in container spec
+391f9db9 Ordering of description of bits in container spec
+d5735776 Metadata specification in container spec
+1c4609b1 Merge commit 'v0.2.1'
+0ca584cb Merge "Color profile specification in container spec"
+e8b41ad1 add NEON asm version for WHT inverse transform
+af6f0db2 Color profile specification in container spec
+a61a824b Merge "Add NULL check in chunk APIs"
+0e8b7eed fix WebPPictureView() unassigned strides
+75e5f17e ARM/NEON: 30% encoding speed-up
+02b43568 Add NULL check in chunk APIs
+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)
+92f8059c Rename some chunks:
+3bb4bbeb Merge "Mux API change:"
+d0c79f05 Mux API change:
+abc06044 Merge "update NEWS" into 0.2.0
+57cf313b update NEWS
+25f585c4 bump version to 0.2.1
+fed7c048 libwebp: validate chunk size in ParseOptionalChunks
+552cd9bc cwebp (windows): fix alpha image import on XP
+b14fea99 autoconf/libwebp: enable dll builds for mingw
+4a8fb272 [cd]webp: always output windows errors
+d6621580 fix double to float conversion warning
+72b96a69 cwebp: fix jpg encodes on XP
+734f762a VP8LAllocateHistogramSet: fix overflow in size calculation
+f9cb58fb GetHistoBits: fix integer overflow
+b30add20 EncodeImageInternal: fix uninitialized free
+3de58d77 fix the -g/O3 discrepancy for 32bit compile
+77aa7d50 fix the BITS=8 case
+e5970bda Make *InitSSE2() functions be empty on non-SSE2 platform
+ef5cc47e make *InitSSE2() functions be empty on non-SSE2 platform
+c4ea259d make VP8DspInitNEON() public
+8344eadf Merge "libwebp: validate chunk size in ParseOptionalChunks"
+4828bb93 Merge "cwebp (windows): fix alpha image import on XP"
+30763333 libwebp: validate chunk size in ParseOptionalChunks
+70481898 AccumulateLSIM: fix double -> float warnings
+eda8ee4b cwebp (windows): fix alpha image import on XP
+c6e98658 Merge "add EXPERIMENTAL code for YUV-JPEG colorspace"
+f0360b4f add EXPERIMENTAL code for YUV-JPEG colorspace
+f86e6abe add LSIM metric to WebPPictureDistortion()
+c3aa215a Speed up HistogramCombine for lower qualities.
+1765cb1c Merge "autoconf/libwebp: enable dll builds for mingw"
+a13562e8 autoconf/libwebp: enable dll builds for mingw
+9f469b57 typo: no_fancy -> no_fancy_upsampling
+1a27f2f8 Merge "fix double to float conversion warning"
+cf1e90de Merge "cwebp: fix jpg encodes on XP"
+f2b5d19b [cd]webp: always output windows errors
+e855208c fix double to float conversion warning
+ecd66f77 cwebp: fix jpg encodes on XP
+7b3eb372 Tune lossless compression to get better gains.
+ce8bff45 Merge "VP8LAllocateHistogramSet: fix overflow in size calculation"
+ab5b67a1 Merge "EncodeImageInternal: fix uninitialized free"
+7fee5d12 Merge "GetHistoBits: fix integer overflow"
+a6ae04d4 VP8LAllocateHistogramSet: fix overflow in size calculation
+80237c43 GetHistoBits: fix integer overflow
+8a997235 EncodeImageInternal: fix uninitialized free
+0b9e6829 minor cosmetics
+a792b913 fix the -g/O3 discrepancy for 32bit compile
+73ba4357 Merge "detect and merge similar segments"
+fee66275 detect and merge similar segments
+0c44f415 src/webp/*.h: don't forward declare enums in C++
+d7a5ac86 vwebp: use demux interface
+931e0ea1 Merge "replace 'typedef struct {} X;" by "typedef struct X X; struct X {};""
+8f216f7e remove cases of equal comparison for qsort()
+28d25c82 replace 'typedef struct {} X;" by "typedef struct X X; struct X {};"
+2afee60a speed up for ARM using 8bit for boolean decoder
+5725caba new segmentation algorithm
+2cf1f815 Merge "fix the BITS=8 case"
+12f78aec fix the BITS=8 case
+6920c71f fix MSVC warnings regarding implicit uint64 to uint32 conversions
+f6c096aa webpmux binary: Rename 'xmp' option to 'meta'
+ddfe871a webpmux help correction
+b7c55442 Merge "Make *InitSSE2() functions be empty on non-SSE2 platform"
+1c04a0d4 Common APIs for chunks metadata and color profile.
+2a3117a1 Merge "Create WebPMuxFrameInfo struct for Mux APIs"
+5c3a7231 Make *InitSSE2() functions be empty on non-SSE2 platform
+7c6e60f4 make *InitSSE2() functions be empty on non-SSE2 platform
+c7eb4576 make VP8DspInitNEON() public
+ab3234ae Create WebPMuxFrameInfo struct for Mux APIs
+e3990fd8 Alignment fixes
+e55fbd6d Merge branch '0.2.0'
+4238bc0a Update ChangeLog (tag: v0.2.0)
+c655380c dec/io.c: cosmetics
+fe1958f1 RGBA4444: harmonize lossless/lossy alpha values
+681cb30a fix RGBA4444 output w/fancy upsampling
+f06c1d8f Merge "Alignment fix" into 0.2.0
+f56e98fd Alignment fix
+6fe843ba avoid rgb-premultiply if there's only trivial alpha values
+528a11af fix the ARGB4444 premultiply arithmetic
+a0a48855 Lossless decoder fix for a special transform order
+62dd9bb2 Update encoding heuristic w.r.t palette colors.
+6f4272b0 remove unused ApplyInverseTransform()
+93bf0faa Update ChangeLog (tag: v0.2.0-rc1)
+5934fc59 update AUTHORS
+014a711d update NEWS
+43b0d610 add support for ARGB -> YUVA conversion for lossless decoder
+33705ca0 bump version to 0.2.0
+c40d7ef1 fix alpha-plane check + add extra checks
+a06f8023 MODE_YUVA: set alpha to opaque if the image has none
+52a87dd7 Merge "silence one more warning" into 0.2.0
+3b023093 silence one more warning
+f94b04f0 move some RGB->YUV functions to yuv.h
+4b71ba03 README: sync [cd]webp help output
+c9ae57f5 man/dwebp.1: add links to output file format details
+292ec5cc quiet a few 'uninitialized' warnings
+4af3f6c4 fix indentation
+9b261bf5 remove the last NOT_HAVE_LOG2 instances
+323dc4d9 remove use of log2(). Use VP8LFastLog2() instead.
+8c515d54 Merge "harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc" into 0.2.0
+d4b4bb02 Merge changes I46090628,I1a41b2ce into 0.2.0
+bff34ac1 harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc
+a3c063c7 Merge "extra size check for security" into 0.2.0
+5e796300 Merge "WebPEncode: clear stats at the start of encode" into 0.2.0
+f1edf62f Merge "rationalize use of color-cache" into 0.2.0
+c1933317 extra size check for security
+906be657 rationalize use of color-cache
+dd1c3873 Add image-hint for low-color images.
+4eb7aa64 Merge "WebPCheckMalloc() and WebPCheckCalloc():" into 0.2.0
+80cc7303 WebPCheckMalloc() and WebPCheckCalloc():
+183cba83 check VP8LBitWriterInit return
+cbfa9eec lossless: fix crash on user abort
+256afefa cwebp: exit immediately on version mismatch
+475d87d7 WebPEncode: clear stats at the start of encode
+a7cc7291 fix type and conversion warnings
+7d853d79 add stats for lossless
+d39177b7 make QuantizeLevels() store the sum of squared error
+5955cf5e replace x*155/100 by x*101581>>16
+7d732f90 make QuantizeLevels() store the sum of squared error
+e45a446a replace x*155/100 by x*101581>>16
+159b75d3 cwebp output size consistency:
+cbee59eb Merge commit 'v0.1.99'
+1889e9b6 dwebp: report -alpha option
+3bc3f7c0 Merge "dwebp: add PAM output support" into 0.2.0
+d919ed06 dwebp: add PAM output support
+85e215d3 README/manpages/configure: update website link
+c3a207b9 Update ChangeLog (tag: v0.1.99)
+d1fd7826 Merge "add extra precision about default values and behaviour" into 0.2.0
+efc826e0 add extra precision about default values and behaviour
+9f29635d header/doc clean up
+ff9fd1ba Makefile.vc: fix webpmux.exe *-dynamic builds
+8aacc7b0 remove INAM, ICOP, ... chunks from the test webp file.
+2fc13015 harmonize authors as "Name (mail@address)"
+4a9f37b7 Merge "update NEWS" into 0.2.0
+7415ae13 makefile.unix: provide examples/webpmux target
+ce82cedc update NEWS
+641e28e8 Merge "man/cwebp.1: wording, change the date" into 0.2.0
+c37c23e5 README: cosmetics
+3976dcd5 man/cwebp.1: wording, change the date
+3e5bbe1c Merge "rename 'use_argb_input' to 'use_argb'" into 0.2.0
+ce90847a Merge "add some padding bytes areas for later use" into 0.2.0
+2390dabc Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0
+02751591 add a very crude progress report for lossless
+a4b9b1c6 Remove some unused enum values.
+dd108176 rename 'use_argb_input' to 'use_argb'
+90516ae8 add some padding bytes areas for later use
+d03b2503 fixing the findings by Frederic Kayser to the bitstream spec
+ce156afc add missing ABI compatibility checks
+9d45416a Merge "Doc: container spec text tweaks" into 0.2.0
+4e2e0a8c Doc: container spec text tweaks
+f7f16a29 add ABI compatibility check
+2a775570 Merge "swig: add WebPEncodeLossless* wrappers" into 0.2.0
+a3ec6225 mux.h: remove '* const' from function parameters
+31426eba encode.h: remove '* const' from function parameters
+9838e5d5 decode.h: remove '* const' from function parameters
+4972302d swig: add WebPEncodeLossless* wrappers
+9ff00cae bump encoder/decoder versions
+c2416c9b add lossless quick encoding functions to the public API
+4c1f5d64 Merge "NEWS: mention decode_vp8.h is no longer installed" into 0.2.0
+6cb2277d NEWS: mention decode_vp8.h is no longer installed
+d5e5ad63 move decode_vp8.h from webp/ to dec/
+8d3b04a2 Merge "header clean-up" into 0.2.0
+02201c35 Merge "remove one malloc() by making color_cache non dynamic" into 0.2.0
+d708ec14 Merge "move MIN/MAX_HISTO_BITS to format_constants.h" into 0.2.0
+ab2da3e9 Merge "add a malloc() check" into 0.2.0
+2d571bd8 add a malloc() check
+7f0c178e remove one malloc() by making color_cache non dynamic
+6569cd7c Merge "VP8LFillBitWindow: use 64-bit path for msvc x64 builds" into 0.2.0
+23d34f31 header clean-up
+2a3ab6f9 move MIN/MAX_HISTO_BITS to format_constants.h
+985d3da6 Merge "shuffle variables in HashChainFindCopy" into 0.2.0
+cdf885c6 shuffle variables in HashChainFindCopy
+c3b014db Android.mk: add missing lossless files
+8c1cc6b5 makefile.unix dist: explicitly name installed includes
+7f4647ee Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0
+cbf69724 clarify the colorspace naming and byte ordering of decoded samples
+857650c8 Mux: Add WebPDataInit() and remove WebPImageInfo
+ff771e77 don't install webp/decode_vp8.h
+596dff78 VP8LFillBitWindow: use 64-bit path for msvc x64 builds
+3ca7ce98 Merge "doc: remove non-finalized chunk references" into 0.2.0
+1efaa5a3 Merge "bump versions" into 0.2.0
+51fa13e1 Merge "README: update cwebp help output" into 0.2.0
+12f9aede README: update cwebp help output
+f0b5defb bump versions
+4c42a61b update AUTHORS
+6431a1ce doc: remove non-finalized chunk references
+8130c4cc Merge "build: remove libwebpmux from default targets/config"
+23b44438 Merge "configure: broaden test for libpng-config"
+85bff2cd Merge "doc: correct lossless prefix coding table & code"
+05108f6e Merge "More spec/code matching in mux:"
+6808e69d More spec/code matching in mux:
+bd2b46f5 Merge "doc/webp-container-spec: light cosmetics"
+20ead329 doc/webp-container-spec: light cosmetics
+1d40a8bc configure: add pthread detection
+b5e9067a fix some int <-> size_t mix for buffer sizes
+e41a7596 build: remove libwebpmux from default targets/config
+0fc2baae configure: broaden test for libpng-config
+45b8272c Merge "restore authorship to lossless bitstream doc"
+06ba0590 restore authorship to lossless bitstream doc
+44a09a3c add missing description of the alpha filtering methods
+63db87dd Merge "vwebp: add checkboard background for alpha display"
+a73b8978 vwebp: add checkboard background for alpha display
+939158ce Merge "vwebp: fix info display"
+b35c07d9 vwebp: fix info display
+48b39eb1 fix underflow for very short bitstreams
+7e622984 cosmetics: param alignment, manpage wording
+1bd7dd50 Merge changes I7b0afb0d,I7ecc9708
+ac69e63e Merge "Updated cwebp man's help for Alpha & Lossless."
+c0e8859d Get rid of image_info_ from WebPChunk struct.
+135ca69e WebP Container Spec:
+eb6f9b8a Updated cwebp man's help for Alpha & Lossless.
+0fa844fb cosmetic fixes on assert and 'const' where applicable
+7f22bd25 check limit of width * height is 32 bits
+16c46e83 autoconf/make: cosmetics: break long lines
+ab22a07a configure: add helper macro to define --with-*
+c17699b3 configure: add libtiff test
+0e09732c Merge "cwebp: fix crash with yuv input + lossless"
+88a510ff Merge "fix big-endian VP8LWriteBits"
+da99e3bf Merge "Makefile.vc: split mux into separate lib"
+7bda392b cwebp: fix crash with yuv input + lossless
+f56a369a fix big-endian VP8LWriteBits
+54169d6c Merge "cwebp: name InputFileFormat members consistently"
+e2feefa9 Makefile.vc: split mux into separate lib
+27caa5aa Merge "cwebp: add basic TIFF support"
+d8921dd4 cwebp: name InputFileFormat members consistently
+6f76d246 cwebp: add basic TIFF support
+4691407b Merge changes If39ab7f5,I3658b5ae
+cca7c7b8 Fixed nit: 10 -> 10.f
+5d09a244 WebPMuxCreate() error handling:
+777341c3 Fix a memleak in WebPMuxCreate()
+61c9d161 doc: correct lossless prefix coding table & code
+4c397579 Merge "mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN"
+e4e36cc6 Merge "Mux: Allow only some frames/tiles to have alpha."
+ad2aad3c Merge "WebP Decoding error handling:"
+97649c8f Mux: Allow only some frames/tiles to have alpha.
+f864be3b Lower the quality settings for Alpha encoding.
+3ba81bbe WebP Decoding error handling:
+fcc69923 add automatic YUVA/ARGB conversion during WebPEncode()
+802e012a fix compilation in non-FANCY_UPSAMPLING mode
+e012dfd9 make width/height coding match the spec
+228d96a5 mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN
+637a314f remove the now unused *KeepA variants
+d11f6fcc webpmux returns error strings rather than numbers
+fcec0593 makefile.unix: cwebp: fix OSX link
+6b811f1b Merge "doc: remove lossless pdf"
+c9634821 doc: remove lossless pdf
+b9ae4f0d cosmetics after mux changes b74ed6e, b494ad5
+b494ad50 Mux: only allow adding frame/tiles at the end.
+2c341b0e Merge "Added image characteristic hint for the codec."
+d373076a Added image characteristic hint for the codec.
+2ed2adb5 Merge "msvc: add intrinsic based BitsLog2Floor"
+e595e7c5 Merge "add demux.c to the makefiles"
+da47b5bd Merge "demux: add {Next,Prev}Chunk"
+e5f46742 add demux.c to the makefiles
+4708393c demux: add {Next,Prev}Chunk
+e8a0a821 demux: quiet msvc warnings
+7f8472a6 Update the WebP Container Spec.
+31b68fe6 cleanup WebPPicture struct and API
+9144a186 add overflow check before calling malloc()
+81720c91 consistency cosmetics
+2ebe8394 Merge "Add kramdown version information to README"
+71443084 enc/vp8l.c: fix build
+b7ac19fe Add kramdown version information to README
+efdcb667 Merge "Edit for consistency, usage and grammar."
+08220102 Enable alpha in vvwebp
+8de9a084 Merge "Mux API change:"
+b74ed6e7 Mux API change:
+233a589e take picture->argb_stride into account for lossless coding
+04e33f17 Edit for consistency, usage and grammar.
+a575b4bc Merge "cosmetics: add missing const"
+8d99b0f4 Merge "cosmetics: remove unimplemented function proto"
+69d02217 cosmetics: add missing const
+5b08318b cosmetics: remove unimplemented function proto
+b7fb0ed5 Log warning for unsupported options for lossless.
+e1f769fe msvc: add intrinsic based BitsLog2Floor
+8a69c7d8 Bug-fix: Clamp backward dist to 1.
+b5b6ac97 Merge "Bring the special writer 'WebPMemoryWriter' to public API"
+a6a1909f Merge "Fix floating point exception with cwebp -progress"
+f2cee067 Fix floating point exception with cwebp -progress
+91b7a8c7 Bring the special writer 'WebPMemoryWriter' to public API
+310e2972 support resize and crop for RGBA input
+a89835d3 Merge changes Ice662960,Ie8d7aa90,I2d996d5e,I01c04772
+ce614c0c Merge "dec/vp8: avoid setting decoder status twice"
+900285da dec/vp8: avoid setting decoder status twice
+8227adc8 Merge changes I6f02b0d0,I5cbc9c0a,I9dd9d4ed,Id684d2a1
+dcda59c1 Merge "demux: rename SetTile to SelectTile"
+622ef12e demux: rename SetTile to SelectTile
+81ebd375 Merge "demux: add {Next,Prev}Frame"
+02dd37a2 demux: add {Next,Prev}Frame
+4b79fa59 Merge "Limit the maximum size of huffman Image to 16MB."
+9aa34b34 Manually number "chapters," as chapter numbers are used in the narrative.
+2a4c6c29 Re-wrap at <= 72 columns
+a45adc19 Apply inline emphasis and monospacing, per gdoc / PDF
+91011206 Incorporate gdoc changes through 2012-06-08
+7a182487 Removed CodeRay syntax declarations ...
+b3ec18c5 Provide for code-block syntax highlighting.
+709d7702 Replace high ASCII artifacts (curly quotes, etc.).
+930e8abb Lossless WebP doc largely ported to markdown text.
+18cae37b msvc: silence some build warnings
+b3923084 Limit the maximum size of huffman Image to 16MB.
+f180df2a Merge "libwebp/demux: add Frame/Chunk iteration"
+2bbe1c9a Merge "Enable lossless encoder code"
+d0601b01 Merge changes I1d97a633,I81c59093
+78f3e345 Enable lossless encoder code
+d974a9cc Merge "libwebp/demux: add simple format parsing"
+26bf2232 Merge "libwebp: add WebPDemux stub functions"
+2f666688 Merge "modify WebPParseHeaders to allow reuse by GetFeatures"
+b402b1fb libwebp/demux: add Frame/Chunk iteration
+ad9ada3b libwebp/demux: add WebPDemuxGetI
+2f2d4d58 libwebp/demux: add extended format parsing
+962dcef6 libwebp/demux: add simple format parsing
+f8f94081 libwebp: add WebPDemux stub functions
+fb47bb5c Merge "NumNamedElements() should take an enum param."
+7c689805 Fix asserts in Palette and BackwardReference code.
+fbdcb7ea NumNamedElements() should take an enum param.
+fb4943bd modify WebPParseHeaders to allow reuse by GetFeatures
+3697b5ce write an ad-hoc EncodeImageInternal variant
+eaee9e79 Bug-Fix: Decode small (less than 32 bytes) images.
+0bceae48 Merge "cwebp: fix alpha reporting in stats output"
+0424b1ef Rebase default encoding settings.
+c71ff9e3 cwebp: fix alpha reporting in stats output
+e2ffe446 Merge "Stop indefinite recursion for Huffman Image."
+70eb2bd6 Stop indefinite recursion for Huffman Image.
+f3bab8eb Update vwebp
+6d5c797c Remove support for partial files in Mux.
+f1df5587 WebPMuxAssemble() returns WebPData*.
+814a0639 Rename 'Add' APIs to 'Set'.
+bbb0218f Update Mux psuedo-code examples.
+4fc4a47f Use WebPData in MUX set APIs
+c67bc979 Merge "add WebPPictureImportRGBX() and WebPPictureImportBGRX()"
+27519bc2 add WebPPictureImportRGBX() and WebPPictureImportBGRX()
+f80cd27e factorize code in Import()
+9b715026 histogram: add log2 wrapper
+8c34378f Merge "fix some implicit type conversion warnings"
+42f6df9d fix some implicit type conversion warnings
+250c16e3 Merge "doc: update lossless pdf"
+9d9daba4 Merge "add a PDF of the lossless spec"
+8fbb9188 prefer webp/types.h over stdint.h
+0ca170c2 doc: update lossless pdf
+0862ac6e add a PDF of the lossless spec
+437999fb introduce a generic WebPPictureHasTransparency() function
+d2b6c6c0 cosmetic fixes after Idaba281a
+b4e6645c Merge "add colorspace for premultiplied alpha"
+48f82757 add colorspace for premultiplied alpha
+069f903a Change in lossless bit-stream.
+5f7bb3f5 Merge "WebPReportProgress: use non-encoder specific params"
+f18281ff WebPReportProgress: use non-encoder specific params
+9ef32283 Add support for raw lossless bitstream in decoder.
+7cbee29a Fix bug: InitIo reseting fancy_upsampling flag.
+880fd98c vwebp: fix exit w/freeglut
+1875d926 trap two unchecked error conditions
+87b4a908 no need to have mux.h as noinst clause in enc/
+88f41ec6 doc: fix bit alignment in VP8X chunk
+52f5a4ef Merge "fix bug with lossy-alpha output stride"
+3bde22d7 fix bug with lossy-alpha output stride
+42d61b6d update the spec for the lossy-alpha compression methods.
+e75dc805 Move some more defines to format_constants.h
+c13f6632 Move consts to internal header format_constants.h
+7f2dfc92 use a bit-set transforms_seen_ instead of looping
+18da1f53 modulate alpha-compression effort according to config.method
+f5f2fff6 Merge "Alpha flag fix for lossless."
+c975c44e Alpha flag fix for lossless.
+4f067fb2 Merge "Android: only build dec_neon with NEON support"
+255c66b4 Android: only build dec_neon with NEON support
+8f9117a9 cosmetics: signature fixes
+39bf5d64 use header-less lossless bitstream for alpha channel
+75d7f3b2 Merge "make input data be 'const' for VP8LInverseTransform()"
+9a721c6d make input data be 'const' for VP8LInverseTransform()
+9fc64edc Disallow re-use of same transformation.
+98ec717f use a function pointer for ProcessRows()
+f7ae5e37 cosmetics: join line
+140b89a3 factor out buffer alloc in AllocateARGBBuffers()
+a107dfa8 Rectify WebPParseOptionalChunks().
+237eab67 Add two more color-spaces for lossless decoding.
+27f417ab fix orthographic typo
+489ec335 add VP8LEncodeStream() to compress lossless image stream
+fa8bc3db make WebPEncodingSetError() take a const picture
+638528cd bitstream update for lossy alpha compression
+d73e63a7 add DequantizeLevels() placeholder
+ec122e09 remove arch-dependent rand()
+d40e7653 fix alignment
+1dd6a8b6 Merge "remove tcoder, switch alpha-plane compression to lossless"
+3e863dda remove tcoder, switch alpha-plane compression to lossless
+8d77dc29 Add support for lossless in mux:
+831bd131 Make tile size a function of encoding method.
+778c5228 Merge "remove some variable shadowing"
+817c9dce Few more HuffmanTreeToken conversions.
+37a77a6b remove some variable shadowing
+89c07c96 Merge "normalize example header includes"
+4aff411f Merge "add example_util.[hc]"
+00b29e28 normalize example header includes
+061263a7 add example_util.[hc]
+c6882c49 merge all tree processing into a single VP8LProcessTree()
+9c7a3cf5 fix VP8LHistogramNumCodes to handle the case palette_code_bits == 0
+b5551d2e Merge "Added HuffmanTreeCode Struct for tree codes."
+8b85d01c Added HuffmanTreeCode Struct for tree codes.
+093f76d8 Merge "Allocate single memory in GetHuffBitLengthsAndCodes."
+41d80494 Allocate single memory in GetHuffBitLengthsAndCodes.
+1b04f6d2 Correct size in VP8L header.
+2924a5ae Makefile.vc: split object lists based on directory
+c8f24165 Merge "add assert(tokens)"
+43239947 add assert(tokens)
+9f547450 Catch an error in DecodeImageData().
+ac8e5e42 minor typo and style fix
+9f566d1d clean-up around Huffman-encode
+c579a710 Introduce CHUNK_SIZE_BYTES in muxi.h.
+14757f8a Make sure huffman trees always have valid symbols
+41050618 makefile.unix: add support for building vwebp
+48b37721 Merge "fixed signed/unsigned comparison warning"
+57f696da Merge "EncodeImageInternal: fix potential leak"
+d972cdf2 EncodeImageInternal: fix potential leak
+5cd12c3d fixed signed/unsigned comparison warning
+cdca30d0 Merge "cosmetics: shorten long line"
+e025fb55 cosmetics: shorten long line
+22671ed6 Merge "enc/vp8l: fix double free on error"
+e1b9b052 Merge "cosmetics: VP8LCreateHuffmanTree: fix indent"
+a8e725f8 enc/vp8l: fix double free on error
+27541fbd cosmetics: VP8LCreateHuffmanTree: fix indent
+1d38b258 cwebp/windows: use MAKE_REFGUID where appropriate
+817ef6e9 Merge "cwebp: fix WIC/Microsoft SDK compatibility issue"
+902d3e3b cwebp: fix WIC/Microsoft SDK compatibility issue
+89d803c4 Merge "Fix a crash due to wrong pointer-integer arithmetic."
+cb1bd741 Merge "Fix a crash in lossless decoder."
+de2fe202 Merge "Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability."
+ce69177a Fix a crash due to wrong pointer-integer arithmetic.
+e40a3684 Fix a crash in lossless decoder.
+3927ff3a remove unneeded error condition for WebPMuxNumNamedElements()
+2c140e11 Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool' outside the loop. - Some renames/tweaks for readability.
+861a5b7b add support for animation
+eb5c16cc Merge "Set correct encode size in encoder's stats."
+4abe04a2 fix the return value and handle missing input file case.
+2fafb855 Set correct encode size in encoder's stats.
+e7167a2b Provide one entry point for backward references.
+c4ccab64 Print relevant lossless encoding stats in cwebp.
+e3302cfd GetHuffBitLengthsAndCodes: reduce level of indirection
+b5f2a9ed enc/vp8l: fix uninitialized variable warning
+7885f8b2 makefile.unix: add lossless encoder files
+1261a4c8 Merge "cosmetics"
+3926b5be Merge "dsp/cpu.c: Android: fix crash on non-neon arm builds"
+834f937f dsp/cpu.c: Android: fix crash on non-neon arm builds
+126e1606 cosmetics
+e38602d2 Merge branch 'lossless_encoder'
+e8d3d6a0 split StoreHuffmanCode() into smaller functions
+d0d88990 more consolidation: introduce VP8LHistogramSet
+1a210ef1 big code clean-up and refactoring and optimization
+41b5c8ff Some cosmetics in histogram.c
+ada6ff77 Approximate FastLog between value range [256, 8192]
+ec123ca3 Forgot to update out_bit_costs to symbol_bit_costs at one instance.
+cf33ccd1 Evaluate output cluster's bit_costs once in HistogramRefine.
+781c01f4 Simple Huffman code changes.
+a2849bc5 Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths().
+b39e7487 Reducing emerging palette size from 11 to 9 bits.
+bfc73db4 Move GetHistImageSymbols to histogram.c
+889a5786 Improve predict vs no-predict heuristic.
+01f50663 code-moving and clean-up
+31035f3b reduce memory usage by allocating only one histo
+fbb501b8 Restrict histo_bits to ensure histo_image size is under 32MB
+8415ddf3 further simplification for the meta-Huffman coding
+e4917299 A quick pass of cleanup in backward reference code
+83332b3c Make transform bits a function of encode method (-m).
+72920caa introduce -lossless option, protected by USE_LOSSLESS_ENCODER
+c6ac4dfb Run TraceBackwards for higher qualities.
+412222c8 Make histo_bits and transform_bits function of quality.
+149b5098 Update lossless encoder strategy:
+0e6fa065 cache_bits passed to EncodeImageInternal()
+e38b40a9 Factorize code for clearing HtreeGroup.
+6f4a16ea Removing the indirection of meta-huffman tables.
+3d33ecd1 Some renaming/comments related to palette in lossless encoder.
+4d02d586 Lossless encoder: correction in Palette storage
+4a636235 fix a memleak in EncodeImageInternal()
+0993a611 Full and final fix for prediction transform
+afd2102f Fix cross-color transform in lossless encoder
+b96d8740 Need to write a '0' bit at the end of transforms.
+54dad7e5 Color cache size should be counted as 0 when cache bits = 0
+4f0c5caf Fix prediction transform in lossless encoder.
+36dabdad Fix memory leak in method EncodeImageInternal for histogram_image.
+352a4f49 Get rid of PackLiteralBitLengths()
+d673b6b9 Change the predictor function to pass left pixel
+b2f99465 Fix CopyTileWithPrediction()
+84547f54 Add EncodeImageInternal() method.
+6b38378a Guard the lossless encoder (in flux) under a flag
+09f7532c Fix few nits (const qualifiers)
+648be393 Added implementation for various lossless functions
+32714ce3 Add VP8L prefix to backward ref & histogram methods.
+fcba7be2 Fixed header file tag (WEBP_UTILS_HUFFMAN_ENCODE_H_)
+bc703746 Add backward_ref, histogram & huffman encode modules from lossless.
+fdccaadd Fixing nits
+227110c4 libwebp interface changes for lossless encoding.
+50679acf minor style fixes
+b38dfccf remove unneeded reference to NUM_LITERAL_CODES
+8979675b harmonize header description
+c04eb7be tcoder.c: define NOT_HAVE_LOG2 for MSVC builds
+9a214fa1 Merge "VP8[L]GetInfo: check input pointers"
+5c5be8ba VP8[L]GetInfo: check input pointers
+0c188fec Merge changes I431acdfe,I713659b7
+b3515c62 mux: drop 'chunk' from ChunkInfo member names
+aea7923c muxi.h: remove some unused defines
+01422492 update NEWS file for next release
+29e3f7ec Merge "dec: remove deprecated WebPINew()"
+4718e449 Merge "muxedit: a few more size_t changes"
+82654f96 Merge "muxedit: remove a few redundant NULL checks"
+02f27fbd dec: remove deprecated WebPINew()
+ccddb3fc muxedit: remove a few redundant NULL checks
+a6cdf710 muxedit: a few more size_t changes
+a3846892 Merge "mux: remove unused LIST_ID"
+11ae46ae alpha.c: quiet some size_t -> int conversion warnings
+dee46692 mux: remove unused LIST_ID
+03f1f493 mux: add version checked entry points
+6a0abdaa Merge "doc: tile/alpha corrections"
+c8139fbe Merge "few cosmetics"
+68338737 Merge "lossless: remove some size_t -> int conversions"
+5249e94a doc: tile/alpha corrections
+d96e722b huffman: quiet int64 -> int conversion warning
+532020f2 lossless: remove some size_t -> int conversions
+23be6edf few cosmetics
+1349edad Merge "configure: AC_ARG_* use AS_HELP_STRING"
+bfbcc60a configure: AC_ARG_* use AS_HELP_STRING
+1427ca8e Merge "Makefile.am: header file maintenance"
+087332e3 Merge "remove unused parameter 'round' from CalcProba()"
+9630e168 remove unused parameter 'round' from CalcProba()
+92092eaa Merge "bit_reader.h: correct include"
+a87fc3f6 Merge "mux: ensure # images = # tiles"
+53af99b1 Merge "mux: use size_t consistently"
+39a57dae Makefile.am: header file maintenance
+1bd0bd0d bit_reader.h: correct include
+326a3c6b mux: ensure # images = # tiles
+95667b8d mux: use size_t consistently
+231ec1fb Removing the indirection of meta-huffman tables.
+15ebcbaa check return pointer from MuxImageGetListFromId
+b0d6c4a7 Merge "configure: remove test for zlib.h"
+8cccac50 Merge "dsp/lossless: silence some build warnings"
+b08819a6 dsp/lossless: silence some build warnings
+7ae22521 Android.mk: SSE2 & NEON updates
+0a49e3f3 Merge "makefile.unix add missing header files"
+2e75a9a1 Merge "decode.h: use size_t consistently"
+fa13035e configure: remove test for zlib.h
+d3adc81d makefile.unix add missing header files
+262fe01b Merge "makefile.unix & Android.mk: cosmetics"
+4cce137e Merge "enc_sse2 add missing stdlib.h include"
+80256b85 enc_sse2 add missing stdlib.h include
+9b3d1f3a decode.h: use size_t consistently
+64083d3c Merge "Makefile.am: cosmetics"
+dceb8b4d Merge changes If1331d3c,I86fe3847
+0e33d7bf Merge "webp/decode.h: fix prototypes"
+fac0f12e rename BitReader to VP8LBitReader
+fbd82b5a types.h: centralize use of stddef.h
+2154835f Makefile.am: cosmetics
+1c92bd37 vp8io: use size_t for buffer size
+90ead710 fix some more uint32_t -> size_t typing
+cbe705c7 webp/decode.h: fix prototypes
+3f8ec1c2 makefile.unix & Android.mk: cosmetics
+217ec7f4 Remove tabs in configure.ac
+b3d35fc1 Merge "Android.mk & Makefile.vc: add new files"
+0df04b9e Android.mk & Makefile.vc: add new files
+e4f20c5b Merge "automake: replace 'silent-rules' w/AM_SILENT_RULES"
+8d254a09 cosmetics
+6860c2ea fix some uint32_t -> size_t typing
+4af1858a Fix a crash due to max symbol in a tree >= alphabet size
+6f01b830 split the VP8 and VP8L decoding properly
+f2623dbe enable lossless decoder
+b96efd7d add dec/vp8i.h changes from experimental
+19f6398e add dec/vp8l{i.h,.c} from experimental
+c4ae53c8 add utils/bit_reader.[hc] changes from experimental
+514d0089 add dsp/lossless.[hc] from experimental
+9c67291d add utils/huffman.[hc] from experimental
+337914a0 add utils/color_cache.[hc] from experimental
+b3bf8fe7 the read-overflow code-path wasn't reporting as an error
+1db888ba take colorspace into account when cropping
+61c2d51f move the rescaling code into its own file and make enc/ and dec/ use it.
+efc2016a Make rescaler methods generic
+3eacee81 Move rescaler methods out of io.c.
+a69b893d automake: replace 'silent-rules' w/AM_SILENT_RULES
+6f7bf645 issue 111: fix little-endian problem in bit-reader
+ed278e22 Removed unnecessary lookup
+cd8c3ba7 fix some warnings: down-cast and possibly-uninitialized variable
+0a7102ba ~1% improvement of alpha compression
+3bc1b141 Merge "Reformat container doc"
+dc17abdc mux: cosmetics
+cb5810df Merge "WebPMuxGetImage: allow image param to be NULL"
+506a4af2 mux: cosmetics
+135e8b19 WebPMuxGetImage: allow image param to be NULL
+de556b68 Merge "README.mux: reword some descriptions"
+0ee2aeb9 Makefile.vc: use batch mode rules
+d9acddc0 msvc: move {i,p}db creation to object directory
+237c9aa7 Merge "expose WebPFree function for DLL builds"
+b3e4054f silence msvc debug build warning
+45feb55d expose WebPFree function for DLL builds
+11316d84 README.mux: reword some descriptions
+4be52f4a factorize WebPMuxValidate
+14f6b9f6 mux: light cleanup
+5e96a5db add more param checks to WebPPictureDistortion()
+8abaf820 Merge "silence some type size related warnings"
+1601a39b silence some type size related warnings
+f3abe520 Merge "idec: simplify buffer size calculation"
+a9c5cd4c idec: simplify buffer size calculation
+7b06bd7f Merge "configure/automake: add silent-rules option"
+e9a7d145 Reformat container doc
+d4e5c7f3 configure/automake: add silent-rules option
+5081db78 configure/automake: no -version-info for convenience libs
+85b6ff68 Merge "idec: fix WebPIUpdate failure"
+7bb6a9cc idec: fix internal state corruption
+89cd1bb8 idec: fix WebPIUpdate failure
+01b63806 4-5% faster decoding, optimized byte loads in arithmetic decoder.
+631117ea Merge "cosmetics & warnings"
+a0b2736d cosmetics & warnings
+f73947f4 use 32bit for storing dequant coeffs, instead of 16b.
+b9600308 Merge "store prediction mode array as uint8_t[16], not int[16]."
+7b67881a store prediction mode array as uint8_t[16], not int[16].
+cab8d4dc Merge "NEON TransformOne"
+ba503fda NEON TransformOne
+9f740e3b Merge "gcc warning fix: remove the 'const' qualifier."
+f76d3587 gcc warning fix: remove the 'const' qualifier.
+e78478d6 Merge "webpmux: make more use of WebPData"
+f85bba3d Merge "manpages: add BUGS section"
+48a43bbf Merge "makefile.unix: variable cosmetics"
+c274dc96 makefile.unix: variable cosmetics
+1f7b8595 re-organize the error-handling in the main loop a bit
+1336fa71 Only recompute level_cost_[] when needed
+771ee449 manpages: add BUGS section
+0f7820e6 webpmux: make more use of WebPData
+974aaff3 examples: logging updates
+6c14aadd Merge "better token buffer code"
+f4054250 better token buffer code
+18d959fa Merge "mux: add WebPData type"
+eec4b877 mux: add WebPData type
+0de3096b use 16bit counters for recording proba counts
+7f23678d fix for LevelCost + little speed-up
+7107d544 further speed-up/cleanup of RecordCoeffs() and GetResidualCost()
+fd221040 Introduce Token buffer (unused for now)
+5fa148f4 Merge "speed-up GetResidualCost()"
+28a9d9b4 speed-up GetResidualCost()
+11e7dadd Merge "misc cosmetics"
+378086bd misc cosmetics
+d61479f9 add -print_psnr and -print_ssim options to cwebp.
+2e3e8b2e add a WebPCleanupTransparentArea() method
+552c1217 Merge "mux: plug some memory leaks on error"
+a2a81f7d Merge "fix Mach-O shared library build"
+b3482c43 Merge "fix gcc-4.0 apple 32-bit build"
+e4e3ec19 fix gcc-4.0 apple 32-bit build
+b0d2fecf mux: plug some memory leaks on error
+f0d2c7a7 pass of cosmetics
+b309a6f9 fix Mach-O shared library build
+241ddd38 doc: delete mux container pdf
+8b1ba272 doc: update VP8 decode guide link
+7e4371c5 WebPMuxCreate: fix unchecked malloc
+eb425586 Merge "have makefile.unix clean up src/webp/*~ too"
+a85c3631 Merge "correct EncodeAlpha documentation"
+a33842fd Merge "Update webp container spec with alpha filter options."
+8d6490da Incremental support for some of the mux APIs.
+b8375abd have makefile.unix clean up src/webp/*~ too
+b5855fc7 correct EncodeAlpha documentation
+dba37fea Update webp container spec with alpha filter options.
+2e74ec8b fix compile under MINGW
+716d1d7f fix suboptimal MAX_LEN cut-off limit
+57cab7b8 Harmonize the alpha-filter predictions at boundary
+3a989534 Merge "Fix bug for Alpha in RGBA_4444 color-mode."
+8ca2076d Introduce a 'fast' alpha mode
+221a06bb Fix bug for Alpha in RGBA_4444 color-mode.
+ad1e163a cosmetics: normalize copyright headers
+c77424d7 cosmetics: light include cleanup
+9d0e17c9 fix msvc build breakage after 252028a
+7c4c177c Some readability fixes for mux library
+d8a47e66 Merge "Add predictive filtering option for Alpha."
+252028aa Add predictive filtering option for Alpha.
+9b69be1c Merge "Simplify mux library code"
+a056170e Simplify mux library code
+992187a3 improve log2 test
+e852f832 update Android.mk file list
+a90cb2be reduce number of copies and mallocs in alpha plane enc/dec
+b1662b05 fix some more type conversion warnings w/MSVC
+223d8c60 fix some uint64_t -> int conversion warnings with MSC
+c1a0437b Merge "simplify checks for enabling SSE2 code"
+f06817aa simplify checks for enabling SSE2 code
+948d4fe9 silence a msvc build warning
+91179549 vwebp: msvc build tweaks
+7937b409 simple WebP viewer, based on OpenGL
+6aac1df1 add a bunch of missing 'extern "C"'
+421eb99d Merge "Remove assigned-but-not-used variable "br""
+91e27f45 better fitting names for upsampling functions
+a5d7ed5c Remove assigned-but-not-used variable "br"
+f62d2c94 remove unused 'has_alpha' from VP8GetInfo() signature
+08e86582 trap alpha-decoding error
+b361eca1 add cut-off to arith coder probability update.
+8666a93a Some bug-fixes for images with alpha.
+273a12a0 fix off-by-1 diff in case cropping and simple filtering
+2f741d1e webpmux: ReadImage: fix ptr free in error case
+721f3f48 fix alpha decode
+60942c8c fix the has_alpha_ order
+30971c9e Implement progress report (and user abort)
+eda520a9 cosmetics after 9523f2a
+38bd5bb5 Merge "Better alpha support in webpmux binary"
+ccbaebfe Merge "Updated the includes to relative paths."
+d71fbdcc fix small typo in error message array
+cdf97aa2 Better alpha support in webpmux binary
+885f25bc Updated the includes to relative paths.
+a0ec9aac Update WebP encoder (cwebp) to support Alpha.
+667b769a Fixed the include for types.h within mux.h
+9523f2a5 Add Alpha Encode support from WebPEncode.
+16612ddd Merge "Add Alpha Decode support from WebPDecode."
+d117a940 Add Alpha Decode support from WebPDecode.
+67228734 cosmetics after e1947a9
+e1947a92 Add Alpha encode/decode code.
+afc4c5d6 simplify code by introducing a CopyPlane() helper func
+113b3128 Merge "MUX API Updates"
+c398f595 MUX API Updates
+5acf04ef remove orphan source file
+059f03ef Merge "dec: validate colorspace before using as array index"
+70a03989 Merge "factorize some code"
+9b243b3d factorize some code
+372e2b46 Correct a bug in ReadPNG() with GRAY_ALPHA images
+469d6eb9 Merge "Makefile.am: remove redundant noinst_HEADERS"
+9fe3372f dec: validate colorspace before using as array index
+8962030f remove orphan source file
+ced3e3f4 Makefile.am: remove redundant noinst_HEADERS
+964387ed use WEBP_INLINE for inline function declarations
+90880a11 Merge "manpages: break long lines"
+b5910895 Merge "manpages: minor formatting updates"
+4c451e4a Merge "Rectify the Chunk parsing logic."
+04e84cf1 examples: slight cleanup
+099717ce manpages: break long lines
+1daf39bb manpages: minor formatting updates
+abd030b5 fix missing "(void)" in function signature
+f6a7d758 remove useless test
+f07b2138 Rectify the Chunk parsing logic.
+b8634f7d webpmux: fix lib link order
+42c2e682 Fix missing coma (on uncompiled code)
+d8329d41 Android.mk: add missing source files
+13a54df5 Merge "More aggressive copy-edit; add TODO; validate HTML5"
+868b96ae More aggressive copy-edit; add TODO; validate HTML5
+767afea2 configure: check for a symbol contained in libpng
+408b8918 Merge "Linewrap at 72 cols. Casual copy-edit."
+3ae318c7 Merge "Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)"
+918eb2d8 Merge "Basic container doc source clean-up; fix lists and pseudocode blocks."
+03bec9e0 Linewrap at 72 cols. Casual copy-edit.
+2678d819 Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)
+428674da Basic container doc source clean-up; fix lists and pseudocode blocks.
+6a77d928 Merge "Makefile.vc: cosmetics"
+28c38e8c Merge "Makefile.vc: condense directory creation rules"
+55be2cf8 Initial import of container spec document, from pdftotext transform.
+a82a788b Makefile.vc: cosmetics
+c8f41ce5 Makefile.vc: condense directory creation rules
+2b877cd0 Some fixes to Makefile.vc to support the src\mux directory.
+3eb969b3 Merge "Add Makefile.vc for Mux library & binary."
+e78e971e Add Makefile.vc for Mux library & binary.
+6aedde58 Add manual for WebPMux tool.
+8a360d0a Merge "Added WebPMux Binary."
+a4f32cae Added WebPMux Binary.
+f3bf4c76 Added Mux Container Spec & README for MUX-API.
+9f761cfa Changed function signature for WebPMuxCreate
+5f31b5ec Merge "Add Mux library for manipulating WebP container."
+2315785f Add Mux library for manipulating WebP container.
+7e198abb update ChangeLog (tag: v0.1.3)
+dfc9c1ea Harmonize the dates
+28ad70c5 Fix PNG decoding bug
+846e93c5 Update AUTHORS & add .mailmap
+563e52d6 cosmetics after '76036f5 Refactor decoder library'
+76036f54 Refactor decoder library
+377ef43c configure.ac: update AC_INIT params
+7a8d8762 use a user-visible MACRO for max width/height.
+d4e9f559 NEON decode support in WebP
+0ee683b5 update libtool version-info
+fdbe02c5 windows: match _cond_destroy logic w/return variable name
+206b686b README: correct advanced decode api pseudo-code
+6a32a0f5 make VP8BitReader a typedef, for better re-use
+b112e836 create a libwebputils under src/utils
+ee697d9f harmonize the include guards and #endif comments
+a1ec07a6 Fixing compiler error in non x86 arch.
+dcfa509a Fixed recursive inclusion of bit_writer.h and vp8enci.h.
+e06ac088 create a separate libwebpdsp under src/dsp
+ebeb412a use unsigned int for bitfields
+341cc56a make kNewRange a static array
+227a91e5 README: minor wording update
+05bd8e6a add man pages to dist
+812dfa1a bump up versions in preparations for 0.1.3
+a5b78c81 wrap alpha-related options under WEBP_EXPERIMENTAL_FEATURES flag
+34dc7907 regen ChangeLog for 0.1.3-rc2
+7c436630 Silence some (more) Visual Studio warnings.
+60306e8c add top-level gitattributes
+2aa6b80e Slience some Visual Studio warnings.
+4cbbb290 Merge "bump up version for next freeze"
+a3291674 bump up version for next freeze
+c7e86aba cosmetics: fix comment line lengths
+c9e037ab makefile.unix: add simple dist target
+87d58ce9 makefile.unix: rule maintenance
+d477de77 mend
+fac15ec7 Update NEWS & README for next release V0.1.3
+6215595c Merge "add a -partition_limit option to limit the number of bits used by intra4x4"
+3814b76c Merge "reorganize chunk-parsing code"
+900286e0 add a -partition_limit option to limit the number of bits used by intra4x4
+cd12b4b0 add the missing cost for I4/I16 mode selection
+dfcc2136 reorganize chunk-parsing code
+3cf20306 initialize pointers to function within VP8DspInit()
+d21b4795 Merge "windows: add decode threading support"
+473ae953 fix hang on thread creation failure
+fccca420 windows: add decode threading support
+a31f843a Use the exact PNG_INCLUDES/PNG_LIBS when testing for -lpng
+ad9b45f1 Merge "Makefile.vc: rule maintenance"
+565a2cab Makefile.vc: rule maintenance
+2d0da681 makefile.unix: disable Wvla by default
+fc7815d6 multi-thread decoding: ~25-30% faster
+acd8ba42 io->teardown() was not always called upon error
+c85527b1 Merge "Makefile.vc: add DLL configs"
+e1e9be35 cosmetics: spelling/grammar in README and lib headers
+b4d0ef8f Makefile.vc: add DLL configs
+998754a7 remove unused nb_i4_ and nb_i16_ fields.
+9f01ce3a rename WebPDecBuffer::memory -> private_memory
+fb5d659b fix an overflow bug in LUT calculation
+d646d5c7 swig: add WebPDecodeARGB
+78aeed40 add missing WebPDecodeARGBInto() and switch ARGB4444 to RGBA4444 as was intended
+cd7c5292 explicitly mark library functions as extern
+19db59f8 add support for RGB565, ARGB4444 and ARGB colorspace (decoder)
+c915fb2a encoder speed-up: hardcode special level values
+c558bdad Rename and improve the API to retrieve decoded area
+bf599d74 Merge "makefile.unix: disable -Wvla by default"
+c9ea03d7 SSE2 version of strong filtering
+993af3e2 makefile.unix: disable -Wvla by default
+3827e1bc Merge "examples: (windows/WIC) add alpha support"
+e291fae0 SSE2 functions for the fancy upsampler.
+a06bbe2e add WebPISetIOHooks() to set some custom hooks on the incremental decoder object.
+7643a6f2 Merge "makefile.unix: use uname to detect OSX environment"
+5142a0be export alpha channel (if present) when dumping to PGM format
+14d5731c makefile.unix: use uname to detect OSX environment
+08057062 examples: quiet warnings
+3cfe0888 examples: (windows/WIC) add alpha support
+13ed94b8 add compile warning for variable-length-array
+5a18eb1a Merge "add Advanced Decoding Interface"
+5c4f27f9 add missing \n
+f4c4e416 80 cols fix
+d2603105 add Advanced Decoding Interface
+bd2f65f6 sse2 version of the complex filter
+96ed9ce0 perform two idct transforms at a time when possible
+01af7b69 use aligned stored
+0e1d1fdf Merge "Makefile.vc: add experimental target"
+2a1292a6 Makefile.vc: add experimental target
+23bf351e Enable decode SSE2 for Visual Studio
+131a4b7b dec/dsp_sse2: fix visual studio compile
+00d9d680 swig: file reorganization
+7fc7e0d9 Merge "swig/java: basic encode support"
+3be57b16 fix MSVC compile for WEBP_EXPERIMENTAL_FEATURES
+40a7e347 dec/dsp: disable sse2 for Visual Studio builds
+e4d540c8 add SSE2 code for transform
+54f2170a swig/java: basic encode support
+c5d4584b call function pointers instead of C-version
+ea43f045 Merge "configure: mingw32 targets: test for WIC support"
+a11009d7 SSE2 version of simple in-loop filtering
+42548da9 shave one unneeded filter-cache line
+31f9dc6f configure: mingw32 targets: test for WIC support
+19559699 Merge "split expression in two."
+415dbe46 split expression in two.
+e29072a8 configure: test for zlib only w/--enable-experimental
+b2b0090b Simplify Visual Studio ifdefs
+ca7a2fd6 Add error reporting from encoding failures.
+6c9405db Merge "Makefile.vc: require CFG with clean target"
+0424ecd9 Makefile.vc: require CFG with clean target
+003417c7 Enable SSE2 for Visual Studio builds
+af10db4a little speed up for VP8BitUpdate()
+e71418f8 more MSVC files to ignore
+46d90363 cosmetics
+edf59ab3 typo fix
+72229f5f Add support for x64 and SSE2 builds under Windows.
+92e5c6e1 VP8GetInfo() + WebPResetDecParams()
+416b7a6b raise the fixed-point precision for the rescaler
+aa87e4e0 fix alignment
+eb66670c disable WEBP_EXPERIMENTAL_FEATURES
+c5ae7f65 typo fix: USE_ => WEBP_
+d041efae swig: add libwebp.jar/libwebp_java_wrap.c
+f6fb3877 add swig interface
+e9273902 align buffer for double too
+842c009b fix -strong option
+d0a70387 Merge "cosmetics"
+fc0a02e5 fix the dichotomy loop
+38369c03 cosmetics
+8dfc4c6f factorize and unify GetAlpha() between the C and SSE2 version
+6d0e66c2 prepare experimentation with yuv444 / 422
+79cc49f5 add a --enable-experimental option to './configure'
+d7575238 sse2 version of CollectHistogram()
+c1c728d6 add an extra #ifdef WEBP_EXPERIMENTAL_FEATURES to avoid 'unused variable' warning
+60c61d2d always call VP*EncDeleteAlpha() unconditionnally, for simplicity
+0f8c6384 simply don't call WriteExtensions() if WEBP_EXPERIMENTAL_FEATURES is not defined
+47c661d5 rename swap -> swap_rb
+10d55bbb move chunk[] declaration out of the for() loop
+517cec21 fix indentation
+f7d9e261 fix merge problems
+8fd42b3a add a stride 'a_stride' for the alpha plane
+b8dcbf2f fix alpha-plane copy and crop methods
+cdef89de fix some 'unused variable' warning
+fb29c262 SSE2 version of the fwd transform and the squared sum metric
+2ab4b72f EXPERIMENTAL: add support for alpha channel
+cfbf88a6 add SSE2 functions. ~2x faster encoding on average.
+e7ff3f9a merge two ITransforms together when applicable and change the TTransform to return the sum directly.
+ca554137 fix WebPIDecGetRGB() to accept any RGB(A) mode, not just MODE_RGB
+8aa50efd fix some 'man' typos
+d3f3bdda update ChangeLog (tag: v0.1.2)
+d7e9a69c update contributor list
+261abb8e add a 'superclean' section
+276ae825 Remove files not mean to be in git, and update .gitignore
+24868455 build: prepare libwebp.pc
+14ceb6e8 add "-version" description to man pages
+b247a3b2 Create the m4 directory, and also place .gitignore in there for libtool.
+cdd734c9 Resolve automake warnings
+c5fa726e build: add pkgconfig files
+b20aaca2 build: just use autoreconf, avoid calling tools manually
+4b0b0d66 cwebp: use modern functions
+efbc6c41 update Android.mk
+7777570b better version of ChangeLog
+fa70d2b7 update version number in the DOC
+f8db5d5d more C89-fixes
+0de013b3 fix typos
+650ffa3b add version getters for decoder and encoder
+be4867d2 doc for incremental decoding
+56732a1b add idec.obj in MSVC makefile
+208afb5e add c++ guards
+8bf76fe0 add incremental decoding
+1f288328 'inline' isn't defined in strict ansi c89
+8b77c632 move the quantization function to dsp.c
+b2c3575c add a 'last_y' field to WebPDecParams
+2654c3da correctly pass along the exact same status returned from ParsePartitions
+4704146a add missing precision in the man
+6d978a6c add error messages
+6463e6ab add some install instructions, and fix intel-mac flags
+05fb7bfc Merge ".gitignore: initial version"
+c33f0195 .gitignore: initial version
+e532b9ab Makefile: allow out of tree builds
+4c0da7aa enable sparse dc/ac transforms
+07dbb8d5 clarify the return logic
+5c69e1bb fix bigger-by-1 array
+7c5267e3 fix a (harmless) typo: non_zero_ -> non_zero_ac_
+bc752135 fix missing free()
+af3e2aaa remove trailing spaces
+13e50da6 make the bitreader preload at least 8bits, instead of post-load them (this makes initialization easier and will be helpful for incremental decoding). Modify ParsePartitions() to accommodate for truncated input.
+f4888f77 emit 9 - nb_bits trailing zeros instead of 8
+3db65255 separate block-parsing into a visible VP8DecodeMB()
+a871de02 add missing extern "C"
+b3ce8c52 remove a gcc warning about type pun by using a proper union'd type
+e1863715 update after addition of webpi.h
+3e856e2d Extract some useful functions around decoding buffer WebPDecParams.
+d5bc05a4 make the filtering process match libvpx and ffvp8
+dd60138d add man pages for cwebp(1) and dwebp(1)
+c4fa3644 fix header
+5b70b378 * add an option to bypass_filtering in VP8Io.
+b97a4003 simplify QuantizeBlock code a bit
+84b58ebb add more checks around picture allocation
+b65a3e10 remove absolute_delta_ field and syntax code
+0744e842 Dont' open output file until we're sure the input file is valid
+d5bd54c7 fix typo and buggy line
+f7a9549d Add a simple top-level makefile.unix for quick & easy build.
+5f36b944 update the doc for the -f option
+f61d14aa a WebP encoder converts PNG & JPEG to WebP
+81c96621 oops: forgotten call to Initialize() + move the error message to a more useful place
+87ffa005 typo: fix a missing 'R', was confusing.
+b04b857a * add decoding measurement using stopwatch.h (use -v option) * support PNG output through WIC on Win32
+746a4820 * make (*put)() hook return a bool for abort request. * add an enum for VP8Status() to make things clearer
+73c973e6 * strengthen riff/chunk size checks * don't consider odd-sized chunks being an error
+1dc4611a add support for PNG output (default) regularize include guards
+860641df fix a typo: sizeof(kYModeProbaInter0) => sizeof(kUVModeProbaInter0)
+3254fc52 fix some petty constness fix the ./configure file too
+504d3393 fix eof_ mis-initialization
+2bc0778f leftover Makefile.* from previous commit
+d2cf04e4 move Makefile.am one level below, to src/dec fix typos here and there dwebp is now an installed program
+ade92de8 typo: vp8.h -> decode_vp8.h
+d7241241 forgot to declare types.h to be installed
+6421a7a4 move the decoder sourcetree to a sub-location src/dec to make room for future libs sources
+a9b3eab6 correct layout name is IMC4.
+2330522c handle corner case of zero-dimensions
+280c3658 make VP8Init() handle short buffers (< 2 bytes) correctly
+b1c9e8b4 handle error cases more robustly
+0e94935c Merge "table-less version of clip_8b()"
+1e0a2d25 table-less version of clip_8b()
+e12109ee dwebp: change -yuv option to -raw change the layout to IMC2
+d72180a4 speed-up fancy upscaler
+9145f3bc reset eof_ at construction time
+a7ee0559 simplify the logic of GetCoeffs()
+f67b5939 lot of cosmetics
+ea27d7c6 fix endian problem on PowerPC
+beb0a1ba fix signature of VP8StoreBlock
+b128c5e2 Merge "fancy chroma upscaling"
+6a37a2aa fancy chroma upscaling
+ff565edc fix two numeric typos
+5a936a0a use uintptr_t for casting pointers to ints
+e14a0301 for cross_compiling=yes to prevent executing any binary
+83b545ee add vc9+ makefile
+296f6914 fix output loop for small height
+cbfbb5c3 convert to plain-C
+f09f96ee Fix declaration after statement warning
+5981ee55 Fix UV plane ac/dc quantizer transposition
+c8d15efa convert to ANSI-C
+c3f41cb4 Initial commit
diff --git a/src/third_party/libwebp/LICENSE b/src/third_party/libwebp/LICENSE
deleted file mode 100644
index 73395c9..0000000
--- a/src/third_party/libwebp/LICENSE
+++ /dev/null
@@ -1,52 +0,0 @@
-Copyright (c) 2010, 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.
-
-Additional IP Rights Grant (Patents)
-
-"This implementation" means the copyrightable works distributed by
-Google as part of the WebM Project.
-
-Google hereby grants to you a perpetual, worldwide, non-exclusive,
-no-charge, royalty-free, irrevocable (except as stated in this section)
-patent license to make, have made, use, offer to sell, sell, import,
-transfer, and otherwise run, modify and propagate the contents of this
-implementation of VP8, where such license applies only to those patent
-claims, both currently owned by Google and acquired in the future,
-licensable by Google that are necessarily infringed by this
-implementation of VP8. This grant does not include claims that would be
-infringed only as a consequence of further modification of this
-implementation. If you or your agent or exclusive licensee institute or
-order or agree to the institution of patent litigation against any
-entity (including a cross-claim or counterclaim in a lawsuit) alleging
-that this implementation of VP8 or any code incorporated within this
-implementation of VP8 constitutes direct or contributory patent
-infringement, or inducement of patent infringement, then any patent
-rights granted to you under this License for this implementation of VP8
-shall terminate as of the date such litigation is filed.
diff --git a/src/third_party/libwebp/Makefile.am b/src/third_party/libwebp/Makefile.am
new file mode 100644
index 0000000..3f73b13
--- /dev/null
+++ b/src/third_party/libwebp/Makefile.am
@@ -0,0 +1,9 @@
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = src imageio man
+EXTRA_DIST = COPYING autogen.sh
+
+if WANT_EXTRAS
+ SUBDIRS += extras
+endif
+
+SUBDIRS += examples
diff --git a/src/third_party/libwebp/Makefile.vc b/src/third_party/libwebp/Makefile.vc
new file mode 100644
index 0000000..5d1bf86
--- /dev/null
+++ b/src/third_party/libwebp/Makefile.vc
@@ -0,0 +1,517 @@
+#
+# Stem for static libs and DLLs
+#
+LIBWEBPDECODER_BASENAME = libwebpdecoder
+LIBWEBP_BASENAME = libwebp
+LIBWEBPMUX_BASENAME = libwebpmux
+LIBWEBPDEMUX_BASENAME = libwebpdemux
+
+!IFNDEF ARCH
+!IF ! [ cl 2>&1 | find "x86" > NUL ]
+ARCH = x86
+!ELSE IF ! [ cl 2>&1 | find "x64" > NUL ]
+ARCH = x64
+!ELSE IF ! [ cl 2>&1 | find "ARM" > NUL ]
+ARCH = ARM
+!ELSE
+!ERROR Unable to auto-detect toolchain architecture! \
+If cl.exe is in your PATH rerun nmake with ARCH=<arch>.
+!ENDIF
+!ENDIF
+
+!IF "$(ARCH)" == "x86"
+PLATFORM_LDFLAGS = /SAFESEH
+!ENDIF
+
+#############################################################
+## Nothing more to do below this line!
+
+NOLOGO = /nologo
+CCNODBG = cl.exe $(NOLOGO) /O2 /DNDEBUG
+CCDEBUG = cl.exe $(NOLOGO) /Od /Gm /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 = $(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
+
+!IF "$(ARCH)" == "ARM"
+CFLAGS = $(CFLAGS) /DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP /DWEBP_USE_THREAD
+!ELSE
+CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
+!ENDIF
+
+CFGSET = FALSE
+!IF "$(OBJDIR)" == ""
+OUTDIR = ..\obj\
+!ELSE
+OUTDIR = $(OBJDIR)
+!ENDIF
+
+!IF "$(HAVE_AVX2)" == "1"
+CFLAGS = $(CFLAGS) /DWEBP_HAVE_AVX2
+AVX2_FLAGS = /arch:AVX2
+!ENDIF
+
+##############################################################
+# Runtime library configuration
+!IF "$(RTLIBCFG)" == "static"
+RTLIB = /MT
+RTLIBD = /MTd
+!ELSE IF "$(RTLIBCFG)" == "legacy"
+RTLIBCFG = static
+RTLIB = /MT
+RTLIBD = /MTd
+CFLAGS = $(CFLAGS) /GS- /arch:IA32
+!ELSE
+RTLIB = /MD
+RTLIBD = /MDd
+!ENDIF
+DIRBASE = $(OUTDIR)\$(CFG)\$(ARCH)
+DIROBJ = $(DIRBASE)\obj
+DIRLIB = $(DIRBASE)\lib
+DIRINC = $(DIRBASE)\include
+DIRBIN = $(DIRBASE)\bin
+LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME).pdb
+OUTPUT_DIRS = $(DIRBIN) $(DIRINC) $(DIRLIB) \
+ $(DIROBJ)\dec \
+ $(DIROBJ)\demux \
+ $(DIROBJ)\dsp \
+ $(DIROBJ)\enc \
+ $(DIROBJ)\examples \
+ $(DIROBJ)\extras \
+ $(DIROBJ)\imageio \
+ $(DIROBJ)\mux \
+ $(DIROBJ)\utils \
+
+# Target configuration
+!IF "$(CFG)" == "release-static"
+CC = $(CCNODBG)
+STATICLIBBUILD = TRUE
+!ELSE IF "$(CFG)" == "debug-static"
+CC = $(CCDEBUG)
+RTLIB = $(RTLIBD)
+STATICLIBBUILD = TRUE
+LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
+LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
+LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
+LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+!ELSE IF "$(CFG)" == "release-dynamic"
+CC = $(CCNODBG)
+RC = $(RCNODBG)
+DLLBUILD = TRUE
+!ELSE IF "$(CFG)" == "debug-dynamic"
+CC = $(CCDEBUG)
+RC = $(RCDEBUG)
+RTLIB = $(RTLIBD)
+DLLBUILD = TRUE
+LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
+LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
+LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
+LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+!ENDIF
+
+!IF "$(STATICLIBBUILD)" == "TRUE"
+CC = $(CC) $(RTLIB)
+CFGSET = TRUE
+LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME).lib
+LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib
+LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
+LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib
+!ELSE IF "$(DLLBUILD)" == "TRUE"
+DLLINC = webp_dll.h
+CC = $(CC) /I$(DIROBJ) /FI$(DLLINC) $(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
+LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME)_dll.pdb
+CFGSET = TRUE
+!ENDIF
+
+#######################
+# Usage
+#
+!IF "$(CFGSET)" == "FALSE"
+!MESSAGE Usage: nmake /f Makefile.vc [CFG=<config>]
+!MESSAGE . [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [<target>]
+!MESSAGE
+!MESSAGE where <config> is one of:
+!MESSAGE - release-static - release static library
+!MESSAGE - debug-static - debug static library
+!MESSAGE - release-dynamic - release dynamic link library (DLL)
+!MESSAGE - debug-dynamic - debug dynamic link library (DLL)
+!MESSAGE
+!MESSAGE <target> may be:
+!MESSAGE - clean - perform a clean for CFG
+!MESSAGE - experimental - build CFG with experimental
+!MESSAGE . features enabled.
+!MESSAGE - (empty) - build libwebp-based targets for CFG
+!MESSAGE - all - build (de)mux-based targets for CFG
+!MESSAGE - gif2webp - requires libgif & >= VS2013
+!MESSAGE - anim_diff - requires libgif & >= VS2013
+!MESSAGE - anim_dump
+!MESSAGE
+!MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'.
+!MESSAGE 'legacy' will produce a Windows 2000 compatible library.
+!MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.),
+!MESSAGE defaults to ..\obj
+
+!IF "$(CFG)" != ""
+!MESSAGE
+!ERROR please choose a valid configuration instead of "$(CFG)"
+!ENDIF
+!ENDIF
+
+#######################
+# Rules
+#
+!IF "$(CFGSET)" == "TRUE"
+# A config was provided, so the library can be built.
+#
+
+DEC_OBJS = \
+ $(DIROBJ)\dec\alpha_dec.obj \
+ $(DIROBJ)\dec\buffer_dec.obj \
+ $(DIROBJ)\dec\frame_dec.obj \
+ $(DIROBJ)\dec\idec_dec.obj \
+ $(DIROBJ)\dec\io_dec.obj \
+ $(DIROBJ)\dec\quant_dec.obj \
+ $(DIROBJ)\dec\tree_dec.obj \
+ $(DIROBJ)\dec\vp8_dec.obj \
+ $(DIROBJ)\dec\vp8l_dec.obj \
+ $(DIROBJ)\dec\webp_dec.obj \
+
+DEMUX_OBJS = \
+ $(DIROBJ)\demux\anim_decode.obj \
+ $(DIROBJ)\demux\demux.obj \
+
+DSP_DEC_OBJS = \
+ $(DIROBJ)\dsp\alpha_processing.obj \
+ $(DIROBJ)\dsp\alpha_processing_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\alpha_processing_neon.obj \
+ $(DIROBJ)\dsp\alpha_processing_sse2.obj \
+ $(DIROBJ)\dsp\alpha_processing_sse41.obj \
+ $(DIROBJ)\dsp\cpu.obj \
+ $(DIROBJ)\dsp\dec.obj \
+ $(DIROBJ)\dsp\dec_clip_tables.obj \
+ $(DIROBJ)\dsp\dec_mips32.obj \
+ $(DIROBJ)\dsp\dec_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\dec_msa.obj \
+ $(DIROBJ)\dsp\dec_neon.obj \
+ $(DIROBJ)\dsp\dec_sse2.obj \
+ $(DIROBJ)\dsp\dec_sse41.obj \
+ $(DIROBJ)\dsp\filters.obj \
+ $(DIROBJ)\dsp\filters_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\filters_msa.obj \
+ $(DIROBJ)\dsp\filters_neon.obj \
+ $(DIROBJ)\dsp\filters_sse2.obj \
+ $(DIROBJ)\dsp\lossless.obj \
+ $(DIROBJ)\dsp\lossless_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\lossless_msa.obj \
+ $(DIROBJ)\dsp\lossless_neon.obj \
+ $(DIROBJ)\dsp\lossless_sse2.obj \
+ $(DIROBJ)\dsp\rescaler.obj \
+ $(DIROBJ)\dsp\rescaler_mips32.obj \
+ $(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\rescaler_msa.obj \
+ $(DIROBJ)\dsp\rescaler_neon.obj \
+ $(DIROBJ)\dsp\rescaler_sse2.obj \
+ $(DIROBJ)\dsp\upsampling.obj \
+ $(DIROBJ)\dsp\upsampling_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\upsampling_msa.obj \
+ $(DIROBJ)\dsp\upsampling_neon.obj \
+ $(DIROBJ)\dsp\upsampling_sse2.obj \
+ $(DIROBJ)\dsp\upsampling_sse41.obj \
+ $(DIROBJ)\dsp\yuv.obj \
+ $(DIROBJ)\dsp\yuv_mips32.obj \
+ $(DIROBJ)\dsp\yuv_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\yuv_neon.obj \
+ $(DIROBJ)\dsp\yuv_sse2.obj \
+ $(DIROBJ)\dsp\yuv_sse41.obj \
+
+DSP_ENC_OBJS = \
+ $(DIROBJ)\dsp\cost.obj \
+ $(DIROBJ)\dsp\cost_mips32.obj \
+ $(DIROBJ)\dsp\cost_mips_dsp_r2.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 \
+ $(DIROBJ)\dsp\enc_neon.obj \
+ $(DIROBJ)\dsp\enc_sse2.obj \
+ $(DIROBJ)\dsp\enc_sse41.obj \
+ $(DIROBJ)\dsp\lossless_enc.obj \
+ $(DIROBJ)\dsp\lossless_enc_mips32.obj \
+ $(DIROBJ)\dsp\lossless_enc_mips_dsp_r2.obj \
+ $(DIROBJ)\dsp\lossless_enc_msa.obj \
+ $(DIROBJ)\dsp\lossless_enc_neon.obj \
+ $(DIROBJ)\dsp\lossless_enc_sse2.obj \
+ $(DIROBJ)\dsp\lossless_enc_sse41.obj \
+ $(DIROBJ)\dsp\ssim.obj \
+ $(DIROBJ)\dsp\ssim_sse2.obj \
+
+EX_ANIM_UTIL_OBJS = \
+ $(DIROBJ)\examples\anim_util.obj \
+
+IMAGEIO_DEC_OBJS = \
+ $(DIROBJ)\imageio\image_dec.obj \
+ $(DIROBJ)\imageio\jpegdec.obj \
+ $(DIROBJ)\imageio\metadata.obj \
+ $(DIROBJ)\imageio\pngdec.obj \
+ $(DIROBJ)\imageio\pnmdec.obj \
+ $(DIROBJ)\imageio\tiffdec.obj \
+ $(DIROBJ)\imageio\webpdec.obj \
+ $(DIROBJ)\imageio\wicdec.obj \
+
+IMAGEIO_ENC_OBJS = \
+ $(DIROBJ)\imageio\image_enc.obj \
+
+EX_GIF_DEC_OBJS = \
+ $(DIROBJ)\examples\gifdec.obj \
+
+EX_UTIL_OBJS = \
+ $(DIROBJ)\examples\example_util.obj \
+
+ENC_OBJS = \
+ $(DIROBJ)\enc\alpha_enc.obj \
+ $(DIROBJ)\enc\analysis_enc.obj \
+ $(DIROBJ)\enc\backward_references_cost_enc.obj \
+ $(DIROBJ)\enc\backward_references_enc.obj \
+ $(DIROBJ)\enc\config_enc.obj \
+ $(DIROBJ)\enc\cost_enc.obj \
+ $(DIROBJ)\enc\filter_enc.obj \
+ $(DIROBJ)\enc\frame_enc.obj \
+ $(DIROBJ)\enc\histogram_enc.obj \
+ $(DIROBJ)\enc\iterator_enc.obj \
+ $(DIROBJ)\enc\near_lossless_enc.obj \
+ $(DIROBJ)\enc\picture_enc.obj \
+ $(DIROBJ)\enc\picture_csp_enc.obj \
+ $(DIROBJ)\enc\picture_psnr_enc.obj \
+ $(DIROBJ)\enc\picture_rescale_enc.obj \
+ $(DIROBJ)\enc\picture_tools_enc.obj \
+ $(DIROBJ)\enc\predictor_enc.obj \
+ $(DIROBJ)\enc\quant_enc.obj \
+ $(DIROBJ)\enc\syntax_enc.obj \
+ $(DIROBJ)\enc\token_enc.obj \
+ $(DIROBJ)\enc\tree_enc.obj \
+ $(DIROBJ)\enc\vp8l_enc.obj \
+ $(DIROBJ)\enc\webp_enc.obj \
+
+EXTRAS_OBJS = \
+ $(DIROBJ)\extras\extras.obj \
+ $(DIROBJ)\extras\quality_estimate.obj \
+
+IMAGEIO_UTIL_OBJS = \
+ $(DIROBJ)\imageio\imageio_util.obj \
+
+MUX_OBJS = \
+ $(DIROBJ)\mux\anim_encode.obj \
+ $(DIROBJ)\mux\muxedit.obj \
+ $(DIROBJ)\mux\muxinternal.obj \
+ $(DIROBJ)\mux\muxread.obj \
+
+UTILS_DEC_OBJS = \
+ $(DIROBJ)\utils\bit_reader_utils.obj \
+ $(DIROBJ)\utils\color_cache_utils.obj \
+ $(DIROBJ)\utils\filters_utils.obj \
+ $(DIROBJ)\utils\huffman_utils.obj \
+ $(DIROBJ)\utils\quant_levels_dec_utils.obj \
+ $(DIROBJ)\utils\rescaler_utils.obj \
+ $(DIROBJ)\utils\random_utils.obj \
+ $(DIROBJ)\utils\thread_utils.obj \
+ $(DIROBJ)\utils\utils.obj \
+
+UTILS_ENC_OBJS = \
+ $(DIROBJ)\utils\bit_writer_utils.obj \
+ $(DIROBJ)\utils\huffman_encode_utils.obj \
+ $(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)
+LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
+LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS)
+
+OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP)
+!IF "$(ARCH)" == "ARM"
+ex: $(OUT_LIBS)
+all: ex
+!ELSE
+OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe
+EXTRA_EXAMPLES = $(DIRBIN)\vwebp.exe $(DIRBIN)\webpmux.exe \
+ $(DIRBIN)\img2webp.exe $(DIRBIN)\get_disto.exe \
+ $(DIRBIN)\webp_quality.exe $(DIRBIN)\vwebp_sdl.exe \
+ $(DIRBIN)\webpinfo.exe
+
+ex: $(OUT_LIBS) $(OUT_EXAMPLES)
+all: ex $(EXTRA_EXAMPLES)
+# NB: gif2webp.exe and anim_diff.exe are excluded from 'all' as libgif requires
+# C99 support which is only available from VS2013 onward.
+gif2webp: $(DIRBIN)\gif2webp.exe
+anim_diff: $(DIRBIN)\anim_diff.exe
+anim_dump: $(DIRBIN)\anim_dump.exe
+
+$(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS)
+$(DIRBIN)\anim_diff.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\anim_diff.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\anim_dump.exe: $(DIROBJ)\examples\anim_dump.obj $(EX_ANIM_UTIL_OBJS)
+$(DIRBIN)\anim_dump.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\anim_dump.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
+$(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)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS)
+$(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS)
+$(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\dwebp.exe: $(LIBWEBPDEMUX)
+$(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS)
+$(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX)
+$(DIRBIN)\gif2webp.exe: $(LIBWEBP)
+$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS)
+$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\vwebp_sdl.obj
+$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\webp_to_sdl.obj
+$(DIRBIN)\vwebp_sdl.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
+$(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX)
+$(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
+$(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)\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)\webpinfo.exe: $(DIROBJ)\examples\webpinfo.obj
+$(DIRBIN)\webpinfo.exe: $(IMAGEIO_DEC_OBJS)
+$(DIRBIN)\webpinfo.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\webpinfo.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
+
+$(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP)
+$(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS)
+$(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(EXTRAS_OBJS): $(OUTPUT_DIRS)
+!ENDIF # ARCH == ARM
+
+$(LIBWEBPDECODER): $(LIBWEBPDECODER_OBJS)
+$(LIBWEBP): $(LIBWEBP_OBJS)
+$(LIBWEBPMUX): $(LIBWEBPMUX_OBJS)
+$(LIBWEBPDEMUX): $(LIBWEBPDEMUX_OBJS)
+
+$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): $(OUTPUT_DIRS)
+
+!IF "$(DLLBUILD)" == "TRUE"
+$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS): \
+ $(DIROBJ)\$(DLLINC)
+
+{$(DIROBJ)}.c{$(DIROBJ)}.obj:
+ $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@ $<
+
+{src}.rc{$(DIROBJ)}.res:
+ $(RC) /fo$@ $<
+{src\demux}.rc{$(DIROBJ)\demux}.res:
+ $(RC) /fo$@ $<
+{src\mux}.rc{$(DIROBJ)\mux}.res:
+ $(RC) /fo$@ $<
+
+$(LIBWEBP): $(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):
+ $(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):
+ $(LNKLIB) /out:$@ $**
+ -xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
+!ENDIF
+
+$(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
+$(DIROBJ)\examples\anim_dump.obj: examples\anim_dump.c
+ $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+ /Fo$(DIROBJ)\examples\ examples\$(@B).c
+$(DIROBJ)\examples\anim_util.obj: examples\anim_util.c
+ $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+ /Fo$(DIROBJ)\examples\ examples\$(@B).c
+$(DIROBJ)\examples\gif2webp.obj: examples\gif2webp.c
+ $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+ /Fo$(DIROBJ)\examples\ examples\$(@B).c
+$(DIROBJ)\examples\gifdec.obj: examples\gifdec.c
+ $(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+ /Fo$(DIROBJ)\examples\ examples\$(@B).c
+# Batch rules
+{examples}.c{$(DIROBJ)\examples}.obj::
+ $(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $<
+{extras}.c{$(DIROBJ)\extras}.obj::
+ $(CC) $(CFLAGS) /Fd$(DIROBJ)\extras\ /Fo$(DIROBJ)\extras\ $<
+{imageio}.c{$(DIROBJ)\imageio}.obj::
+ $(CC) $(CFLAGS) /Fd$(DIROBJ)\imageio\ /Fo$(DIROBJ)\imageio\ $<
+{src\dec}.c{$(DIROBJ)\dec}.obj::
+ $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
+{src\demux}.c{$(DIROBJ)\demux}.obj::
+ $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\demux\ $<
+{src\dsp}.c{$(DIROBJ)\dsp}.obj::
+ $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ $<
+{src\enc}.c{$(DIROBJ)\enc}.obj::
+ $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\enc\ $<
+{src\mux}.c{$(DIROBJ)\mux}.obj::
+ $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\mux\ $<
+{src\utils}.c{$(DIROBJ)\utils}.obj::
+ $(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\utils\ $<
+
+{$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe:
+ $(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
+ ole32.lib windowscodecs.lib shlwapi.lib
+ $(MT) -manifest $@.manifest -outputresource:$@;1
+ del $@.manifest
+
+{$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe:
+ $(LNKEXE) $(LDFLAGS) /OUT:$@ $** \
+ ole32.lib windowscodecs.lib shlwapi.lib
+ $(MT) -manifest $@.manifest -outputresource:$@;1
+ del $@.manifest
+
+clean::
+ @-erase /s $(DIROBJ)\*.dll 2> NUL
+ @-erase /s $(DIROBJ)\*.exp 2> NUL
+ @-erase /s $(DIROBJ)\*.idb 2> NUL
+ @-erase /s $(DIROBJ)\*.lib 2> NUL
+ @-erase /s $(DIROBJ)\*.obj 2> NUL
+ @-erase /s $(DIROBJ)\*.pch 2> NUL
+ @-erase /s $(DIROBJ)\*.pdb 2> NUL
+ @-erase /s $(DIROBJ)\*.res 2> NUL
+
+!ENDIF # End of case where a config was provided.
diff --git a/src/third_party/libwebp/NEWS b/src/third_party/libwebp/NEWS
new file mode 100644
index 0000000..480cb7d
--- /dev/null
+++ b/src/third_party/libwebp/NEWS
@@ -0,0 +1,199 @@
+- 4/2/2018: version 1.0.0
+ This is a binary compatible release.
+ * lossy encoder improvements to avoid chroma shifts in various circumstances
+ (issues #308, #340)
+ * big-endian fixes for decode, RGBA import and WebPPictureDistortion
+ Tool updates:
+ gifwebp, anim_diff - default duration behavior (<= 10ms) changed to match
+ web browsers, transcoding tools (issue #379)
+ img2webp, webpmux - allow options to be passed in via a file (issue #355)
+
+- 11/24/2017: version 0.6.1
+ This is a binary compatible release.
+ * lossless performance and compression improvements + a new 'cruncher' mode
+ (-m 6 -q 100)
+ * ARM performance improvements with clang (15-20% w/ndk r15c, issue #339)
+ * webp-js: emscripten/webassembly based javascript decoder
+ * miscellaneous bug & build fixes (issue #329, #332, #343, #353, #360, #361,
+ #363)
+ Tool updates / additions:
+ added webpinfo - prints file format information (issue #330)
+ gif2webp - loop behavior modified to match Chrome M63+ (crbug.com/649264);
+ '-loop_compatibility' can be used for the old behavior
+
+- 1/26/2017: version 0.6.0
+ * lossless performance and compression improvements
+ * miscellaneous performance improvements (SSE2, NEON, MSA)
+ * webpmux gained a -duration option allowing for frame timing modification
+ * new img2webp utility allowing a sequence of images to be converted to
+ animated webp
+ * API changes:
+ - libwebp:
+ WebPPictureSharpARGBToYUVA
+ WebPPlaneDistortion
+ - libwebpmux / gif2webp:
+ WebPAnimEncoderOptions: kmax <= 0 now disables keyframes, kmax == 1
+ forces all keyframes. See mux.h and the gif2webp
+ manpage for details.
+
+- 12/13/2016: version 0.5.2
+ This is a binary compatible release.
+ This release covers CVE-2016-8888 and CVE-2016-9085.
+ * further security related hardening in the tools; fixes to
+ gif2webp/AnimEncoder (issues #310, #314, #316, #322), cwebp/libwebp (issue
+ #312)
+ * full libwebp (encoder & decoder) iOS framework; libwebpdecoder
+ WebP.framework renamed to WebPDecoder.framework (issue #307)
+ * CMake support for Android Studio (2.2)
+ * miscellaneous build related fixes (issue #306, #313)
+ * miscellaneous documentation improvements (issue #225)
+ * minor lossy encoder fixes and improvements
+
+- 6/14/2016: version 0.5.1
+ This is a binary compatible release.
+ * miscellaneous bug fixes (issues #280, #289)
+ * reverted alpha plane encoding with color cache for compatibility with
+ libwebp 0.4.0->0.4.3 (issues #291, #298)
+ * lossless encoding performance improvements
+ * memory reduction in both lossless encoding and decoding
+ * force mux output to be in the extended format (VP8X) when undefined chunks
+ are present (issue #294)
+ * gradle, cmake build support
+ * workaround for compiler bug causing 64-bit decode failures on android
+ devices using clang-3.8 in the r11c NDK
+ * various WebPAnimEncoder improvements
+
+- 12/17/2015: version 0.5.0
+ * miscellaneous bug & build fixes (issues #234, #258, #274, #275, #278)
+ * encoder & decoder speed-ups on x86/ARM/MIPS for lossy & lossless
+ - note! YUV->RGB conversion was sped-up, but the results will be slightly
+ different from previous releases
+ * various lossless encoder improvements
+ * gif2webp improvements, -min_size option added
+ * tools fully support input from stdin and output to stdout (issue #168)
+ * New WebPAnimEncoder API for creating animations
+ * New WebPAnimDecoder API for decoding animations
+ * other API changes:
+ - libwebp:
+ WebPPictureSmartARGBToYUVA() (-pre 4 in cwebp)
+ WebPConfig::exact (-exact in cwebp; -alpha_cleanup is now the default)
+ WebPConfig::near_lossless (-near_lossless in cwebp)
+ WebPFree() (free'ing webp allocated memory in other languages)
+ WebPConfigLosslessPreset()
+ WebPMemoryWriterClear()
+ - libwebpdemux: removed experimental fragment related fields and functions
+ - libwebpmux: WebPMuxSetCanvasSize()
+ * new libwebpextras library with some uncommon import functions:
+ WebPImportGray/WebPImportRGB565/WebPImportRGB4444
+
+- 10/15/15: version 0.4.4
+ This is a binary compatible release.
+ * rescaling out-of-bounds read fix (issue #254)
+ * various build fixes and improvements (issues #253, #259, #262, #267, #268)
+ * container documentation update
+ * gif2webp transparency fix (issue #245)
+
+- 3/3/15: version 0.4.3
+ This is a binary compatible release.
+ * Android / gcc / iOS / MSVS build fixes and improvements
+ * lossless decode fix (issue #239 -- since 0.4.0)
+ * documentation / vwebp updates for animation
+ * multi-threading fix (issue #234)
+
+- 10/13/14: version 0.4.2
+ This is a binary compatible release.
+ * Android / gcc build fixes
+ * (Windows) fix reading from stdin and writing to stdout
+ * gif2webp: miscellaneous fixes
+ * fix 'alpha-leak' with lossy compression (issue #220)
+ * the lossless bitstream spec has been amended to reflect the current code
+
+- 7/24/14: version 0.4.1
+ This is a binary compatible release.
+ * AArch64 (arm64) & MIPS support/optimizations
+ * NEON assembly additions:
+ - ~25% faster lossy decode / encode (-m 4)
+ - ~10% faster lossless decode
+ - ~5-10% faster lossless encode (-m 3/4)
+ * dwebp/vwebp can read from stdin
+ * cwebp/gif2webp can write to stdout
+ * cwebp can read webp files; useful if storing sources as webp lossless
+
+- 12/19/13: version 0.4.0
+ * improved gif2webp tool
+ * numerous fixes, compression improvement and speed-up
+ * dither option added to decoder (dwebp -dither 50 ...)
+ * improved multi-threaded modes (-mt option)
+ * improved filtering strength determination
+ * New function: WebPMuxGetCanvasSize
+ * BMP and TIFF format output added to 'dwebp'
+ * Significant memory reduction for decoding lossy images with alpha.
+ * Intertwined decoding of RGB and alpha for a shorter
+ time-to-first-decoded-pixel.
+ * WebPIterator has a new member 'has_alpha' denoting whether the frame
+ contains transparency.
+ * Container spec amended with new 'blending method' for animation.
+
+- 6/13/13: version 0.3.1
+ This is a binary compatible release.
+ * Add incremental decoding support for images containing ALPH and ICCP chunks.
+ * Python bindings via swig for the simple encode/decode interfaces similar to
+ Java.
+
+- 3/20/13: version 0.3.0
+ This is a binary compatible release.
+ * WebPINewRGB/WebPINewYUVA accept being passed a NULL output buffer
+ and will perform auto-allocation.
+ * default filter option is now '-strong -f 60'
+ * encoding speed-up for lossy methods 3 to 6
+ * alpha encoding can be done in parallel to lossy using 'cwebp -mt ...'
+ * color profile, metadata (XMP/EXIF) and animation support finalized in the
+ container.
+ * various NEON assembly additions
+ Tool updates / additions:
+ * gif2webp added
+ * vwebp given color profile & animation support
+ * cwebp can preserve color profile / metadata with '-metadata'
+
+- 10/30/12: version 0.2.1
+ * Various security related fixes
+ * cwebp.exe: fix import errors on Windows XP
+ * enable DLL builds for mingw targets
+
+- 8/3/12: version 0.2.0
+ * Add support for ARGB -> YUVA conversion for lossless decoder
+ New functions: WebPINewYUVA, WebPIDecGetYUVA
+ * Add stats for lossless and alpha encoding
+ * Security related hardening: allocation and size checks
+ * Add PAM output support to dwebp
+
+- 7/19/12: version 0.1.99
+ * This is a pre-release of 0.2.0, not an rc to allow for further
+ incompatible changes based on user feedback.
+ * Alpha channel encode/decode support.
+ * Lossless encoder/decoder.
+ * Add TIFF input support to cwebp.
+ Incompatible changes:
+ * The encode ABI has been modified to support alpha encoding.
+ * Deprecated function WebPINew() has been removed.
+ * Decode function signatures have changed to consistently use size_t over
+ int/uint32_t.
+ * decode_vp8.h is no longer installed system-wide.
+ * cwebp will encode the alpha channel if present.
+
+- 9/19/11: version 0.1.3
+ * Advanced decoding APIs.
+ * On-the-fly cropping and rescaling of images.
+ * SSE2 instructions for decoding performance optimizations on x86 based
+ platforms.
+ * Support Multi-threaded decoding.
+ * 40% improvement in Decoding performance.
+ * Add support for RGB565, RGBA4444 & ARGB image colorspace.
+ * Better handling of large picture encoding.
+
+- 3/25/11: version 0.1.2
+ * Incremental decoding: picture can be decoded byte-by-byte if needs be.
+ * lot of bug-fixes, consolidation and stabilization
+
+- 2/23/11: initial release of version 0.1, with the new encoder
+- 9/30/10: initial release version with only the lightweight decoder
diff --git a/src/third_party/libwebp/PATENTS b/src/third_party/libwebp/PATENTS
new file mode 100644
index 0000000..caedf60
--- /dev/null
+++ b/src/third_party/libwebp/PATENTS
@@ -0,0 +1,23 @@
+Additional IP Rights Grant (Patents)
+------------------------------------
+
+"These implementations" means the copyrightable works that implement the WebM
+codecs distributed by Google as part of the WebM Project.
+
+Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
+royalty-free, irrevocable (except as stated in this section) patent license to
+make, have made, use, offer to sell, sell, import, transfer, and otherwise
+run, modify and propagate the contents of these implementations of WebM, where
+such license applies only to those patent claims, both currently owned by
+Google and acquired in the future, licensable by Google that are necessarily
+infringed by these implementations of WebM. This grant does not include claims
+that would be infringed only as a consequence of further modification of these
+implementations. If you or your agent or exclusive licensee institute or order
+or agree to the institution of patent litigation or any other patent
+enforcement activity against any entity (including a cross-claim or
+counterclaim in a lawsuit) alleging that any of these implementations of WebM
+or any code incorporated within any of these implementations of WebM
+constitute direct or contributory patent infringement, or inducement of
+patent infringement, then any patent rights granted to you under this License
+for these implementations of WebM shall terminate as of the date such
+litigation is filed.
diff --git a/src/third_party/libwebp/README b/src/third_party/libwebp/README
new file mode 100644
index 0000000..a76b378
--- /dev/null
+++ b/src/third_party/libwebp/README
@@ -0,0 +1,782 @@
+ __ __ ____ ____ ____
+ / \\/ \/ _ \/ _ )/ _ \
+ \ / __/ _ \ __/
+ \__\__/\____/\_____/__/ ____ ___
+ / _/ / \ \ / _ \/ _/
+ / \_/ / / \ \ __/ \__
+ \____/____/\_____/_____/____/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/src/third_party/libwebp/README.chromium b/src/third_party/libwebp/README.chromium
deleted file mode 100644
index 94ca1cf..0000000
--- a/src/third_party/libwebp/README.chromium
+++ /dev/null
@@ -1,22 +0,0 @@
-Name: WebP image encoder/decoder
-Short Name: libwebp
-URL: http://developers.google.com/speed/webp
-Version: v0.3.1
-License: BSD
-License File: LICENSE
-Security Critical: Yes
-
-Description:
-Source archive:
- http://code.google.com/p/webp/downloads/detail?name=libwebp-0.3.1.tar.gz
-
-WebP is an image format that does both lossy and lossless compression of
-digital photographic images. WebP consists of a codec based on VP8, that Google
-open-sourced in May 2010 and a container based on RIFF. Webmasters, web
-developers and browser developers can use WebP to compress, archive and
-distribute digital images more efficiently.
-
-Local changes:
- * Removed examples/, documentation and build related files, keeping only
- the contents of src/ less mux/ which is unused.
- * Merged COPYING/PATENTS to LICENSE
diff --git a/src/third_party/libwebp/README.mux b/src/third_party/libwebp/README.mux
new file mode 100644
index 0000000..bd4f92f
--- /dev/null
+++ b/src/third_party/libwebp/README.mux
@@ -0,0 +1,227 @@
+ __ __ ____ ____ ____ __ __ _ __ __
+ / \\/ \/ _ \/ _ \/ _ \/ \ \/ \___/_ / _\
+ \ / __/ _ \ __/ / / (_/ /__
+ \__\__/\_____/_____/__/ \__//_/\_____/__/___/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/src/third_party/libwebp/README.webp_js b/src/third_party/libwebp/README.webp_js
new file mode 100644
index 0000000..2805354
--- /dev/null
+++ b/src/third_party/libwebp/README.webp_js
@@ -0,0 +1,76 @@
+ __ __ ____ ____ ____ __ ____
+ / \\/ \ _ \ _ \ _ \ (__)/ __\
+ \ / __/ _ \ __/ _) \_ \
+ \__\__/_____/____/_/ /____/____/
+
+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/src/third_party/libwebp/autogen.sh b/src/third_party/libwebp/autogen.sh
new file mode 100755
index 0000000..8ef9bab
--- /dev/null
+++ b/src/third_party/libwebp/autogen.sh
@@ -0,0 +1,2 @@
+#! /bin/sh -e
+exec autoreconf -fi
diff --git a/src/third_party/libwebp/build.gradle b/src/third_party/libwebp/build.gradle
new file mode 100644
index 0000000..88ad129
--- /dev/null
+++ b/src/third_party/libwebp/build.gradle
@@ -0,0 +1,439 @@
+// Define dependencies.
+buildscript {
+ repositories {
+ maven {
+ url "https://jcenter.bintray.com"
+ }
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}"
+ }
+}
+
+// Define versions in the project.
+project.ext {
+ buildToolsVersion = "${BUILD_TOOLS_VERSION}"
+ compileSdkVersion = COMPILE_SDK_VERSION.toInteger()
+}
+
+// Core libraries and executables.
+apply plugin: "c"
+def NEON
+model {
+ buildTypes {
+ debug
+ release
+ }
+ platforms {
+ arm {
+ architecture "arm"
+ }
+ arm64 {
+ architecture "arm64"
+ }
+ x86 {
+ architecture "x86"
+ }
+ x64 {
+ architecture "x86_64"
+ }
+ mips32r2
+ mips32r5
+ mips64r6
+ }
+ toolChains {
+ gcc(Gcc) {
+ target("mips32r2") {
+ cCompiler.args "-mips32r2"
+ }
+ target("mips32r5") {
+ cCompiler.args "-mips32r5"
+ }
+ target("mips64r6") {
+ cCompiler.args "-mips64r6"
+ }
+ }
+ }
+ binaries {
+ all {
+ if (toolChain in Gcc) {
+ cCompiler.args "-fPIC"
+ cCompiler.args "-Wall"
+ cCompiler.define "ANDROID"
+ cCompiler.define "HAVE_MALLOC_H"
+ }
+ // Optimizations.
+ if (buildType == buildTypes.release) {
+ if (toolChain in Gcc) {
+ cCompiler.args "-finline-functions"
+ cCompiler.args "-ffast-math"
+ cCompiler.args "-ffunction-sections"
+ cCompiler.args "-fdata-sections"
+ }
+ if (toolChain in Clang) {
+ cCompiler.args "-frename-registers -s"
+ }
+ }
+ // mips32 fails to build with clang from r14b
+ // https://bugs.chromium.org/p/webp/issues/detail?id=343
+ if (toolChain in Clang) {
+ if (getTargetPlatform() == "mips") {
+ cCompiler.args "-no-integrated-as"
+ }
+ }
+ // Check for NEON usage.
+ if (getTargetPlatform() == "arm") {
+ NEON = "c.neon"
+ cCompiler.define "HAVE_CPU_FEATURES_H"
+ } else {
+ NEON = "c"
+ }
+
+ cCompiler.args "-I" + file(".").absolutePath
+ }
+ // Link to pthread for shared libraries.
+ withType(SharedLibraryBinarySpec) {
+ if (toolChain in Gcc) {
+ cCompiler.define "HAVE_PTHREAD"
+ cCompiler.define "WEBP_USE_THREAD"
+ linker.args "-pthread"
+ }
+ }
+ }
+ components {
+ webp(NativeLibrarySpec) {
+ sources {
+ c {
+ source {
+ srcDir "src/dec"
+ include "alpha_dec.c"
+ include "buffer_dec.c"
+ include "frame_dec.c"
+ include "idec_dec.c"
+ include "io_dec.c"
+ include "quant_dec.c"
+ include "tree_dec.c"
+ include "vp8_dec.c"
+ include "vp8l_dec.c"
+ include "webp_dec.c"
+ srcDir "src/dsp"
+ include "alpha_processing.c"
+ include "alpha_processing_mips_dsp_r2.c"
+ include "alpha_processing_neon.$NEON"
+ include "alpha_processing_sse2.c"
+ include "alpha_processing_sse41.c"
+ include "cpu.c"
+ include "dec.c"
+ include "dec_clip_tables.c"
+ include "dec_mips32.c"
+ include "dec_mips_dsp_r2.c"
+ include "dec_msa.c"
+ include "dec_neon.$NEON"
+ include "dec_sse2.c"
+ include "dec_sse41.c"
+ include "filters.c"
+ include "filters_mips_dsp_r2.c"
+ include "filters_msa.c"
+ include "filters_neon.$NEON"
+ include "filters_sse2.c"
+ include "lossless.c"
+ include "lossless_mips_dsp_r2.c"
+ include "lossless_msa.c"
+ include "lossless_neon.$NEON"
+ include "lossless_sse2.c"
+ include "rescaler.c"
+ include "rescaler_mips32.c"
+ include "rescaler_mips_dsp_r2.c"
+ include "rescaler_msa.c"
+ include "rescaler_neon.$NEON"
+ include "rescaler_sse2.c"
+ include "upsampling.c"
+ include "upsampling_mips_dsp_r2.c"
+ include "upsampling_msa.c"
+ include "upsampling_neon.$NEON"
+ include "upsampling_sse2.c"
+ include "upsampling_sse41.c"
+ include "yuv.c"
+ include "yuv_mips32.c"
+ include "yuv_mips_dsp_r2.c"
+ include "yuv_neon.$NEON"
+ include "yuv_sse2.c"
+ include "yuv_sse41.c"
+ srcDir "src/utils"
+ include "bit_reader_utils.c"
+ include "color_cache_utils.c"
+ include "filters_utils.c"
+ include "huffman_utils.c"
+ include "quant_levels_dec_utils.c"
+ include "random_utils.c"
+ include "rescaler_utils.c"
+ include "thread_utils.c"
+ include "utils.c"
+ srcDir "src/dsp"
+ include "cost.c"
+ include "cost_mips32.c"
+ include "cost_mips_dsp_r2.c"
+ 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"
+ include "enc_neon.$NEON"
+ include "enc_sse2.c"
+ include "enc_sse41.c"
+ include "lossless_enc.c"
+ include "lossless_enc_mips32.c"
+ include "lossless_enc_mips_dsp_r2.c"
+ include "lossless_enc_msa.c"
+ include "lossless_enc_neon.$NEON"
+ include "lossless_enc_sse2.c"
+ include "lossless_enc_sse41.c"
+ include "ssim.c"
+ include "ssim_sse2.c"
+ srcDir "src/enc"
+ include "alpha_enc.c"
+ include "analysis_enc.c"
+ include "backward_references_cost_enc.c"
+ include "backward_references_enc.c"
+ include "config_enc.c"
+ include "cost_enc.c"
+ include "filter_enc.c"
+ include "frame_enc.c"
+ include "histogram_enc.c"
+ include "iterator_enc.c"
+ include "near_lossless_enc.c"
+ include "picture_enc.c"
+ include "picture_csp_enc.c"
+ include "picture_psnr_enc.c"
+ include "picture_rescale_enc.c"
+ include "picture_tools_enc.c"
+ include "predictor_enc.c"
+ include "quant_enc.c"
+ include "syntax_enc.c"
+ include "token_enc.c"
+ include "tree_enc.c"
+ include "vp8l_enc.c"
+ include "webp_enc.c"
+ srcDir "src/utils"
+ include "bit_writer_utils.c"
+ include "huffman_encode_utils.c"
+ include "quant_levels_utils.c"
+ }
+ exportedHeaders {
+ srcDir "src"
+ }
+ }
+ }
+ }
+
+ webpdemux(NativeLibrarySpec) {
+ sources {
+ c {
+ source {
+ srcDir "src/demux"
+ include "anim_decode.c"
+ include "demux.c"
+ }
+ }
+ }
+ }
+
+ webpmux(NativeLibrarySpec) {
+ sources {
+ c {
+ source {
+ srcDir "src/mux/"
+ include "anim_encode.c"
+ include "muxedit.c"
+ include "muxinternal.c"
+ include "muxread.c"
+ }
+ }
+ }
+ }
+
+ // Executables from examples.
+ example_util(NativeLibrarySpec) {
+ binaries {
+ all {
+ lib library: "webp", linkage: "static"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./examples"
+ include "example_util.c"
+ }
+ }
+ }
+ }
+
+ imageio_util(NativeLibrarySpec) {
+ binaries {
+ all {
+ lib library: "webp", linkage: "static"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./imageio"
+ include "imageio_util.c"
+ }
+ }
+ }
+ }
+
+ imagedec(NativeLibrarySpec) {
+ binaries {
+ all {
+ lib library: "webpdemux", linkage: "static"
+ lib library: "webp", linkage: "static"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./imageio"
+ include "image_dec.c"
+ include "jpegdec.c"
+ include "metadata.c"
+ include "pngdec.c"
+ include "pnmdec.c"
+ include "tiffdec.c"
+ include "webpdec.c"
+ }
+ }
+ }
+ }
+
+ imageenc(NativeLibrarySpec) {
+ binaries {
+ all {
+ lib library: "webp", linkage: "static"
+ lib library: "imageio_util", linkage: "static"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./imageio"
+ include "image_enc.c"
+ }
+ }
+ }
+ }
+
+ cwebp(NativeExecutableSpec) {
+ binaries {
+ all {
+ lib library: "example_util", linkage: "static"
+ lib library: "imagedec", linkage: "static"
+ lib library: "imageio_util", linkage: "static"
+ lib library: "webpdemux", linkage: "static"
+ lib library: "webp", linkage: "static"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./examples"
+ include "cwebp.c"
+ }
+ }
+ }
+ }
+
+ dwebp(NativeExecutableSpec) {
+ binaries {
+ all {
+ lib library: "example_util", linkage: "static"
+ lib library: "imagedec", linkage: "static"
+ lib library: "imageenc", linkage: "static"
+ lib library: "imageio_util", linkage: "static"
+ lib library: "webpdemux", linkage: "static"
+ lib library: "webp"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./examples"
+ include "dwebp.c"
+ }
+ }
+ }
+ }
+
+ webpmux_example(NativeExecutableSpec) {
+ binaries {
+ all {
+ lib library: "example_util", linkage: "static"
+ lib library: "imageio_util", linkage: "static"
+ lib library: "webpmux", linkage: "static"
+ lib library: "webp"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./examples"
+ include "webpmux.c"
+ }
+ }
+ }
+ }
+
+ img2webp_example(NativeExecutableSpec) {
+ binaries {
+ all {
+ lib library: "example_util", linkage: "static"
+ lib library: "imagedec", linkage: "static"
+ lib library: "imageio_util", linkage: "static"
+ lib library: "webpmux", linkage: "static"
+ lib library: "webpdemux", linkage: "static"
+ lib library: "webp"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./examples"
+ include "img2webp.c"
+ }
+ }
+ }
+ }
+
+ webpinfo_example(NativeExecutableSpec) {
+ binaries {
+ all {
+ lib library: "example_util", linkage: "static"
+ lib library: "imageio_util", linkage: "static"
+ lib library: "webp"
+ }
+ }
+ sources {
+ c {
+ source {
+ srcDir "./examples"
+ include "webpinfo.c"
+ }
+ }
+ }
+ }
+ }
+ tasks {
+ // Task to test all possible configurations.
+ buildAllExecutables(Task) {
+ dependsOn $.binaries.findAll { it.buildable }
+ }
+ }
+}
+
+// Task to generate the wrapper.
+task wrapper(type: Wrapper) {
+ gradleVersion = '2.13'
+}
diff --git a/src/third_party/libwebp/cmake/WebPConfig.cmake.in b/src/third_party/libwebp/cmake/WebPConfig.cmake.in
new file mode 100644
index 0000000..ef3df2f
--- /dev/null
+++ b/src/third_party/libwebp/cmake/WebPConfig.cmake.in
@@ -0,0 +1,6 @@
+@PACKAGE_INIT@
+
+set(WebP_INCLUDE_DIRS "webp")
+set(WEBP_INCLUDE_DIRS ${WebP_INCLUDE_DIRS})
+set(WebP_LIBRARIES "@INSTALLED_LIBRARIES@")
+set(WEBP_LIBRARIES "${WebP_LIBRARIES}")
diff --git a/src/third_party/libwebp/cmake/config.h.in b/src/third_party/libwebp/cmake/config.h.in
new file mode 100644
index 0000000..ec84acd
--- /dev/null
+++ b/src/third_party/libwebp/cmake/config.h.in
@@ -0,0 +1,156 @@
+/* Adapted from the autotools src/webp/config.h.in. */
+
+/* Define if building universal (internal helper macro) */
+/* TODO: handle properly in CMake */
+#cmakedefine AC_APPLE_UNIVERSAL_BUILD 1
+
+/* Set to 1 if __builtin_bswap16 is available */
+#cmakedefine HAVE_BUILTIN_BSWAP16 1
+
+/* Set to 1 if __builtin_bswap32 is available */
+#cmakedefine HAVE_BUILTIN_BSWAP32 1
+
+/* Set to 1 if __builtin_bswap64 is available */
+#cmakedefine HAVE_BUILTIN_BSWAP64 1
+
+/* 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
+
+/* Define to 1 if you have the <wincodec.h> header file. */
+#cmakedefine HAVE_WINCODEC_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+#cmakedefine HAVE_WINDOWS_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+/* TODO: handle properly in CMake */
+#cmakedefine LT_OBJDIR "@LT_OBJDIR@"
+
+/* Name of package */
+#cmakedefine PACKAGE "@PROJECT_NAME@"
+
+/* Define to the address where bug reports for this package should be sent. */
+#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
+
+/* Define to the full name of this package. */
+#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@"
+
+/* Define to the full name and version of this package. */
+#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@"
+
+/* Define to the one symbol short name of this package. */
+#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@"
+
+/* Define to the home page for this package. */
+#cmakedefine PACKAGE_URL "@PACKAGE_URL@"
+
+/* 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
+
+/* Set to 1 if OpenGL is supported */
+#cmakedefine WEBP_HAVE_GL 1
+
+/* Set to 1 if JPEG library is installed */
+#cmakedefine WEBP_HAVE_JPEG 1
+
+/* Set to 1 if NEON is supported */
+#cmakedefine WEBP_HAVE_NEON
+
+/* Set to 1 if runtime detection of NEON is enabled */
+/* TODO: handle properly in CMake */
+#cmakedefine WEBP_HAVE_NEON_RTCD
+
+/* Set to 1 if PNG library is installed */
+#cmakedefine WEBP_HAVE_PNG 1
+
+/* Set to 1 if SDL library is installed */
+#cmakedefine WEBP_HAVE_SDL 1
+
+/* Set to 1 if SSE2 is supported */
+#cmakedefine WEBP_HAVE_SSE2 1
+
+/* Set to 1 if SSE4.1 is supported */
+#cmakedefine WEBP_HAVE_SSE41 1
+
+/* Set to 1 if TIFF library is installed */
+#cmakedefine WEBP_HAVE_TIFF 1
+
+/* Enable near lossless encoding */
+#cmakedefine WEBP_NEAR_LOSSLESS 1
+
+/* Undefine this to disable thread support. */
+#cmakedefine WEBP_USE_THREAD 1
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
diff --git a/src/third_party/libwebp/cmake/cpu.cmake b/src/third_party/libwebp/cmake/cpu.cmake
new file mode 100644
index 0000000..5aa1bfd
--- /dev/null
+++ b/src/third_party/libwebp/cmake/cpu.cmake
@@ -0,0 +1,125 @@
+## Check for SIMD extensions.
+include(CMakePushCheckState)
+
+function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
+ if(NOT ENABLE_SIMD)
+ message(STATUS "Disabling ${WEBP_SIMD_FLAG} optimization.")
+ set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE)
+ return()
+ endif()
+ unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE)
+ cmake_push_check_state()
+ set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
+ check_c_source_compiles("
+ #include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
+ int main(void) {
+ #if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
+ this is not valid code
+ #endif
+ return 0;
+ }
+ " 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)
+ else()
+ set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE)
+ endif()
+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(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")
+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).
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon ")
+ endif()
+endif()
+
+list(LENGTH WEBP_SIMD_FLAGS WEBP_SIMD_FLAGS_LENGTH)
+math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1")
+
+foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
+ list(GET WEBP_SIMD_FLAGS ${I_SIMD} WEBP_SIMD_FLAG)
+
+ # First try with no extra flag added as the compiler might have default flags
+ # (especially on Android).
+ unset(WEBP_HAVE_${WEBP_SIMD_FLAG} CACHE)
+ cmake_push_check_state()
+ set(CMAKE_REQUIRED_FLAGS)
+ 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)
+ set(CMAKE_REQUIRED_FLAGS ${SIMD_COMPILE_FLAG})
+ webp_check_compiler_flag(${WEBP_SIMD_FLAG} ${WEBP_ENABLE_SIMD})
+ else()
+ set(SIMD_COMPILE_FLAG " ")
+ 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}"
+ )
+ if(WEBP_HAVE_${WEBP_SIMD_FLAG})
+ # Memorize the file and flags.
+ foreach(FILE ${SIMD_FILES})
+ list(APPEND WEBP_SIMD_FILES_TO_INCLUDE ${FILE})
+ list(APPEND WEBP_SIMD_FLAGS_TO_INCLUDE ${SIMD_COMPILE_FLAG})
+ endforeach()
+ else()
+ # Remove the file from the list.
+ foreach(FILE ${SIMD_FILES})
+ list(APPEND WEBP_SIMD_FILES_NOT_TO_INCLUDE ${FILE})
+ endforeach()
+ # Explicitly disable SIMD.
+ if(SIMD_DISABLE_FLAGS)
+ list(GET SIMD_DISABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
+ include(CheckCCompilerFlag)
+ if(SIMD_COMPILE_FLAG)
+ unset(HAS_COMPILE_FLAG CACHE)
+ check_c_compiler_flag(${SIMD_COMPILE_FLAG} HAS_COMPILE_FLAG)
+ if(HAS_COMPILE_FLAG)
+ # Do one more check for Clang to circumvent CMake issue 13194.
+ if(COMMAND check_compiler_flag_common_patterns)
+ # Only in CMake 3.0 and above.
+ check_compiler_flag_common_patterns(COMMON_PATTERNS)
+ else()
+ 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}
+ )
+ if(NOT FLAG_${SIMD_COMPILE_FLAG})
+ unset(HAS_COMPILE_FLAG CACHE)
+ endif()
+ endif()
+ if(HAS_COMPILE_FLAG)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILE_FLAG}")
+ endif()
+ endif()
+ endif()
+ endif()
+ cmake_pop_check_state()
+endforeach()
diff --git a/src/third_party/libwebp/cmake/deps.cmake b/src/third_party/libwebp/cmake/deps.cmake
new file mode 100644
index 0000000..3d5d10a
--- /dev/null
+++ b/src/third_party/libwebp/cmake/deps.cmake
@@ -0,0 +1,166 @@
+# Generate the config.h to compile with specific intrinsics / libs.
+
+## Check for compiler options.
+include(CheckCSourceCompiles)
+check_c_source_compiles("
+ int main(void) {
+ (void)__builtin_bswap16(0);
+ return 0;
+ }
+ "
+ HAVE_BUILTIN_BSWAP16
+)
+check_c_source_compiles("
+ int main(void) {
+ (void)__builtin_bswap32(0);
+ return 0;
+ }
+ "
+ HAVE_BUILTIN_BSWAP32
+)
+check_c_source_compiles("
+ int main(void) {
+ (void)__builtin_bswap64(0);
+ return 0;
+ }
+ "
+ 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")
+ 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})
+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 the standard C math library.
+find_library(MATH_LIBRARY NAMES m)
+if(MATH_LIBRARY)
+ list(APPEND WEBP_DEP_LIBRARIES ${MATH_LIBRARY})
+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})
+ 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("
+ #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)
+ endif()
+endif()
+
+## 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)
+
+# Windows specifics
+if(HAVE_WINCODEC_H)
+ list(APPEND WEBP_DEP_LIBRARIES shlwapi ole32 windowscodecs)
+endif()
+
+## Check for SIMD extensions.
+include(${CMAKE_CURRENT_LIST_DIR}/cpu.cmake)
+
+## 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}
+)
+function(strip_bracket VAR)
+ string(LENGTH ${${VAR}} TMP_LEN)
+ math(EXPR TMP_LEN ${TMP_LEN}-2)
+ string(SUBSTRING ${${VAR}} 1 ${TMP_LEN} TMP_SUB)
+ set(${VAR} ${TMP_SUB} PARENT_SCOPE)
+endfunction()
+
+list(GET CONFIGURE_AC_PACKAGE_INFO 1 PACKAGE_VERSION)
+strip_bracket(PACKAGE_VERSION)
+list(GET CONFIGURE_AC_PACKAGE_INFO 2 PACKAGE_BUGREPORT)
+strip_bracket(PACKAGE_BUGREPORT)
+list(GET CONFIGURE_AC_PACKAGE_INFO 3 PACKAGE_URL)
+strip_bracket(PACKAGE_URL)
+
+# Build more info.
+set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
+set(PACKAGE_TARNAME ${PACKAGE_NAME})
+set(VERSION ${PACKAGE_VERSION})
diff --git a/src/third_party/libwebp/configure.ac b/src/third_party/libwebp/configure.ac
new file mode 100644
index 0000000..896e5ff
--- /dev/null
+++ b/src/third_party/libwebp/configure.ac
@@ -0,0 +1,802 @@
+AC_INIT([libwebp], [1.0.0],
+ [https://bugs.chromium.org/p/webp],,
+ [http://developers.google.com/speed/webp])
+AC_CANONICAL_HOST
+AC_PREREQ([2.60])
+AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
+
+dnl === automake >= 1.12 requires this for 'unusual archivers' support.
+dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL).
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+
+AC_PROG_LIBTOOL
+AC_PROG_SED
+AM_PROG_CC_C_O
+
+dnl === Enable less verbose output when building.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl == test endianness
+AC_C_BIGENDIAN
+
+dnl === SET_IF_UNSET(shell_var, value)
+dnl === Set the shell variable 'shell_var' to 'value' if it is unset.
+AC_DEFUN([SET_IF_UNSET], [test "${$1+set}" = "set" || $1=$2])
+
+AC_ARG_ENABLE([everything],
+ 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_libwebpdemux], [$enableval])
+ SET_IF_UNSET([enable_libwebpextras], [$enableval])
+ SET_IF_UNSET([enable_libwebpmux], [$enableval])])
+
+dnl === If --enable-asserts is not defined, define NDEBUG
+
+AC_MSG_CHECKING(whether asserts are enabled)
+AC_ARG_ENABLE([asserts],
+ AS_HELP_STRING([--enable-asserts],
+ [Enable assert checks]))
+if test "x${enable_asserts-no}" = "xno"; then
+ AM_CPPFLAGS="${AM_CPPFLAGS} -DNDEBUG"
+fi
+AC_MSG_RESULT(${enable_asserts-no})
+AC_SUBST([AM_CPPFLAGS])
+
+AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR],
+ [Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]),
+ [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
+AC_SUBST([pkgconfigdir])
+
+dnl === TEST_AND_ADD_CFLAGS(var, flag)
+dnl === Checks whether $CC supports 'flag' and adds it to 'var'
+dnl === on success.
+AC_DEFUN([TEST_AND_ADD_CFLAGS],
+ [SAVED_CFLAGS="$CFLAGS"
+ CFLAGS="-Werror $2"
+ AC_MSG_CHECKING([whether $CC supports $2])
+ dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition.
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])],
+ [AC_MSG_RESULT([yes])]
+ dnl Simply append the variable avoiding a
+ dnl compatibility ifdef for AS_VAR_APPEND as this
+ dnl variable shouldn't grow all that large.
+ [$1="${$1} $2"],
+ [AC_MSG_RESULT([no])])
+ CFLAGS="$SAVED_CFLAGS"])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-fvisibility=hidden])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wconstant-conversion])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wfloat-conversion])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-nonliteral])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-security])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wparentheses-equality])
+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])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wvla])
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62040
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61622
+AS_IF([test "$GCC" = "yes" ], [
+ gcc_version=`$CC -dumpversion`
+ gcc_wht_bug=""
+ case "$host_cpu" in
+ aarch64|arm64)
+ case "$gcc_version" in
+ 4.9|4.9.0|4.9.1) gcc_wht_bug=yes ;;
+ esac
+ esac
+ AS_IF([test "$gcc_wht_bug" = "yes"], [
+ TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-frename-registers])])])
+# Use -flax-vector-conversions, if available, when building intrinsics with
+# older versions of gcc. The flag appeared in 4.3.x, but if backported, and
+# -fno-lax-vector-conversions is set, errors may occur with the intrinsics
+# files along with the older system includes, e.g., emmintrin.h.
+# Originally observed with cc (GCC) 4.2.1 20070831 patched [FreeBSD] (9.3).
+# https://bugs.chromium.org/p/webp/issues/detail?id=274
+AS_IF([test "$GCC" = "yes" ], [
+ case "$host_cpu" in
+ amd64|i?86|x86_64)
+ AC_COMPILE_IFELSE(
+ dnl only check for -flax-vector-conversions with older gcc, skip
+ dnl clang as it reports itself as 4.2.1, but the flag isn't needed.
+ [AC_LANG_SOURCE([#if !defined(__clang__) && defined(__GNUC__) && \
+ ((__GNUC__ << 8) | __GNUC_MINOR__) < 0x403
+ #error old gcc
+ #endif
+ int main(void) { return 0; }
+ ])],,
+ [TEST_AND_ADD_CFLAGS([INTRINSICS_CFLAGS],
+ [-flax-vector-conversions])])
+ ;;
+ esac])
+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
+ @<:@default=auto@:>@]))
+
+AS_IF([test "x$enable_sse4_1" != "xno" -a "x$enable_sse2" != "xno"], [
+ SSE41_FLAGS="$INTRINSICS_CFLAGS $SSE41_FLAGS"
+ TEST_AND_ADD_CFLAGS([SSE41_FLAGS], [-msse4.1])
+ AS_IF([test -n "$SSE41_FLAGS"], [
+ SAVED_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $SSE41_FLAGS"
+ AC_CHECK_HEADER([smmintrin.h],
+ [AC_DEFINE(WEBP_HAVE_SSE41, [1],
+ [Set to 1 if SSE4.1 is supported])],
+ [SSE41_FLAGS=""])
+ CFLAGS=$SAVED_CFLAGS])
+ AC_SUBST([SSE41_FLAGS])])
+
+AC_ARG_ENABLE([sse2],
+ AS_HELP_STRING([--disable-sse2],
+ [Disable detection of SSE2 support
+ @<:@default=auto@:>@]))
+
+AS_IF([test "x$enable_sse2" != "xno"], [
+ SSE2_FLAGS="$INTRINSICS_CFLAGS $SSE2_FLAGS"
+ TEST_AND_ADD_CFLAGS([SSE2_FLAGS], [-msse2])
+ AS_IF([test -n "$SSE2_FLAGS"], [
+ SAVED_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $SSE2_FLAGS"
+ AC_CHECK_HEADER([emmintrin.h],
+ [AC_DEFINE(WEBP_HAVE_SSE2, [1],
+ [Set to 1 if SSE2 is supported])],
+ [SSE2_FLAGS=""])
+ CFLAGS=$SAVED_CFLAGS])
+ AC_SUBST([SSE2_FLAGS])])
+
+AC_ARG_ENABLE([neon],
+ AS_HELP_STRING([--disable-neon],
+ [Disable detection of NEON support
+ @<:@default=auto@:>@]))
+
+AC_ARG_ENABLE([neon_rtcd],
+ AS_HELP_STRING([--disable-neon-rtcd],
+ [Disable runtime detection of NEON support via
+ /proc/cpuinfo on Linux hosts
+ @<:@default=auto@:>@]))
+# For ARM(7) hosts:
+# Both NEON flags unset and NEON support detected = build all modules with NEON
+# NEON detected with the use of -mfpu=neon = build only NEON modules with NEON
+AS_IF([test "x$enable_neon" != "xno"], [
+ case "$host_cpu" in
+ arm|armv7*)
+ # Test for NEON support without flags before falling back to -mfpu=neon
+ for flag in '' '-mfpu=neon'; do
+ LOCAL_NEON_FLAGS="$INTRINSICS_CFLAGS $NEON_FLAGS"
+ TEST_AND_ADD_CFLAGS([LOCAL_NEON_FLAGS], [$flag])
+ SAVED_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $LOCAL_NEON_FLAGS"
+
+ dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition.
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+ #include <arm_neon.h>
+ int main(void) {
+ int8x8_t v = vdup_n_s8(0);
+ (void)v;
+ return 0;
+ }])],
+ [NEON_FLAGS="$(echo $LOCAL_NEON_FLAGS | $SED 's/^ *//')"
+ AS_IF([test -n "$NEON_FLAGS"], [
+ AS_IF([test "${host_os%%-*}" = "linux" -o \
+ "x$enable_neon_rtcd" = "xno"], [
+ CFLAGS=$SAVED_CFLAGS
+ AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
+ break
+ ],[
+ AC_MSG_WARN(m4_normalize([NEON runtime cpu-detection is
+ unavailable for ${host_os%%-*}. Force
+ with CFLAGS=-mfpu=neon or
+ --disable-neon-rtcd.]))
+ enable_neon_rtcd=no
+ NEON_FLAGS=""
+ ])
+ ],[
+ CFLAGS=$SAVED_CFLAGS
+ AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
+ break
+ ])])
+ CFLAGS=$SAVED_CFLAGS
+ done
+
+ AS_IF([test -n "$NEON_FLAGS"], [
+ # If NEON is available and rtcd is disabled apply NEON_FLAGS globally.
+ AS_IF([test "x$enable_neon_rtcd" = "xno"], [
+ AM_CFLAGS="$AM_CFLAGS $NEON_FLAGS"
+ NEON_FLAGS=""],
+ [AC_DEFINE(WEBP_HAVE_NEON_RTCD, [1],
+ [Set to 1 if runtime detection of NEON is enabled])])])
+
+ case "$host_os" in
+ *android*) AC_CHECK_HEADERS([cpu-features.h]) ;;
+ esac
+ ;;
+ esac
+ AC_SUBST([NEON_FLAGS])])
+
+dnl === CLEAR_LIBVARS([var_pfx])
+dnl === Clears <var_pfx>_{INCLUDES,LIBS}.
+AC_DEFUN([CLEAR_LIBVARS], [$1_INCLUDES=""; $1_LIBS=""])
+
+dnl === WITHLIB_OPTION([opt_pfx], [outvar_pfx])
+dnl === Defines --with-<opt_pfx>{include,lib}dir options which set
+dnl === the variables <outvar_pfx>_{INCLUDES,LIBS}.
+AC_DEFUN([WITHLIB_OPTION],
+ [AC_ARG_WITH([$1includedir],
+ AS_HELP_STRING([--with-$1includedir=DIR],
+ [use $2 includes from DIR]),
+ $2_INCLUDES="-I$withval")
+ AC_ARG_WITH([$1libdir],
+ AS_HELP_STRING([--with-$1libdir=DIR],
+ [use $2 libraries from DIR]),
+ [$2_LIBS="-L$withval"])])
+
+dnl === LIBCHECK_PROLOGUE([var_pfx])
+dnl === Caches the current values of CPPFLAGS/LIBS in SAVED_* then
+dnl === prepends the current values with <var_pfx>_{INCLUDES,LIBS}.
+AC_DEFUN([LIBCHECK_PROLOGUE],
+ [SAVED_CPPFLAGS=$CPPFLAGS
+ SAVED_LIBS=$LIBS
+ CPPFLAGS="$$1_INCLUDES $CPPFLAGS"
+ LIBS="$$1_LIBS $LIBS"])
+
+dnl === LIBCHECK_EPILOGUE([var_pfx])
+dnl === Restores the values of CPPFLAGS/LIBS from SAVED_* and exports
+dnl === <var_pfx>_{INCLUDES,LIBS} with AC_SUBST.
+AC_DEFUN([LIBCHECK_EPILOGUE],
+ [AC_SUBST($1_LIBS)
+ AC_SUBST($1_INCLUDES)
+ CPPFLAGS=$SAVED_CPPFLAGS
+ LIBS=$SAVED_LIBS])
+
+dnl === Check for gcc builtins
+
+dnl === CHECK_FOR_BUILTIN([builtin], [param], [define])
+dnl === links a C AC_LANG_PROGRAM, with <builtin>(<param>)
+dnl === AC_DEFINE'ing <define> if successful.
+AC_DEFUN([CHECK_FOR_BUILTIN],
+ [AC_LANG_PUSH([C])
+ AC_MSG_CHECKING([for $1])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [(void)$1($2)])],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE([$3], [1],
+ [Set to 1 if $1 is available])],
+ [AC_MSG_RESULT([no])]),
+ AC_LANG_POP])
+
+dnl AC_CHECK_FUNC doesn't work with builtin's.
+CHECK_FOR_BUILTIN([__builtin_bswap16], [1u << 15], [HAVE_BUILTIN_BSWAP16])
+CHECK_FOR_BUILTIN([__builtin_bswap32], [1u << 31], [HAVE_BUILTIN_BSWAP32])
+CHECK_FOR_BUILTIN([__builtin_bswap64], [1ull << 63], [HAVE_BUILTIN_BSWAP64])
+
+dnl === Check for pthread support
+AC_ARG_ENABLE([threading],
+ AS_HELP_STRING([--disable-threading],
+ [Disable detection of thread support]),,
+ [enable_threading=yes])
+if test "$enable_threading" = "yes"; then
+ AC_MSG_NOTICE([checking for threading support...])
+ AX_PTHREAD([AC_DEFINE([WEBP_USE_THREAD], [1],
+ [Undefine this to disable thread support.])
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+ ],
+ [AC_CHECK_FUNC([_beginthreadex],
+ [AC_DEFINE([WEBP_USE_THREAD], [1],
+ [Undefine this to disable thread
+ support.])],
+ [enable_threading=no])])
+fi
+AC_MSG_NOTICE([checking if threading is enabled... ${enable_threading-no}])
+
+dnl === check for OpenGL/GLUT support ===
+
+AC_ARG_ENABLE([gl], AS_HELP_STRING([--disable-gl],
+ [Disable detection of OpenGL support
+ @<:@default=auto@:>@]))
+AS_IF([test "x$enable_gl" != "xno"], [
+ CLEAR_LIBVARS([GL])
+ WITHLIB_OPTION([gl], [GL])
+
+ LIBCHECK_PROLOGUE([GL])
+
+ glut_cflags="none"
+ glut_ldflags="none"
+ case $host_os in
+ darwin*)
+ # Special case for OSX builds. Append these to give the user a chance to
+ # override with --with-gl*
+ glut_cflags="$glut_cflags|-framework GLUT -framework OpenGL"
+ glut_ldflags="$glut_ldflags|-framework GLUT -framework OpenGL"
+ # quiet deprecation warnings for glut
+ TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wno-deprecated-declarations])
+ ;;
+ esac
+
+ GLUT_SAVED_CPPFLAGS="$CPPFLAGS"
+ SAVED_IFS="$IFS"
+ IFS="|"
+ for flag in $glut_cflags; do
+ # restore IFS immediately as the autoconf macros may need the default.
+ IFS="$SAVED_IFS"
+ unset ac_cv_header_GL_glut_h
+ unset ac_cv_header_OpenGL_glut_h
+
+ case $flag in
+ none) ;;
+ *) CPPFLAGS="$flag $CPPFLAGS";;
+ esac
+ AC_CHECK_HEADERS([GL/glut.h GLUT/glut.h OpenGL/glut.h],
+ [glut_headers=yes;
+ test "$flag" = "none" || GL_INCLUDES="$CPPFLAGS";
+ break])
+ CPPFLAGS="$GLUT_SAVED_CPPFLAGS"
+ test "$glut_headers" = "yes" && break
+ done
+ IFS="$SAVED_IFS"
+
+ if test "$glut_headers" = "yes"; then
+ AC_LANG_PUSH([C])
+ GLUT_SAVED_LDFLAGS="$LDFLAGS"
+ SAVED_IFS="$IFS"
+ IFS="|"
+ for flag in $glut_ldflags; do
+ # restore IFS immediately as the autoconf macros may need the default.
+ IFS="$SAVED_IFS"
+ unset ac_cv_search_glBegin
+
+ case $flag in
+ none) ;;
+ *) LDFLAGS="$flag $LDFLAGS";;
+ esac
+
+ # find libGL
+ GL_SAVED_LIBS="$LIBS"
+ AC_SEARCH_LIBS([glBegin], [GL OpenGL opengl32])
+ LIBS="$GL_SAVED_LIBS"
+
+ # A direct link to libGL may not be necessary on e.g., linux.
+ GLUT_SAVED_LIBS="$LIBS"
+ for lib in "" "-lglut" "-lglut $ac_cv_search_glBegin"; do
+ LIBS="$lib"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([
+ #ifdef __cplusplus
+ # define EXTERN_C extern "C"
+ #else
+ # define EXTERN_C
+ #endif
+ EXTERN_C char glOrtho();
+ EXTERN_C char glutMainLoop();
+ ],[
+ glOrtho();
+ glutMainLoop();
+ ])
+ ],
+ AC_DEFINE(WEBP_HAVE_GL, [1],
+ [Set to 1 if OpenGL is supported])
+ [glut_support=yes], []
+ )
+ if test "$glut_support" = "yes"; then
+ GL_LIBS="$LDFLAGS $lib"
+ break
+ fi
+ done
+ LIBS="$GLUT_SAVED_LIBS"
+ LDFLAGS="$GLUT_SAVED_LDFLAGS"
+ test "$glut_support" = "yes" && break
+ done
+ IFS="$SAVED_IFS"
+ AC_LANG_POP
+ fi
+
+ LIBCHECK_EPILOGUE([GL])
+
+ if test "$glut_support" = "yes" -a "$enable_libwebpdemux" = "yes"; then
+ build_vwebp=yes
+ fi
+])
+AM_CONDITIONAL([BUILD_VWEBP], [test "$build_vwebp" = "yes"])
+
+dnl === check for SDL support ===
+
+AC_ARG_ENABLE([sdl],
+ AS_HELP_STRING([--disable-sdl],
+ [Disable detection of SDL support
+ @<:@default=auto@:>@]))
+AS_IF([test "x$enable_sdl" != "xno"], [
+ CLEAR_LIBVARS([SDL])
+ AC_PATH_PROGS([LIBSDL_CONFIG], [sdl-config])
+ if test -n "$LIBSDL_CONFIG"; then
+ SDL_INCLUDES=`$LIBSDL_CONFIG --cflags`
+ SDL_LIBS="`$LIBSDL_CONFIG --libs`"
+ fi
+
+ WITHLIB_OPTION([sdl], [SDL])
+
+ sdl_header="no"
+ LIBCHECK_PROLOGUE([SDL])
+ AC_CHECK_HEADER([SDL/SDL.h], [sdl_header="SDL/SDL.h"],
+ [AC_CHECK_HEADER([SDL.h], [sdl_header="SDL.h"],
+ [AC_MSG_WARN(SDL library not available - no sdl.h)])])
+ if test x"$sdl_header" != "xno"; then
+ AC_LANG_PUSH(C)
+ SDL_SAVED_LIBS="$LIBS"
+ for lib in "" "-lSDL" "-lSDLmain -lSDL"; do
+ LIBS="$SDL_SAVED_LIBS $lib"
+ # Perform a full link to ensure SDL_main is resolved if needed.
+ AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([
+ #include <$sdl_header>
+ int main(int argc, char** argv) {
+ SDL_Init(0);
+ return 0;
+ }])],
+ [SDL_LIBS="$LDFLAGS $LIBS"
+ SDL_INCLUDES="$SDL_INCLUDES -DWEBP_HAVE_SDL"
+ AC_DEFINE(WEBP_HAVE_SDL, [1],
+ [Set to 1 if SDL library is installed])
+ sdl_support=yes]
+ )
+ if test x"$sdl_support" = "xyes"; then
+ break
+ fi
+ done
+ # LIBS is restored by LIBCHECK_EPILOGUE
+ AC_LANG_POP
+ if test x"$sdl_header" = "xSDL.h"; then
+ SDL_INCLUDES="$SDL_INCLUDES -DWEBP_HAVE_JUST_SDL_H"
+ fi
+ fi
+ LIBCHECK_EPILOGUE([SDL])
+
+ if test x"$sdl_support" = "xyes"; then
+ build_vwebp_sdl=yes
+ else
+ AC_MSG_WARN(Optional SDL library not found)
+ fi
+])
+
+AM_CONDITIONAL([BUILD_VWEBP_SDL], [test "$build_vwebp_sdl" = "yes"])
+
+dnl === check for PNG support ===
+
+AC_ARG_ENABLE([png], AS_HELP_STRING([--disable-png],
+ [Disable detection of PNG format support
+ @<:@default=auto@:>@]))
+AS_IF([test "x$enable_png" != "xno"], [
+ CLEAR_LIBVARS([PNG])
+ AC_PATH_PROGS([LIBPNG_CONFIG],
+ [libpng-config libpng16-config libpng15-config libpng14-config \
+ libpng12-config])
+ if test -n "$LIBPNG_CONFIG"; then
+ PNG_INCLUDES=`$LIBPNG_CONFIG --cflags`
+ PNG_LIBS="`$LIBPNG_CONFIG --ldflags`"
+ fi
+
+ WITHLIB_OPTION([png], [PNG])
+
+ LIBCHECK_PROLOGUE([PNG])
+ AC_CHECK_HEADER(png.h,
+ AC_SEARCH_LIBS(png_get_libpng_ver, [png],
+ [test "$ac_cv_search_png_get_libpng_ver" = "none required" \
+ || PNG_LIBS="$PNG_LIBS $ac_cv_search_png_get_libpng_ver"
+ PNG_INCLUDES="$PNG_INCLUDES -DWEBP_HAVE_PNG"
+ AC_DEFINE(WEBP_HAVE_PNG, [1],
+ [Set to 1 if PNG library is installed])
+ png_support=yes
+ ],
+ [AC_MSG_WARN(Optional png library not found)
+ PNG_LIBS=""
+ PNG_INCLUDES=""
+ ],
+ [$MATH_LIBS]),
+ [AC_MSG_WARN(png library not available - no png.h)
+ PNG_LIBS=""
+ PNG_INCLUDES=""
+ ],
+ )
+ LIBCHECK_EPILOGUE([PNG])
+])
+
+dnl === check for JPEG support ===
+
+AC_ARG_ENABLE([jpeg],
+ AS_HELP_STRING([--disable-jpeg],
+ [Disable detection of JPEG format support
+ @<:@default=auto@:>@]))
+AS_IF([test "x$enable_jpeg" != "xno"], [
+ CLEAR_LIBVARS([JPEG])
+ WITHLIB_OPTION([jpeg], [JPEG])
+
+ LIBCHECK_PROLOGUE([JPEG])
+ AC_CHECK_HEADER(jpeglib.h,
+ AC_CHECK_LIB(jpeg, jpeg_set_defaults,
+ [JPEG_LIBS="$JPEG_LIBS -ljpeg"
+ JPEG_INCLUDES="$JPEG_INCLUDES -DWEBP_HAVE_JPEG"
+ AC_DEFINE(WEBP_HAVE_JPEG, [1],
+ [Set to 1 if JPEG library is installed])
+ jpeg_support=yes
+ ],
+ AC_MSG_WARN(Optional jpeg library not found),
+ [$MATH_LIBS]),
+ AC_MSG_WARN(jpeg library not available - no jpeglib.h)
+ )
+ LIBCHECK_EPILOGUE([JPEG])
+])
+
+dnl === check for TIFF support ===
+
+AC_ARG_ENABLE([tiff],
+ AS_HELP_STRING([--disable-tiff],
+ [Disable detection of TIFF format support
+ @<:@default=auto@:>@]))
+AS_IF([test "x$enable_tiff" != "xno"], [
+ CLEAR_LIBVARS([TIFF])
+ WITHLIB_OPTION([tiff], [TIFF])
+
+ LIBCHECK_PROLOGUE([TIFF])
+ AC_CHECK_HEADER(tiffio.h,
+ AC_CHECK_LIB(tiff, TIFFGetVersion,
+ [TIFF_LIBS="$TIFF_LIBS -ltiff"
+ TIFF_INCLUDES="$TIFF_INCLUDES -DWEBP_HAVE_TIFF"
+ AC_DEFINE(WEBP_HAVE_TIFF, [1],
+ [Set to 1 if TIFF library is installed])
+ tiff_support=yes
+ ],
+ AC_MSG_WARN(Optional tiff library not found),
+ [$MATH_LIBS]),
+ AC_MSG_WARN(tiff library not available - no tiffio.h)
+ )
+ LIBCHECK_EPILOGUE([TIFF])
+])
+
+dnl === check for GIF support ===
+
+AC_ARG_ENABLE([gif], AS_HELP_STRING([--disable-gif],
+ [Disable detection of GIF format support
+ @<:@default=auto@:>@]))
+AS_IF([test "x$enable_gif" != "xno"], [
+ CLEAR_LIBVARS([GIF])
+ WITHLIB_OPTION([gif], [GIF])
+
+ LIBCHECK_PROLOGUE([GIF])
+ AC_CHECK_HEADER(gif_lib.h,
+ AC_CHECK_LIB([gif], [DGifOpenFileHandle],
+ [GIF_LIBS="$GIF_LIBS -lgif"
+ AC_DEFINE(WEBP_HAVE_GIF, [1],
+ [Set to 1 if GIF library is installed])
+ gif_support=yes
+ ],
+ AC_MSG_WARN(Optional gif library not found),
+ [$MATH_LIBS]),
+ AC_MSG_WARN(gif library not available - no gif_lib.h)
+ )
+ LIBCHECK_EPILOGUE([GIF])
+
+ if test "$gif_support" = "yes" -a \
+ "$enable_libwebpdemux" = "yes"; then
+ build_anim_diff=yes
+ fi
+
+ if test "$gif_support" = "yes" -a \
+ "$enable_libwebpmux" = "yes"; then
+ build_gif2webp=yes
+ fi
+])
+AM_CONDITIONAL([BUILD_ANIMDIFF], [test "${build_anim_diff}" = "yes"])
+AM_CONDITIONAL([BUILD_GIF2WEBP], [test "${build_gif2webp}" = "yes"])
+
+if test "$enable_libwebpdemux" = "yes" -a "$enable_libwebpmux" = "yes"; then
+ build_img2webp=yes
+fi
+AM_CONDITIONAL([BUILD_IMG2WEBP], [test "${build_img2webp}" = "yes"])
+
+if test "$enable_libwebpmux" = "yes"; then
+ build_webpinfo=yes
+fi
+AM_CONDITIONAL([BUILD_WEBPINFO], [test "${build_webpinfo}" = "yes"])
+
+dnl === check for WIC support ===
+
+AC_ARG_ENABLE([wic],
+ AS_HELP_STRING([--disable-wic],
+ [Disable Windows Imaging Component (WIC) detection.
+ @<:@default=auto@:>@]),,
+ [enable_wic=yes])
+
+case $host_os in
+mingw*)
+if test "$enable_wic" = "yes"; then
+ AC_CHECK_HEADERS([wincodec.h shlwapi.h windows.h])
+ if test "$ac_cv_header_wincodec_h" = "yes"; then
+ AC_MSG_CHECKING(for Windows Imaging Component support)
+ SAVED_LIBS=$LIBS
+ LIBS="-lshlwapi -lole32 $LIBS"
+ # match include structure from [cd]webp.c
+ wic_headers="
+ #define INITGUID
+ #define CINTERFACE
+ #define COBJMACROS
+ #define _WIN32_IE 0x500
+
+ #include <shlwapi.h>
+ #include <windows.h>
+ #include <wincodec.h>
+ "
+ # test for functions from each lib and the GUID is created properly
+ wic_main="
+ int main(void) {
+ CLSID_WICImagingFactory;
+ CoInitialize(NULL);
+ SHCreateStreamOnFile(NULL, 0, NULL);
+ return 0;
+ }
+ "
+ AC_LANG_PUSH(C)
+ AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([
+ $wic_headers
+ $wic_main])],
+ [wic_support=yes],
+ [wic_support=no]
+ )
+ AC_LANG_POP
+
+ test "$wic_support" = "yes" || LIBS=$SAVED_LIBS
+ AC_MSG_RESULT(${wic_support-no})
+ fi
+fi
+esac
+
+dnl === If --enable-swap-16bit-csp is defined, add -DWEBP_SWAP_16BIT_CSP=1
+
+USE_SWAP_16BIT_CSP=""
+AC_MSG_CHECKING(if --enable-swap-16bit-csp option is specified)
+AC_ARG_ENABLE([swap-16bit-csp],
+ AS_HELP_STRING([--enable-swap-16bit-csp],
+ [Enable byte swap for 16 bit colorspaces]))
+if test "$enable_swap_16bit_csp" = "yes"; then
+ USE_SWAP_16BIT_CSP="-DWEBP_SWAP_16BIT_CSP=1"
+fi
+AC_MSG_RESULT(${enable_swap_16bit_csp-no})
+AC_SUBST(USE_SWAP_16BIT_CSP)
+
+dnl === If --disable-near-lossless is defined, add -DWEBP_NEAR_LOSSLESS=0
+
+AC_DEFINE(WEBP_NEAR_LOSSLESS, [1], [Enable near lossless encoding])
+AC_MSG_CHECKING(if --disable-near-lossless option is specified)
+AC_ARG_ENABLE([near_lossless],
+ AS_HELP_STRING([--disable-near-lossless],
+ [Disable near lossless encoding]),
+ [], [enable_near_lossless=yes])
+if test "$enable_near_lossless" = "no"; then
+ AC_DEFINE(WEBP_NEAR_LOSSLESS, [0], [Enable near lossless encoding])
+ AC_MSG_RESULT([yes])
+else
+ 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 =========================
+
+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 \
+ src/dec/Makefile src/enc/Makefile src/dsp/Makefile \
+ src/demux/Makefile src/mux/Makefile \
+ src/utils/Makefile \
+ src/libwebp.pc src/libwebpdecoder.pc \
+ src/demux/libwebpdemux.pc src/mux/libwebpmux.pc])
+
+
+AC_OUTPUT
+
+AC_MSG_NOTICE([
+WebP Configuration Summary
+--------------------------
+
+Shared libraries: ${enable_shared}
+Static libraries: ${enable_static}
+Threading support: ${enable_threading-no}
+libwebp: yes
+libwebpdecoder: ${enable_libwebpdecoder-no}
+libwebpdemux: ${enable_libwebpdemux-no}
+libwebpmux: ${enable_libwebpmux-no}
+libwebpextras: ${enable_libwebpextras-no}
+
+Tools:
+cwebp : ${enable_libwebpdemux-no}
+ Input format support
+ ====================
+ JPEG : ${jpeg_support-no}
+ PNG : ${png_support-no}
+ TIFF : ${tiff_support-no}
+ WIC : ${wic_support-no}
+dwebp : ${enable_libwebpdemux-no}
+ Output format support
+ =====================
+ PNG : ${png_support-no}
+ WIC : ${wic_support-no}
+GIF support : ${gif_support-no}
+anim_diff : ${build_anim_diff-no}
+gif2webp : ${build_gif2webp-no}
+img2webp : ${build_img2webp-no}
+webpmux : ${enable_libwebpmux-no}
+vwebp : ${build_vwebp-no}
+webpinfo : ${build_webpinfo-no}
+SDL support : ${sdl_support-no}
+vwebp_sdl : ${build_vwebp_sdl-no}
+])
diff --git a/src/third_party/libwebp/dec/alpha.c b/src/third_party/libwebp/dec/alpha.c
deleted file mode 100644
index c81c481..0000000
--- a/src/third_party/libwebp/dec/alpha.c
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Alpha-plane decompression.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h>
-#endif
-#include "./vp8i.h"
-#include "./vp8li.h"
-#include "../utils/filters.h"
-#include "../utils/quant_levels_dec.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
-// The 'output' buffer should be pre-allocated and must be of the same
-// dimension 'height'x'width', as that of the image.
-//
-// Returns 1 on successfully decoding the compressed alpha and
-// 0 if either:
-// error in bit-stream header (invalid compression mode or filter), or
-// error returned by appropriate compression method.
-
-static int DecodeAlpha(const uint8_t* data, size_t data_size,
- int width, int height, uint8_t* output) {
- WEBP_FILTER_TYPE filter;
- int pre_processing;
- int rsrv;
- int ok = 0;
- int method;
- const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
- const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
-
- SB_DCHECK(width > 0 && height > 0);
- SB_DCHECK(data != NULL && output != NULL);
-
- if (data_size <= ALPHA_HEADER_LEN) {
- return 0;
- }
-
- method = (data[0] >> 0) & 0x03;
- filter = (data[0] >> 2) & 0x03;
- pre_processing = (data[0] >> 4) & 0x03;
- rsrv = (data[0] >> 6) & 0x03;
- if (method < ALPHA_NO_COMPRESSION ||
- method > ALPHA_LOSSLESS_COMPRESSION ||
- filter >= WEBP_FILTER_LAST ||
- pre_processing > ALPHA_PREPROCESSED_LEVELS ||
- rsrv != 0) {
- return 0;
- }
-
- if (method == ALPHA_NO_COMPRESSION) {
- const size_t alpha_decoded_size = height * width;
- ok = (alpha_data_size >= alpha_decoded_size);
- if (ok) SbMemoryCopy(output, alpha_data, alpha_decoded_size);
- } else {
- ok = VP8LDecodeAlphaImageStream(width, height, alpha_data, alpha_data_size,
- output);
- }
-
- if (ok) {
- WebPUnfilterFunc unfilter_func = WebPUnfilters[filter];
- if (unfilter_func != NULL) {
- // TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
- // and apply filter per image-row.
- unfilter_func(width, height, width, output);
- }
- if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
- ok = DequantizeLevels(output, width, height);
- }
- }
-
- return ok;
-}
-
-//------------------------------------------------------------------------------
-
-const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
- int row, int num_rows) {
- const int width = dec->pic_hdr_.width_;
- const int height = dec->pic_hdr_.height_;
-
- if (row < 0 || num_rows < 0 || row + num_rows > height) {
- return NULL; // sanity check.
- }
-
- if (row == 0) {
- // Decode everything during the first call.
- SB_DCHECK(!dec->is_alpha_decoded_);
- if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_,
- width, height, dec->alpha_plane_)) {
- return NULL; // Error.
- }
- dec->is_alpha_decoded_ = 1;
- }
-
- // Return a pointer to the current decoded row.
- return dec->alpha_plane_ + row * width;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/buffer.c b/src/third_party/libwebp/dec/buffer.c
deleted file mode 100644
index 24e8abb..0000000
--- a/src/third_party/libwebp/dec/buffer.c
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Everything about WebPDecBuffer
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include <stdlib.h>
-
-#include "./vp8i.h"
-#include "./webpi.h"
-#include "../utils/utils.h"
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// WebPDecBuffer
-
-// Number of bytes per pixel for the different color-spaces.
-static const int kModeBpp[MODE_LAST] = {
- 3, 4, 3, 4, 4, 2, 2,
- 4, 4, 4, 2, // pre-multiplied modes
- 1, 1 };
-
-// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
-// Convert to an integer to handle both the unsigned/signed enum cases
-// without the need for casting to remove type limit warnings.
-static int IsValidColorspace(int webp_csp_mode) {
- return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
-}
-
-static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
- int ok = 1;
- const WEBP_CSP_MODE mode = buffer->colorspace;
- const int width = buffer->width;
- const int height = buffer->height;
- if (!IsValidColorspace(mode)) {
- ok = 0;
- } else if (!WebPIsRGBMode(mode)) { // YUV checks
- const WebPYUVABuffer* const buf = &buffer->u.YUVA;
- const uint64_t y_size = (uint64_t)buf->y_stride * height;
- const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2);
- const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2);
- const uint64_t a_size = (uint64_t)buf->a_stride * height;
- ok &= (y_size <= buf->y_size);
- ok &= (u_size <= buf->u_size);
- ok &= (v_size <= buf->v_size);
- ok &= (buf->y_stride >= width);
- ok &= (buf->u_stride >= (width + 1) / 2);
- ok &= (buf->v_stride >= (width + 1) / 2);
- ok &= (buf->y != NULL);
- ok &= (buf->u != NULL);
- ok &= (buf->v != NULL);
- if (mode == MODE_YUVA) {
- ok &= (buf->a_stride >= width);
- ok &= (a_size <= buf->a_size);
- ok &= (buf->a != NULL);
- }
- } else { // RGB checks
- const WebPRGBABuffer* const buf = &buffer->u.RGBA;
- const uint64_t size = (uint64_t)buf->stride * height;
- ok &= (size <= buf->size);
- ok &= (buf->stride >= width * kModeBpp[mode]);
- ok &= (buf->rgba != NULL);
- }
- return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
-}
-
-static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
- const int w = buffer->width;
- const int h = buffer->height;
- const WEBP_CSP_MODE mode = buffer->colorspace;
-
- if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) {
- return VP8_STATUS_INVALID_PARAM;
- }
-
- if (!buffer->is_external_memory && buffer->private_memory == NULL) {
- uint8_t* output;
- int uv_stride = 0, a_stride = 0;
- uint64_t uv_size = 0, a_size = 0, total_size;
- // We need memory and it hasn't been allocated yet.
- // => initialize output buffer, now that dimensions are known.
- const int stride = w * kModeBpp[mode];
- const uint64_t size = (uint64_t)stride * h;
-
- if (!WebPIsRGBMode(mode)) {
- uv_stride = (w + 1) / 2;
- uv_size = (uint64_t)uv_stride * ((h + 1) / 2);
- if (mode == MODE_YUVA) {
- a_stride = w;
- a_size = (uint64_t)a_stride * h;
- }
- }
- total_size = size + 2 * uv_size + a_size;
-
- // Security/sanity checks
- output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output));
- if (output == NULL) {
- return VP8_STATUS_OUT_OF_MEMORY;
- }
- buffer->private_memory = output;
-
- if (!WebPIsRGBMode(mode)) { // YUVA initialization
- WebPYUVABuffer* const buf = &buffer->u.YUVA;
- buf->y = output;
- buf->y_stride = stride;
- buf->y_size = (size_t)size;
- buf->u = output + size;
- buf->u_stride = uv_stride;
- buf->u_size = (size_t)uv_size;
- buf->v = output + size + uv_size;
- buf->v_stride = uv_stride;
- buf->v_size = (size_t)uv_size;
- if (mode == MODE_YUVA) {
- buf->a = output + size + 2 * uv_size;
- }
- buf->a_size = (size_t)a_size;
- buf->a_stride = a_stride;
- } else { // RGBA initialization
- WebPRGBABuffer* const buf = &buffer->u.RGBA;
- buf->rgba = output;
- buf->stride = stride;
- buf->size = (size_t)size;
- }
- }
- return CheckDecBuffer(buffer);
-}
-
-VP8StatusCode WebPAllocateDecBuffer(int w, int h,
- const WebPDecoderOptions* const options,
- WebPDecBuffer* const out) {
- if (out == NULL || w <= 0 || h <= 0) {
- return VP8_STATUS_INVALID_PARAM;
- }
- if (options != NULL) { // First, apply options if there is any.
- if (options->use_cropping) {
- const int cw = options->crop_width;
- const int ch = options->crop_height;
- const int x = options->crop_left & ~1;
- const int y = options->crop_top & ~1;
- if (x < 0 || y < 0 || cw <= 0 || ch <= 0 || x + cw > w || y + ch > h) {
- return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
- }
- w = cw;
- h = ch;
- }
- if (options->use_scaling) {
- if (options->scaled_width <= 0 || options->scaled_height <= 0) {
- return VP8_STATUS_INVALID_PARAM;
- }
- w = options->scaled_width;
- h = options->scaled_height;
- }
- }
- out->width = w;
- out->height = h;
-
- // Then, allocate buffer for real
- return AllocateBuffer(out);
-}
-
-//------------------------------------------------------------------------------
-// constructors / destructors
-
-int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
- if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
- return 0; // version mismatch
- }
- if (buffer == NULL) return 0;
- SbMemorySet(buffer, 0, sizeof(*buffer));
- return 1;
-}
-
-void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
- if (buffer != NULL) {
- if (!buffer->is_external_memory)
- SbMemoryDeallocate(buffer->private_memory);
- buffer->private_memory = NULL;
- }
-}
-
-void WebPCopyDecBuffer(const WebPDecBuffer* const src,
- WebPDecBuffer* const dst) {
- if (src != NULL && dst != NULL) {
- *dst = *src;
- if (src->private_memory != NULL) {
- dst->is_external_memory = 1; // dst buffer doesn't own the memory.
- dst->private_memory = NULL;
- }
- }
-}
-
-// Copy and transfer ownership from src to dst (beware of parameter order!)
-void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
- if (src != NULL && dst != NULL) {
- *dst = *src;
- if (src->private_memory != NULL) {
- src->is_external_memory = 1; // src relinquishes ownership
- src->private_memory = NULL;
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/decode_vp8.h b/src/third_party/libwebp/dec/decode_vp8.h
deleted file mode 100644
index acdb15a..0000000
--- a/src/third_party/libwebp/dec/decode_vp8.h
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Low-level API for VP8 decoder
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_WEBP_DECODE_VP8_H_
-#define WEBP_WEBP_DECODE_VP8_H_
-
-#include "../webp/decode.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Lower-level API
-//
-// These functions provide fine-grained control of the decoding process.
-// The call flow should resemble:
-//
-// VP8Io io;
-// VP8InitIo(&io);
-// io.data = data;
-// io.data_size = size;
-// /* customize io's functions (setup()/put()/teardown()) if needed. */
-//
-// VP8Decoder* dec = VP8New();
-// bool ok = VP8Decode(dec);
-// if (!ok) printf("Error: %s\n", VP8StatusMessage(dec));
-// VP8Delete(dec);
-// return ok;
-
-// Input / Output
-typedef struct VP8Io VP8Io;
-typedef int (*VP8IoPutHook)(const VP8Io* io);
-typedef int (*VP8IoSetupHook)(VP8Io* io);
-typedef void (*VP8IoTeardownHook)(const VP8Io* io);
-
-struct VP8Io {
- // set by VP8GetHeaders()
- int width, height; // picture dimensions, in pixels (invariable).
- // These are the original, uncropped dimensions.
- // The actual area passed to put() is stored
- // in mb_w / mb_h fields.
-
- // set before calling put()
- int mb_y; // position of the current rows (in pixels)
- int mb_w; // number of columns in the sample
- int mb_h; // number of rows in the sample
- const uint8_t* y, *u, *v; // rows to copy (in yuv420 format)
- int y_stride; // row stride for luma
- int uv_stride; // row stride for chroma
-
- void* opaque; // user data
-
- // called when fresh samples are available. Currently, samples are in
- // YUV420 format, and can be up to width x 24 in size (depending on the
- // in-loop filtering level, e.g.). Should return false in case of error
- // or abort request. The actual size of the area to update is mb_w x mb_h
- // in size, taking cropping into account.
- VP8IoPutHook put;
-
- // called just before starting to decode the blocks.
- // Must return false in case of setup error, true otherwise. If false is
- // returned, teardown() will NOT be called. But if the setup succeeded
- // and true is returned, then teardown() will always be called afterward.
- VP8IoSetupHook setup;
-
- // Called just after block decoding is finished (or when an error occurred
- // during put()). Is NOT called if setup() failed.
- VP8IoTeardownHook teardown;
-
- // this is a recommendation for the user-side yuv->rgb converter. This flag
- // is set when calling setup() hook and can be overwritten by it. It then
- // can be taken into consideration during the put() method.
- int fancy_upsampling;
-
- // Input buffer.
- size_t data_size;
- const uint8_t* data;
-
- // If true, in-loop filtering will not be performed even if present in the
- // bitstream. Switching off filtering may speed up decoding at the expense
- // of more visible blocking. Note that output will also be non-compliant
- // with the VP8 specifications.
- int bypass_filtering;
-
- // Cropping parameters.
- int use_cropping;
- int crop_left, crop_right, crop_top, crop_bottom;
-
- // Scaling parameters.
- int use_scaling;
- int scaled_width, scaled_height;
-
- // If non NULL, pointer to the alpha data (if present) corresponding to the
- // start of the current row (That is: it is pre-offset by mb_y and takes
- // cropping into account).
- const uint8_t* a;
-};
-
-// Internal, version-checked, entry point
-int VP8InitIoInternal(VP8Io* const, int);
-
-// Set the custom IO function pointers and user-data. The setter for IO hooks
-// should be called before initiating incremental decoding. Returns true if
-// WebPIDecoder object is successfully modified, false otherwise.
-int WebPISetIOHooks(WebPIDecoder* const idec,
- VP8IoPutHook put,
- VP8IoSetupHook setup,
- VP8IoTeardownHook teardown,
- void* user_data);
-
-// Main decoding object. This is an opaque structure.
-typedef struct VP8Decoder VP8Decoder;
-
-// Create a new decoder object.
-VP8Decoder* VP8New(void);
-
-// Must be called to make sure 'io' is initialized properly.
-// Returns false in case of version mismatch. Upon such failure, no other
-// decoding function should be called (VP8Decode, VP8GetHeaders, ...)
-static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
- return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
-}
-
-// Start decoding a new picture. Returns true if ok.
-int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
-
-// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
-// Returns false in case of error.
-int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
-
-// Return current status of the decoder:
-VP8StatusCode VP8Status(VP8Decoder* const dec);
-
-// return readable string corresponding to the last status.
-const char* VP8StatusMessage(VP8Decoder* const dec);
-
-// Resets the decoder in its initial state, reclaiming memory.
-// Not a mandatory call between calls to VP8Decode().
-void VP8Clear(VP8Decoder* const dec);
-
-// Destroy the decoder object.
-void VP8Delete(VP8Decoder* const dec);
-
-//------------------------------------------------------------------------------
-// Miscellaneous VP8/VP8L bitstream probing functions.
-
-// Returns true if the next 3 bytes in data contain the VP8 signature.
-WEBP_EXTERN(int) VP8CheckSignature(const uint8_t* const data, size_t data_size);
-
-// Validates the VP8 data-header and retrieves basic header information viz
-// width and height. Returns 0 in case of formatting error. *width/*height
-// can be passed NULL.
-WEBP_EXTERN(int) VP8GetInfo(
- const uint8_t* data,
- size_t data_size, // data available so far
- size_t chunk_size, // total data size expected in the chunk
- int* const width, int* const height);
-
-// Returns true if the next byte(s) in data is a VP8L signature.
-WEBP_EXTERN(int) VP8LCheckSignature(const uint8_t* const data, size_t size);
-
-// Validates the VP8L data-header and retrieves basic header information viz
-// width, height and alpha. Returns 0 in case of formatting error.
-// width/height/has_alpha can be passed NULL.
-WEBP_EXTERN(int) VP8LGetInfo(
- const uint8_t* data, size_t data_size, // data available so far
- int* const width, int* const height, int* const has_alpha);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_WEBP_DECODE_VP8_H_ */
diff --git a/src/third_party/libwebp/dec/frame.c b/src/third_party/libwebp/dec/frame.c
deleted file mode 100644
index d73528a..0000000
--- a/src/third_party/libwebp/dec/frame.c
+++ /dev/null
@@ -1,699 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Frame-reconstruction function. Memory allocation.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h>
-#endif
-#include "./vp8i.h"
-#include "../utils/utils.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define ALIGN_MASK (32 - 1)
-
-//------------------------------------------------------------------------------
-// Filtering
-
-// kFilterExtraRows[] = How many extra lines are needed on the MB boundary
-// for caching, given a filtering level.
-// Simple filter: up to 2 luma samples are read and 1 is written.
-// Complex filter: up to 4 luma samples are read and 3 are written. Same for
-// U/V, so it's 8 samples total (because of the 2x upsampling).
-static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
-
-static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
- if (keyframe) {
- return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
- } else {
- return (level >= 40) ? 3 : (level >= 20) ? 2 : (level >= 15) ? 1 : 0;
- }
-}
-
-static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
- const VP8ThreadContext* const ctx = &dec->thread_ctx_;
- const int y_bps = dec->cache_y_stride_;
- VP8FInfo* const f_info = ctx->f_info_ + mb_x;
- uint8_t* const y_dst = dec->cache_y_ + ctx->id_ * 16 * y_bps + mb_x * 16;
- const int level = f_info->f_level_;
- const int ilevel = f_info->f_ilevel_;
- const int limit = 2 * level + ilevel;
- if (level == 0) {
- return;
- }
- if (dec->filter_type_ == 1) { // simple
- if (mb_x > 0) {
- VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
- }
- if (f_info->f_inner_) {
- VP8SimpleHFilter16i(y_dst, y_bps, limit);
- }
- if (mb_y > 0) {
- VP8SimpleVFilter16(y_dst, y_bps, limit + 4);
- }
- if (f_info->f_inner_) {
- VP8SimpleVFilter16i(y_dst, y_bps, limit);
- }
- } else { // complex
- const int uv_bps = dec->cache_uv_stride_;
- uint8_t* const u_dst = dec->cache_u_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
- uint8_t* const v_dst = dec->cache_v_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
- const int hev_thresh =
- hev_thresh_from_level(level, dec->frm_hdr_.key_frame_);
- if (mb_x > 0) {
- VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
- VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
- }
- if (f_info->f_inner_) {
- VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
- VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
- }
- if (mb_y > 0) {
- VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
- VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
- }
- if (f_info->f_inner_) {
- VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
- VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
- }
- }
-}
-
-// Filter the decoded macroblock row (if needed)
-static void FilterRow(const VP8Decoder* const dec) {
- int mb_x;
- const int mb_y = dec->thread_ctx_.mb_y_;
- SB_DCHECK(dec->thread_ctx_.filter_row_);
- for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
- DoFilter(dec, mb_x, mb_y);
- }
-}
-
-//------------------------------------------------------------------------------
-// Precompute the filtering strength for each segment and each i4x4/i16x16 mode.
-
-static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
- if (dec->filter_type_ > 0) {
- int s;
- const VP8FilterHeader* const hdr = &dec->filter_hdr_;
- for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
- int i4x4;
- // First, compute the initial level
- int base_level;
- if (dec->segment_hdr_.use_segment_) {
- base_level = dec->segment_hdr_.filter_strength_[s];
- if (!dec->segment_hdr_.absolute_delta_) {
- base_level += hdr->level_;
- }
- } else {
- base_level = hdr->level_;
- }
- for (i4x4 = 0; i4x4 <= 1; ++i4x4) {
- VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
- int level = base_level;
- if (hdr->use_lf_delta_) {
- // TODO(skal): only CURRENT is handled for now.
- level += hdr->ref_lf_delta_[0];
- if (i4x4) {
- level += hdr->mode_lf_delta_[0];
- }
- }
- level = (level < 0) ? 0 : (level > 63) ? 63 : level;
- info->f_level_ = level;
-
- if (hdr->sharpness_ > 0) {
- if (hdr->sharpness_ > 4) {
- level >>= 2;
- } else {
- level >>= 1;
- }
- if (level > 9 - hdr->sharpness_) {
- level = 9 - hdr->sharpness_;
- }
- }
- info->f_ilevel_ = (level < 1) ? 1 : level;
- info->f_inner_ = 0;
- }
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// This function is called after a row of macroblocks is finished decoding.
-// It also takes into account the following restrictions:
-// * In case of in-loop filtering, we must hold off sending some of the bottom
-// pixels as they are yet unfiltered. They will be when the next macroblock
-// row is decoded. Meanwhile, we must preserve them by rotating them in the
-// cache area. This doesn't hold for the very bottom row of the uncropped
-// picture of course.
-// * we must clip the remaining pixels against the cropping area. The VP8Io
-// struct must have the following fields set correctly before calling put():
-
-#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
-
-// Finalize and transmit a complete row. Return false in case of user-abort.
-static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
- int ok = 1;
- const VP8ThreadContext* const ctx = &dec->thread_ctx_;
- const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
- const int ysize = extra_y_rows * dec->cache_y_stride_;
- const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
- const int y_offset = ctx->id_ * 16 * dec->cache_y_stride_;
- const int uv_offset = ctx->id_ * 8 * dec->cache_uv_stride_;
- uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
- uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
- uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
- const int first_row = (ctx->mb_y_ == 0);
- const int last_row = (ctx->mb_y_ >= dec->br_mb_y_ - 1);
- int y_start = MACROBLOCK_VPOS(ctx->mb_y_);
- int y_end = MACROBLOCK_VPOS(ctx->mb_y_ + 1);
-
- if (ctx->filter_row_) {
- FilterRow(dec);
- }
-
- if (io->put) {
- if (!first_row) {
- y_start -= extra_y_rows;
- io->y = ydst;
- io->u = udst;
- io->v = vdst;
- } else {
- io->y = dec->cache_y_ + y_offset;
- io->u = dec->cache_u_ + uv_offset;
- io->v = dec->cache_v_ + uv_offset;
- }
-
- if (!last_row) {
- y_end -= extra_y_rows;
- }
- if (y_end > io->crop_bottom) {
- y_end = io->crop_bottom; // make sure we don't overflow on last row.
- }
- io->a = NULL;
- if (dec->alpha_data_ != NULL && y_start < y_end) {
- // TODO(skal): several things to correct here:
- // * testing presence of alpha with dec->alpha_data_ is not a good idea
- // * we're actually decompressing the full plane only once. It should be
- // more obvious from signature.
- // * we could free alpha_data_ right after this call, but we don't own.
- io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
- if (io->a == NULL) {
- return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
- "Could not decode alpha data.");
- }
- }
- if (y_start < io->crop_top) {
- const int delta_y = io->crop_top - y_start;
- y_start = io->crop_top;
- SB_DCHECK(!(delta_y & 1));
- io->y += dec->cache_y_stride_ * delta_y;
- io->u += dec->cache_uv_stride_ * (delta_y >> 1);
- io->v += dec->cache_uv_stride_ * (delta_y >> 1);
- if (io->a != NULL) {
- io->a += io->width * delta_y;
- }
- }
- if (y_start < y_end) {
- io->y += io->crop_left;
- io->u += io->crop_left >> 1;
- io->v += io->crop_left >> 1;
- if (io->a != NULL) {
- io->a += io->crop_left;
- }
- io->mb_y = y_start - io->crop_top;
- io->mb_w = io->crop_right - io->crop_left;
- io->mb_h = y_end - y_start;
- ok = io->put(io);
- }
- }
- // rotate top samples if needed
- if (ctx->id_ + 1 == dec->num_caches_) {
- if (!last_row) {
- SbMemoryCopy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
- SbMemoryCopy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
- SbMemoryCopy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
- }
- }
-
- return ok;
-}
-
-#undef MACROBLOCK_VPOS
-
-//------------------------------------------------------------------------------
-
-int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
- int ok = 1;
- VP8ThreadContext* const ctx = &dec->thread_ctx_;
- if (!dec->use_threads_) {
- // ctx->id_ and ctx->f_info_ are already set
- ctx->mb_y_ = dec->mb_y_;
- ctx->filter_row_ = dec->filter_row_;
- ok = FinishRow(dec, io);
- } else {
- WebPWorker* const worker = &dec->worker_;
- // Finish previous job *before* updating context
- ok &= WebPWorkerSync(worker);
- SB_DCHECK(worker->status_ == OK);
- if (ok) { // spawn a new deblocking/output job
- ctx->io_ = *io;
- ctx->id_ = dec->cache_id_;
- ctx->mb_y_ = dec->mb_y_;
- ctx->filter_row_ = dec->filter_row_;
- if (ctx->filter_row_) { // just swap filter info
- VP8FInfo* const tmp = ctx->f_info_;
- ctx->f_info_ = dec->f_info_;
- dec->f_info_ = tmp;
- }
- WebPWorkerLaunch(worker);
- if (++dec->cache_id_ == dec->num_caches_) {
- dec->cache_id_ = 0;
- }
- }
- }
- return ok;
-}
-
-//------------------------------------------------------------------------------
-// Finish setting up the decoding parameter once user's setup() is called.
-
-VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
- // Call setup() first. This may trigger additional decoding features on 'io'.
- // Note: Afterward, we must call teardown() not matter what.
- if (io->setup && !io->setup(io)) {
- VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
- return dec->status_;
- }
-
- // Disable filtering per user request
- if (io->bypass_filtering) {
- dec->filter_type_ = 0;
- }
- // TODO(skal): filter type / strength / sharpness forcing
-
- // Define the area where we can skip in-loop filtering, in case of cropping.
- //
- // 'Simple' filter reads two luma samples outside of the macroblock and
- // and filters one. It doesn't filter the chroma samples. Hence, we can
- // avoid doing the in-loop filtering before crop_top/crop_left position.
- // For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
- // Means: there's a dependency chain that goes all the way up to the
- // top-left corner of the picture (MB #0). We must filter all the previous
- // macroblocks.
- // TODO(skal): add an 'approximate_decoding' option, that won't produce
- // a 1:1 bit-exactness for complex filtering?
- {
- const int extra_pixels = kFilterExtraRows[dec->filter_type_];
- if (dec->filter_type_ == 2) {
- // For complex filter, we need to preserve the dependency chain.
- dec->tl_mb_x_ = 0;
- dec->tl_mb_y_ = 0;
- } else {
- // For simple filter, we can filter only the cropped region.
- // We include 'extra_pixels' on the other side of the boundary, since
- // vertical or horizontal filtering of the previous macroblock can
- // modify some abutting pixels.
- dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4;
- dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4;
- if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0;
- if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0;
- }
- // We need some 'extra' pixels on the right/bottom.
- dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4;
- dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4;
- if (dec->br_mb_x_ > dec->mb_w_) {
- dec->br_mb_x_ = dec->mb_w_;
- }
- if (dec->br_mb_y_ > dec->mb_h_) {
- dec->br_mb_y_ = dec->mb_h_;
- }
- }
- PrecomputeFilterStrengths(dec);
- return VP8_STATUS_OK;
-}
-
-int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
- int ok = 1;
- if (dec->use_threads_) {
- ok = WebPWorkerSync(&dec->worker_);
- }
-
- if (io->teardown) {
- io->teardown(io);
- }
- return ok;
-}
-
-//------------------------------------------------------------------------------
-// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
-//
-// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
-// immediately, and needs to wait for first few rows of the next macroblock to
-// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
-// on strength).
-// With two threads, the vertical positions of the rows being decoded are:
-// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
-// Deblock: [ 0..11][12..27][28..43][44..59][...
-// If we use two threads and two caches of 16 pixels, the sequence would be:
-// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
-// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
-// The problem occurs during row [12..15!!] that both the decoding and
-// deblocking threads are writing simultaneously.
-// With 3 cache lines, one get a safe write pattern:
-// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
-// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
-// Note that multi-threaded output _without_ deblocking can make use of two
-// cache lines of 16 pixels only, since there's no lagging behind. The decoding
-// and output process have non-concurrent writing:
-// Decode: [ 0..15][16..31][ 0..15][16..31][...
-// io->put: [ 0..15][16..31][ 0..15][...
-
-#define MT_CACHE_LINES 3
-#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
-
-// Initialize multi/single-thread worker
-static int InitThreadContext(VP8Decoder* const dec) {
- dec->cache_id_ = 0;
- if (dec->use_threads_) {
- WebPWorker* const worker = &dec->worker_;
- if (!WebPWorkerReset(worker)) {
- return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
- "thread initialization failed.");
- }
- worker->data1 = dec;
- worker->data2 = (void*)&dec->thread_ctx_.io_;
- worker->hook = (WebPWorkerHook)FinishRow;
- dec->num_caches_ =
- (dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
- } else {
- dec->num_caches_ = ST_CACHE_LINES;
- }
- return 1;
-}
-
-#undef MT_CACHE_LINES
-#undef ST_CACHE_LINES
-
-//------------------------------------------------------------------------------
-// Memory setup
-
-static int AllocateMemory(VP8Decoder* const dec) {
- const int num_caches = dec->num_caches_;
- const int mb_w = dec->mb_w_;
- // Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
- const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
- const size_t top_size = (16 + 8 + 8) * mb_w;
- const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
- const size_t f_info_size =
- (dec->filter_type_ > 0) ?
- mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
- : 0;
- const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
- const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
- const size_t cache_height = (16 * num_caches
- + kFilterExtraRows[dec->filter_type_]) * 3 / 2;
- const size_t cache_size = top_size * cache_height;
- // alpha_size is the only one that scales as width x height.
- const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
- (uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
- const uint64_t needed = (uint64_t)intra_pred_mode_size
- + top_size + mb_info_size + f_info_size
- + yuv_size + coeffs_size
- + cache_size + alpha_size + ALIGN_MASK;
- uint8_t* mem;
-
- if (needed != (size_t)needed) return 0; // check for overflow
- if (needed > dec->mem_size_) {
- SbMemoryDeallocate(dec->mem_);
- dec->mem_size_ = 0;
- dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
- if (dec->mem_ == NULL) {
- return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
- "no memory during frame initialization.");
- }
- // down-cast is ok, thanks to WebPSafeAlloc() above.
- dec->mem_size_ = (size_t)needed;
- }
-
- mem = (uint8_t*)dec->mem_;
- dec->intra_t_ = (uint8_t*)mem;
- mem += intra_pred_mode_size;
-
- dec->y_t_ = (uint8_t*)mem;
- mem += 16 * mb_w;
- dec->u_t_ = (uint8_t*)mem;
- mem += 8 * mb_w;
- dec->v_t_ = (uint8_t*)mem;
- mem += 8 * mb_w;
-
- dec->mb_info_ = ((VP8MB*)mem) + 1;
- mem += mb_info_size;
-
- dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
- mem += f_info_size;
- dec->thread_ctx_.id_ = 0;
- dec->thread_ctx_.f_info_ = dec->f_info_;
- if (dec->use_threads_) {
- // secondary cache line. The deblocking process need to make use of the
- // filtering strength from previous macroblock row, while the new ones
- // are being decoded in parallel. We'll just swap the pointers.
- dec->thread_ctx_.f_info_ += mb_w;
- }
-
- mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
- SB_DCHECK((yuv_size & ALIGN_MASK) == 0);
- dec->yuv_b_ = (uint8_t*)mem;
- mem += yuv_size;
-
- dec->coeffs_ = (int16_t*)mem;
- mem += coeffs_size;
-
- dec->cache_y_stride_ = 16 * mb_w;
- dec->cache_uv_stride_ = 8 * mb_w;
- {
- const int extra_rows = kFilterExtraRows[dec->filter_type_];
- const int extra_y = extra_rows * dec->cache_y_stride_;
- const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
- dec->cache_y_ = ((uint8_t*)mem) + extra_y;
- dec->cache_u_ = dec->cache_y_
- + 16 * num_caches * dec->cache_y_stride_ + extra_uv;
- dec->cache_v_ = dec->cache_u_
- + 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
- dec->cache_id_ = 0;
- }
- mem += cache_size;
-
- // alpha plane
- dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
- mem += alpha_size;
- SB_DCHECK(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
-
- // note: left-info is initialized once for all.
- SbMemorySet(dec->mb_info_ - 1, 0, mb_info_size);
-
- // initialize top
- SbMemorySet(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
-
- return 1;
-}
-
-static void InitIo(VP8Decoder* const dec, VP8Io* io) {
- // prepare 'io'
- io->mb_y = 0;
- io->y = dec->cache_y_;
- io->u = dec->cache_u_;
- io->v = dec->cache_v_;
- io->y_stride = dec->cache_y_stride_;
- io->uv_stride = dec->cache_uv_stride_;
- io->a = NULL;
-}
-
-int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
- if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
- if (!AllocateMemory(dec)) return 0;
- InitIo(dec, io);
- VP8DspInit(); // Init critical function pointers and look-up tables.
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Main reconstruction function.
-
-static const int kScan[16] = {
- 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
- 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
- 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
- 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
-};
-
-static WEBP_INLINE int CheckMode(VP8Decoder* const dec, int mode) {
- if (mode == B_DC_PRED) {
- if (dec->mb_x_ == 0) {
- return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
- } else {
- return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
- }
- }
- return mode;
-}
-
-static WEBP_INLINE void Copy32b(uint8_t* dst, uint8_t* src) {
- *(uint32_t*)dst = *(uint32_t*)src;
-}
-
-void VP8ReconstructBlock(VP8Decoder* const dec) {
- int j;
- uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
- uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
- uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
-
- // Rotate in the left samples from previously decoded block. We move four
- // pixels at a time for alignment reason, and because of in-loop filter.
- if (dec->mb_x_ > 0) {
- for (j = -1; j < 16; ++j) {
- Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
- }
- for (j = -1; j < 8; ++j) {
- Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
- Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
- }
- } else {
- for (j = 0; j < 16; ++j) {
- y_dst[j * BPS - 1] = 129;
- }
- for (j = 0; j < 8; ++j) {
- u_dst[j * BPS - 1] = 129;
- v_dst[j * BPS - 1] = 129;
- }
- // Init top-left sample on left column too
- if (dec->mb_y_ > 0) {
- y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
- }
- }
- {
- // bring top samples into the cache
- uint8_t* const top_y = dec->y_t_ + dec->mb_x_ * 16;
- uint8_t* const top_u = dec->u_t_ + dec->mb_x_ * 8;
- uint8_t* const top_v = dec->v_t_ + dec->mb_x_ * 8;
- const int16_t* coeffs = dec->coeffs_;
- int n;
-
- if (dec->mb_y_ > 0) {
- SbMemoryCopy(y_dst - BPS, top_y, 16);
- SbMemoryCopy(u_dst - BPS, top_u, 8);
- SbMemoryCopy(v_dst - BPS, top_v, 8);
- } else if (dec->mb_x_ == 0) {
- // we only need to do this init once at block (0,0).
- // Afterward, it remains valid for the whole topmost row.
- SbMemorySet(y_dst - BPS - 1, 127, 16 + 4 + 1);
- SbMemorySet(u_dst - BPS - 1, 127, 8 + 1);
- SbMemorySet(v_dst - BPS - 1, 127, 8 + 1);
- }
-
- // predict and add residuals
-
- if (dec->is_i4x4_) { // 4x4
- uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
-
- if (dec->mb_y_ > 0) {
- if (dec->mb_x_ >= dec->mb_w_ - 1) { // on rightmost border
- top_right[0] = top_y[15] * 0x01010101u;
- } else {
- SbMemoryCopy(top_right, top_y + 16, sizeof(*top_right));
- }
- }
- // replicate the top-right pixels below
- top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
-
- // predict and add residues for all 4x4 blocks in turn.
- for (n = 0; n < 16; n++) {
- uint8_t* const dst = y_dst + kScan[n];
- VP8PredLuma4[dec->imodes_[n]](dst);
- if (dec->non_zero_ac_ & (1 << n)) {
- VP8Transform(coeffs + n * 16, dst, 0);
- } else if (dec->non_zero_ & (1 << n)) { // only DC is present
- VP8TransformDC(coeffs + n * 16, dst);
- }
- }
- } else { // 16x16
- const int pred_func = CheckMode(dec, dec->imodes_[0]);
- VP8PredLuma16[pred_func](y_dst);
- if (dec->non_zero_) {
- for (n = 0; n < 16; n++) {
- uint8_t* const dst = y_dst + kScan[n];
- if (dec->non_zero_ac_ & (1 << n)) {
- VP8Transform(coeffs + n * 16, dst, 0);
- } else if (dec->non_zero_ & (1 << n)) { // only DC is present
- VP8TransformDC(coeffs + n * 16, dst);
- }
- }
- }
- }
- {
- // Chroma
- const int pred_func = CheckMode(dec, dec->uvmode_);
- VP8PredChroma8[pred_func](u_dst);
- VP8PredChroma8[pred_func](v_dst);
-
- if (dec->non_zero_ & 0x0f0000) { // chroma-U
- const int16_t* const u_coeffs = dec->coeffs_ + 16 * 16;
- if (dec->non_zero_ac_ & 0x0f0000) {
- VP8TransformUV(u_coeffs, u_dst);
- } else {
- VP8TransformDCUV(u_coeffs, u_dst);
- }
- }
- if (dec->non_zero_ & 0xf00000) { // chroma-V
- const int16_t* const v_coeffs = dec->coeffs_ + 20 * 16;
- if (dec->non_zero_ac_ & 0xf00000) {
- VP8TransformUV(v_coeffs, v_dst);
- } else {
- VP8TransformDCUV(v_coeffs, v_dst);
- }
- }
-
- // stash away top samples for next block
- if (dec->mb_y_ < dec->mb_h_ - 1) {
- SbMemoryCopy(top_y, y_dst + 15 * BPS, 16);
- SbMemoryCopy(top_u, u_dst + 7 * BPS, 8);
- SbMemoryCopy(top_v, v_dst + 7 * BPS, 8);
- }
- }
- }
- // Transfer reconstructed samples from yuv_b_ cache to final destination.
- {
- const int y_offset = dec->cache_id_ * 16 * dec->cache_y_stride_;
- const int uv_offset = dec->cache_id_ * 8 * dec->cache_uv_stride_;
- uint8_t* const y_out = dec->cache_y_ + dec->mb_x_ * 16 + y_offset;
- uint8_t* const u_out = dec->cache_u_ + dec->mb_x_ * 8 + uv_offset;
- uint8_t* const v_out = dec->cache_v_ + dec->mb_x_ * 8 + uv_offset;
- for (j = 0; j < 16; ++j) {
- SbMemoryCopy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
- }
- for (j = 0; j < 8; ++j) {
- SbMemoryCopy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8);
- SbMemoryCopy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/idec.c b/src/third_party/libwebp/dec/idec.c
deleted file mode 100644
index d72ac60..0000000
--- a/src/third_party/libwebp/dec/idec.c
+++ /dev/null
@@ -1,849 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Incremental decoding
-//
-// Author: somnath@google.com (Somnath Banerjee)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-#endif
-
-#include "./webpi.h"
-#include "./vp8i.h"
-#include "../utils/utils.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// In append mode, buffer allocations increase as multiples of this value.
-// Needs to be a power of 2.
-#define CHUNK_SIZE 4096
-#define MAX_MB_SIZE 4096
-
-//------------------------------------------------------------------------------
-// Data structures for memory and states
-
-// Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE.
-// If there is any error the decoder goes into state ERROR.
-typedef enum {
- STATE_PRE_VP8, // All data before that of the first VP8 chunk.
- STATE_VP8_FRAME_HEADER, // For VP8 Frame header (within VP8 chunk).
- STATE_VP8_PARTS0,
- STATE_VP8_DATA,
- STATE_VP8L_HEADER,
- STATE_VP8L_DATA,
- STATE_DONE,
- STATE_ERROR
-} DecState;
-
-// Operating state for the MemBuffer
-typedef enum {
- MEM_MODE_NONE = 0,
- MEM_MODE_APPEND,
- MEM_MODE_MAP
-} MemBufferMode;
-
-// storage for partition #0 and partial data (in a rolling fashion)
-typedef struct {
- MemBufferMode mode_; // Operation mode
- size_t start_; // start location of the data to be decoded
- size_t end_; // end location
- size_t buf_size_; // size of the allocated buffer
- uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()
-
- size_t part0_size_; // size of partition #0
- const uint8_t* part0_buf_; // buffer to store partition #0
-} MemBuffer;
-
-struct WebPIDecoder {
- DecState state_; // current decoding state
- WebPDecParams params_; // Params to store output info
- int is_lossless_; // for down-casting 'dec_'.
- void* dec_; // either a VP8Decoder or a VP8LDecoder instance
- VP8Io io_;
-
- MemBuffer mem_; // input memory buffer.
- WebPDecBuffer output_; // output buffer (when no external one is supplied)
- size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
-};
-
-// MB context to restore in case VP8DecodeMB() fails
-typedef struct {
- VP8MB left_;
- VP8MB info_;
- uint8_t intra_t_[4];
- uint8_t intra_l_[4];
- VP8BitReader br_;
- VP8BitReader token_br_;
-} MBContext;
-
-//------------------------------------------------------------------------------
-// MemBuffer: incoming data handling
-
-static void RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
- if (br->buf_ != NULL) {
- br->buf_ += offset;
- br->buf_end_ += offset;
- }
-}
-
-static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
- return (mem->end_ - mem->start_);
-}
-
-// Check if we need to preserve the compressed alpha data, as it may not have
-// been decoded yet.
-static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
- if (idec->state_ == STATE_PRE_VP8) {
- // We haven't parsed the headers yet, so we don't know whether the image is
- // lossy or lossless. This also means that we haven't parsed the ALPH chunk.
- return 0;
- }
- if (idec->is_lossless_) {
- return 0; // ALPH chunk is not present for lossless images.
- } else {
- const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
- SB_DCHECK(dec != NULL); // Must be true as idec->state_ != STATE_PRE_VP8.
- return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;
- }
-}
-
-static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
- MemBuffer* const mem = &idec->mem_;
- const uint8_t* const new_base = mem->buf_ + mem->start_;
- // note: for VP8, setting up idec->io_ is only really needed at the beginning
- // of the decoding, till partition #0 is complete.
- idec->io_.data = new_base;
- idec->io_.data_size = MemDataSize(mem);
-
- if (idec->dec_ != NULL) {
- if (!idec->is_lossless_) {
- VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
- const int last_part = dec->num_parts_ - 1;
- if (offset != 0) {
- int p;
- for (p = 0; p <= last_part; ++p) {
- RemapBitReader(dec->parts_ + p, offset);
- }
- // Remap partition #0 data pointer to new offset, but only in MAP
- // mode (in APPEND mode, partition #0 is copied into a fixed memory).
- if (mem->mode_ == MEM_MODE_MAP) {
- RemapBitReader(&dec->br_, offset);
- }
- }
- SB_DCHECK(last_part >= 0);
- dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
- if (NeedCompressedAlpha(idec)) dec->alpha_data_ += offset;
- } else { // Resize lossless bitreader
- VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
- VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
- }
- }
-}
-
-// Appends data to the end of MemBuffer->buf_. It expands the allocated memory
-// size if required and also updates VP8BitReader's if new memory is allocated.
-static int AppendToMemBuffer(WebPIDecoder* const idec,
- const uint8_t* const data, size_t data_size) {
- VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
- MemBuffer* const mem = &idec->mem_;
- const int need_compressed_alpha = NeedCompressedAlpha(idec);
- const uint8_t* const old_start = mem->buf_ + mem->start_;
- const uint8_t* const old_base =
- need_compressed_alpha ? dec->alpha_data_ : old_start;
- SB_DCHECK(mem->mode_ == MEM_MODE_APPEND);
- if (data_size > MAX_CHUNK_PAYLOAD) {
- // security safeguard: trying to allocate more than what the format
- // allows for a chunk should be considered a smoke smell.
- return 0;
- }
-
- if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
- const size_t new_mem_start = old_start - old_base;
- const size_t current_size = MemDataSize(mem) + new_mem_start;
- const uint64_t new_size = (uint64_t)current_size + data_size;
- const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
- uint8_t* const new_buf =
- (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
- if (new_buf == NULL) return 0;
- SbMemoryCopy(new_buf, old_base, current_size);
- SbMemoryDeallocate(mem->buf_);
- mem->buf_ = new_buf;
- mem->buf_size_ = (size_t)extra_size;
- mem->start_ = new_mem_start;
- mem->end_ = current_size;
- }
-
- SbMemoryCopy(mem->buf_ + mem->end_, data, data_size);
- mem->end_ += data_size;
- SB_DCHECK(mem->end_ <= mem->buf_size_);
-
- DoRemap(idec, mem->buf_ + mem->start_ - old_start);
- return 1;
-}
-
-static int RemapMemBuffer(WebPIDecoder* const idec,
- const uint8_t* const data, size_t data_size) {
- MemBuffer* const mem = &idec->mem_;
- const uint8_t* const old_buf = mem->buf_;
- const uint8_t* const old_start = old_buf + mem->start_;
- SB_DCHECK(mem->mode_ == MEM_MODE_MAP);
-
- if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
-
- mem->buf_ = (uint8_t*)data;
- mem->end_ = mem->buf_size_ = data_size;
-
- DoRemap(idec, mem->buf_ + mem->start_ - old_start);
- return 1;
-}
-
-static void InitMemBuffer(MemBuffer* const mem) {
- mem->mode_ = MEM_MODE_NONE;
- mem->buf_ = NULL;
- mem->buf_size_ = 0;
- mem->part0_buf_ = NULL;
- mem->part0_size_ = 0;
-}
-
-static void ClearMemBuffer(MemBuffer* const mem) {
- SB_DCHECK(mem);
- if (mem->mode_ == MEM_MODE_APPEND) {
- SbMemoryDeallocate(mem->buf_);
- SbMemoryDeallocate((void*)mem->part0_buf_);
- }
-}
-
-static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
- if (mem->mode_ == MEM_MODE_NONE) {
- mem->mode_ = expected; // switch to the expected mode
- } else if (mem->mode_ != expected) {
- return 0; // we mixed the modes => error
- }
- SB_DCHECK(mem->mode_ == expected); // mode is ok
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Macroblock-decoding contexts
-
-static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
- MBContext* const context) {
- const VP8BitReader* const br = &dec->br_;
- const VP8MB* const left = dec->mb_info_ - 1;
- const VP8MB* const info = dec->mb_info_ + dec->mb_x_;
-
- context->left_ = *left;
- context->info_ = *info;
- context->br_ = *br;
- context->token_br_ = *token_br;
- SbMemoryCopy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4);
- SbMemoryCopy(context->intra_l_, dec->intra_l_, 4);
-}
-
-static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
- VP8BitReader* const token_br) {
- VP8BitReader* const br = &dec->br_;
- VP8MB* const left = dec->mb_info_ - 1;
- VP8MB* const info = dec->mb_info_ + dec->mb_x_;
-
- *left = context->left_;
- *info = context->info_;
- *br = context->br_;
- *token_br = context->token_br_;
- SbMemoryCopy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4);
- SbMemoryCopy(dec->intra_l_, context->intra_l_, 4);
-}
-
-//------------------------------------------------------------------------------
-
-static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
- if (idec->state_ == STATE_VP8_DATA) {
- VP8Io* const io = &idec->io_;
- if (io->teardown) {
- io->teardown(io);
- }
- }
- idec->state_ = STATE_ERROR;
- return error;
-}
-
-static void ChangeState(WebPIDecoder* const idec, DecState new_state,
- size_t consumed_bytes) {
- MemBuffer* const mem = &idec->mem_;
- idec->state_ = new_state;
- mem->start_ += consumed_bytes;
- SB_DCHECK(mem->start_ <= mem->end_);
- idec->io_.data = mem->buf_ + mem->start_;
- idec->io_.data_size = MemDataSize(mem);
-}
-
-// Headers
-static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
- MemBuffer* const mem = &idec->mem_;
- const uint8_t* data = mem->buf_ + mem->start_;
- size_t curr_size = MemDataSize(mem);
- VP8StatusCode status;
- WebPHeaderStructure headers;
-
- headers.data = data;
- headers.data_size = curr_size;
- status = WebPParseHeaders(&headers);
- if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
- return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
- } else if (status != VP8_STATUS_OK) {
- return IDecError(idec, status);
- }
-
- idec->chunk_size_ = headers.compressed_size;
- idec->is_lossless_ = headers.is_lossless;
- if (!idec->is_lossless_) {
- VP8Decoder* const dec = VP8New();
- if (dec == NULL) {
- return VP8_STATUS_OUT_OF_MEMORY;
- }
- idec->dec_ = dec;
-#ifdef WEBP_USE_THREAD
- dec->use_threads_ = (idec->params_.options != NULL) &&
- (idec->params_.options->use_threads > 0);
-#else
- dec->use_threads_ = 0;
-#endif
- dec->alpha_data_ = headers.alpha_data;
- dec->alpha_data_size_ = headers.alpha_data_size;
- ChangeState(idec, STATE_VP8_FRAME_HEADER, headers.offset);
- } else {
- VP8LDecoder* const dec = VP8LNew();
- if (dec == NULL) {
- return VP8_STATUS_OUT_OF_MEMORY;
- }
- idec->dec_ = dec;
- ChangeState(idec, STATE_VP8L_HEADER, headers.offset);
- }
- return VP8_STATUS_OK;
-}
-
-static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
- const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
- const size_t curr_size = MemDataSize(&idec->mem_);
- uint32_t bits;
-
- if (curr_size < VP8_FRAME_HEADER_SIZE) {
- // Not enough data bytes to extract VP8 Frame Header.
- return VP8_STATUS_SUSPENDED;
- }
- if (!VP8GetInfo(data, curr_size, idec->chunk_size_, NULL, NULL)) {
- return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
- }
-
- bits = data[0] | (data[1] << 8) | (data[2] << 16);
- idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE;
-
- idec->io_.data = data;
- idec->io_.data_size = curr_size;
- idec->state_ = STATE_VP8_PARTS0;
- return VP8_STATUS_OK;
-}
-
-// Partition #0
-static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {
- VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
- VP8BitReader* const br = &dec->br_;
- const size_t part_size = br->buf_end_ - br->buf_;
- MemBuffer* const mem = &idec->mem_;
- SB_DCHECK(!idec->is_lossless_);
- SB_DCHECK(mem->part0_buf_ == NULL);
- // the following is a format limitation, no need for runtime check:
- SB_DCHECK(part_size <= mem->part0_size_);
- if (part_size == 0) { // can't have zero-size partition #0
- return VP8_STATUS_BITSTREAM_ERROR;
- }
- if (mem->mode_ == MEM_MODE_APPEND) {
- // We copy and grab ownership of the partition #0 data.
- uint8_t* const part0_buf = (uint8_t*)SbMemoryAllocate(part_size);
- if (part0_buf == NULL) {
- return VP8_STATUS_OUT_OF_MEMORY;
- }
- SbMemoryCopy(part0_buf, br->buf_, part_size);
- mem->part0_buf_ = part0_buf;
- br->buf_ = part0_buf;
- br->buf_end_ = part0_buf + part_size;
- } else {
- // Else: just keep pointers to the partition #0's data in dec_->br_.
- }
- mem->start_ += part_size;
- return VP8_STATUS_OK;
-}
-
-static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
- VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
- VP8Io* const io = &idec->io_;
- const WebPDecParams* const params = &idec->params_;
- WebPDecBuffer* const output = params->output;
-
- // Wait till we have enough data for the whole partition #0
- if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) {
- return VP8_STATUS_SUSPENDED;
- }
-
- if (!VP8GetHeaders(dec, io)) {
- const VP8StatusCode status = dec->status_;
- if (status == VP8_STATUS_SUSPENDED ||
- status == VP8_STATUS_NOT_ENOUGH_DATA) {
- // treating NOT_ENOUGH_DATA as SUSPENDED state
- return VP8_STATUS_SUSPENDED;
- }
- return IDecError(idec, status);
- }
-
- // Allocate/Verify output buffer now
- dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
- output);
- if (dec->status_ != VP8_STATUS_OK) {
- return IDecError(idec, dec->status_);
- }
-
- dec->status_ = CopyParts0Data(idec);
- if (dec->status_ != VP8_STATUS_OK) {
- return IDecError(idec, dec->status_);
- }
-
- // Finish setting up the decoding parameters. Will call io->setup().
- if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) {
- return IDecError(idec, dec->status_);
- }
-
- // Note: past this point, teardown() must always be called
- // in case of error.
- idec->state_ = STATE_VP8_DATA;
- // Allocate memory and prepare everything.
- if (!VP8InitFrame(dec, io)) {
- return IDecError(idec, dec->status_);
- }
- return VP8_STATUS_OK;
-}
-
-// Remaining partitions
-static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
- VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
- VP8Io* const io = &idec->io_;
-
- SB_DCHECK(dec->ready_);
-
- for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
- VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
- if (dec->mb_x_ == 0) {
- VP8InitScanline(dec);
- }
- for (; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
- MBContext context;
- SaveContext(dec, token_br, &context);
-
- if (!VP8DecodeMB(dec, token_br)) {
- RestoreContext(&context, dec, token_br);
- // We shouldn't fail when MAX_MB data was available
- if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
- return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
- }
- return VP8_STATUS_SUSPENDED;
- }
- // Reconstruct and emit samples.
- VP8ReconstructBlock(dec);
-
- // Release buffer only if there is only one partition
- if (dec->num_parts_ == 1) {
- idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
- SB_DCHECK(idec->mem_.start_ <= idec->mem_.end_);
- }
- }
- if (!VP8ProcessRow(dec, io)) {
- return IDecError(idec, VP8_STATUS_USER_ABORT);
- }
- dec->mb_x_ = 0;
- }
- // Synchronize the thread and check for errors.
- if (!VP8ExitCritical(dec, io)) {
- return IDecError(idec, VP8_STATUS_USER_ABORT);
- }
- dec->ready_ = 0;
- idec->state_ = STATE_DONE;
-
- return VP8_STATUS_OK;
-}
-
-static int ErrorStatusLossless(WebPIDecoder* const idec, VP8StatusCode status) {
- if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
- return VP8_STATUS_SUSPENDED;
- }
- return IDecError(idec, status);
-}
-
-static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
- VP8Io* const io = &idec->io_;
- VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
- const WebPDecParams* const params = &idec->params_;
- WebPDecBuffer* const output = params->output;
- size_t curr_size = MemDataSize(&idec->mem_);
- SB_DCHECK(idec->is_lossless_);
-
- // Wait until there's enough data for decoding header.
- if (curr_size < (idec->chunk_size_ >> 3)) {
- return VP8_STATUS_SUSPENDED;
- }
- if (!VP8LDecodeHeader(dec, io)) {
- return ErrorStatusLossless(idec, dec->status_);
- }
- // Allocate/verify output buffer now.
- dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
- output);
- if (dec->status_ != VP8_STATUS_OK) {
- return IDecError(idec, dec->status_);
- }
-
- idec->state_ = STATE_VP8L_DATA;
- return VP8_STATUS_OK;
-}
-
-static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
- VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
- const size_t curr_size = MemDataSize(&idec->mem_);
- SB_DCHECK(idec->is_lossless_);
-
- // At present Lossless decoder can't decode image incrementally. So wait till
- // all the image data is aggregated before image can be decoded.
- if (curr_size < idec->chunk_size_) {
- return VP8_STATUS_SUSPENDED;
- }
-
- if (!VP8LDecodeImage(dec)) {
- return ErrorStatusLossless(idec, dec->status_);
- }
-
- idec->state_ = STATE_DONE;
-
- return VP8_STATUS_OK;
-}
-
- // Main decoding loop
-static VP8StatusCode IDecode(WebPIDecoder* idec) {
- VP8StatusCode status = VP8_STATUS_SUSPENDED;
-
- if (idec->state_ == STATE_PRE_VP8) {
- status = DecodeWebPHeaders(idec);
- } else {
- if (idec->dec_ == NULL) {
- return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
- }
- }
- if (idec->state_ == STATE_VP8_FRAME_HEADER) {
- status = DecodeVP8FrameHeader(idec);
- }
- if (idec->state_ == STATE_VP8_PARTS0) {
- status = DecodePartition0(idec);
- }
- if (idec->state_ == STATE_VP8_DATA) {
- status = DecodeRemaining(idec);
- }
- if (idec->state_ == STATE_VP8L_HEADER) {
- status = DecodeVP8LHeader(idec);
- }
- if (idec->state_ == STATE_VP8L_DATA) {
- status = DecodeVP8LData(idec);
- }
- return status;
-}
-
-//------------------------------------------------------------------------------
-// Public functions
-
-WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
- WebPIDecoder* idec = (WebPIDecoder*)SbMemoryCalloc(1, sizeof(*idec));
- if (idec == NULL) {
- return NULL;
- }
-
- idec->state_ = STATE_PRE_VP8;
- idec->chunk_size_ = 0;
-
- InitMemBuffer(&idec->mem_);
- WebPInitDecBuffer(&idec->output_);
- VP8InitIo(&idec->io_);
-
- WebPResetDecParams(&idec->params_);
- idec->params_.output = output_buffer ? output_buffer : &idec->output_;
- WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
-
- return idec;
-}
-
-WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
- WebPDecoderConfig* config) {
- WebPIDecoder* idec;
-
- // Parse the bitstream's features, if requested:
- if (data != NULL && data_size > 0 && config != NULL) {
- if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) {
- return NULL;
- }
- }
- // Create an instance of the incremental decoder
- idec = WebPINewDecoder(config ? &config->output : NULL);
- if (idec == NULL) {
- return NULL;
- }
- // Finish initialization
- if (config != NULL) {
- idec->params_.options = &config->options;
- }
- return idec;
-}
-
-void WebPIDelete(WebPIDecoder* idec) {
- if (idec == NULL) return;
- if (idec->dec_ != NULL) {
- if (!idec->is_lossless_) {
- VP8Delete(idec->dec_);
- } else {
- VP8LDelete(idec->dec_);
- }
- }
- ClearMemBuffer(&idec->mem_);
- WebPFreeDecBuffer(&idec->output_);
- SbMemoryDeallocate(idec);
-}
-
-//------------------------------------------------------------------------------
-// Wrapper toward WebPINewDecoder
-
-WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
- size_t output_buffer_size, int output_stride) {
- const int is_external_memory = (output_buffer != NULL);
- WebPIDecoder* idec;
-
- if (mode >= MODE_YUV) return NULL;
- if (!is_external_memory) { // Overwrite parameters to sane values.
- output_buffer_size = 0;
- output_stride = 0;
- } else { // A buffer was passed. Validate the other params.
- if (output_stride == 0 || output_buffer_size == 0) {
- return NULL; // invalid parameter.
- }
- }
- idec = WebPINewDecoder(NULL);
- if (idec == NULL) return NULL;
- idec->output_.colorspace = mode;
- idec->output_.is_external_memory = is_external_memory;
- idec->output_.u.RGBA.rgba = output_buffer;
- idec->output_.u.RGBA.stride = output_stride;
- idec->output_.u.RGBA.size = output_buffer_size;
- return idec;
-}
-
-WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
- uint8_t* u, size_t u_size, int u_stride,
- uint8_t* v, size_t v_size, int v_stride,
- uint8_t* a, size_t a_size, int a_stride) {
- const int is_external_memory = (luma != NULL);
- WebPIDecoder* idec;
- WEBP_CSP_MODE colorspace;
-
- if (!is_external_memory) { // Overwrite parameters to sane values.
- luma_size = u_size = v_size = a_size = 0;
- luma_stride = u_stride = v_stride = a_stride = 0;
- u = v = a = NULL;
- colorspace = MODE_YUVA;
- } else { // A luma buffer was passed. Validate the other parameters.
- if (u == NULL || v == NULL) return NULL;
- if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL;
- if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL;
- if (a != NULL) {
- if (a_size == 0 || a_stride == 0) return NULL;
- }
- colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
- }
-
- idec = WebPINewDecoder(NULL);
- if (idec == NULL) return NULL;
-
- idec->output_.colorspace = colorspace;
- idec->output_.is_external_memory = is_external_memory;
- idec->output_.u.YUVA.y = luma;
- idec->output_.u.YUVA.y_stride = luma_stride;
- idec->output_.u.YUVA.y_size = luma_size;
- idec->output_.u.YUVA.u = u;
- idec->output_.u.YUVA.u_stride = u_stride;
- idec->output_.u.YUVA.u_size = u_size;
- idec->output_.u.YUVA.v = v;
- idec->output_.u.YUVA.v_stride = v_stride;
- idec->output_.u.YUVA.v_size = v_size;
- idec->output_.u.YUVA.a = a;
- idec->output_.u.YUVA.a_stride = a_stride;
- idec->output_.u.YUVA.a_size = a_size;
- return idec;
-}
-
-WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
- uint8_t* u, size_t u_size, int u_stride,
- uint8_t* v, size_t v_size, int v_stride) {
- return WebPINewYUVA(luma, luma_size, luma_stride,
- u, u_size, u_stride,
- v, v_size, v_stride,
- NULL, 0, 0);
-}
-
-//------------------------------------------------------------------------------
-
-static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
- SB_DCHECK(idec);
- if (idec->state_ == STATE_ERROR) {
- return VP8_STATUS_BITSTREAM_ERROR;
- }
- if (idec->state_ == STATE_DONE) {
- return VP8_STATUS_OK;
- }
- return VP8_STATUS_SUSPENDED;
-}
-
-VP8StatusCode WebPIAppend(WebPIDecoder* idec,
- const uint8_t* data, size_t data_size) {
- VP8StatusCode status;
- if (idec == NULL || data == NULL) {
- return VP8_STATUS_INVALID_PARAM;
- }
- status = IDecCheckStatus(idec);
- if (status != VP8_STATUS_SUSPENDED) {
- return status;
- }
- // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
- if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) {
- return VP8_STATUS_INVALID_PARAM;
- }
- // Append data to memory buffer
- if (!AppendToMemBuffer(idec, data, data_size)) {
- return VP8_STATUS_OUT_OF_MEMORY;
- }
- return IDecode(idec);
-}
-
-VP8StatusCode WebPIUpdate(WebPIDecoder* idec,
- const uint8_t* data, size_t data_size) {
- VP8StatusCode status;
- if (idec == NULL || data == NULL) {
- return VP8_STATUS_INVALID_PARAM;
- }
- status = IDecCheckStatus(idec);
- if (status != VP8_STATUS_SUSPENDED) {
- return status;
- }
- // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
- if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) {
- return VP8_STATUS_INVALID_PARAM;
- }
- // Make the memory buffer point to the new buffer
- if (!RemapMemBuffer(idec, data, data_size)) {
- return VP8_STATUS_INVALID_PARAM;
- }
- return IDecode(idec);
-}
-
-//------------------------------------------------------------------------------
-
-static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
- if (idec == NULL || idec->dec_ == NULL) {
- return NULL;
- }
- if (idec->state_ <= STATE_VP8_PARTS0) {
- return NULL;
- }
- return idec->params_.output;
-}
-
-const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
- int* left, int* top,
- int* width, int* height) {
- const WebPDecBuffer* const src = GetOutputBuffer(idec);
- if (left != NULL) *left = 0;
- if (top != NULL) *top = 0;
- // TODO(skal): later include handling of rotations.
- if (src) {
- if (width != NULL) *width = src->width;
- if (height != NULL) *height = idec->params_.last_y;
- } else {
- if (width != NULL) *width = 0;
- if (height != NULL) *height = 0;
- }
- return src;
-}
-
-uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
- int* width, int* height, int* stride) {
- const WebPDecBuffer* const src = GetOutputBuffer(idec);
- if (src == NULL) return NULL;
- if (src->colorspace >= MODE_YUV) {
- return NULL;
- }
-
- if (last_y != NULL) *last_y = idec->params_.last_y;
- if (width != NULL) *width = src->width;
- if (height != NULL) *height = src->height;
- if (stride != NULL) *stride = src->u.RGBA.stride;
-
- return src->u.RGBA.rgba;
-}
-
-uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
- uint8_t** u, uint8_t** v, uint8_t** a,
- int* width, int* height,
- int* stride, int* uv_stride, int* a_stride) {
- const WebPDecBuffer* const src = GetOutputBuffer(idec);
- if (src == NULL) return NULL;
- if (src->colorspace < MODE_YUV) {
- return NULL;
- }
-
- if (last_y != NULL) *last_y = idec->params_.last_y;
- if (u != NULL) *u = src->u.YUVA.u;
- if (v != NULL) *v = src->u.YUVA.v;
- if (a != NULL) *a = src->u.YUVA.a;
- if (width != NULL) *width = src->width;
- if (height != NULL) *height = src->height;
- if (stride != NULL) *stride = src->u.YUVA.y_stride;
- if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride;
- if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride;
-
- return src->u.YUVA.y;
-}
-
-int WebPISetIOHooks(WebPIDecoder* const idec,
- VP8IoPutHook put,
- VP8IoSetupHook setup,
- VP8IoTeardownHook teardown,
- void* user_data) {
- if (idec == NULL || idec->state_ > STATE_PRE_VP8) {
- return 0;
- }
-
- idec->io_.put = put;
- idec->io_.setup = setup;
- idec->io_.teardown = teardown;
- idec->io_.opaque = user_data;
-
- return 1;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/io.c b/src/third_party/libwebp/dec/io.c
deleted file mode 100644
index 60a6865..0000000
--- a/src/third_party/libwebp/dec/io.c
+++ /dev/null
@@ -1,641 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// functions for sample output.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#endif
-
-#include "../dec/vp8i.h"
-#include "./webpi.h"
-#include "../dsp/dsp.h"
-#include "../dsp/yuv.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Main YUV<->RGB conversion functions
-
-static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
- WebPDecBuffer* output = p->output;
- const WebPYUVABuffer* const buf = &output->u.YUVA;
- uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride;
- uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride;
- uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride;
- const int mb_w = io->mb_w;
- const int mb_h = io->mb_h;
- const int uv_w = (mb_w + 1) / 2;
- const int uv_h = (mb_h + 1) / 2;
- int j;
- for (j = 0; j < mb_h; ++j) {
- SbMemoryCopy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
- }
- for (j = 0; j < uv_h; ++j) {
- SbMemoryCopy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
- SbMemoryCopy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
- }
- return io->mb_h;
-}
-
-// Point-sampling U/V sampler.
-static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
- WebPDecBuffer* output = p->output;
- const WebPRGBABuffer* const buf = &output->u.RGBA;
- uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
- const uint8_t* y_src = io->y;
- const uint8_t* u_src = io->u;
- const uint8_t* v_src = io->v;
- const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
- const int mb_w = io->mb_w;
- const int last = io->mb_h - 1;
- int j;
- for (j = 0; j < last; j += 2) {
- sample(y_src, y_src + io->y_stride, u_src, v_src,
- dst, dst + buf->stride, mb_w);
- y_src += 2 * io->y_stride;
- u_src += io->uv_stride;
- v_src += io->uv_stride;
- dst += 2 * buf->stride;
- }
- if (j == last) { // Just do the last line twice
- sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
- }
- return io->mb_h;
-}
-
-//------------------------------------------------------------------------------
-// YUV444 -> RGB conversion
-
-#if 0 // TODO(skal): this is for future rescaling.
-static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
- WebPDecBuffer* output = p->output;
- const WebPRGBABuffer* const buf = &output->u.RGBA;
- uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
- const uint8_t* y_src = io->y;
- const uint8_t* u_src = io->u;
- const uint8_t* v_src = io->v;
- const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace];
- const int mb_w = io->mb_w;
- const int last = io->mb_h;
- int j;
- for (j = 0; j < last; ++j) {
- convert(y_src, u_src, v_src, dst, mb_w);
- y_src += io->y_stride;
- u_src += io->uv_stride;
- v_src += io->uv_stride;
- dst += buf->stride;
- }
- return io->mb_h;
-}
-#endif
-
-//------------------------------------------------------------------------------
-// Fancy upsampling
-
-#ifdef FANCY_UPSAMPLING
-static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
- int num_lines_out = io->mb_h; // a priori guess
- const WebPRGBABuffer* const buf = &p->output->u.RGBA;
- uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
- WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
- const uint8_t* cur_y = io->y;
- const uint8_t* cur_u = io->u;
- const uint8_t* cur_v = io->v;
- const uint8_t* top_u = p->tmp_u;
- const uint8_t* top_v = p->tmp_v;
- int y = io->mb_y;
- const int y_end = io->mb_y + io->mb_h;
- const int mb_w = io->mb_w;
- const int uv_w = (mb_w + 1) / 2;
-
- if (y == 0) {
- // First line is special cased. We mirror the u/v samples at boundary.
- upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
- } else {
- // We can finish the left-over line from previous call.
- upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
- dst - buf->stride, dst, mb_w);
- ++num_lines_out;
- }
- // Loop over each output pairs of row.
- for (; y + 2 < y_end; y += 2) {
- top_u = cur_u;
- top_v = cur_v;
- cur_u += io->uv_stride;
- cur_v += io->uv_stride;
- dst += 2 * buf->stride;
- cur_y += 2 * io->y_stride;
- upsample(cur_y - io->y_stride, cur_y,
- top_u, top_v, cur_u, cur_v,
- dst - buf->stride, dst, mb_w);
- }
- // move to last row
- cur_y += io->y_stride;
- if (io->crop_top + y_end < io->crop_bottom) {
- // Save the unfinished samples for next call (as we're not done yet).
- SbMemoryCopy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
- SbMemoryCopy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
- SbMemoryCopy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
- // The fancy upsampler leaves a row unfinished behind
- // (except for the very last row)
- num_lines_out--;
- } else {
- // Process the very last row of even-sized picture
- if (!(y_end & 1)) {
- upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
- dst + buf->stride, NULL, mb_w);
- }
- }
- return num_lines_out;
-}
-
-#endif /* FANCY_UPSAMPLING */
-
-//------------------------------------------------------------------------------
-
-static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
- const uint8_t* alpha = io->a;
- const WebPYUVABuffer* const buf = &p->output->u.YUVA;
- const int mb_w = io->mb_w;
- const int mb_h = io->mb_h;
- uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
- int j;
-
- if (alpha != NULL) {
- for (j = 0; j < mb_h; ++j) {
- SbMemoryCopy(dst, alpha, mb_w * sizeof(*dst));
- alpha += io->width;
- dst += buf->a_stride;
- }
- } else if (buf->a != NULL) {
- // the user requested alpha, but there is none, set it to opaque.
- for (j = 0; j < mb_h; ++j) {
- SbMemorySet(dst, 0xff, mb_w * sizeof(*dst));
- dst += buf->a_stride;
- }
- }
- return 0;
-}
-
-static int GetAlphaSourceRow(const VP8Io* const io,
- const uint8_t** alpha, int* const num_rows) {
- int start_y = io->mb_y;
- *num_rows = io->mb_h;
-
- // Compensate for the 1-line delay of the fancy upscaler.
- // This is similar to EmitFancyRGB().
- if (io->fancy_upsampling) {
- if (start_y == 0) {
- // We don't process the last row yet. It'll be done during the next call.
- --*num_rows;
- } else {
- --start_y;
- // Fortunately, *alpha data is persistent, so we can go back
- // one row and finish alpha blending, now that the fancy upscaler
- // completed the YUV->RGB interpolation.
- *alpha -= io->width;
- }
- if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
- // If it's the very last call, we process all the remaining rows!
- *num_rows = io->crop_bottom - io->crop_top - start_y;
- }
- }
- return start_y;
-}
-
-static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
- const uint8_t* alpha = io->a;
- if (alpha != NULL) {
- const int mb_w = io->mb_w;
- const WEBP_CSP_MODE colorspace = p->output->colorspace;
- const int alpha_first =
- (colorspace == MODE_ARGB || colorspace == MODE_Argb);
- const WebPRGBABuffer* const buf = &p->output->u.RGBA;
- int num_rows;
- const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
- uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
- uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
- uint32_t alpha_mask = 0xff;
- int i, j;
-
- for (j = 0; j < num_rows; ++j) {
- for (i = 0; i < mb_w; ++i) {
- const uint32_t alpha_value = alpha[i];
- dst[4 * i] = alpha_value;
- alpha_mask &= alpha_value;
- }
- alpha += io->width;
- dst += buf->stride;
- }
- // alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
- if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
- WebPApplyAlphaMultiply(base_rgba, alpha_first,
- mb_w, num_rows, buf->stride);
- }
- }
- return 0;
-}
-
-static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
- const uint8_t* alpha = io->a;
- if (alpha != NULL) {
- const int mb_w = io->mb_w;
- const WEBP_CSP_MODE colorspace = p->output->colorspace;
- const WebPRGBABuffer* const buf = &p->output->u.RGBA;
- int num_rows;
- const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
- uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
- uint8_t* alpha_dst = base_rgba + 1;
- uint32_t alpha_mask = 0x0f;
- int i, j;
-
- for (j = 0; j < num_rows; ++j) {
- for (i = 0; i < mb_w; ++i) {
- // Fill in the alpha value (converted to 4 bits).
- const uint32_t alpha_value = alpha[i] >> 4;
- alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
- alpha_mask &= alpha_value;
- }
- alpha += io->width;
- alpha_dst += buf->stride;
- }
- if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
- WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
- }
- }
- return 0;
-}
-
-//------------------------------------------------------------------------------
-// YUV rescaling (no final RGB conversion needed)
-
-static int Rescale(const uint8_t* src, int src_stride,
- int new_lines, WebPRescaler* const wrk) {
- int num_lines_out = 0;
- while (new_lines > 0) { // import new contributions of source rows.
- const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
- src += lines_in * src_stride;
- new_lines -= lines_in;
- num_lines_out += WebPRescalerExport(wrk); // emit output row(s)
- }
- return num_lines_out;
-}
-
-static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
- const int mb_h = io->mb_h;
- const int uv_mb_h = (mb_h + 1) >> 1;
- const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
- Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
- Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
- return num_lines_out;
-}
-
-static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
- if (io->a != NULL) {
- Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
- }
- return 0;
-}
-
-static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
- const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
- const WebPYUVABuffer* const buf = &p->output->u.YUVA;
- const int out_width = io->scaled_width;
- const int out_height = io->scaled_height;
- const int uv_out_width = (out_width + 1) >> 1;
- const int uv_out_height = (out_height + 1) >> 1;
- const int uv_in_width = (io->mb_w + 1) >> 1;
- const int uv_in_height = (io->mb_h + 1) >> 1;
- const size_t work_size = 2 * out_width; // scratch memory for luma rescaler
- const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
- size_t tmp_size;
- int32_t* work;
-
- tmp_size = work_size + 2 * uv_work_size;
- if (has_alpha) {
- tmp_size += work_size;
- }
- p->memory = SbMemoryCalloc(1, tmp_size * sizeof(*work));
- if (p->memory == NULL) {
- return 0; // memory error
- }
- work = (int32_t*)p->memory;
- WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
- buf->y, out_width, out_height, buf->y_stride, 1,
- io->mb_w, out_width, io->mb_h, out_height,
- work);
- WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
- buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
- uv_in_width, uv_out_width,
- uv_in_height, uv_out_height,
- work + work_size);
- WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
- buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
- uv_in_width, uv_out_width,
- uv_in_height, uv_out_height,
- work + work_size + uv_work_size);
- p->emit = EmitRescaledYUV;
-
- if (has_alpha) {
- WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
- buf->a, out_width, out_height, buf->a_stride, 1,
- io->mb_w, out_width, io->mb_h, out_height,
- work + work_size + 2 * uv_work_size);
- p->emit_alpha = EmitRescaledAlphaYUV;
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// RGBA rescaling
-
-static int ExportRGB(WebPDecParams* const p, int y_pos) {
- const WebPYUV444Converter convert =
- WebPYUV444Converters[p->output->colorspace];
- const WebPRGBABuffer* const buf = &p->output->u.RGBA;
- uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
- int num_lines_out = 0;
- // For RGB rescaling, because of the YUV420, current scan position
- // U/V can be +1/-1 line from the Y one. Hence the double test.
- while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
- WebPRescalerHasPendingOutput(&p->scaler_u)) {
- SB_DCHECK(p->last_y + y_pos + num_lines_out < p->output->height);
- SB_DCHECK(p->scaler_u.y_accum == p->scaler_v.y_accum);
- WebPRescalerExportRow(&p->scaler_y);
- WebPRescalerExportRow(&p->scaler_u);
- WebPRescalerExportRow(&p->scaler_v);
- convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
- dst, p->scaler_y.dst_width);
- dst += buf->stride;
- ++num_lines_out;
- }
- return num_lines_out;
-}
-
-static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
- const int mb_h = io->mb_h;
- const int uv_mb_h = (mb_h + 1) >> 1;
- int j = 0, uv_j = 0;
- int num_lines_out = 0;
- while (j < mb_h) {
- const int y_lines_in =
- WebPRescalerImport(&p->scaler_y, mb_h - j,
- io->y + j * io->y_stride, io->y_stride);
- const int u_lines_in =
- WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
- io->u + uv_j * io->uv_stride, io->uv_stride);
- const int v_lines_in =
- WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
- io->v + uv_j * io->uv_stride, io->uv_stride);
- (void)v_lines_in; // remove a gcc warning
- SB_DCHECK(u_lines_in == v_lines_in);
- j += y_lines_in;
- uv_j += u_lines_in;
- num_lines_out += ExportRGB(p, num_lines_out);
- }
- return num_lines_out;
-}
-
-static int ExportAlpha(WebPDecParams* const p, int y_pos) {
- const WebPRGBABuffer* const buf = &p->output->u.RGBA;
- uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
- const WEBP_CSP_MODE colorspace = p->output->colorspace;
- const int alpha_first =
- (colorspace == MODE_ARGB || colorspace == MODE_Argb);
- uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
- int num_lines_out = 0;
- const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
- uint32_t alpha_mask = 0xff;
- const int width = p->scaler_a.dst_width;
-
- while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
- int i;
- SB_DCHECK(p->last_y + y_pos + num_lines_out < p->output->height);
- WebPRescalerExportRow(&p->scaler_a);
- for (i = 0; i < width; ++i) {
- const uint32_t alpha_value = p->scaler_a.dst[i];
- dst[4 * i] = alpha_value;
- alpha_mask &= alpha_value;
- }
- dst += buf->stride;
- ++num_lines_out;
- }
- if (is_premult_alpha && alpha_mask != 0xff) {
- WebPApplyAlphaMultiply(base_rgba, alpha_first,
- width, num_lines_out, buf->stride);
- }
- return num_lines_out;
-}
-
-static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
- const WebPRGBABuffer* const buf = &p->output->u.RGBA;
- uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
- uint8_t* alpha_dst = base_rgba + 1;
- int num_lines_out = 0;
- const WEBP_CSP_MODE colorspace = p->output->colorspace;
- const int width = p->scaler_a.dst_width;
- const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
- uint32_t alpha_mask = 0x0f;
-
- while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
- int i;
- SB_DCHECK(p->last_y + y_pos + num_lines_out < p->output->height);
- WebPRescalerExportRow(&p->scaler_a);
- for (i = 0; i < width; ++i) {
- // Fill in the alpha value (converted to 4 bits).
- const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
- alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
- alpha_mask &= alpha_value;
- }
- alpha_dst += buf->stride;
- ++num_lines_out;
- }
- if (is_premult_alpha && alpha_mask != 0x0f) {
- WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
- }
- return num_lines_out;
-}
-
-static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
- if (io->a != NULL) {
- WebPRescaler* const scaler = &p->scaler_a;
- int j = 0;
- int pos = 0;
- while (j < io->mb_h) {
- j += WebPRescalerImport(scaler, io->mb_h - j,
- io->a + j * io->width, io->width);
- pos += p->emit_alpha_row(p, pos);
- }
- }
- return 0;
-}
-
-static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
- const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
- const int out_width = io->scaled_width;
- const int out_height = io->scaled_height;
- const int uv_in_width = (io->mb_w + 1) >> 1;
- const int uv_in_height = (io->mb_h + 1) >> 1;
- const size_t work_size = 2 * out_width; // scratch memory for one rescaler
- int32_t* work; // rescalers work area
- uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
- size_t tmp_size1, tmp_size2;
-
- tmp_size1 = 3 * work_size;
- tmp_size2 = 3 * out_width;
- if (has_alpha) {
- tmp_size1 += work_size;
- tmp_size2 += out_width;
- }
- p->memory = SbMemoryCalloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
- if (p->memory == NULL) {
- return 0; // memory error
- }
- work = (int32_t*)p->memory;
- tmp = (uint8_t*)(work + tmp_size1);
- WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
- tmp + 0 * out_width, out_width, out_height, 0, 1,
- io->mb_w, out_width, io->mb_h, out_height,
- work + 0 * work_size);
- WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
- tmp + 1 * out_width, out_width, out_height, 0, 1,
- io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
- work + 1 * work_size);
- WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
- tmp + 2 * out_width, out_width, out_height, 0, 1,
- io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
- work + 2 * work_size);
- p->emit = EmitRescaledRGB;
-
- if (has_alpha) {
- WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
- tmp + 3 * out_width, out_width, out_height, 0, 1,
- io->mb_w, out_width, io->mb_h, out_height,
- work + 3 * work_size);
- p->emit_alpha = EmitRescaledAlphaRGB;
- if (p->output->colorspace == MODE_RGBA_4444 ||
- p->output->colorspace == MODE_rgbA_4444) {
- p->emit_alpha_row = ExportAlphaRGBA4444;
- } else {
- p->emit_alpha_row = ExportAlpha;
- }
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Default custom functions
-
-static int CustomSetup(VP8Io* io) {
- WebPDecParams* const p = (WebPDecParams*)io->opaque;
- const WEBP_CSP_MODE colorspace = p->output->colorspace;
- const int is_rgb = WebPIsRGBMode(colorspace);
- const int is_alpha = WebPIsAlphaMode(colorspace);
-
- p->memory = NULL;
- p->emit = NULL;
- p->emit_alpha = NULL;
- p->emit_alpha_row = NULL;
- if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
- return 0;
- }
-
- if (io->use_scaling) {
- const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
- if (!ok) {
- return 0; // memory error
- }
- } else {
- if (is_rgb) {
- p->emit = EmitSampledRGB; // default
-#ifdef FANCY_UPSAMPLING
- if (io->fancy_upsampling) {
- const int uv_width = (io->mb_w + 1) >> 1;
- p->memory = SbMemoryAllocate(io->mb_w + 2 * uv_width);
- if (p->memory == NULL) {
- return 0; // memory error.
- }
- p->tmp_y = (uint8_t*)p->memory;
- p->tmp_u = p->tmp_y + io->mb_w;
- p->tmp_v = p->tmp_u + uv_width;
- p->emit = EmitFancyRGB;
- WebPInitUpsamplers();
- }
-#endif
- } else {
- p->emit = EmitYUV;
- }
- if (is_alpha) { // need transparency output
- if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
- p->emit_alpha =
- (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
- EmitAlphaRGBA4444
- : is_rgb ? EmitAlphaRGB
- : EmitAlphaYUV;
- }
- }
-
- if (is_rgb) {
- VP8YUVInit();
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-static int CustomPut(const VP8Io* io) {
- WebPDecParams* const p = (WebPDecParams*)io->opaque;
- const int mb_w = io->mb_w;
- const int mb_h = io->mb_h;
- int num_lines_out;
- SB_DCHECK(!(io->mb_y & 1));
-
- if (mb_w <= 0 || mb_h <= 0) {
- return 0;
- }
- num_lines_out = p->emit(io, p);
- if (p->emit_alpha) {
- p->emit_alpha(io, p);
- }
- p->last_y += num_lines_out;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-static void CustomTeardown(const VP8Io* io) {
- WebPDecParams* const p = (WebPDecParams*)io->opaque;
- SbMemoryDeallocate(p->memory);
- p->memory = NULL;
-}
-
-//------------------------------------------------------------------------------
-// Main entry point
-
-void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
- io->put = CustomPut;
- io->setup = CustomSetup;
- io->teardown = CustomTeardown;
- io->opaque = params;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/layer.c b/src/third_party/libwebp/dec/layer.c
deleted file mode 100644
index 3a7042f..0000000
--- a/src/third_party/libwebp/dec/layer.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Enhancement layer (for YUV444/422)
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#endif
-
-#include "./vp8i.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-
-int VP8DecodeLayer(VP8Decoder* const dec) {
- SB_DCHECK(dec);
- SB_DCHECK(dec->layer_data_size_ > 0);
- (void)dec;
-
- // TODO: handle enhancement layer here.
-
- return 1;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/quant.c b/src/third_party/libwebp/dec/quant.c
deleted file mode 100644
index a4cc693..0000000
--- a/src/third_party/libwebp/dec/quant.c
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Quantizer initialization
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "./vp8i.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-static WEBP_INLINE int clip(int v, int M) {
- return v < 0 ? 0 : v > M ? M : v;
-}
-
-// Paragraph 14.1
-static const uint8_t kDcTable[128] = {
- 4, 5, 6, 7, 8, 9, 10, 10,
- 11, 12, 13, 14, 15, 16, 17, 17,
- 18, 19, 20, 20, 21, 21, 22, 22,
- 23, 23, 24, 25, 25, 26, 27, 28,
- 29, 30, 31, 32, 33, 34, 35, 36,
- 37, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 46, 47, 48, 49, 50,
- 51, 52, 53, 54, 55, 56, 57, 58,
- 59, 60, 61, 62, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 72, 73, 74,
- 75, 76, 76, 77, 78, 79, 80, 81,
- 82, 83, 84, 85, 86, 87, 88, 89,
- 91, 93, 95, 96, 98, 100, 101, 102,
- 104, 106, 108, 110, 112, 114, 116, 118,
- 122, 124, 126, 128, 130, 132, 134, 136,
- 138, 140, 143, 145, 148, 151, 154, 157
-};
-
-static const uint16_t kAcTable[128] = {
- 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30, 31, 32, 33, 34, 35,
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, 57, 58, 60,
- 62, 64, 66, 68, 70, 72, 74, 76,
- 78, 80, 82, 84, 86, 88, 90, 92,
- 94, 96, 98, 100, 102, 104, 106, 108,
- 110, 112, 114, 116, 119, 122, 125, 128,
- 131, 134, 137, 140, 143, 146, 149, 152,
- 155, 158, 161, 164, 167, 170, 173, 177,
- 181, 185, 189, 193, 197, 201, 205, 209,
- 213, 217, 221, 225, 229, 234, 239, 245,
- 249, 254, 259, 264, 269, 274, 279, 284
-};
-
-//------------------------------------------------------------------------------
-// Paragraph 9.6
-
-void VP8ParseQuant(VP8Decoder* const dec) {
- VP8BitReader* const br = &dec->br_;
- const int base_q0 = VP8GetValue(br, 7);
- const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
- const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
- const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
- const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
- const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
-
- const VP8SegmentHeader* const hdr = &dec->segment_hdr_;
- int i;
-
- for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
- int q;
- if (hdr->use_segment_) {
- q = hdr->quantizer_[i];
- if (!hdr->absolute_delta_) {
- q += base_q0;
- }
- } else {
- if (i > 0) {
- dec->dqm_[i] = dec->dqm_[0];
- continue;
- } else {
- q = base_q0;
- }
- }
- {
- VP8QuantMatrix* const m = &dec->dqm_[i];
- m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)];
- m->y1_mat_[1] = kAcTable[clip(q + 0, 127)];
-
- m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2;
- // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
- // The smallest precision for that is '(x*6349) >> 12' but 16 is a good
- // word size.
- m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16;
- if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8;
-
- m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
- m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/tree.c b/src/third_party/libwebp/dec/tree.c
deleted file mode 100644
index dcb32fc..0000000
--- a/src/third_party/libwebp/dec/tree.c
+++ /dev/null
@@ -1,595 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Coding trees and probas
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "vp8i.h"
-
-#define USE_GENERIC_TREE
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#ifdef USE_GENERIC_TREE
-static const int8_t kYModesIntra4[18] = {
- -B_DC_PRED, 1,
- -B_TM_PRED, 2,
- -B_VE_PRED, 3,
- 4, 6,
- -B_HE_PRED, 5,
- -B_RD_PRED, -B_VR_PRED,
- -B_LD_PRED, 7,
- -B_VL_PRED, 8,
- -B_HD_PRED, -B_HU_PRED
-};
-#endif
-
-#ifndef ONLY_KEYFRAME_CODE
-
-// inter prediction modes
-enum {
- LEFT4 = 0, ABOVE4 = 1, ZERO4 = 2, NEW4 = 3,
- NEARESTMV, NEARMV, ZEROMV, NEWMV, SPLITMV };
-
-static const int8_t kYModesInter[8] = {
- -DC_PRED, 1,
- 2, 3,
- -V_PRED, -H_PRED,
- -TM_PRED, -B_PRED
-};
-
-static const int8_t kMBSplit[6] = {
- -3, 1,
- -2, 2,
- -0, -1
-};
-
-static const int8_t kMVRef[8] = {
- -ZEROMV, 1,
- -NEARESTMV, 2,
- -NEARMV, 3,
- -NEWMV, -SPLITMV
-};
-
-static const int8_t kMVRef4[6] = {
- -LEFT4, 1,
- -ABOVE4, 2,
- -ZERO4, -NEW4
-};
-#endif
-
-//------------------------------------------------------------------------------
-// Default probabilities
-
-// Inter
-#ifndef ONLY_KEYFRAME_CODE
-static const uint8_t kYModeProbaInter0[4] = { 112, 86, 140, 37 };
-static const uint8_t kUVModeProbaInter0[3] = { 162, 101, 204 };
-static const uint8_t kMVProba0[2][NUM_MV_PROBAS] = {
- { 162, 128, 225, 146, 172, 147, 214, 39,
- 156, 128, 129, 132, 75, 145, 178, 206,
- 239, 254, 254 },
- { 164, 128, 204, 170, 119, 235, 140, 230,
- 228, 128, 130, 130, 74, 148, 180, 203,
- 236, 254, 254 }
-};
-#endif
-
-// Paragraph 13.5
-static const uint8_t
- CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
- // genereated using vp8_default_coef_probs() in entropy.c:129
- { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
- { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
- { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
- },
- { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
- { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
- { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
- },
- { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
- { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
- { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
- },
- { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
- { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
- { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
- },
- { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
- { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
- { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
- },
- { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
- { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
- { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
- },
- { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
- }
- },
- { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
- { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
- { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
- },
- { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
- { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
- { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
- },
- { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
- { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
- { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
- },
- { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
- { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
- { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
- },
- { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
- { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
- { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
- },
- { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
- { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
- { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
- },
- { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
- { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
- { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
- },
- { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
- { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
- }
- },
- { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
- { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
- { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
- },
- { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
- { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
- { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
- },
- { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
- { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
- { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
- },
- { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
- { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
- },
- { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
- { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
- }
- },
- { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
- { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
- { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
- },
- { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
- { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
- { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
- },
- { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
- { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
- { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
- },
- { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
- { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
- { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
- },
- { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
- { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
- { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
- },
- { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
- { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
- { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
- },
- { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
- { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
- { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
- },
- { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- }
- }
-};
-
-// Paragraph 11.5
-static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
- { { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
- { 152, 179, 64, 126, 170, 118, 46, 70, 95 },
- { 175, 69, 143, 80, 85, 82, 72, 155, 103 },
- { 56, 58, 10, 171, 218, 189, 17, 13, 152 },
- { 114, 26, 17, 163, 44, 195, 21, 10, 173 },
- { 121, 24, 80, 195, 26, 62, 44, 64, 85 },
- { 144, 71, 10, 38, 171, 213, 144, 34, 26 },
- { 170, 46, 55, 19, 136, 160, 33, 206, 71 },
- { 63, 20, 8, 114, 114, 208, 12, 9, 226 },
- { 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
- { { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
- { 72, 187, 100, 130, 157, 111, 32, 75, 80 },
- { 66, 102, 167, 99, 74, 62, 40, 234, 128 },
- { 41, 53, 9, 178, 241, 141, 26, 8, 107 },
- { 74, 43, 26, 146, 73, 166, 49, 23, 157 },
- { 65, 38, 105, 160, 51, 52, 31, 115, 128 },
- { 104, 79, 12, 27, 217, 255, 87, 17, 7 },
- { 87, 68, 71, 44, 114, 51, 15, 186, 23 },
- { 47, 41, 14, 110, 182, 183, 21, 17, 194 },
- { 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
- { { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
- { 43, 97, 183, 117, 85, 38, 35, 179, 61 },
- { 39, 53, 200, 87, 26, 21, 43, 232, 171 },
- { 56, 34, 51, 104, 114, 102, 29, 93, 77 },
- { 39, 28, 85, 171, 58, 165, 90, 98, 64 },
- { 34, 22, 116, 206, 23, 34, 43, 166, 73 },
- { 107, 54, 32, 26, 51, 1, 81, 43, 31 },
- { 68, 25, 106, 22, 64, 171, 36, 225, 114 },
- { 34, 19, 21, 102, 132, 188, 16, 76, 124 },
- { 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
- { { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
- { 60, 148, 31, 172, 219, 228, 21, 18, 111 },
- { 112, 113, 77, 85, 179, 255, 38, 120, 114 },
- { 40, 42, 1, 196, 245, 209, 10, 25, 109 },
- { 88, 43, 29, 140, 166, 213, 37, 43, 154 },
- { 61, 63, 30, 155, 67, 45, 68, 1, 209 },
- { 100, 80, 8, 43, 154, 1, 51, 26, 71 },
- { 142, 78, 78, 16, 255, 128, 34, 197, 171 },
- { 41, 40, 5, 102, 211, 183, 4, 1, 221 },
- { 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
- { { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
- { 67, 87, 58, 169, 82, 115, 26, 59, 179 },
- { 63, 59, 90, 180, 59, 166, 93, 73, 154 },
- { 40, 40, 21, 116, 143, 209, 34, 39, 175 },
- { 47, 15, 16, 183, 34, 223, 49, 45, 183 },
- { 46, 17, 33, 183, 6, 98, 15, 32, 183 },
- { 57, 46, 22, 24, 128, 1, 54, 17, 37 },
- { 65, 32, 73, 115, 28, 128, 23, 128, 205 },
- { 40, 3, 9, 115, 51, 192, 18, 6, 223 },
- { 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
- { { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
- { 64, 90, 70, 205, 40, 41, 23, 26, 57 },
- { 54, 57, 112, 184, 5, 41, 38, 166, 213 },
- { 30, 34, 26, 133, 152, 116, 10, 32, 134 },
- { 39, 19, 53, 221, 26, 114, 32, 73, 255 },
- { 31, 9, 65, 234, 2, 15, 1, 118, 73 },
- { 75, 32, 12, 51, 192, 255, 160, 43, 51 },
- { 88, 31, 35, 67, 102, 85, 55, 186, 85 },
- { 56, 21, 23, 111, 59, 205, 45, 37, 192 },
- { 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
- { { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
- { 95, 84, 53, 89, 128, 100, 113, 101, 45 },
- { 75, 79, 123, 47, 51, 128, 81, 171, 1 },
- { 57, 17, 5, 71, 102, 57, 53, 41, 49 },
- { 38, 33, 13, 121, 57, 73, 26, 1, 85 },
- { 41, 10, 67, 138, 77, 110, 90, 47, 114 },
- { 115, 21, 2, 10, 102, 255, 166, 23, 6 },
- { 101, 29, 16, 10, 85, 128, 101, 196, 26 },
- { 57, 18, 10, 102, 102, 213, 34, 20, 43 },
- { 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
- { { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
- { 69, 60, 71, 38, 73, 119, 28, 222, 37 },
- { 68, 45, 128, 34, 1, 47, 11, 245, 171 },
- { 62, 17, 19, 70, 146, 85, 55, 62, 70 },
- { 37, 43, 37, 154, 100, 163, 85, 160, 1 },
- { 63, 9, 92, 136, 28, 64, 32, 201, 85 },
- { 75, 15, 9, 9, 64, 255, 184, 119, 16 },
- { 86, 6, 28, 5, 64, 255, 25, 248, 1 },
- { 56, 8, 17, 132, 137, 255, 55, 116, 128 },
- { 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
- { { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
- { 51, 103, 44, 131, 131, 123, 31, 6, 158 },
- { 86, 40, 64, 135, 148, 224, 45, 183, 128 },
- { 22, 26, 17, 131, 240, 154, 14, 1, 209 },
- { 45, 16, 21, 91, 64, 222, 7, 1, 197 },
- { 56, 21, 39, 155, 60, 138, 23, 102, 213 },
- { 83, 12, 13, 54, 192, 255, 68, 47, 28 },
- { 85, 26, 85, 85, 128, 128, 32, 146, 171 },
- { 18, 11, 7, 63, 144, 171, 4, 4, 246 },
- { 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
- { { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
- { 85, 126, 47, 87, 176, 51, 41, 20, 32 },
- { 101, 75, 128, 139, 118, 146, 116, 128, 85 },
- { 56, 41, 15, 176, 236, 85, 37, 9, 62 },
- { 71, 30, 17, 119, 118, 255, 17, 18, 138 },
- { 101, 38, 60, 138, 55, 70, 43, 26, 142 },
- { 146, 36, 19, 30, 171, 255, 97, 27, 20 },
- { 138, 45, 61, 62, 219, 1, 81, 188, 64 },
- { 32, 41, 20, 117, 151, 142, 20, 21, 163 },
- { 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
-};
-
-void VP8ResetProba(VP8Proba* const proba) {
- SbMemorySet(proba->segments_, 255u, sizeof(proba->segments_));
- SbMemoryCopy(proba->coeffs_, CoeffsProba0, sizeof(CoeffsProba0));
-#ifndef ONLY_KEYFRAME_CODE
- SbMemoryCopy(proba->mv_, kMVProba0, sizeof(kMVProba0));
- SbMemoryCopy(proba->ymode_, kYModeProbaInter0, sizeof(kYModeProbaInter0));
- SbMemoryCopy(proba->uvmode_, kUVModeProbaInter0, sizeof(kUVModeProbaInter0));
-#endif
-}
-
-void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
- uint8_t* const top = dec->intra_t_ + 4 * dec->mb_x_;
- uint8_t* const left = dec->intra_l_;
- // Hardcoded 16x16 intra-mode decision tree.
- dec->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
- if (!dec->is_i4x4_) {
- const int ymode =
- VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED)
- : (VP8GetBit(br, 163) ? V_PRED : DC_PRED);
- dec->imodes_[0] = ymode;
- SbMemorySet(top, ymode, 4 * sizeof(top[0]));
- SbMemorySet(left, ymode, 4 * sizeof(left[0]));
- } else {
- uint8_t* modes = dec->imodes_;
- int y;
- for (y = 0; y < 4; ++y) {
- int ymode = left[y];
- int x;
- for (x = 0; x < 4; ++x) {
- const uint8_t* const prob = kBModesProba[top[x]][ymode];
-#ifdef USE_GENERIC_TREE
- // Generic tree-parsing
- int i = 0;
- do {
- i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])];
- } while (i > 0);
- ymode = -i;
-#else
- // Hardcoded tree parsing
- ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED :
- !VP8GetBit(br, prob[1]) ? B_TM_PRED :
- !VP8GetBit(br, prob[2]) ? B_VE_PRED :
- !VP8GetBit(br, prob[3]) ?
- (!VP8GetBit(br, prob[4]) ? B_HE_PRED :
- (!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) :
- (!VP8GetBit(br, prob[6]) ? B_LD_PRED :
- (!VP8GetBit(br, prob[7]) ? B_VL_PRED :
- (!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED)));
-#endif // USE_GENERIC_TREE
- top[x] = ymode;
- *modes++ = ymode;
- }
- left[y] = ymode;
- }
- }
- // Hardcoded UVMode decision tree
- dec->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
- : !VP8GetBit(br, 114) ? V_PRED
- : VP8GetBit(br, 183) ? TM_PRED : H_PRED;
-}
-
-//------------------------------------------------------------------------------
-// Paragraph 13
-
-static const uint8_t
- CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
- { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
- { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- },
- { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
- { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
- },
- { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- },
- { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
- },
- { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- },
- { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
- { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- }
-};
-
-#ifndef ONLY_KEYFRAME_CODE
-static const uint8_t MVUpdateProba[2][NUM_MV_PROBAS] = {
- { 237, 246, 253, 253, 254, 254, 254, 254,
- 254, 254, 254, 254, 254, 254, 250, 250,
- 252, 254, 254 },
- { 231, 243, 245, 253, 254, 254, 254, 254,
- 254, 254, 254, 254, 254, 254, 251, 251,
- 254, 254, 254 }
-};
-#endif
-
-// Paragraph 9.9
-void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
- VP8Proba* const proba = &dec->proba_;
- int t, b, c, p;
- for (t = 0; t < NUM_TYPES; ++t) {
- for (b = 0; b < NUM_BANDS; ++b) {
- for (c = 0; c < NUM_CTX; ++c) {
- for (p = 0; p < NUM_PROBAS; ++p) {
- if (VP8GetBit(br, CoeffsUpdateProba[t][b][c][p])) {
- proba->coeffs_[t][b][c][p] = VP8GetValue(br, 8);
- }
- }
- }
- }
- }
- dec->use_skip_proba_ = VP8Get(br);
- if (dec->use_skip_proba_) {
- dec->skip_p_ = VP8GetValue(br, 8);
- }
-#ifndef ONLY_KEYFRAME_CODE
- if (!dec->frm_hdr_.key_frame_) {
- int i;
- dec->intra_p_ = VP8GetValue(br, 8);
- dec->last_p_ = VP8GetValue(br, 8);
- dec->golden_p_ = VP8GetValue(br, 8);
- if (VP8Get(br)) { // update y-mode
- for (i = 0; i < 4; ++i) {
- proba->ymode_[i] = VP8GetValue(br, 8);
- }
- }
- if (VP8Get(br)) { // update uv-mode
- for (i = 0; i < 3; ++i) {
- proba->uvmode_[i] = VP8GetValue(br, 8);
- }
- }
- // update MV
- for (i = 0; i < 2; ++i) {
- int k;
- for (k = 0; k < NUM_MV_PROBAS; ++k) {
- if (VP8GetBit(br, MVUpdateProba[i][k])) {
- const int v = VP8GetValue(br, 7);
- proba->mv_[i][k] = v ? v << 1 : 1;
- }
- }
- }
- }
-#endif
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/vp8.c b/src/third_party/libwebp/dec/vp8.c
deleted file mode 100644
index ad3ae29..0000000
--- a/src/third_party/libwebp/dec/vp8.c
+++ /dev/null
@@ -1,789 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// main entry for the decoder
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h>
-#endif
-
-#include "./vp8i.h"
-#include "./vp8li.h"
-#include "./webpi.h"
-#include "../utils/bit_reader.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-
-int WebPGetDecoderVersion(void) {
- return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
-}
-
-//------------------------------------------------------------------------------
-// VP8Decoder
-
-static void SetOk(VP8Decoder* const dec) {
- dec->status_ = VP8_STATUS_OK;
- dec->error_msg_ = "OK";
-}
-
-int VP8InitIoInternal(VP8Io* const io, int version) {
- if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
- return 0; // mismatch error
- }
- if (io != NULL) {
- SbMemorySet(io, 0, sizeof(*io));
- }
- return 1;
-}
-
-VP8Decoder* VP8New(void) {
- VP8Decoder* const dec = (VP8Decoder*)SbMemoryCalloc(1, sizeof(*dec));
- if (dec != NULL) {
- SetOk(dec);
- WebPWorkerInit(&dec->worker_);
- dec->ready_ = 0;
- dec->num_parts_ = 1;
- }
- return dec;
-}
-
-VP8StatusCode VP8Status(VP8Decoder* const dec) {
- if (!dec) return VP8_STATUS_INVALID_PARAM;
- return dec->status_;
-}
-
-const char* VP8StatusMessage(VP8Decoder* const dec) {
- if (dec == NULL) return "no object";
- if (!dec->error_msg_) return "OK";
- return dec->error_msg_;
-}
-
-void VP8Delete(VP8Decoder* const dec) {
- if (dec != NULL) {
- VP8Clear(dec);
- SbMemoryDeallocate(dec);
- }
-}
-
-int VP8SetError(VP8Decoder* const dec,
- VP8StatusCode error, const char* const msg) {
- // TODO This check would be unnecessary if alpha decompression was separated
- // from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to
- // something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression
- // failure.
- if (dec->status_ == VP8_STATUS_OK) {
- dec->status_ = error;
- dec->error_msg_ = msg;
- dec->ready_ = 0;
- }
- return 0;
-}
-
-//------------------------------------------------------------------------------
-
-int VP8CheckSignature(const uint8_t* const data, size_t data_size) {
- return (data_size >= 3 &&
- data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a);
-}
-
-int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
- int* const width, int* const height) {
- if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) {
- return 0; // not enough data
- }
- // check signature
- if (!VP8CheckSignature(data + 3, data_size - 3)) {
- return 0; // Wrong signature.
- } else {
- const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
- const int key_frame = !(bits & 1);
- const int w = ((data[7] << 8) | data[6]) & 0x3fff;
- const int h = ((data[9] << 8) | data[8]) & 0x3fff;
-
- if (!key_frame) { // Not a keyframe.
- return 0;
- }
-
- if (((bits >> 1) & 7) > 3) {
- return 0; // unknown profile
- }
- if (!((bits >> 4) & 1)) {
- return 0; // first frame is invisible!
- }
- if (((bits >> 5)) >= chunk_size) { // partition_length
- return 0; // inconsistent size information.
- }
-
- if (width) {
- *width = w;
- }
- if (height) {
- *height = h;
- }
-
- return 1;
- }
-}
-
-//------------------------------------------------------------------------------
-// Header parsing
-
-static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
- SB_DCHECK(hdr != NULL);
- hdr->use_segment_ = 0;
- hdr->update_map_ = 0;
- hdr->absolute_delta_ = 1;
- SbMemorySet(hdr->quantizer_, 0, sizeof(hdr->quantizer_));
- SbMemorySet(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_));
-}
-
-// Paragraph 9.3
-static int ParseSegmentHeader(VP8BitReader* br,
- VP8SegmentHeader* hdr, VP8Proba* proba) {
- SB_DCHECK(br != NULL);
- SB_DCHECK(hdr != NULL);
- hdr->use_segment_ = VP8Get(br);
- if (hdr->use_segment_) {
- hdr->update_map_ = VP8Get(br);
- if (VP8Get(br)) { // update data
- int s;
- hdr->absolute_delta_ = VP8Get(br);
- for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
- hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0;
- }
- for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
- hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0;
- }
- }
- if (hdr->update_map_) {
- int s;
- for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
- proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u;
- }
- }
- } else {
- hdr->update_map_ = 0;
- }
- return !br->eof_;
-}
-
-// Paragraph 9.5
-// This function returns VP8_STATUS_SUSPENDED if we don't have all the
-// necessary data in 'buf'.
-// This case is not necessarily an error (for incremental decoding).
-// Still, no bitreader is ever initialized to make it possible to read
-// unavailable memory.
-// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
-// is returned, and this is an unrecoverable error.
-// If the partitions were positioned ok, VP8_STATUS_OK is returned.
-static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
- const uint8_t* buf, size_t size) {
- VP8BitReader* const br = &dec->br_;
- const uint8_t* sz = buf;
- const uint8_t* buf_end = buf + size;
- const uint8_t* part_start;
- int last_part;
- int p;
-
- dec->num_parts_ = 1 << VP8GetValue(br, 2);
- last_part = dec->num_parts_ - 1;
- part_start = buf + last_part * 3;
- if (buf_end < part_start) {
- // we can't even read the sizes with sz[]! That's a failure.
- return VP8_STATUS_NOT_ENOUGH_DATA;
- }
- for (p = 0; p < last_part; ++p) {
- const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
- const uint8_t* part_end = part_start + psize;
- if (part_end > buf_end) part_end = buf_end;
- VP8InitBitReader(dec->parts_ + p, part_start, part_end);
- part_start = part_end;
- sz += 3;
- }
- VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
- return (part_start < buf_end) ? VP8_STATUS_OK :
- VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
-}
-
-// Paragraph 9.4
-static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
- VP8FilterHeader* const hdr = &dec->filter_hdr_;
- hdr->simple_ = VP8Get(br);
- hdr->level_ = VP8GetValue(br, 6);
- hdr->sharpness_ = VP8GetValue(br, 3);
- hdr->use_lf_delta_ = VP8Get(br);
- if (hdr->use_lf_delta_) {
- if (VP8Get(br)) { // update lf-delta?
- int i;
- for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
- if (VP8Get(br)) {
- hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6);
- }
- }
- for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
- if (VP8Get(br)) {
- hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6);
- }
- }
- }
- }
- dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
- return !br->eof_;
-}
-
-// Topmost call
-int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
- const uint8_t* buf;
- size_t buf_size;
- VP8FrameHeader* frm_hdr;
- VP8PictureHeader* pic_hdr;
- VP8BitReader* br;
- VP8StatusCode status;
- WebPHeaderStructure headers;
-
- if (dec == NULL) {
- return 0;
- }
- SetOk(dec);
- if (io == NULL) {
- return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
- "null VP8Io passed to VP8GetHeaders()");
- }
-
- // Process Pre-VP8 chunks.
- headers.data = io->data;
- headers.data_size = io->data_size;
- status = WebPParseHeaders(&headers);
- if (status != VP8_STATUS_OK) {
- return VP8SetError(dec, status, "Incorrect/incomplete header.");
- }
- if (headers.is_lossless) {
- return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
- "Unexpected lossless format encountered.");
- }
-
- if (dec->alpha_data_ == NULL) {
- SB_DCHECK(dec->alpha_data_size_ == 0);
- // We have NOT set alpha data yet. Set it now.
- // (This is to ensure that dec->alpha_data_ is NOT reset to NULL if
- // WebPParseHeaders() is called more than once, as in incremental decoding
- // case.)
- dec->alpha_data_ = headers.alpha_data;
- dec->alpha_data_size_ = headers.alpha_data_size;
- }
-
- // Process the VP8 frame header.
- buf = headers.data + headers.offset;
- buf_size = headers.data_size - headers.offset;
- SB_DCHECK(headers.data_size >= headers.offset); // WebPParseHeaders' guarantee
- if (buf_size < 4) {
- return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
- "Truncated header.");
- }
-
- // Paragraph 9.1
- {
- const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
- frm_hdr = &dec->frm_hdr_;
- frm_hdr->key_frame_ = !(bits & 1);
- frm_hdr->profile_ = (bits >> 1) & 7;
- frm_hdr->show_ = (bits >> 4) & 1;
- frm_hdr->partition_length_ = (bits >> 5);
- if (frm_hdr->profile_ > 3)
- return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
- "Incorrect keyframe parameters.");
- if (!frm_hdr->show_)
- return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
- "Frame not displayable.");
- buf += 3;
- buf_size -= 3;
- }
-
- pic_hdr = &dec->pic_hdr_;
- if (frm_hdr->key_frame_) {
- // Paragraph 9.2
- if (buf_size < 7) {
- return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
- "cannot parse picture header");
- }
- if (!VP8CheckSignature(buf, buf_size)) {
- return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
- "Bad code word");
- }
- pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff;
- pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
- pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff;
- pic_hdr->yscale_ = buf[6] >> 6;
- buf += 7;
- buf_size -= 7;
-
- dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
- dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
- // Setup default output area (can be later modified during io->setup())
- io->width = pic_hdr->width_;
- io->height = pic_hdr->height_;
- io->use_scaling = 0;
- io->use_cropping = 0;
- io->crop_top = 0;
- io->crop_left = 0;
- io->crop_right = io->width;
- io->crop_bottom = io->height;
- io->mb_w = io->width; // sanity check
- io->mb_h = io->height; // ditto
-
- VP8ResetProba(&dec->proba_);
- ResetSegmentHeader(&dec->segment_hdr_);
- dec->segment_ = 0; // default for intra
- }
-
- // Check if we have all the partition #0 available, and initialize dec->br_
- // to read this partition (and this partition only).
- if (frm_hdr->partition_length_ > buf_size) {
- return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
- "bad partition length");
- }
-
- br = &dec->br_;
- VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
- buf += frm_hdr->partition_length_;
- buf_size -= frm_hdr->partition_length_;
-
- if (frm_hdr->key_frame_) {
- pic_hdr->colorspace_ = VP8Get(br);
- pic_hdr->clamp_type_ = VP8Get(br);
- }
- if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
- return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
- "cannot parse segment header");
- }
- // Filter specs
- if (!ParseFilterHeader(br, dec)) {
- return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
- "cannot parse filter header");
- }
- status = ParsePartitions(dec, buf, buf_size);
- if (status != VP8_STATUS_OK) {
- return VP8SetError(dec, status, "cannot parse partitions");
- }
-
- // quantizer change
- VP8ParseQuant(dec);
-
- // Frame buffer marking
- if (!frm_hdr->key_frame_) {
- // Paragraph 9.7
-#ifndef ONLY_KEYFRAME_CODE
- dec->buffer_flags_ = VP8Get(br) << 0; // update golden
- dec->buffer_flags_ |= VP8Get(br) << 1; // update alt ref
- if (!(dec->buffer_flags_ & 1)) {
- dec->buffer_flags_ |= VP8GetValue(br, 2) << 2;
- }
- if (!(dec->buffer_flags_ & 2)) {
- dec->buffer_flags_ |= VP8GetValue(br, 2) << 4;
- }
- dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden
- dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref
-#else
- return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
- "Not a key frame.");
-#endif
- } else {
- dec->buffer_flags_ = 0x003 | 0x100;
- }
-
- // Paragraph 9.8
-#ifndef ONLY_KEYFRAME_CODE
- dec->update_proba_ = VP8Get(br);
- if (!dec->update_proba_) { // save for later restore
- dec->proba_saved_ = dec->proba_;
- }
- dec->buffer_flags_ &= 1 << 8;
- dec->buffer_flags_ |=
- (frm_hdr->key_frame_ || VP8Get(br)) << 8; // refresh last frame
-#else
- VP8Get(br); // just ignore the value of update_proba_
-#endif
-
- VP8ParseProba(br, dec);
-
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- // Extensions
- if (dec->pic_hdr_.colorspace_) {
- const size_t kTrailerSize = 8;
- const uint8_t kTrailerMarker = 0x01;
- const uint8_t* ext_buf = buf - kTrailerSize;
- size_t size;
-
- if (frm_hdr->partition_length_ < kTrailerSize ||
- ext_buf[kTrailerSize - 1] != kTrailerMarker) {
- return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
- "RIFF: Inconsistent extra information.");
- }
-
- // Layer
- size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);
- dec->layer_data_size_ = size;
- dec->layer_data_ = NULL; // will be set later
- dec->layer_colorspace_ = ext_buf[3];
- }
-#endif
-
- // sanitized state
- dec->ready_ = 1;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Residual decoding (Paragraph 13.2 / 13.3)
-
-static const int kBands[16 + 1] = {
- 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
- 0 // extra entry as sentinel
-};
-
-static const uint8_t kCat3[] = { 173, 148, 140, 0 };
-static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 };
-static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
-static const uint8_t kCat6[] =
- { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 };
-static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
-static const uint8_t kZigzag[16] = {
- 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
-};
-
-typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting
-typedef const uint8_t (*ProbaCtxArray)[NUM_PROBAS];
-
-// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
-static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
- int v;
- if (!VP8GetBit(br, p[3])) {
- if (!VP8GetBit(br, p[4])) {
- v = 2;
- } else {
- v = 3 + VP8GetBit(br, p[5]);
- }
- } else {
- if (!VP8GetBit(br, p[6])) {
- if (!VP8GetBit(br, p[7])) {
- v = 5 + VP8GetBit(br, 159);
- } else {
- v = 7 + 2 * VP8GetBit(br, 165);
- v += VP8GetBit(br, 145);
- }
- } else {
- const uint8_t* tab;
- const int bit1 = VP8GetBit(br, p[8]);
- const int bit0 = VP8GetBit(br, p[9 + bit1]);
- const int cat = 2 * bit1 + bit0;
- v = 0;
- for (tab = kCat3456[cat]; *tab; ++tab) {
- v += v + VP8GetBit(br, *tab);
- }
- v += 3 + (8 << cat);
- }
- }
- return v;
-}
-
-// Returns the position of the last non-zero coeff plus one
-// (and 0 if there's no coeff at all)
-static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
- int ctx, const quant_t dq, int n, int16_t* out) {
- // n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
- const uint8_t* p = prob[n][ctx];
- if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
- return 0;
- }
- for (; n < 16; ++n) {
- const ProbaCtxArray p_ctx = prob[kBands[n + 1]];
- if (!VP8GetBit(br, p[1])) {
- p = p_ctx[0];
- } else { // non zero coeff
- int v;
- if (!VP8GetBit(br, p[2])) {
- v = 1;
- p = p_ctx[1];
- } else {
- v = GetLargeValue(br, p);
- p = p_ctx[2];
- }
- out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
- if (n < 15 && !VP8GetBit(br, p[0])) { // EOB
- return n + 1;
- }
- }
- }
- return 16;
-}
-
-// Alias-safe way of converting 4bytes to 32bits.
-typedef union {
- uint8_t i8[4];
- uint32_t i32;
-} PackedNz;
-
-// Table to unpack four bits into four bytes
-static const PackedNz kUnpackTab[16] = {
- {{0, 0, 0, 0}}, {{1, 0, 0, 0}}, {{0, 1, 0, 0}}, {{1, 1, 0, 0}},
- {{0, 0, 1, 0}}, {{1, 0, 1, 0}}, {{0, 1, 1, 0}}, {{1, 1, 1, 0}},
- {{0, 0, 0, 1}}, {{1, 0, 0, 1}}, {{0, 1, 0, 1}}, {{1, 1, 0, 1}},
- {{0, 0, 1, 1}}, {{1, 0, 1, 1}}, {{0, 1, 1, 1}}, {{1, 1, 1, 1}} };
-
-// Macro to pack four LSB of four bytes into four bits.
-#if defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
- defined(__BIG_ENDIAN__)
-#define PACK_CST 0x08040201U
-#else
-#define PACK_CST 0x01020408U
-#endif
-#define PACK(X, S) ((((X).i32 * PACK_CST) & 0xff000000) >> (S))
-
-static void ParseResiduals(VP8Decoder* const dec,
- VP8MB* const mb, VP8BitReader* const token_br) {
- int out_t_nz, out_l_nz, first;
- ProbaArray ac_prob;
- const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
- int16_t* dst = dec->coeffs_;
- VP8MB* const left_mb = dec->mb_info_ - 1;
- PackedNz nz_ac, nz_dc;
- PackedNz tnz, lnz;
- uint32_t non_zero_ac = 0;
- uint32_t non_zero_dc = 0;
- int x, y, ch;
-
- nz_dc.i32 = nz_ac.i32 = 0;
- SbMemorySet(dst, 0, 384 * sizeof(*dst));
- if (!dec->is_i4x4_) { // parse DC
- int16_t dc[16] = { 0 };
- const int ctx = mb->dc_nz_ + left_mb->dc_nz_;
- mb->dc_nz_ = left_mb->dc_nz_ =
- (GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1],
- ctx, q->y2_mat_, 0, dc) > 0);
- first = 1;
- ac_prob = (ProbaArray)dec->proba_.coeffs_[0];
- VP8TransformWHT(dc, dst);
- } else {
- first = 0;
- ac_prob = (ProbaArray)dec->proba_.coeffs_[3];
- }
-
- tnz = kUnpackTab[mb->nz_ & 0xf];
- lnz = kUnpackTab[left_mb->nz_ & 0xf];
- for (y = 0; y < 4; ++y) {
- int l = lnz.i8[y];
- for (x = 0; x < 4; ++x) {
- const int ctx = l + tnz.i8[x];
- const int nz = GetCoeffs(token_br, ac_prob, ctx,
- q->y1_mat_, first, dst);
- tnz.i8[x] = l = (nz > 0);
- nz_dc.i8[x] = (dst[0] != 0);
- nz_ac.i8[x] = (nz > 1);
- dst += 16;
- }
- lnz.i8[y] = l;
- non_zero_dc |= PACK(nz_dc, 24 - y * 4);
- non_zero_ac |= PACK(nz_ac, 24 - y * 4);
- }
- out_t_nz = PACK(tnz, 24);
- out_l_nz = PACK(lnz, 24);
-
- tnz = kUnpackTab[mb->nz_ >> 4];
- lnz = kUnpackTab[left_mb->nz_ >> 4];
- for (ch = 0; ch < 4; ch += 2) {
- for (y = 0; y < 2; ++y) {
- int l = lnz.i8[ch + y];
- for (x = 0; x < 2; ++x) {
- const int ctx = l + tnz.i8[ch + x];
- const int nz =
- GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2],
- ctx, q->uv_mat_, 0, dst);
- tnz.i8[ch + x] = l = (nz > 0);
- nz_dc.i8[y * 2 + x] = (dst[0] != 0);
- nz_ac.i8[y * 2 + x] = (nz > 1);
- dst += 16;
- }
- lnz.i8[ch + y] = l;
- }
- non_zero_dc |= PACK(nz_dc, 8 - ch * 2);
- non_zero_ac |= PACK(nz_ac, 8 - ch * 2);
- }
- out_t_nz |= PACK(tnz, 20);
- out_l_nz |= PACK(lnz, 20);
- mb->nz_ = out_t_nz;
- left_mb->nz_ = out_l_nz;
-
- dec->non_zero_ac_ = non_zero_ac;
- dec->non_zero_ = non_zero_ac | non_zero_dc;
- mb->skip_ = !dec->non_zero_;
-}
-#undef PACK
-
-//------------------------------------------------------------------------------
-// Main loop
-
-int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
- VP8BitReader* const br = &dec->br_;
- VP8MB* const left = dec->mb_info_ - 1;
- VP8MB* const info = dec->mb_info_ + dec->mb_x_;
-
- // Note: we don't save segment map (yet), as we don't expect
- // to decode more than 1 keyframe.
- if (dec->segment_hdr_.update_map_) {
- // Hardcoded tree parsing
- dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
- VP8GetBit(br, dec->proba_.segments_[1]) :
- 2 + VP8GetBit(br, dec->proba_.segments_[2]);
- }
- info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
-
- VP8ParseIntraMode(br, dec);
- if (br->eof_) {
- return 0;
- }
-
- if (!info->skip_) {
- ParseResiduals(dec, info, token_br);
- } else {
- left->nz_ = info->nz_ = 0;
- if (!dec->is_i4x4_) {
- left->dc_nz_ = info->dc_nz_ = 0;
- }
- dec->non_zero_ = 0;
- dec->non_zero_ac_ = 0;
- }
-
- if (dec->filter_type_ > 0) { // store filter info
- VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
- *finfo = dec->fstrengths_[dec->segment_][dec->is_i4x4_];
- finfo->f_inner_ = (!info->skip_ || dec->is_i4x4_);
- }
-
- return (!token_br->eof_);
-}
-
-void VP8InitScanline(VP8Decoder* const dec) {
- VP8MB* const left = dec->mb_info_ - 1;
- left->nz_ = 0;
- left->dc_nz_ = 0;
- SbMemorySet(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
- dec->filter_row_ =
- (dec->filter_type_ > 0) &&
- (dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
-}
-
-static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
- for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
- VP8BitReader* const token_br =
- &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
- VP8InitScanline(dec);
- for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
- if (!VP8DecodeMB(dec, token_br)) {
- return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
- "Premature end-of-file encountered.");
- }
- // Reconstruct and emit samples.
- VP8ReconstructBlock(dec);
- }
- if (!VP8ProcessRow(dec, io)) {
- return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
- }
- }
- if (dec->use_threads_ && !WebPWorkerSync(&dec->worker_)) {
- return 0;
- }
-
- // Finish
-#ifndef ONLY_KEYFRAME_CODE
- if (!dec->update_proba_) {
- dec->proba_ = dec->proba_saved_;
- }
-#endif
-
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (dec->layer_data_size_ > 0) {
- if (!VP8DecodeLayer(dec)) {
- return 0;
- }
- }
-#endif
-
- return 1;
-}
-
-// Main entry point
-int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
- int ok = 0;
- if (dec == NULL) {
- return 0;
- }
- if (io == NULL) {
- return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
- "NULL VP8Io parameter in VP8Decode().");
- }
-
- if (!dec->ready_) {
- if (!VP8GetHeaders(dec, io)) {
- return 0;
- }
- }
- SB_DCHECK(dec->ready_);
-
- // Finish setting up the decoding parameter. Will call io->setup().
- ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK);
- if (ok) { // good to go.
- // Will allocate memory and prepare everything.
- if (ok) ok = VP8InitFrame(dec, io);
-
- // Main decoding loop
- if (ok) ok = ParseFrame(dec, io);
-
- // Exit.
- ok &= VP8ExitCritical(dec, io);
- }
-
- if (!ok) {
- VP8Clear(dec);
- return 0;
- }
-
- dec->ready_ = 0;
- return ok;
-}
-
-void VP8Clear(VP8Decoder* const dec) {
- if (dec == NULL) {
- return;
- }
- if (dec->use_threads_) {
- WebPWorkerEnd(&dec->worker_);
- }
- if (dec->mem_) {
- SbMemoryDeallocate(dec->mem_);
- }
- dec->mem_ = NULL;
- dec->mem_size_ = 0;
- SbMemorySet(&dec->br_, 0, sizeof(dec->br_));
- dec->ready_ = 0;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/vp8i.h b/src/third_party/libwebp/dec/vp8i.h
deleted file mode 100644
index 91d27f1..0000000
--- a/src/third_party/libwebp/dec/vp8i.h
+++ /dev/null
@@ -1,341 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// VP8 decoder: internal header.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_DEC_VP8I_H_
-#define WEBP_DEC_VP8I_H_
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#else
-#include <string.h> // for memcpy()
-#endif
-
-#include "./vp8li.h"
-#include "../utils/bit_reader.h"
-#include "../utils/thread.h"
-#include "../dsp/dsp.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Various defines and enums
-
-// version numbers
-#define DEC_MAJ_VERSION 0
-#define DEC_MIN_VERSION 3
-#define DEC_REV_VERSION 1
-
-#define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
-
-// intra prediction modes
-enum { B_DC_PRED = 0, // 4x4 modes
- B_TM_PRED,
- B_VE_PRED,
- B_HE_PRED,
- B_RD_PRED,
- B_VR_PRED,
- B_LD_PRED,
- B_VL_PRED,
- B_HD_PRED,
- B_HU_PRED,
- NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
-
- // Luma16 or UV modes
- DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
- H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
- B_PRED = NUM_BMODES, // refined I4x4 mode
-
- // special modes
- B_DC_PRED_NOTOP = 4,
- B_DC_PRED_NOLEFT = 5,
- B_DC_PRED_NOTOPLEFT = 6,
- NUM_B_DC_MODES = 7 };
-
-enum { MB_FEATURE_TREE_PROBS = 3,
- NUM_MB_SEGMENTS = 4,
- NUM_REF_LF_DELTAS = 4,
- NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
- MAX_NUM_PARTITIONS = 8,
- // Probabilities
- NUM_TYPES = 4,
- NUM_BANDS = 8,
- NUM_CTX = 3,
- NUM_PROBAS = 11,
- NUM_MV_PROBAS = 19 };
-
-// YUV-cache parameters.
-// Constraints are: We need to store one 16x16 block of luma samples (y),
-// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
-// in order to be SIMD-friendly. We also need to store the top, left and
-// top-left samples (from previously decoded blocks), along with four
-// extra top-right samples for luma (intra4x4 prediction only).
-// One possible layout is, using 32 * (17 + 9) bytes:
-//
-// .+------ <- only 1 pixel high
-// .|yyyyt.
-// .|yyyyt.
-// .|yyyyt.
-// .|yyyy..
-// .+--.+-- <- only 1 pixel high
-// .|uu.|vv
-// .|uu.|vv
-//
-// Every character is a 4x4 block, with legend:
-// '.' = unused
-// 'y' = y-samples 'u' = u-samples 'v' = u-samples
-// '|' = left sample, '-' = top sample, '+' = top-left sample
-// 't' = extra top-right sample for 4x4 modes
-// With this layout, BPS (=Bytes Per Scan-line) is one cacheline size.
-#define BPS 32 // this is the common stride used by yuv[]
-#define YUV_SIZE (BPS * 17 + BPS * 9)
-#define Y_SIZE (BPS * 17)
-#define Y_OFF (BPS * 1 + 8)
-#define U_OFF (Y_OFF + BPS * 16 + BPS)
-#define V_OFF (U_OFF + 16)
-
-//------------------------------------------------------------------------------
-// Headers
-
-typedef struct {
- uint8_t key_frame_;
- uint8_t profile_;
- uint8_t show_;
- uint32_t partition_length_;
-} VP8FrameHeader;
-
-typedef struct {
- uint16_t width_;
- uint16_t height_;
- uint8_t xscale_;
- uint8_t yscale_;
- uint8_t colorspace_; // 0 = YCbCr
- uint8_t clamp_type_;
-} VP8PictureHeader;
-
-// segment features
-typedef struct {
- int use_segment_;
- int update_map_; // whether to update the segment map or not
- int absolute_delta_; // absolute or delta values for quantizer and filter
- int8_t quantizer_[NUM_MB_SEGMENTS]; // quantization changes
- int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
-} VP8SegmentHeader;
-
-// Struct collecting all frame-persistent probabilities.
-typedef struct {
- uint8_t segments_[MB_FEATURE_TREE_PROBS];
- // Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
- uint8_t coeffs_[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
-#ifndef ONLY_KEYFRAME_CODE
- uint8_t ymode_[4], uvmode_[3];
- uint8_t mv_[2][NUM_MV_PROBAS];
-#endif
-} VP8Proba;
-
-// Filter parameters
-typedef struct {
- int simple_; // 0=complex, 1=simple
- int level_; // [0..63]
- int sharpness_; // [0..7]
- int use_lf_delta_;
- int ref_lf_delta_[NUM_REF_LF_DELTAS];
- int mode_lf_delta_[NUM_MODE_LF_DELTAS];
-} VP8FilterHeader;
-
-//------------------------------------------------------------------------------
-// Informations about the macroblocks.
-
-typedef struct { // filter specs
- unsigned int f_level_:6; // filter strength: 0..63
- unsigned int f_ilevel_:6; // inner limit: 1..63
- unsigned int f_inner_:1; // do inner filtering?
-} VP8FInfo;
-
-typedef struct { // used for syntax-parsing
- unsigned int nz_:24; // non-zero AC/DC coeffs (24bit)
- unsigned int dc_nz_:1; // non-zero DC coeffs
- unsigned int skip_:1; // block type
-} VP8MB;
-
-// Dequantization matrices
-typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
-typedef struct {
- quant_t y1_mat_, y2_mat_, uv_mat_;
-} VP8QuantMatrix;
-
-// Persistent information needed by the parallel processing
-typedef struct {
- int id_; // cache row to process (in [0..2])
- int mb_y_; // macroblock position of the row
- int filter_row_; // true if row-filtering is needed
- VP8FInfo* f_info_; // filter strengths
- VP8Io io_; // copy of the VP8Io to pass to put()
-} VP8ThreadContext;
-
-//------------------------------------------------------------------------------
-// VP8Decoder: the main opaque structure handed over to user
-
-struct VP8Decoder {
- VP8StatusCode status_;
- int ready_; // true if ready to decode a picture with VP8Decode()
- const char* error_msg_; // set when status_ is not OK.
-
- // Main data source
- VP8BitReader br_;
-
- // headers
- VP8FrameHeader frm_hdr_;
- VP8PictureHeader pic_hdr_;
- VP8FilterHeader filter_hdr_;
- VP8SegmentHeader segment_hdr_;
-
- // Worker
- WebPWorker worker_;
- int use_threads_; // use multi-thread
- int cache_id_; // current cache row
- int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3)
- VP8ThreadContext thread_ctx_; // Thread context
-
- // dimension, in macroblock units.
- int mb_w_, mb_h_;
-
- // Macroblock to process/filter, depending on cropping and filter_type.
- int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered
- int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded
-
- // number of partitions.
- int num_parts_;
- // per-partition boolean decoders.
- VP8BitReader parts_[MAX_NUM_PARTITIONS];
-
- // buffer refresh flags
- // bit 0: refresh Gold, bit 1: refresh Alt
- // bit 2-3: copy to Gold, bit 4-5: copy to Alt
- // bit 6: Gold sign bias, bit 7: Alt sign bias
- // bit 8: refresh last frame
- uint32_t buffer_flags_;
-
- // dequantization (one set of DC/AC dequant factor per segment)
- VP8QuantMatrix dqm_[NUM_MB_SEGMENTS];
-
- // probabilities
- VP8Proba proba_;
- int use_skip_proba_;
- uint8_t skip_p_;
-#ifndef ONLY_KEYFRAME_CODE
- uint8_t intra_p_, last_p_, golden_p_;
- VP8Proba proba_saved_;
- int update_proba_;
-#endif
-
- // Boundary data cache and persistent buffers.
- uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
- uint8_t intra_l_[4]; // left intra modes values
- uint8_t* y_t_; // top luma samples: 16 * mb_w_
- uint8_t* u_t_, *v_t_; // top u/v samples: 8 * mb_w_ each
-
- VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
- VP8FInfo* f_info_; // filter strength info
- uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
- int16_t* coeffs_; // 384 coeffs = (16+8+8) * 4*4
-
- uint8_t* cache_y_; // macroblock row for storing unfiltered samples
- uint8_t* cache_u_;
- uint8_t* cache_v_;
- int cache_y_stride_;
- int cache_uv_stride_;
-
- // main memory chunk for the above data. Persistent.
- void* mem_;
- size_t mem_size_;
-
- // Per macroblock non-persistent infos.
- int mb_x_, mb_y_; // current position, in macroblock units
- uint8_t is_i4x4_; // true if intra4x4
- uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
- uint8_t uvmode_; // chroma prediction mode
- uint8_t segment_; // block's segment
-
- // bit-wise info about the content of each sub-4x4 blocks: there are 16 bits
- // for luma (bits #0->#15), then 4 bits for chroma-u (#16->#19) and 4 bits for
- // chroma-v (#20->#23), each corresponding to one 4x4 block in decoding order.
- // If the bit is set, the 4x4 block contains some non-zero coefficients.
- uint32_t non_zero_;
- uint32_t non_zero_ac_;
-
- // Filtering side-info
- int filter_type_; // 0=off, 1=simple, 2=complex
- int filter_row_; // per-row flag
- VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type
-
- // extensions
- const uint8_t* alpha_data_; // compressed alpha data (if present)
- size_t alpha_data_size_;
- int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
- uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
-
- int layer_colorspace_;
- const uint8_t* layer_data_; // compressed layer data (if present)
- size_t layer_data_size_;
-};
-
-//------------------------------------------------------------------------------
-// internal functions. Not public.
-
-// in vp8.c
-int VP8SetError(VP8Decoder* const dec,
- VP8StatusCode error, const char* const msg);
-
-// in tree.c
-void VP8ResetProba(VP8Proba* const proba);
-void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec);
-void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec);
-
-// in quant.c
-void VP8ParseQuant(VP8Decoder* const dec);
-
-// in frame.c
-int VP8InitFrame(VP8Decoder* const dec, VP8Io* io);
-// Predict a block and add residual
-void VP8ReconstructBlock(VP8Decoder* const dec);
-// Call io->setup() and finish setting up scan parameters.
-// After this call returns, one must always call VP8ExitCritical() with the
-// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
-// if ok, otherwise sets and returns the error status on *dec.
-VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
-// Must always be called in pair with VP8EnterCritical().
-// Returns false in case of error.
-int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
-// Process the last decoded row (filtering + output)
-int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
-// To be called at the start of a new scanline, to initialize predictors.
-void VP8InitScanline(VP8Decoder* const dec);
-// Decode one macroblock. Returns false if there is not enough data.
-int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
-
-// in alpha.c
-const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
- int row, int num_rows);
-
-// in layer.c
-int VP8DecodeLayer(VP8Decoder* const dec);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_DEC_VP8I_H_ */
diff --git a/src/third_party/libwebp/dec/vp8l.c b/src/third_party/libwebp/dec/vp8l.c
deleted file mode 100644
index 83ea9e2..0000000
--- a/src/third_party/libwebp/dec/vp8l.c
+++ /dev/null
@@ -1,1255 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// main entry for the decoder
-//
-// Authors: Vikas Arora (vikaas.arora@gmail.com)
-// Jyrki Alakuijala (jyrki@google.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdio.h>
-#include <stdlib.h>
-#endif
-
-#include "./vp8li.h"
-#include "../dsp/lossless.h"
-#include "../dsp/yuv.h"
-#include "../utils/huffman.h"
-#include "../utils/utils.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define NUM_ARGB_CACHE_ROWS 16
-
-static const int kCodeLengthLiterals = 16;
-static const int kCodeLengthRepeatCode = 16;
-static const int kCodeLengthExtraBits[3] = { 2, 3, 7 };
-static const int kCodeLengthRepeatOffsets[3] = { 3, 3, 11 };
-
-// -----------------------------------------------------------------------------
-// Five Huffman codes are used at each meta code:
-// 1. green + length prefix codes + color cache codes,
-// 2. alpha,
-// 3. red,
-// 4. blue, and,
-// 5. distance prefix codes.
-typedef enum {
- GREEN = 0,
- RED = 1,
- BLUE = 2,
- ALPHA = 3,
- DIST = 4
-} HuffIndex;
-
-static const uint16_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = {
- NUM_LITERAL_CODES + NUM_LENGTH_CODES,
- NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES,
- NUM_DISTANCE_CODES
-};
-
-
-#define NUM_CODE_LENGTH_CODES 19
-static const uint8_t kCodeLengthCodeOrder[NUM_CODE_LENGTH_CODES] = {
- 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
-};
-
-#define CODE_TO_PLANE_CODES 120
-static const uint8_t code_to_plane_lut[CODE_TO_PLANE_CODES] = {
- 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,
- 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,
- 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,
- 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,
- 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,
- 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,
- 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,
- 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,
- 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,
- 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,
- 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,
- 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70
-};
-
-static int DecodeImageStream(int xsize, int ysize,
- int is_level0,
- VP8LDecoder* const dec,
- uint32_t** const decoded_data);
-
-//------------------------------------------------------------------------------
-
-int VP8LCheckSignature(const uint8_t* const data, size_t size) {
- return (size >= 1) && (data[0] == VP8L_MAGIC_BYTE);
-}
-
-static int ReadImageInfo(VP8LBitReader* const br,
- int* const width, int* const height,
- int* const has_alpha) {
- const uint8_t signature = VP8LReadBits(br, 8);
- if (!VP8LCheckSignature(&signature, 1)) {
- return 0;
- }
- *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1;
- *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1;
- *has_alpha = VP8LReadBits(br, 1);
- VP8LReadBits(br, VP8L_VERSION_BITS); // Read/ignore the version number.
- return 1;
-}
-
-int VP8LGetInfo(const uint8_t* data, size_t data_size,
- int* const width, int* const height, int* const has_alpha) {
- if (data == NULL || data_size < VP8L_FRAME_HEADER_SIZE) {
- return 0; // not enough data
- } else {
- int w, h, a;
- VP8LBitReader br;
- VP8LInitBitReader(&br, data, data_size);
- if (!ReadImageInfo(&br, &w, &h, &a)) {
- return 0;
- }
- if (width != NULL) *width = w;
- if (height != NULL) *height = h;
- if (has_alpha != NULL) *has_alpha = a;
- return 1;
- }
-}
-
-//------------------------------------------------------------------------------
-
-static WEBP_INLINE int GetCopyDistance(int distance_symbol,
- VP8LBitReader* const br) {
- int extra_bits, offset;
- if (distance_symbol < 4) {
- return distance_symbol + 1;
- }
- extra_bits = (distance_symbol - 2) >> 1;
- offset = (2 + (distance_symbol & 1)) << extra_bits;
- return offset + VP8LReadBits(br, extra_bits) + 1;
-}
-
-static WEBP_INLINE int GetCopyLength(int length_symbol,
- VP8LBitReader* const br) {
- // Length and distance prefixes are encoded the same way.
- return GetCopyDistance(length_symbol, br);
-}
-
-static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) {
- if (plane_code > CODE_TO_PLANE_CODES) {
- return plane_code - CODE_TO_PLANE_CODES;
- } else {
- const int dist_code = code_to_plane_lut[plane_code - 1];
- const int yoffset = dist_code >> 4;
- const int xoffset = 8 - (dist_code & 0xf);
- const int dist = yoffset * xsize + xoffset;
- return (dist >= 1) ? dist : 1;
- }
-}
-
-//------------------------------------------------------------------------------
-// Decodes the next Huffman code from bit-stream.
-// FillBitWindow(br) needs to be called at minimum every second call
-// to ReadSymbol, in order to pre-fetch enough bits.
-static WEBP_INLINE int ReadSymbol(const HuffmanTree* tree,
- VP8LBitReader* const br) {
- const HuffmanTreeNode* node = tree->root_;
- int num_bits = 0;
- uint32_t bits = VP8LPrefetchBits(br);
- SB_DCHECK(node != NULL);
- while (!HuffmanTreeNodeIsLeaf(node)) {
- node = HuffmanTreeNextNode(node, bits & 1);
- bits >>= 1;
- ++num_bits;
- }
- VP8LDiscardBits(br, num_bits);
- return node->symbol_;
-}
-
-static int ReadHuffmanCodeLengths(
- VP8LDecoder* const dec, const int* const code_length_code_lengths,
- int num_symbols, int* const code_lengths) {
- int ok = 0;
- VP8LBitReader* const br = &dec->br_;
- int symbol;
- int max_symbol;
- int prev_code_len = DEFAULT_CODE_LENGTH;
- HuffmanTree tree;
-
- if (!HuffmanTreeBuildImplicit(&tree, code_length_code_lengths,
- NUM_CODE_LENGTH_CODES)) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- return 0;
- }
-
- if (VP8LReadBits(br, 1)) { // use length
- const int length_nbits = 2 + 2 * VP8LReadBits(br, 3);
- max_symbol = 2 + VP8LReadBits(br, length_nbits);
- if (max_symbol > num_symbols) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- goto End;
- }
- } else {
- max_symbol = num_symbols;
- }
-
- symbol = 0;
- while (symbol < num_symbols) {
- int code_len;
- if (max_symbol-- == 0) break;
- VP8LFillBitWindow(br);
- code_len = ReadSymbol(&tree, br);
- if (code_len < kCodeLengthLiterals) {
- code_lengths[symbol++] = code_len;
- if (code_len != 0) prev_code_len = code_len;
- } else {
- const int use_prev = (code_len == kCodeLengthRepeatCode);
- const int slot = code_len - kCodeLengthLiterals;
- const int extra_bits = kCodeLengthExtraBits[slot];
- const int repeat_offset = kCodeLengthRepeatOffsets[slot];
- int repeat = VP8LReadBits(br, extra_bits) + repeat_offset;
- if (symbol + repeat > num_symbols) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- goto End;
- } else {
- const int length = use_prev ? prev_code_len : 0;
- while (repeat-- > 0) code_lengths[symbol++] = length;
- }
- }
- }
- ok = 1;
-
- End:
- HuffmanTreeRelease(&tree);
- return ok;
-}
-
-static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
- HuffmanTree* const tree) {
- int ok = 0;
- VP8LBitReader* const br = &dec->br_;
- const int simple_code = VP8LReadBits(br, 1);
-
- if (simple_code) { // Read symbols, codes & code lengths directly.
- int symbols[2];
- int codes[2];
- int code_lengths[2];
- const int num_symbols = VP8LReadBits(br, 1) + 1;
- const int first_symbol_len_code = VP8LReadBits(br, 1);
- // The first code is either 1 bit or 8 bit code.
- symbols[0] = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8);
- codes[0] = 0;
- code_lengths[0] = num_symbols - 1;
- // The second code (if present), is always 8 bit long.
- if (num_symbols == 2) {
- symbols[1] = VP8LReadBits(br, 8);
- codes[1] = 1;
- code_lengths[1] = num_symbols - 1;
- }
- ok = HuffmanTreeBuildExplicit(tree, code_lengths, codes, symbols,
- alphabet_size, num_symbols);
- } else { // Decode Huffman-coded code lengths.
- int* code_lengths = NULL;
- int i;
- int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 };
- const int num_codes = VP8LReadBits(br, 4) + 4;
- if (num_codes > NUM_CODE_LENGTH_CODES) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- return 0;
- }
-
- code_lengths =
- (int*)WebPSafeCalloc((uint64_t)alphabet_size, sizeof(*code_lengths));
- if (code_lengths == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- return 0;
- }
-
- for (i = 0; i < num_codes; ++i) {
- code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3);
- }
- ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size,
- code_lengths);
- if (ok) {
- ok = HuffmanTreeBuildImplicit(tree, code_lengths, alphabet_size);
- }
- SbMemoryDeallocate(code_lengths);
- }
- ok = ok && !br->error_;
- if (!ok) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- return 0;
- }
- return 1;
-}
-
-static void DeleteHtreeGroups(HTreeGroup* htree_groups, int num_htree_groups) {
- if (htree_groups != NULL) {
- int i, j;
- for (i = 0; i < num_htree_groups; ++i) {
- HuffmanTree* const htrees = htree_groups[i].htrees_;
- for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
- HuffmanTreeRelease(&htrees[j]);
- }
- }
- SbMemoryDeallocate(htree_groups);
- }
-}
-
-static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
- int color_cache_bits, int allow_recursion) {
- int i, j;
- VP8LBitReader* const br = &dec->br_;
- VP8LMetadata* const hdr = &dec->hdr_;
- uint32_t* huffman_image = NULL;
- HTreeGroup* htree_groups = NULL;
- int num_htree_groups = 1;
-
- if (allow_recursion && VP8LReadBits(br, 1)) {
- // use meta Huffman codes.
- const int huffman_precision = VP8LReadBits(br, 3) + 2;
- const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision);
- const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision);
- const int huffman_pixs = huffman_xsize * huffman_ysize;
- if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec,
- &huffman_image)) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- goto Error;
- }
- hdr->huffman_subsample_bits_ = huffman_precision;
- for (i = 0; i < huffman_pixs; ++i) {
- // The huffman data is stored in red and green bytes.
- const int group = (huffman_image[i] >> 8) & 0xffff;
- huffman_image[i] = group;
- if (group >= num_htree_groups) {
- num_htree_groups = group + 1;
- }
- }
- }
-
- if (br->error_) goto Error;
-
- SB_DCHECK(num_htree_groups <= 0x10000);
- htree_groups =
- (HTreeGroup*)WebPSafeCalloc((uint64_t)num_htree_groups,
- sizeof(*htree_groups));
- if (htree_groups == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- goto Error;
- }
-
- for (i = 0; i < num_htree_groups; ++i) {
- HuffmanTree* const htrees = htree_groups[i].htrees_;
- for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
- int alphabet_size = kAlphabetSize[j];
- if (j == 0 && color_cache_bits > 0) {
- alphabet_size += 1 << color_cache_bits;
- }
- if (!ReadHuffmanCode(alphabet_size, dec, htrees + j)) goto Error;
- }
- }
-
- // All OK. Finalize pointers and return.
- hdr->huffman_image_ = huffman_image;
- hdr->num_htree_groups_ = num_htree_groups;
- hdr->htree_groups_ = htree_groups;
- return 1;
-
- Error:
- SbMemoryDeallocate(huffman_image);
- DeleteHtreeGroups(htree_groups, num_htree_groups);
- return 0;
-}
-
-//------------------------------------------------------------------------------
-// Scaling.
-
-static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
- const int num_channels = 4;
- const int in_width = io->mb_w;
- const int out_width = io->scaled_width;
- const int in_height = io->mb_h;
- const int out_height = io->scaled_height;
- const uint64_t work_size = 2 * num_channels * (uint64_t)out_width;
- int32_t* work; // Rescaler work area.
- const uint64_t scaled_data_size = num_channels * (uint64_t)out_width;
- uint32_t* scaled_data; // Temporary storage for scaled BGRA data.
- const uint64_t memory_size = sizeof(*dec->rescaler) +
- work_size * sizeof(*work) +
- scaled_data_size * sizeof(*scaled_data);
- uint8_t* memory = (uint8_t*)WebPSafeCalloc(memory_size, sizeof(*memory));
- if (memory == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- return 0;
- }
- SB_DCHECK(dec->rescaler_memory == NULL);
- dec->rescaler_memory = memory;
-
- dec->rescaler = (WebPRescaler*)memory;
- memory += sizeof(*dec->rescaler);
- work = (int32_t*)memory;
- memory += work_size * sizeof(*work);
- scaled_data = (uint32_t*)memory;
-
- WebPRescalerInit(dec->rescaler, in_width, in_height, (uint8_t*)scaled_data,
- out_width, out_height, 0, num_channels,
- in_width, out_width, in_height, out_height, work);
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Export to ARGB
-
-// We have special "export" function since we need to convert from BGRA
-static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace,
- int rgba_stride, uint8_t* const rgba) {
- const uint32_t* const src = (const uint32_t*)rescaler->dst;
- const int dst_width = rescaler->dst_width;
- int num_lines_out = 0;
- while (WebPRescalerHasPendingOutput(rescaler)) {
- uint8_t* const dst = rgba + num_lines_out * rgba_stride;
- WebPRescalerExportRow(rescaler);
- VP8LConvertFromBGRA(src, dst_width, colorspace, dst);
- ++num_lines_out;
- }
- return num_lines_out;
-}
-
-// Emit scaled rows.
-static int EmitRescaledRows(const VP8LDecoder* const dec,
- const uint32_t* const data, int in_stride, int mb_h,
- uint8_t* const out, int out_stride) {
- const WEBP_CSP_MODE colorspace = dec->output_->colorspace;
- const uint8_t* const in = (const uint8_t*)data;
- int num_lines_in = 0;
- int num_lines_out = 0;
- while (num_lines_in < mb_h) {
- const uint8_t* const row_in = in + num_lines_in * in_stride;
- uint8_t* const row_out = out + num_lines_out * out_stride;
- num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in,
- row_in, in_stride);
- num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out);
- }
- return num_lines_out;
-}
-
-// Emit rows without any scaling.
-static int EmitRows(WEBP_CSP_MODE colorspace,
- const uint32_t* const data, int in_stride,
- int mb_w, int mb_h,
- uint8_t* const out, int out_stride) {
- int lines = mb_h;
- const uint8_t* row_in = (const uint8_t*)data;
- uint8_t* row_out = out;
- while (lines-- > 0) {
- VP8LConvertFromBGRA((const uint32_t*)row_in, mb_w, colorspace, row_out);
- row_in += in_stride;
- row_out += out_stride;
- }
- return mb_h; // Num rows out == num rows in.
-}
-
-//------------------------------------------------------------------------------
-// Export to YUVA
-
-static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos,
- const WebPDecBuffer* const output) {
- const WebPYUVABuffer* const buf = &output->u.YUVA;
- // first, the luma plane
- {
- int i;
- uint8_t* const y = buf->y + y_pos * buf->y_stride;
- for (i = 0; i < width; ++i) {
- const uint32_t p = src[i];
- y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff);
- }
- }
-
- // then U/V planes
- {
- uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride;
- uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride;
- const int uv_width = width >> 1;
- int i;
- for (i = 0; i < uv_width; ++i) {
- const uint32_t v0 = src[2 * i + 0];
- const uint32_t v1 = src[2 * i + 1];
- // VP8RGBToU/V expects four accumulated pixels. Hence we need to
- // scale r/g/b value by a factor 2. We just shift v0/v1 one bit less.
- const int r = ((v0 >> 15) & 0x1fe) + ((v1 >> 15) & 0x1fe);
- const int g = ((v0 >> 7) & 0x1fe) + ((v1 >> 7) & 0x1fe);
- const int b = ((v0 << 1) & 0x1fe) + ((v1 << 1) & 0x1fe);
- if (!(y_pos & 1)) { // even lines: store values
- u[i] = VP8RGBToU(r, g, b);
- v[i] = VP8RGBToV(r, g, b);
- } else { // odd lines: average with previous values
- const int tmp_u = VP8RGBToU(r, g, b);
- const int tmp_v = VP8RGBToV(r, g, b);
- // Approximated average-of-four. But it's an acceptable diff.
- u[i] = (u[i] + tmp_u + 1) >> 1;
- v[i] = (v[i] + tmp_v + 1) >> 1;
- }
- }
- if (width & 1) { // last pixel
- const uint32_t v0 = src[2 * i + 0];
- const int r = (v0 >> 14) & 0x3fc;
- const int g = (v0 >> 6) & 0x3fc;
- const int b = (v0 << 2) & 0x3fc;
- if (!(y_pos & 1)) { // even lines
- u[i] = VP8RGBToU(r, g, b);
- v[i] = VP8RGBToV(r, g, b);
- } else { // odd lines (note: we could just skip this)
- const int tmp_u = VP8RGBToU(r, g, b);
- const int tmp_v = VP8RGBToV(r, g, b);
- u[i] = (u[i] + tmp_u + 1) >> 1;
- v[i] = (v[i] + tmp_v + 1) >> 1;
- }
- }
- }
- // Lastly, store alpha if needed.
- if (buf->a != NULL) {
- int i;
- uint8_t* const a = buf->a + y_pos * buf->a_stride;
- for (i = 0; i < width; ++i) a[i] = (src[i] >> 24);
- }
-}
-
-static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) {
- WebPRescaler* const rescaler = dec->rescaler;
- const uint32_t* const src = (const uint32_t*)rescaler->dst;
- const int dst_width = rescaler->dst_width;
- int num_lines_out = 0;
- while (WebPRescalerHasPendingOutput(rescaler)) {
- WebPRescalerExportRow(rescaler);
- ConvertToYUVA(src, dst_width, y_pos, dec->output_);
- ++y_pos;
- ++num_lines_out;
- }
- return num_lines_out;
-}
-
-static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec,
- const uint32_t* const data,
- int in_stride, int mb_h) {
- const uint8_t* const in = (const uint8_t*)data;
- int num_lines_in = 0;
- int y_pos = dec->last_out_row_;
- while (num_lines_in < mb_h) {
- const uint8_t* const row_in = in + num_lines_in * in_stride;
- num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in,
- row_in, in_stride);
- y_pos += ExportYUVA(dec, y_pos);
- }
- return y_pos;
-}
-
-static int EmitRowsYUVA(const VP8LDecoder* const dec,
- const uint32_t* const data, int in_stride,
- int mb_w, int num_rows) {
- int y_pos = dec->last_out_row_;
- const uint8_t* row_in = (const uint8_t*)data;
- while (num_rows-- > 0) {
- ConvertToYUVA((const uint32_t*)row_in, mb_w, y_pos, dec->output_);
- row_in += in_stride;
- ++y_pos;
- }
- return y_pos;
-}
-
-//------------------------------------------------------------------------------
-// Cropping.
-
-// Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and
-// crop options. Also updates the input data pointer, so that it points to the
-// start of the cropped window.
-// Note that 'pixel_stride' is in units of 'uint32_t' (and not 'bytes).
-// Returns true if the crop window is not empty.
-static int SetCropWindow(VP8Io* const io, int y_start, int y_end,
- const uint32_t** const in_data, int pixel_stride) {
- SB_DCHECK(y_start < y_end);
- SB_DCHECK(io->crop_left < io->crop_right);
- if (y_end > io->crop_bottom) {
- y_end = io->crop_bottom; // make sure we don't overflow on last row.
- }
- if (y_start < io->crop_top) {
- const int delta = io->crop_top - y_start;
- y_start = io->crop_top;
- *in_data += pixel_stride * delta;
- }
- if (y_start >= y_end) return 0; // Crop window is empty.
-
- *in_data += io->crop_left;
-
- io->mb_y = y_start - io->crop_top;
- io->mb_w = io->crop_right - io->crop_left;
- io->mb_h = y_end - y_start;
- return 1; // Non-empty crop window.
-}
-
-//------------------------------------------------------------------------------
-
-static WEBP_INLINE int GetMetaIndex(
- const uint32_t* const image, int xsize, int bits, int x, int y) {
- if (bits == 0) return 0;
- return image[xsize * (y >> bits) + (x >> bits)];
-}
-
-static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr,
- int x, int y) {
- const int meta_index = GetMetaIndex(hdr->huffman_image_, hdr->huffman_xsize_,
- hdr->huffman_subsample_bits_, x, y);
- SB_DCHECK(meta_index < hdr->num_htree_groups_);
- return hdr->htree_groups_ + meta_index;
-}
-
-//------------------------------------------------------------------------------
-// Main loop, with custom row-processing function
-
-typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row);
-
-static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows,
- const uint32_t* const rows) {
- int n = dec->next_transform_;
- const int cache_pixs = dec->width_ * num_rows;
- const int start_row = dec->last_row_;
- const int end_row = start_row + num_rows;
- const uint32_t* rows_in = rows;
- uint32_t* const rows_out = dec->argb_cache_;
-
- // Inverse transforms.
- // TODO: most transforms only need to operate on the cropped region only.
- SbMemoryCopy(rows_out, rows_in, cache_pixs * sizeof(*rows_out));
- while (n-- > 0) {
- VP8LTransform* const transform = &dec->transforms_[n];
- VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out);
- rows_in = rows_out;
- }
-}
-
-// Special method for paletted alpha data.
-static void ApplyInverseTransformsAlpha(VP8LDecoder* const dec, int num_rows,
- const uint8_t* const rows) {
- const int start_row = dec->last_row_;
- const int end_row = start_row + num_rows;
- const uint8_t* rows_in = rows;
- uint8_t* rows_out = (uint8_t*)dec->io_->opaque + dec->io_->width * start_row;
- VP8LTransform* const transform = &dec->transforms_[0];
- SB_DCHECK(dec->next_transform_ == 1);
- SB_DCHECK(transform->type_ == COLOR_INDEXING_TRANSFORM);
- VP8LColorIndexInverseTransformAlpha(transform, start_row, end_row, rows_in,
- rows_out);
-}
-
-// Processes (transforms, scales & color-converts) the rows decoded after the
-// last call.
-static void ProcessRows(VP8LDecoder* const dec, int row) {
- const uint32_t* const rows = dec->pixels_ + dec->width_ * dec->last_row_;
- const int num_rows = row - dec->last_row_;
-
- if (num_rows <= 0) return; // Nothing to be done.
- ApplyInverseTransforms(dec, num_rows, rows);
-
- // Emit output.
- {
- VP8Io* const io = dec->io_;
- const uint32_t* rows_data = dec->argb_cache_;
- if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) {
- // Nothing to output (this time).
- } else {
- const WebPDecBuffer* const output = dec->output_;
- const int in_stride = io->width * sizeof(*rows_data);
- if (output->colorspace < MODE_YUV) { // convert to RGBA
- const WebPRGBABuffer* const buf = &output->u.RGBA;
- uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
- const int num_rows_out = io->use_scaling ?
- EmitRescaledRows(dec, rows_data, in_stride, io->mb_h,
- rgba, buf->stride) :
- EmitRows(output->colorspace, rows_data, in_stride,
- io->mb_w, io->mb_h, rgba, buf->stride);
- // Update 'last_out_row_'.
- dec->last_out_row_ += num_rows_out;
- } else { // convert to YUVA
- dec->last_out_row_ = io->use_scaling ?
- EmitRescaledRowsYUVA(dec, rows_data, in_stride, io->mb_h) :
- EmitRowsYUVA(dec, rows_data, in_stride, io->mb_w, io->mb_h);
- }
- SB_DCHECK(dec->last_out_row_ <= output->height);
- }
- }
-
- // Update 'last_row_'.
- dec->last_row_ = row;
- SB_DCHECK(dec->last_row_ <= dec->height_);
-}
-
-#define DECODE_DATA_FUNC(FUNC_NAME, TYPE, STORE_PIXEL) \
-static int FUNC_NAME(VP8LDecoder* const dec, TYPE* const data, int width, \
- int height, ProcessRowsFunc process_func) { \
- int ok = 1; \
- int col = 0, row = 0; \
- VP8LBitReader* const br = &dec->br_; \
- VP8LMetadata* const hdr = &dec->hdr_; \
- HTreeGroup* htree_group = hdr->htree_groups_; \
- TYPE* src = data; \
- TYPE* last_cached = data; \
- TYPE* const src_end = data + width * height; \
- const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; \
- const int color_cache_limit = len_code_limit + hdr->color_cache_size_; \
- VP8LColorCache* const color_cache = \
- (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL; \
- const int mask = hdr->huffman_mask_; \
- SB_DCHECK(htree_group != NULL); \
- while (!br->eos_ && src < src_end) { \
- int code; \
- /* Only update when changing tile. Note we could use this test: */ \
- /* if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed */ \
- /* but that's actually slower and needs storing the previous col/row. */ \
- if ((col & mask) == 0) { \
- htree_group = GetHtreeGroupForPos(hdr, col, row); \
- } \
- VP8LFillBitWindow(br); \
- code = ReadSymbol(&htree_group->htrees_[GREEN], br); \
- if (code < NUM_LITERAL_CODES) { /* Literal*/ \
- int red, green, blue, alpha; \
- red = ReadSymbol(&htree_group->htrees_[RED], br); \
- green = code; \
- VP8LFillBitWindow(br); \
- blue = ReadSymbol(&htree_group->htrees_[BLUE], br); \
- alpha = ReadSymbol(&htree_group->htrees_[ALPHA], br); \
- *src = STORE_PIXEL(alpha, red, green, blue); \
- AdvanceByOne: \
- ++src; \
- ++col; \
- if (col >= width) { \
- col = 0; \
- ++row; \
- if ((process_func != NULL) && (row % NUM_ARGB_CACHE_ROWS == 0)) { \
- process_func(dec, row); \
- } \
- if (color_cache != NULL) { \
- while (last_cached < src) { \
- VP8LColorCacheInsert(color_cache, *last_cached++); \
- } \
- } \
- } \
- } else if (code < len_code_limit) { /* Backward reference */ \
- int dist_code, dist; \
- const int length_sym = code - NUM_LITERAL_CODES; \
- const int length = GetCopyLength(length_sym, br); \
- const int dist_symbol = ReadSymbol(&htree_group->htrees_[DIST], br); \
- VP8LFillBitWindow(br); \
- dist_code = GetCopyDistance(dist_symbol, br); \
- dist = PlaneCodeToDistance(width, dist_code); \
- if (src - data < dist || src_end - src < length) { \
- ok = 0; \
- goto End; \
- } \
- { \
- int i; \
- for (i = 0; i < length; ++i) src[i] = src[i - dist]; \
- src += length; \
- } \
- col += length; \
- while (col >= width) { \
- col -= width; \
- ++row; \
- if ((process_func != NULL) && (row % NUM_ARGB_CACHE_ROWS == 0)) { \
- process_func(dec, row); \
- } \
- } \
- if (src < src_end) { \
- htree_group = GetHtreeGroupForPos(hdr, col, row); \
- if (color_cache != NULL) { \
- while (last_cached < src) { \
- VP8LColorCacheInsert(color_cache, *last_cached++); \
- } \
- } \
- } \
- } else if (code < color_cache_limit) { /* Color cache */ \
- const int key = code - len_code_limit; \
- SB_DCHECK(color_cache != NULL); \
- while (last_cached < src) { \
- VP8LColorCacheInsert(color_cache, *last_cached++); \
- } \
- *src = VP8LColorCacheLookup(color_cache, key); \
- goto AdvanceByOne; \
- } else { /* Not reached */ \
- ok = 0; \
- goto End; \
- } \
- ok = !br->error_; \
- if (!ok) goto End; \
- } \
- /* Process the remaining rows corresponding to last row-block. */ \
- if (process_func != NULL) process_func(dec, row); \
-End: \
- if (br->error_ || !ok || (br->eos_ && src < src_end)) { \
- ok = 0; \
- dec->status_ = \
- (!br->eos_) ? VP8_STATUS_BITSTREAM_ERROR : VP8_STATUS_SUSPENDED; \
- } else if (src == src_end) { \
- dec->state_ = READ_DATA; \
- } \
- return ok; \
-}
-
-static WEBP_INLINE uint32_t GetARGBPixel(int alpha, int red, int green,
- int blue) {
- return (alpha << 24) | (red << 16) | (green << 8) | blue;
-}
-
-static WEBP_INLINE uint8_t GetAlphaPixel(int alpha, int red, int green,
- int blue) {
- (void)alpha;
- (void)red;
- (void)blue;
- return green; // Alpha value is stored in green channel.
-}
-
-DECODE_DATA_FUNC(DecodeImageData, uint32_t, GetARGBPixel)
-DECODE_DATA_FUNC(DecodeAlphaData, uint8_t, GetAlphaPixel)
-
-#undef DECODE_DATA_FUNC
-
-// -----------------------------------------------------------------------------
-// VP8LTransform
-
-static void ClearTransform(VP8LTransform* const transform) {
- SbMemoryDeallocate(transform->data_);
- transform->data_ = NULL;
-}
-
-// For security reason, we need to remap the color map to span
-// the total possible bundled values, and not just the num_colors.
-static int ExpandColorMap(int num_colors, VP8LTransform* const transform) {
- int i;
- const int final_num_colors = 1 << (8 >> transform->bits_);
- uint32_t* const new_color_map =
- (uint32_t*)WebPSafeMalloc((uint64_t)final_num_colors,
- sizeof(*new_color_map));
- if (new_color_map == NULL) {
- return 0;
- } else {
- uint8_t* const data = (uint8_t*)transform->data_;
- uint8_t* const new_data = (uint8_t*)new_color_map;
- new_color_map[0] = transform->data_[0];
- for (i = 4; i < 4 * num_colors; ++i) {
- // Equivalent to AddPixelEq(), on a byte-basis.
- new_data[i] = (data[i] + new_data[i - 4]) & 0xff;
- }
- for (; i < 4 * final_num_colors; ++i)
- new_data[i] = 0; // black tail.
- SbMemoryDeallocate(transform->data_);
- transform->data_ = new_color_map;
- }
- return 1;
-}
-
-static int ReadTransform(int* const xsize, int const* ysize,
- VP8LDecoder* const dec) {
- int ok = 1;
- VP8LBitReader* const br = &dec->br_;
- VP8LTransform* transform = &dec->transforms_[dec->next_transform_];
- const VP8LImageTransformType type =
- (VP8LImageTransformType)VP8LReadBits(br, 2);
-
- // Each transform type can only be present once in the stream.
- if (dec->transforms_seen_ & (1U << type)) {
- return 0; // Already there, let's not accept the second same transform.
- }
- dec->transforms_seen_ |= (1U << type);
-
- transform->type_ = type;
- transform->xsize_ = *xsize;
- transform->ysize_ = *ysize;
- transform->data_ = NULL;
- ++dec->next_transform_;
- SB_DCHECK(dec->next_transform_ <= NUM_TRANSFORMS);
-
- switch (type) {
- case PREDICTOR_TRANSFORM:
- case CROSS_COLOR_TRANSFORM:
- transform->bits_ = VP8LReadBits(br, 3) + 2;
- ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_,
- transform->bits_),
- VP8LSubSampleSize(transform->ysize_,
- transform->bits_),
- 0, dec, &transform->data_);
- break;
- case COLOR_INDEXING_TRANSFORM: {
- const int num_colors = VP8LReadBits(br, 8) + 1;
- const int bits = (num_colors > 16) ? 0
- : (num_colors > 4) ? 1
- : (num_colors > 2) ? 2
- : 3;
- *xsize = VP8LSubSampleSize(transform->xsize_, bits);
- transform->bits_ = bits;
- ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_);
- ok = ok && ExpandColorMap(num_colors, transform);
- break;
- }
- case SUBTRACT_GREEN:
- break;
- default:
- SB_DCHECK(0); // can't happen
- break;
- }
-
- return ok;
-}
-
-// -----------------------------------------------------------------------------
-// VP8LMetadata
-
-static void InitMetadata(VP8LMetadata* const hdr) {
- SB_DCHECK(hdr);
- SbMemorySet(hdr, 0, sizeof(*hdr));
-}
-
-static void ClearMetadata(VP8LMetadata* const hdr) {
- SB_DCHECK(hdr);
-
- SbMemoryDeallocate(hdr->huffman_image_);
- DeleteHtreeGroups(hdr->htree_groups_, hdr->num_htree_groups_);
- VP8LColorCacheClear(&hdr->color_cache_);
- InitMetadata(hdr);
-}
-
-// -----------------------------------------------------------------------------
-// VP8LDecoder
-
-VP8LDecoder* VP8LNew(void) {
- VP8LDecoder* const dec = (VP8LDecoder*)SbMemoryCalloc(1, sizeof(*dec));
- if (dec == NULL) return NULL;
- dec->status_ = VP8_STATUS_OK;
- dec->action_ = READ_DIM;
- dec->state_ = READ_DIM;
- return dec;
-}
-
-void VP8LClear(VP8LDecoder* const dec) {
- int i;
- if (dec == NULL) return;
- ClearMetadata(&dec->hdr_);
-
- SbMemoryDeallocate(dec->pixels_);
- dec->pixels_ = NULL;
- for (i = 0; i < dec->next_transform_; ++i) {
- ClearTransform(&dec->transforms_[i]);
- }
- dec->next_transform_ = 0;
- dec->transforms_seen_ = 0;
-
- SbMemoryDeallocate(dec->rescaler_memory);
- dec->rescaler_memory = NULL;
-
- dec->output_ = NULL; // leave no trace behind
-}
-
-void VP8LDelete(VP8LDecoder* const dec) {
- if (dec != NULL) {
- VP8LClear(dec);
- SbMemoryDeallocate(dec);
- }
-}
-
-static void UpdateDecoder(VP8LDecoder* const dec, int width, int height) {
- VP8LMetadata* const hdr = &dec->hdr_;
- const int num_bits = hdr->huffman_subsample_bits_;
- dec->width_ = width;
- dec->height_ = height;
-
- hdr->huffman_xsize_ = VP8LSubSampleSize(width, num_bits);
- hdr->huffman_mask_ = (num_bits == 0) ? ~0 : (1 << num_bits) - 1;
-}
-
-static int DecodeImageStream(int xsize, int ysize,
- int is_level0,
- VP8LDecoder* const dec,
- uint32_t** const decoded_data) {
- int ok = 1;
- int transform_xsize = xsize;
- int transform_ysize = ysize;
- VP8LBitReader* const br = &dec->br_;
- VP8LMetadata* const hdr = &dec->hdr_;
- uint32_t* data = NULL;
- int color_cache_bits = 0;
-
- // Read the transforms (may recurse).
- if (is_level0) {
- while (ok && VP8LReadBits(br, 1)) {
- ok = ReadTransform(&transform_xsize, &transform_ysize, dec);
- }
- }
-
- // Color cache
- if (ok && VP8LReadBits(br, 1)) {
- color_cache_bits = VP8LReadBits(br, 4);
- ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS);
- if (!ok) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- goto End;
- }
- }
-
- // Read the Huffman codes (may recurse).
- ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize,
- color_cache_bits, is_level0);
- if (!ok) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- goto End;
- }
-
- // Finish setting up the color-cache
- if (color_cache_bits > 0) {
- hdr->color_cache_size_ = 1 << color_cache_bits;
- if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- ok = 0;
- goto End;
- }
- } else {
- hdr->color_cache_size_ = 0;
- }
- UpdateDecoder(dec, transform_xsize, transform_ysize);
-
- if (is_level0) { // level 0 complete
- dec->state_ = READ_HDR;
- goto End;
- }
-
- {
- const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize;
- data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data));
- if (data == NULL) {
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- ok = 0;
- goto End;
- }
- }
-
- // Use the Huffman trees to decode the LZ77 encoded data.
- ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, NULL);
- ok = ok && !br->error_;
-
- End:
-
- if (!ok) {
- SbMemoryDeallocate(data);
- ClearMetadata(hdr);
- // If not enough data (br.eos_) resulted in BIT_STREAM_ERROR, update the
- // status appropriately.
- if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR && dec->br_.eos_) {
- dec->status_ = VP8_STATUS_SUSPENDED;
- }
- } else {
- if (decoded_data != NULL) {
- *decoded_data = data;
- } else {
- // We allocate image data in this function only for transforms. At level 0
- // (that is: not the transforms), we shouldn't have allocated anything.
- SB_DCHECK(data == NULL);
- SB_DCHECK(is_level0);
- }
- if (!is_level0) ClearMetadata(hdr); // Clean up temporary data behind.
- }
- return ok;
-}
-
-//------------------------------------------------------------------------------
-// Allocate internal buffers dec->pixels_ and dec->argb_cache_.
-static int AllocateInternalBuffers(VP8LDecoder* const dec, int final_width,
- size_t bytes_per_pixel) {
- const int argb_cache_needed = (bytes_per_pixel == sizeof(uint32_t));
- const uint64_t num_pixels = (uint64_t)dec->width_ * dec->height_;
- // Scratch buffer corresponding to top-prediction row for transforming the
- // first row in the row-blocks. Not needed for paletted alpha.
- const uint64_t cache_top_pixels =
- argb_cache_needed ? (uint16_t)final_width : 0ULL;
- // Scratch buffer for temporary BGRA storage. Not needed for paletted alpha.
- const uint64_t cache_pixels =
- argb_cache_needed ? (uint64_t)final_width * NUM_ARGB_CACHE_ROWS : 0ULL;
- const uint64_t total_num_pixels =
- num_pixels + cache_top_pixels + cache_pixels;
-
- SB_DCHECK(dec->width_ <= final_width);
- dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, bytes_per_pixel);
- if (dec->pixels_ == NULL) {
- dec->argb_cache_ = NULL; // for sanity check
- dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
- return 0;
- }
- dec->argb_cache_ =
- argb_cache_needed ? dec->pixels_ + num_pixels + cache_top_pixels : NULL;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-// Special row-processing that only stores the alpha data.
-static void ExtractAlphaRows(VP8LDecoder* const dec, int row) {
- const int num_rows = row - dec->last_row_;
- const uint32_t* const in = dec->pixels_ + dec->width_ * dec->last_row_;
-
- if (num_rows <= 0) return; // Nothing to be done.
- ApplyInverseTransforms(dec, num_rows, in);
-
- // Extract alpha (which is stored in the green plane).
- {
- const int width = dec->io_->width; // the final width (!= dec->width_)
- const int cache_pixs = width * num_rows;
- uint8_t* const dst = (uint8_t*)dec->io_->opaque + width * dec->last_row_;
- const uint32_t* const src = dec->argb_cache_;
- int i;
- for (i = 0; i < cache_pixs; ++i) dst[i] = (src[i] >> 8) & 0xff;
- }
- dec->last_row_ = dec->last_out_row_ = row;
-}
-
-// Row-processing for the special case when alpha data contains only one
-// transform: color indexing.
-static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int row) {
- const int num_rows = row - dec->last_row_;
- const uint8_t* const in =
- (uint8_t*)dec->pixels_ + dec->width_ * dec->last_row_;
- if (num_rows <= 0) return; // Nothing to be done.
- ApplyInverseTransformsAlpha(dec, num_rows, in);
- dec->last_row_ = dec->last_out_row_ = row;
-}
-
-int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
- size_t data_size, uint8_t* const output) {
- VP8Io io;
- int ok = 0;
- VP8LDecoder* const dec = VP8LNew();
- size_t bytes_per_pixel = sizeof(uint32_t); // Default: BGRA mode.
- if (dec == NULL) return 0;
-
- dec->width_ = width;
- dec->height_ = height;
- dec->io_ = &io;
-
- VP8InitIo(&io);
- WebPInitCustomIo(NULL, &io); // Just a sanity Init. io won't be used.
- io.opaque = output;
- io.width = width;
- io.height = height;
-
- dec->status_ = VP8_STATUS_OK;
- VP8LInitBitReader(&dec->br_, data, data_size);
-
- dec->action_ = READ_HDR;
- if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Err;
-
- // Special case: if alpha data uses only the color indexing transform and
- // doesn't use color cache (a frequent case), we will use DecodeAlphaData()
- // method that only needs allocation of 1 byte per pixel (alpha channel).
- if (dec->next_transform_ == 1 &&
- dec->transforms_[0].type_ == COLOR_INDEXING_TRANSFORM &&
- dec->hdr_.color_cache_size_ == 0) {
- bytes_per_pixel = sizeof(uint8_t);
- }
-
- // Allocate internal buffers (note that dec->width_ may have changed here).
- if (!AllocateInternalBuffers(dec, width, bytes_per_pixel)) goto Err;
-
- // Decode (with special row processing).
- dec->action_ = READ_DATA;
- ok = (bytes_per_pixel == sizeof(uint8_t)) ?
- DecodeAlphaData(dec, (uint8_t*)dec->pixels_, dec->width_, dec->height_,
- ExtractPalettedAlphaRows) :
- DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_,
- ExtractAlphaRows);
-
- Err:
- VP8LDelete(dec);
- return ok;
-}
-
-//------------------------------------------------------------------------------
-
-int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
- int width, height, has_alpha;
-
- if (dec == NULL) return 0;
- if (io == NULL) {
- dec->status_ = VP8_STATUS_INVALID_PARAM;
- return 0;
- }
-
- dec->io_ = io;
- dec->status_ = VP8_STATUS_OK;
- VP8LInitBitReader(&dec->br_, io->data, io->data_size);
- if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) {
- dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
- goto Error;
- }
- dec->state_ = READ_DIM;
- io->width = width;
- io->height = height;
-
- dec->action_ = READ_HDR;
- if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error;
- return 1;
-
- Error:
- VP8LClear(dec);
- SB_DCHECK(dec->status_ != VP8_STATUS_OK);
- return 0;
-}
-
-int VP8LDecodeImage(VP8LDecoder* const dec) {
- const size_t bytes_per_pixel = sizeof(uint32_t);
- VP8Io* io = NULL;
- WebPDecParams* params = NULL;
-
- // Sanity checks.
- if (dec == NULL) return 0;
-
- io = dec->io_;
- SB_DCHECK(io != NULL);
- params = (WebPDecParams*)io->opaque;
- SB_DCHECK(params != NULL);
- dec->output_ = params->output;
- SB_DCHECK(dec->output_ != NULL);
-
- // Initialization.
- if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) {
- dec->status_ = VP8_STATUS_INVALID_PARAM;
- goto Err;
- }
-
- if (!AllocateInternalBuffers(dec, io->width, bytes_per_pixel)) goto Err;
-
- if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err;
-
- // Decode.
- dec->action_ = READ_DATA;
- if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_,
- ProcessRows)) {
- goto Err;
- }
-
- // Cleanup.
- params->last_y = dec->last_out_row_;
- VP8LClear(dec);
- return 1;
-
- Err:
- VP8LClear(dec);
- SB_DCHECK(dec->status_ != VP8_STATUS_OK);
- return 0;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/vp8li.h b/src/third_party/libwebp/dec/vp8li.h
deleted file mode 100644
index dbdd9c1..0000000
--- a/src/third_party/libwebp/dec/vp8li.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Lossless decoder: internal header.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-// Vikas Arora(vikaas.arora@gmail.com)
-
-#ifndef WEBP_DEC_VP8LI_H_
-#define WEBP_DEC_VP8LI_H_
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#else
-#include <string.h> // for memcpy()
-#endif
-
-#include "./webpi.h"
-#include "../utils/bit_reader.h"
-#include "../utils/color_cache.h"
-#include "../utils/huffman.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-typedef enum {
- READ_DATA = 0,
- READ_HDR = 1,
- READ_DIM = 2
-} VP8LDecodeState;
-
-typedef struct VP8LTransform VP8LTransform;
-struct VP8LTransform {
- VP8LImageTransformType type_; // transform type.
- int bits_; // subsampling bits defining transform window.
- int xsize_; // transform window X index.
- int ysize_; // transform window Y index.
- uint32_t *data_; // transform data.
-};
-
-typedef struct {
- HuffmanTree htrees_[HUFFMAN_CODES_PER_META_CODE];
-} HTreeGroup;
-
-typedef struct {
- int color_cache_size_;
- VP8LColorCache color_cache_;
-
- int huffman_mask_;
- int huffman_subsample_bits_;
- int huffman_xsize_;
- uint32_t *huffman_image_;
- int num_htree_groups_;
- HTreeGroup *htree_groups_;
-} VP8LMetadata;
-
-typedef struct {
- VP8StatusCode status_;
- VP8LDecodeState action_;
- VP8LDecodeState state_;
- VP8Io *io_;
-
- const WebPDecBuffer *output_; // shortcut to io->opaque->output
-
- uint32_t *pixels_; // Internal data: either uint8_t* for alpha
- // or uint32_t* for BGRA.
- uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
-
- VP8LBitReader br_;
-
- int width_;
- int height_;
- int last_row_; // last input row decoded so far.
- int last_out_row_; // last row output so far.
-
- VP8LMetadata hdr_;
-
- int next_transform_;
- VP8LTransform transforms_[NUM_TRANSFORMS];
- // or'd bitset storing the transforms types.
- uint32_t transforms_seen_;
-
- uint8_t *rescaler_memory; // Working memory for rescaling work.
- WebPRescaler *rescaler; // Common rescaler for all channels.
-} VP8LDecoder;
-
-//------------------------------------------------------------------------------
-// internal functions. Not public.
-
-// in vp8l.c
-
-// Decodes a raw image stream (without header) and store the alpha data
-// into *output, which must be of size width x height. Returns false in case
-// of error.
-int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
- size_t data_size, uint8_t* const output);
-
-// Allocates and initialize a new lossless decoder instance.
-VP8LDecoder* VP8LNew(void);
-
-// Decodes the image header. Returns false in case of error.
-int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
-
-// Decodes an image. It's required to decode the lossless header before calling
-// this function. Returns false in case of error, with updated dec->status_.
-int VP8LDecodeImage(VP8LDecoder* const dec);
-
-// Resets the decoder in its initial state, reclaiming memory.
-// Preserves the dec->status_ value.
-void VP8LClear(VP8LDecoder* const dec);
-
-// Clears and deallocate a lossless decoder instance.
-void VP8LDelete(VP8LDecoder* const dec);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_DEC_VP8LI_H_ */
diff --git a/src/third_party/libwebp/dec/webp.c b/src/third_party/libwebp/dec/webp.c
deleted file mode 100644
index 3364ab8..0000000
--- a/src/third_party/libwebp/dec/webp.c
+++ /dev/null
@@ -1,796 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Main decoding functions for WEBP images.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h>
-#endif
-
-#include "./vp8i.h"
-#include "./vp8li.h"
-#include "./webpi.h"
-#include "../webp/mux_types.h" // ALPHA_FLAG
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// RIFF layout is:
-// Offset tag
-// 0...3 "RIFF" 4-byte tag
-// 4...7 size of image data (including metadata) starting at offset 8
-// 8...11 "WEBP" our form-type signature
-// The RIFF container (12 bytes) is followed by appropriate chunks:
-// 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format
-// 16..19 size of the raw VP8 image data, starting at offset 20
-// 20.... the VP8 bytes
-// Or,
-// 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
-// 16..19 size of the raw VP8L image data, starting at offset 20
-// 20.... the VP8L bytes
-// Or,
-// 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
-// 16..19 size of the VP8X chunk starting at offset 20.
-// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
-// 24..26 Width of the Canvas Image.
-// 27..29 Height of the Canvas Image.
-// There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
-// VP8L, XMP, EXIF ...)
-// All sizes are in little-endian order.
-// Note: chunk data size must be padded to multiple of 2 when written.
-
-static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) {
- return data[0] | (data[1] << 8) | (data[2] << 16);
-}
-
-static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) {
- return (uint32_t)get_le24(data) | (data[3] << 24);
-}
-
-// Validates the RIFF container (if detected) and skips over it.
-// If a RIFF container is detected,
-// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
-// VP8_STATUS_OK otherwise.
-// In case there are not enough bytes (partial RIFF container), return 0 for
-// *riff_size. Else return the RIFF size extracted from the header.
-static VP8StatusCode ParseRIFF(const uint8_t** const data,
- size_t* const data_size,
- size_t* const riff_size) {
- SB_DCHECK(data != NULL);
- SB_DCHECK(data_size != NULL);
- SB_DCHECK(riff_size != NULL);
-
- *riff_size = 0; // Default: no RIFF present.
- if (*data_size >= RIFF_HEADER_SIZE && !SbMemoryCompare(*data, "RIFF", TAG_SIZE)) {
- if (SbMemoryCompare(*data + 8, "WEBP", TAG_SIZE)) {
- return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
- } else {
- const uint32_t size = get_le32(*data + TAG_SIZE);
- // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
- if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
- return VP8_STATUS_BITSTREAM_ERROR;
- }
- if (size > MAX_CHUNK_PAYLOAD) {
- return VP8_STATUS_BITSTREAM_ERROR;
- }
- // We have a RIFF container. Skip it.
- *riff_size = size;
- *data += RIFF_HEADER_SIZE;
- *data_size -= RIFF_HEADER_SIZE;
- }
- }
- return VP8_STATUS_OK;
-}
-
-// Validates the VP8X header and skips over it.
-// Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
-// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
-// VP8_STATUS_OK otherwise.
-// If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
-// *height_ptr and *flags_ptr are set to the corresponding values extracted
-// from the VP8X chunk.
-static VP8StatusCode ParseVP8X(const uint8_t** const data,
- size_t* const data_size,
- int* const found_vp8x,
- int* const width_ptr, int* const height_ptr,
- uint32_t* const flags_ptr) {
- const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
- SB_DCHECK(data != NULL);
- SB_DCHECK(data_size != NULL);
- SB_DCHECK(found_vp8x != NULL);
-
- *found_vp8x = 0;
-
- if (*data_size < CHUNK_HEADER_SIZE) {
- return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
- }
-
- if (!SbMemoryCompare(*data, "VP8X", TAG_SIZE)) {
- int width, height;
- uint32_t flags;
- const uint32_t chunk_size = get_le32(*data + TAG_SIZE);
- if (chunk_size != VP8X_CHUNK_SIZE) {
- return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
- }
-
- // Verify if enough data is available to validate the VP8X chunk.
- if (*data_size < vp8x_size) {
- return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
- }
- flags = get_le32(*data + 8);
- width = 1 + get_le24(*data + 12);
- height = 1 + get_le24(*data + 15);
- if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
- return VP8_STATUS_BITSTREAM_ERROR; // image is too large
- }
-
- if (flags_ptr != NULL) *flags_ptr = flags;
- if (width_ptr != NULL) *width_ptr = width;
- if (height_ptr != NULL) *height_ptr = height;
- // Skip over VP8X header bytes.
- *data += vp8x_size;
- *data_size -= vp8x_size;
- *found_vp8x = 1;
- }
- return VP8_STATUS_OK;
-}
-
-// Skips to the next VP8/VP8L chunk header in the data given the size of the
-// RIFF chunk 'riff_size'.
-// Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
-// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
-// VP8_STATUS_OK otherwise.
-// If an alpha chunk is found, *alpha_data and *alpha_size are set
-// appropriately.
-static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
- size_t* const data_size,
- size_t const riff_size,
- const uint8_t** const alpha_data,
- size_t* const alpha_size) {
- const uint8_t* buf;
- size_t buf_size;
- uint32_t total_size = TAG_SIZE + // "WEBP".
- CHUNK_HEADER_SIZE + // "VP8Xnnnn".
- VP8X_CHUNK_SIZE; // data.
- SB_DCHECK(data != NULL);
- SB_DCHECK(data_size != NULL);
- buf = *data;
- buf_size = *data_size;
-
- SB_DCHECK(alpha_data != NULL);
- SB_DCHECK(alpha_size != NULL);
- *alpha_data = NULL;
- *alpha_size = 0;
-
- while (1) {
- uint32_t chunk_size;
- uint32_t disk_chunk_size; // chunk_size with padding
-
- *data = buf;
- *data_size = buf_size;
-
- if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data.
- return VP8_STATUS_NOT_ENOUGH_DATA;
- }
-
- chunk_size = get_le32(buf + TAG_SIZE);
- if (chunk_size > MAX_CHUNK_PAYLOAD) {
- return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
- }
- // For odd-sized chunk-payload, there's one byte padding at the end.
- disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
- total_size += disk_chunk_size;
-
- // Check that total bytes skipped so far does not exceed riff_size.
- if (riff_size > 0 && (total_size > riff_size)) {
- return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
- }
-
- // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
- // parsed all the optional chunks.
- // Note: This check must occur before the check 'buf_size < disk_chunk_size'
- // below to allow incomplete VP8/VP8L chunks.
- if (!SbMemoryCompare(buf, "VP8 ", TAG_SIZE) ||
- !SbMemoryCompare(buf, "VP8L", TAG_SIZE)) {
- return VP8_STATUS_OK;
- }
-
- if (buf_size < disk_chunk_size) { // Insufficient data.
- return VP8_STATUS_NOT_ENOUGH_DATA;
- }
-
- if (!SbMemoryCompare(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
- *alpha_data = buf + CHUNK_HEADER_SIZE;
- *alpha_size = chunk_size;
- }
-
- // We have a full and valid chunk; skip it.
- buf += disk_chunk_size;
- buf_size -= disk_chunk_size;
- }
-}
-
-// Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
-// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
-// riff_size) VP8/VP8L header,
-// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
-// VP8_STATUS_OK otherwise.
-// If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
-// extracted from the VP8/VP8L chunk header.
-// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
-static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
- size_t* const data_size,
- size_t riff_size,
- size_t* const chunk_size,
- int* const is_lossless) {
- const uint8_t* const data = *data_ptr;
- const int is_vp8 = !SbMemoryCompare(data, "VP8 ", TAG_SIZE);
- const int is_vp8l = !SbMemoryCompare(data, "VP8L", TAG_SIZE);
- const uint32_t minimal_size =
- TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR
- // "WEBP" + "VP8Lnnnn"
- SB_DCHECK(data != NULL);
- SB_DCHECK(data_size != NULL);
- SB_DCHECK(chunk_size != NULL);
- SB_DCHECK(is_lossless != NULL);
-
- if (*data_size < CHUNK_HEADER_SIZE) {
- return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
- }
-
- if (is_vp8 || is_vp8l) {
- // Bitstream contains VP8/VP8L header.
- const uint32_t size = get_le32(data + TAG_SIZE);
- if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
- return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
- }
- // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
- *chunk_size = size;
- *data_ptr += CHUNK_HEADER_SIZE;
- *data_size -= CHUNK_HEADER_SIZE;
- *is_lossless = is_vp8l;
- } else {
- // Raw VP8/VP8L bitstream (no header).
- *is_lossless = VP8LCheckSignature(data, *data_size);
- *chunk_size = *data_size;
- }
-
- return VP8_STATUS_OK;
-}
-
-//------------------------------------------------------------------------------
-
-// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
-// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
-// minimal amount will be read to fetch the remaining parameters.
-// If 'headers' is non-NULL this function will attempt to locate both alpha
-// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
-// Note: The following chunk sequences (before the raw VP8/VP8L data) are
-// considered valid by this function:
-// RIFF + VP8(L)
-// RIFF + VP8X + (optional chunks) + VP8(L)
-// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
-// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
-static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
- size_t data_size,
- int* const width,
- int* const height,
- int* const has_alpha,
- int* const has_animation,
- WebPHeaderStructure* const headers) {
- int found_riff = 0;
- int found_vp8x = 0;
- VP8StatusCode status;
- WebPHeaderStructure hdrs;
-
- if (data == NULL || data_size < RIFF_HEADER_SIZE) {
- return VP8_STATUS_NOT_ENOUGH_DATA;
- }
- SbMemorySet(&hdrs, 0, sizeof(hdrs));
- hdrs.data = data;
- hdrs.data_size = data_size;
-
- // Skip over RIFF header.
- status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
- if (status != VP8_STATUS_OK) {
- return status; // Wrong RIFF header / insufficient data.
- }
- found_riff = (hdrs.riff_size > 0);
-
- // Skip over VP8X.
- {
- uint32_t flags = 0;
- status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
- if (status != VP8_STATUS_OK) {
- return status; // Wrong VP8X / insufficient data.
- }
- if (!found_riff && found_vp8x) {
- // Note: This restriction may be removed in the future, if it becomes
- // necessary to send VP8X chunk to the decoder.
- return VP8_STATUS_BITSTREAM_ERROR;
- }
- if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
- if (has_animation != NULL) *has_animation = !!(flags & ANIMATION_FLAG);
- if (found_vp8x && headers == NULL) {
- return VP8_STATUS_OK; // Return features from VP8X header.
- }
- }
-
- if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
-
- // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
- if ((found_riff && found_vp8x) ||
- (!found_riff && !found_vp8x && !SbMemoryCompare(data, "ALPH", TAG_SIZE))) {
- status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
- &hdrs.alpha_data, &hdrs.alpha_data_size);
- if (status != VP8_STATUS_OK) {
- return status; // Found an invalid chunk size / insufficient data.
- }
- }
-
- // Skip over VP8/VP8L header.
- status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
- &hdrs.compressed_size, &hdrs.is_lossless);
- if (status != VP8_STATUS_OK) {
- return status; // Wrong VP8/VP8L chunk-header / insufficient data.
- }
- if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
- return VP8_STATUS_BITSTREAM_ERROR;
- }
-
- if (!hdrs.is_lossless) {
- if (data_size < VP8_FRAME_HEADER_SIZE) {
- return VP8_STATUS_NOT_ENOUGH_DATA;
- }
- // Validates raw VP8 data.
- if (!VP8GetInfo(data, data_size,
- (uint32_t)hdrs.compressed_size, width, height)) {
- return VP8_STATUS_BITSTREAM_ERROR;
- }
- } else {
- if (data_size < VP8L_FRAME_HEADER_SIZE) {
- return VP8_STATUS_NOT_ENOUGH_DATA;
- }
- // Validates raw VP8L data.
- if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
- return VP8_STATUS_BITSTREAM_ERROR;
- }
- }
-
- if (has_alpha != NULL) {
- // If the data did not contain a VP8X/VP8L chunk the only definitive way
- // to set this is by looking for alpha data (from an ALPH chunk).
- *has_alpha |= (hdrs.alpha_data != NULL);
- }
- if (headers != NULL) {
- *headers = hdrs;
- headers->offset = data - headers->data;
- SB_DCHECK((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
- SB_DCHECK(headers->offset == headers->data_size - data_size);
- }
- return VP8_STATUS_OK; // Return features from VP8 header.
-}
-
-VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
- VP8StatusCode status;
- int has_animation = 0;
- SB_DCHECK(headers != NULL);
- // fill out headers, ignore width/height/has_alpha.
- status = ParseHeadersInternal(headers->data, headers->data_size,
- NULL, NULL, NULL, &has_animation, headers);
- if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
- // TODO(jzern): full support of animation frames will require API additions.
- if (has_animation) {
- status = VP8_STATUS_UNSUPPORTED_FEATURE;
- }
- }
- return status;
-}
-
-//------------------------------------------------------------------------------
-// WebPDecParams
-
-void WebPResetDecParams(WebPDecParams* const params) {
- if (params) {
- SbMemorySet(params, 0, sizeof(*params));
- }
-}
-
-//------------------------------------------------------------------------------
-// "Into" decoding variants
-
-// Main flow
-static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
- WebPDecParams* const params) {
- VP8StatusCode status;
- VP8Io io;
- WebPHeaderStructure headers;
-
- headers.data = data;
- headers.data_size = data_size;
- status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
- if (status != VP8_STATUS_OK) {
- return status;
- }
-
- SB_DCHECK(params != NULL);
- VP8InitIo(&io);
- io.data = headers.data + headers.offset;
- io.data_size = headers.data_size - headers.offset;
- WebPInitCustomIo(params, &io); // Plug the I/O functions.
-
- if (!headers.is_lossless) {
- VP8Decoder* const dec = VP8New();
- if (dec == NULL) {
- return VP8_STATUS_OUT_OF_MEMORY;
- }
-#ifdef WEBP_USE_THREAD
- dec->use_threads_ = params->options && (params->options->use_threads > 0);
-#else
- dec->use_threads_ = 0;
-#endif
- dec->alpha_data_ = headers.alpha_data;
- dec->alpha_data_size_ = headers.alpha_data_size;
-
- // Decode bitstream header, update io->width/io->height.
- if (!VP8GetHeaders(dec, &io)) {
- status = dec->status_; // An error occurred. Grab error status.
- } else {
- // Allocate/check output buffers.
- status = WebPAllocateDecBuffer(io.width, io.height, params->options,
- params->output);
- if (status == VP8_STATUS_OK) { // Decode
- if (!VP8Decode(dec, &io)) {
- status = dec->status_;
- }
- }
- }
- VP8Delete(dec);
- } else {
- VP8LDecoder* const dec = VP8LNew();
- if (dec == NULL) {
- return VP8_STATUS_OUT_OF_MEMORY;
- }
- if (!VP8LDecodeHeader(dec, &io)) {
- status = dec->status_; // An error occurred. Grab error status.
- } else {
- // Allocate/check output buffers.
- status = WebPAllocateDecBuffer(io.width, io.height, params->options,
- params->output);
- if (status == VP8_STATUS_OK) { // Decode
- if (!VP8LDecodeImage(dec)) {
- status = dec->status_;
- }
- }
- }
- VP8LDelete(dec);
- }
-
- if (status != VP8_STATUS_OK) {
- WebPFreeDecBuffer(params->output);
- }
- return status;
-}
-
-// Helpers
-static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
- const uint8_t* const data,
- size_t data_size,
- uint8_t* const rgba,
- int stride, size_t size) {
- WebPDecParams params;
- WebPDecBuffer buf;
- if (rgba == NULL) {
- return NULL;
- }
- WebPInitDecBuffer(&buf);
- WebPResetDecParams(¶ms);
- params.output = &buf;
- buf.colorspace = colorspace;
- buf.u.RGBA.rgba = rgba;
- buf.u.RGBA.stride = stride;
- buf.u.RGBA.size = size;
- buf.is_external_memory = 1;
- if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
- return NULL;
- }
- return rgba;
-}
-
-uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
- uint8_t* output, size_t size, int stride) {
- return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
-}
-
-uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
- uint8_t* output, size_t size, int stride) {
- return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
-}
-
-uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
- uint8_t* output, size_t size, int stride) {
- return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
-}
-
-uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
- uint8_t* output, size_t size, int stride) {
- return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
-}
-
-uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
- uint8_t* output, size_t size, int stride) {
- return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
-}
-
-uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
- uint8_t* luma, size_t luma_size, int luma_stride,
- uint8_t* u, size_t u_size, int u_stride,
- uint8_t* v, size_t v_size, int v_stride) {
- WebPDecParams params;
- WebPDecBuffer output;
- if (luma == NULL) return NULL;
- WebPInitDecBuffer(&output);
- WebPResetDecParams(¶ms);
- params.output = &output;
- output.colorspace = MODE_YUV;
- output.u.YUVA.y = luma;
- output.u.YUVA.y_stride = luma_stride;
- output.u.YUVA.y_size = luma_size;
- output.u.YUVA.u = u;
- output.u.YUVA.u_stride = u_stride;
- output.u.YUVA.u_size = u_size;
- output.u.YUVA.v = v;
- output.u.YUVA.v_stride = v_stride;
- output.u.YUVA.v_size = v_size;
- output.is_external_memory = 1;
- if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
- return NULL;
- }
- return luma;
-}
-
-//------------------------------------------------------------------------------
-
-static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
- size_t data_size, int* const width, int* const height,
- WebPDecBuffer* const keep_info) {
- WebPDecParams params;
- WebPDecBuffer output;
-
- WebPInitDecBuffer(&output);
- WebPResetDecParams(¶ms);
- params.output = &output;
- output.colorspace = mode;
-
- // Retrieve (and report back) the required dimensions from bitstream.
- if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
- return NULL;
- }
- if (width != NULL) *width = output.width;
- if (height != NULL) *height = output.height;
-
- // Decode
- if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
- return NULL;
- }
- if (keep_info != NULL) { // keep track of the side-info
- WebPCopyDecBuffer(&output, keep_info);
- }
- // return decoded samples (don't clear 'output'!)
- return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
-}
-
-uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
- int* width, int* height) {
- return Decode(MODE_RGB, data, data_size, width, height, NULL);
-}
-
-uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
- int* width, int* height) {
- return Decode(MODE_RGBA, data, data_size, width, height, NULL);
-}
-
-uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
- int* width, int* height) {
- return Decode(MODE_ARGB, data, data_size, width, height, NULL);
-}
-
-uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
- int* width, int* height) {
- return Decode(MODE_BGR, data, data_size, width, height, NULL);
-}
-
-uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
- int* width, int* height) {
- return Decode(MODE_BGRA, data, data_size, width, height, NULL);
-}
-
-uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
- int* width, int* height, uint8_t** u, uint8_t** v,
- int* stride, int* uv_stride) {
- WebPDecBuffer output; // only to preserve the side-infos
- uint8_t* const out = Decode(MODE_YUV, data, data_size,
- width, height, &output);
-
- if (out != NULL) {
- const WebPYUVABuffer* const buf = &output.u.YUVA;
- *u = buf->u;
- *v = buf->v;
- *stride = buf->y_stride;
- *uv_stride = buf->u_stride;
- SB_DCHECK(buf->u_stride == buf->v_stride);
- }
- return out;
-}
-
-static void DefaultFeatures(WebPBitstreamFeatures* const features) {
- SB_DCHECK(features != NULL);
- SbMemorySet(features, 0, sizeof(*features));
- features->bitstream_version = 0;
-}
-
-static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
- WebPBitstreamFeatures* const features) {
- if (features == NULL || data == NULL) {
- return VP8_STATUS_INVALID_PARAM;
- }
- DefaultFeatures(features);
-
- // Only parse enough of the data to retrieve the features.
- return ParseHeadersInternal(data, data_size,
- &features->width, &features->height,
- &features->has_alpha, &features->has_animation,
- NULL);
-}
-
-//------------------------------------------------------------------------------
-// WebPGetInfo()
-
-int WebPGetInfo(const uint8_t* data, size_t data_size,
- int* width, int* height) {
- WebPBitstreamFeatures features;
-
- if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) {
- return 0;
- }
-
- if (width != NULL) {
- *width = features.width;
- }
- if (height != NULL) {
- *height = features.height;
- }
-
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Advance decoding API
-
-int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
- int version) {
- if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
- return 0; // version mismatch
- }
- if (config == NULL) {
- return 0;
- }
- SbMemorySet(config, 0, sizeof(*config));
- DefaultFeatures(&config->input);
- WebPInitDecBuffer(&config->output);
- return 1;
-}
-
-VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
- WebPBitstreamFeatures* features,
- int version) {
- if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
- return VP8_STATUS_INVALID_PARAM; // version mismatch
- }
- if (features == NULL) {
- return VP8_STATUS_INVALID_PARAM;
- }
- return GetFeatures(data, data_size, features);
-}
-
-VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
- WebPDecoderConfig* config) {
- WebPDecParams params;
- VP8StatusCode status;
-
- if (config == NULL) {
- return VP8_STATUS_INVALID_PARAM;
- }
-
- status = GetFeatures(data, data_size, &config->input);
- if (status != VP8_STATUS_OK) {
- if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
- return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error.
- }
- return status;
- }
-
- WebPResetDecParams(¶ms);
- params.output = &config->output;
- params.options = &config->options;
- status = DecodeInto(data, data_size, ¶ms);
-
- return status;
-}
-
-//------------------------------------------------------------------------------
-// Cropping and rescaling.
-
-int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
- VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
- const int W = io->width;
- const int H = io->height;
- int x = 0, y = 0, w = W, h = H;
-
- // Cropping
- io->use_cropping = (options != NULL) && (options->use_cropping > 0);
- if (io->use_cropping) {
- w = options->crop_width;
- h = options->crop_height;
- x = options->crop_left;
- y = options->crop_top;
- if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422
- x &= ~1;
- y &= ~1; // TODO(later): only for YUV420, not YUV422.
- }
- if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
- return 0; // out of frame boundary error
- }
- }
- io->crop_left = x;
- io->crop_top = y;
- io->crop_right = x + w;
- io->crop_bottom = y + h;
- io->mb_w = w;
- io->mb_h = h;
-
- // Scaling
- io->use_scaling = (options != NULL) && (options->use_scaling > 0);
- if (io->use_scaling) {
- if (options->scaled_width <= 0 || options->scaled_height <= 0) {
- return 0;
- }
- io->scaled_width = options->scaled_width;
- io->scaled_height = options->scaled_height;
- }
-
- // Filter
- io->bypass_filtering = options && options->bypass_filtering;
-
- // Fancy upsampler
-#ifdef FANCY_UPSAMPLING
- io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
-#endif
-
- if (io->use_scaling) {
- // disable filter (only for large downscaling ratio).
- io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
- (io->scaled_height < H * 3 / 4);
- io->fancy_upsampling = 0;
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dec/webpi.h b/src/third_party/libwebp/dec/webpi.h
deleted file mode 100644
index 4ae0bfc..0000000
--- a/src/third_party/libwebp/dec/webpi.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Internal header: WebP decoding parameters and custom IO on buffer
-//
-// Author: somnath@google.com (Somnath Banerjee)
-
-#ifndef WEBP_DEC_WEBPI_H_
-#define WEBP_DEC_WEBPI_H_
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#include "../utils/rescaler.h"
-#include "./decode_vp8.h"
-
-//------------------------------------------------------------------------------
-// WebPDecParams: Decoding output parameters. Transient internal object.
-
-typedef struct WebPDecParams WebPDecParams;
-typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
-typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos);
-
-struct WebPDecParams {
- WebPDecBuffer* output; // output buffer.
- uint8_t* tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler
- // or used for tmp rescaling
-
- int last_y; // coordinate of the line that was last output
- const WebPDecoderOptions* options; // if not NULL, use alt decoding features
- // rescalers
- WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a;
- void* memory; // overall scratch memory for the output work.
-
- OutputFunc emit; // output RGB or YUV samples
- OutputFunc emit_alpha; // output alpha channel
- OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
-};
-
-// Should be called first, before any use of the WebPDecParams object.
-void WebPResetDecParams(WebPDecParams* const params);
-
-//------------------------------------------------------------------------------
-// Header parsing helpers
-
-// Structure storing a description of the RIFF headers.
-typedef struct {
- const uint8_t* data; // input buffer
- size_t data_size; // input buffer size
- size_t offset; // offset to main data chunk (VP8 or VP8L)
- const uint8_t* alpha_data; // points to alpha chunk (if present)
- size_t alpha_data_size; // alpha chunk size
- size_t compressed_size; // VP8/VP8L compressed data size
- size_t riff_size; // size of the riff payload (or 0 if absent)
- int is_lossless; // true if a VP8L chunk is present
-} WebPHeaderStructure;
-
-// Skips over all valid chunks prior to the first VP8/VP8L frame header.
-// Returns: VP8_STATUS_OK, VP8_STATUS_BITSTREAM_ERROR (invalid header/chunk),
-// VP8_STATUS_NOT_ENOUGH_DATA (partial input) or VP8_STATUS_UNSUPPORTED_FEATURE
-// in the case of non-decodable features (animation for instance).
-// In 'headers', compressed_size, offset, alpha_data, alpha_size, and lossless
-// fields are updated appropriately upon success.
-VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers);
-
-//------------------------------------------------------------------------------
-// Misc utils
-
-// Initializes VP8Io with custom setup, io and teardown functions. The default
-// hooks will use the supplied 'params' as io->opaque handle.
-void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io);
-
-// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers
-// to the *compressed* format, not the output one.
-int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
- VP8Io* const io, WEBP_CSP_MODE src_colorspace);
-
-//------------------------------------------------------------------------------
-// Internal functions regarding WebPDecBuffer memory (in buffer.c).
-// Don't really need to be externally visible for now.
-
-// Prepare 'buffer' with the requested initial dimensions width/height.
-// If no external storage is supplied, initializes buffer by allocating output
-// memory and setting up the stride information. Validate the parameters. Return
-// an error code in case of problem (no memory, or invalid stride / size /
-// dimension / etc.). If *options is not NULL, also verify that the options'
-// parameters are valid and apply them to the width/height dimensions of the
-// output buffer. This takes cropping / scaling / rotation into account.
-VP8StatusCode WebPAllocateDecBuffer(int width, int height,
- const WebPDecoderOptions* const options,
- WebPDecBuffer* const buffer);
-
-// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the
-// memory (still held by 'src').
-void WebPCopyDecBuffer(const WebPDecBuffer* const src,
- WebPDecBuffer* const dst);
-
-// Copy and transfer ownership from src to dst (beware of parameter order!)
-void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
-
-
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_DEC_WEBPI_H_ */
diff --git a/src/third_party/libwebp/demux/demux.c b/src/third_party/libwebp/demux/demux.c
deleted file mode 100644
index 8bca6c6..0000000
--- a/src/third_party/libwebp/demux/demux.c
+++ /dev/null
@@ -1,959 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// WebP container demux.
-//
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#endif
-
-#include "../utils/utils.h"
-#include "../webp/decode.h" // WebPGetFeatures
-#include "../webp/demux.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define DMUX_MAJ_VERSION 0
-#define DMUX_MIN_VERSION 1
-#define DMUX_REV_VERSION 1
-
-typedef struct {
- size_t start_; // start location of the data
- size_t end_; // end location
- size_t riff_end_; // riff chunk end location, can be > end_.
- size_t buf_size_; // size of the buffer
- const uint8_t* buf_;
-} MemBuffer;
-
-typedef struct {
- size_t offset_;
- size_t size_;
-} ChunkData;
-
-typedef struct Frame {
- int x_offset_, y_offset_;
- int width_, height_;
- int duration_;
- WebPMuxAnimDispose dispose_method_;
- int is_fragment_; // this is a frame fragment (and not a full frame).
- int frame_num_; // the referent frame number for use in assembling fragments.
- int complete_; // img_components_ contains a full image.
- ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
- struct Frame* next_;
-} Frame;
-
-typedef struct Chunk {
- ChunkData data_;
- struct Chunk* next_;
-} Chunk;
-
-struct WebPDemuxer {
- MemBuffer mem_;
- WebPDemuxState state_;
- int is_ext_format_;
- uint32_t feature_flags_;
- int canvas_width_, canvas_height_;
- int loop_count_;
- uint32_t bgcolor_;
- int num_frames_;
- Frame* frames_;
- Frame** frames_tail_;
- Chunk* chunks_; // non-image chunks
-};
-
-typedef enum {
- PARSE_OK,
- PARSE_NEED_MORE_DATA,
- PARSE_ERROR
-} ParseStatus;
-
-typedef struct ChunkParser {
- uint8_t id[4];
- ParseStatus (*parse)(WebPDemuxer* const dmux);
- int (*valid)(const WebPDemuxer* const dmux);
-} ChunkParser;
-
-static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
-static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
-static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
-static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
-
-static const ChunkParser kMasterChunks[] = {
- { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
- { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
- { { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat },
- { { '0', '0', '0', '0' }, NULL, NULL },
-};
-
-//------------------------------------------------------------------------------
-
-int WebPGetDemuxVersion(void) {
- return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
-}
-
-// -----------------------------------------------------------------------------
-// MemBuffer
-
-static int RemapMemBuffer(MemBuffer* const mem,
- const uint8_t* data, size_t size) {
- if (size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
-
- mem->buf_ = data;
- mem->end_ = mem->buf_size_ = size;
- return 1;
-}
-
-static int InitMemBuffer(MemBuffer* const mem,
- const uint8_t* data, size_t size) {
- SbMemorySet(mem, 0, sizeof(*mem));
- return RemapMemBuffer(mem, data, size);
-}
-
-// Return the remaining data size available in 'mem'.
-static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
- return (mem->end_ - mem->start_);
-}
-
-// Return true if 'size' exceeds the end of the RIFF chunk.
-static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
- return (size > mem->riff_end_ - mem->start_);
-}
-
-static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
- mem->start_ += size;
-}
-
-static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
- mem->start_ -= size;
-}
-
-static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
- return mem->buf_ + mem->start_;
-}
-
-// Read from 'mem' and skip the read bytes.
-static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
- const uint8_t byte = mem->buf_[mem->start_];
- Skip(mem, 1);
- return byte;
-}
-
-static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
- const uint8_t* const data = mem->buf_ + mem->start_;
- const int val = GetLE16(data);
- Skip(mem, 2);
- return val;
-}
-
-static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
- const uint8_t* const data = mem->buf_ + mem->start_;
- const int val = GetLE24(data);
- Skip(mem, 3);
- return val;
-}
-
-static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
- const uint8_t* const data = mem->buf_ + mem->start_;
- const uint32_t val = GetLE32(data);
- Skip(mem, 4);
- return val;
-}
-
-// -----------------------------------------------------------------------------
-// Secondary chunk parsing
-
-static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
- Chunk** c = &dmux->chunks_;
- while (*c != NULL) c = &(*c)->next_;
- *c = chunk;
- chunk->next_ = NULL;
-}
-
-// Add a frame to the end of the list, ensuring the last frame is complete.
-// Returns true on success, false otherwise.
-static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
- const Frame* const last_frame = *dmux->frames_tail_;
- if (last_frame != NULL && !last_frame->complete_) return 0;
-
- *dmux->frames_tail_ = frame;
- frame->next_ = NULL;
- dmux->frames_tail_ = &frame->next_;
- return 1;
-}
-
-// Store image bearing chunks to 'frame'.
-// If 'has_vp8l_alpha' is not NULL, it will be set to true if the frame is a
-// lossless image with alpha.
-static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
- MemBuffer* const mem, Frame* const frame,
- int* const has_vp8l_alpha) {
- int alpha_chunks = 0;
- int image_chunks = 0;
- int done = (MemDataSize(mem) < min_size);
- ParseStatus status = PARSE_OK;
-
- if (has_vp8l_alpha != NULL) *has_vp8l_alpha = 0; // Default.
-
- if (done) return PARSE_NEED_MORE_DATA;
-
- do {
- const size_t chunk_start_offset = mem->start_;
- const uint32_t fourcc = ReadLE32(mem);
- const uint32_t payload_size = ReadLE32(mem);
- const uint32_t payload_size_padded = payload_size + (payload_size & 1);
- const size_t payload_available = (payload_size_padded > MemDataSize(mem))
- ? MemDataSize(mem) : payload_size_padded;
- const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
-
- if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
- if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
- if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
-
- switch (fourcc) {
- case MKFOURCC('A', 'L', 'P', 'H'):
- if (alpha_chunks == 0) {
- ++alpha_chunks;
- frame->img_components_[1].offset_ = chunk_start_offset;
- frame->img_components_[1].size_ = chunk_size;
- frame->frame_num_ = frame_num;
- Skip(mem, payload_available);
- } else {
- goto Done;
- }
- break;
- case MKFOURCC('V', 'P', '8', 'L'):
- if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha
- // fall through
- case MKFOURCC('V', 'P', '8', ' '):
- if (image_chunks == 0) {
- // Extract the bitstream features, tolerating failures when the data
- // is incomplete.
- WebPBitstreamFeatures features;
- const VP8StatusCode vp8_status =
- WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
- &features);
- if (status == PARSE_NEED_MORE_DATA &&
- vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
- return PARSE_NEED_MORE_DATA;
- } else if (vp8_status != VP8_STATUS_OK) {
- // We have enough data, and yet WebPGetFeatures() failed.
- return PARSE_ERROR;
- }
- ++image_chunks;
- frame->img_components_[0].offset_ = chunk_start_offset;
- frame->img_components_[0].size_ = chunk_size;
- frame->width_ = features.width;
- frame->height_ = features.height;
- if (has_vp8l_alpha != NULL) *has_vp8l_alpha = features.has_alpha;
- frame->frame_num_ = frame_num;
- frame->complete_ = (status == PARSE_OK);
- Skip(mem, payload_available);
- } else {
- goto Done;
- }
- break;
- Done:
- default:
- // Restore fourcc/size when moving up one level in parsing.
- Rewind(mem, CHUNK_HEADER_SIZE);
- done = 1;
- break;
- }
-
- if (mem->start_ == mem->riff_end_) {
- done = 1;
- } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
- status = PARSE_NEED_MORE_DATA;
- }
- } while (!done && status == PARSE_OK);
-
- return status;
-}
-
-// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
-// enough data ('min_size') to parse the payload.
-// Returns PARSE_OK on success with *frame pointing to the new Frame.
-// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
-static ParseStatus NewFrame(const MemBuffer* const mem,
- uint32_t min_size, uint32_t actual_size,
- Frame** frame) {
- if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
- if (actual_size < min_size) return PARSE_ERROR;
- if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
-
- *frame = (Frame*)SbMemoryCalloc(1, sizeof(**frame));
- return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
-}
-
-// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
-// 'frame_chunk_size' is the previously validated, padded chunk size.
-static ParseStatus ParseAnimationFrame(
- WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
- const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
- const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
- int added_frame = 0;
- MemBuffer* const mem = &dmux->mem_;
- Frame* frame;
- ParseStatus status =
- NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
- if (status != PARSE_OK) return status;
-
- frame->x_offset_ = 2 * ReadLE24s(mem);
- frame->y_offset_ = 2 * ReadLE24s(mem);
- frame->width_ = 1 + ReadLE24s(mem);
- frame->height_ = 1 + ReadLE24s(mem);
- frame->duration_ = ReadLE24s(mem);
- frame->dispose_method_ = (WebPMuxAnimDispose)(ReadByte(mem) & 1);
- if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
- SbMemoryDeallocate(frame);
- return PARSE_ERROR;
- }
-
- // Store a frame only if the animation flag is set there is some data for
- // this frame is available.
- status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame,
- NULL);
- if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) {
- added_frame = AddFrame(dmux, frame);
- if (added_frame) {
- ++dmux->num_frames_;
- } else {
- status = PARSE_ERROR;
- }
- }
-
- if (!added_frame) SbMemoryDeallocate(frame);
- return status;
-}
-
-#ifdef WEBP_EXPERIMENTAL_FEATURES
-// Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
-// 'fragment_chunk_size' is the previously validated, padded chunk size.
-static ParseStatus ParseFragment(WebPDemuxer* const dmux,
- uint32_t fragment_chunk_size) {
- const int frame_num = 1; // All fragments belong to the 1st (and only) frame.
- const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
- const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE;
- int added_fragment = 0;
- MemBuffer* const mem = &dmux->mem_;
- Frame* frame;
- ParseStatus status =
- NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
- if (status != PARSE_OK) return status;
-
- frame->is_fragment_ = 1;
- frame->x_offset_ = 2 * ReadLE24s(mem);
- frame->y_offset_ = 2 * ReadLE24s(mem);
-
- // Store a fragment only if the fragments flag is set there is some data for
- // this fragment is available.
- status = StoreFrame(frame_num, frgm_payload_size, mem, frame, NULL);
- if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) {
- added_fragment = AddFrame(dmux, frame);
- if (!added_fragment) {
- status = PARSE_ERROR;
- } else {
- dmux->num_frames_ = 1;
- }
- }
-
- if (!added_fragment) SbMemoryDeallocate(frame);
- return status;
-}
-#endif // WEBP_EXPERIMENTAL_FEATURES
-
-// General chunk storage, starting with the header at 'start_offset', allowing
-// the user to request the payload via a fourcc string. 'size' includes the
-// header and the unpadded payload size.
-// Returns true on success, false otherwise.
-static int StoreChunk(WebPDemuxer* const dmux,
- size_t start_offset, uint32_t size) {
- Chunk* const chunk = (Chunk*)SbMemoryCalloc(1, sizeof(*chunk));
- if (chunk == NULL) return 0;
-
- chunk->data_.offset_ = start_offset;
- chunk->data_.size_ = size;
- AddChunk(dmux, chunk);
- return 1;
-}
-
-// -----------------------------------------------------------------------------
-// Primary chunk parsing
-
-static int ReadHeader(MemBuffer* const mem) {
- const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
- uint32_t riff_size;
-
- // Basic file level validation.
- if (MemDataSize(mem) < min_size) return 0;
- if (SbMemoryCompare(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
- SbMemoryCompare(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
- return 0;
- }
-
- riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
- if (riff_size < CHUNK_HEADER_SIZE) return 0;
- if (riff_size > MAX_CHUNK_PAYLOAD) return 0;
-
- // There's no point in reading past the end of the RIFF chunk
- mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
- if (mem->buf_size_ > mem->riff_end_) {
- mem->buf_size_ = mem->end_ = mem->riff_end_;
- }
-
- Skip(mem, RIFF_HEADER_SIZE);
- return 1;
-}
-
-static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
- const size_t min_size = CHUNK_HEADER_SIZE;
- MemBuffer* const mem = &dmux->mem_;
- Frame* frame;
- ParseStatus status;
- int has_vp8l_alpha = 0; // Frame contains a lossless image with alpha.
-
- if (dmux->frames_ != NULL) return PARSE_ERROR;
- if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
- if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
-
- frame = (Frame*)SbMemoryCalloc(1, sizeof(*frame));
- if (frame == NULL) return PARSE_ERROR;
-
- // For the single image case we allow parsing of a partial frame, but we need
- // at least CHUNK_HEADER_SIZE for parsing.
- status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame,
- &has_vp8l_alpha);
- if (status != PARSE_ERROR) {
- const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
- // Clear any alpha when the alpha flag is missing.
- if (!has_alpha && frame->img_components_[1].size_ > 0) {
- frame->img_components_[1].offset_ = 0;
- frame->img_components_[1].size_ = 0;
- }
-
- // Use the frame width/height as the canvas values for non-vp8x files.
- // Also, set ALPHA_FLAG if this is a lossless image with alpha.
- if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
- dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
- dmux->canvas_width_ = frame->width_;
- dmux->canvas_height_ = frame->height_;
- dmux->feature_flags_ |= has_vp8l_alpha ? ALPHA_FLAG : 0;
- }
- AddFrame(dmux, frame);
- dmux->num_frames_ = 1;
- } else {
- SbMemoryDeallocate(frame);
- }
-
- return status;
-}
-
-static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
- MemBuffer* const mem = &dmux->mem_;
- int anim_chunks = 0;
- uint32_t vp8x_size;
- ParseStatus status = PARSE_OK;
-
- if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
-
- dmux->is_ext_format_ = 1;
- Skip(mem, TAG_SIZE); // VP8X
- vp8x_size = ReadLE32(mem);
- if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
- if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
- vp8x_size += vp8x_size & 1;
- if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
- if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
-
- dmux->feature_flags_ = ReadByte(mem);
- Skip(mem, 3); // Reserved.
- dmux->canvas_width_ = 1 + ReadLE24s(mem);
- dmux->canvas_height_ = 1 + ReadLE24s(mem);
- if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
- return PARSE_ERROR; // image final dimension is too large
- }
- Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
- dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
-
- if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
- if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
-
- do {
- int store_chunk = 1;
- const size_t chunk_start_offset = mem->start_;
- const uint32_t fourcc = ReadLE32(mem);
- const uint32_t chunk_size = ReadLE32(mem);
- const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
-
- if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
- if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
-
- switch (fourcc) {
- case MKFOURCC('V', 'P', '8', 'X'): {
- return PARSE_ERROR;
- }
- case MKFOURCC('A', 'L', 'P', 'H'):
- case MKFOURCC('V', 'P', '8', ' '):
- case MKFOURCC('V', 'P', '8', 'L'): {
- // check that this isn't an animation (all frames should be in an ANMF).
- if (anim_chunks > 0) return PARSE_ERROR;
-
- Rewind(mem, CHUNK_HEADER_SIZE);
- status = ParseSingleImage(dmux);
- break;
- }
- case MKFOURCC('A', 'N', 'I', 'M'): {
- if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
-
- if (MemDataSize(mem) < chunk_size_padded) {
- status = PARSE_NEED_MORE_DATA;
- } else if (anim_chunks == 0) {
- ++anim_chunks;
- dmux->bgcolor_ = ReadLE32(mem);
- dmux->loop_count_ = ReadLE16s(mem);
- Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
- } else {
- store_chunk = 0;
- goto Skip;
- }
- break;
- }
- case MKFOURCC('A', 'N', 'M', 'F'): {
- if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames.
- status = ParseAnimationFrame(dmux, chunk_size_padded);
- break;
- }
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- case MKFOURCC('F', 'R', 'G', 'M'): {
- status = ParseFragment(dmux, chunk_size_padded);
- break;
- }
-#endif
- case MKFOURCC('I', 'C', 'C', 'P'): {
- store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
- goto Skip;
- }
- case MKFOURCC('X', 'M', 'P', ' '): {
- store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
- goto Skip;
- }
- case MKFOURCC('E', 'X', 'I', 'F'): {
- store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
- goto Skip;
- }
- Skip:
- default: {
- if (chunk_size_padded <= MemDataSize(mem)) {
- if (store_chunk) {
- // Store only the chunk header and unpadded size as only the payload
- // will be returned to the user.
- if (!StoreChunk(dmux, chunk_start_offset,
- CHUNK_HEADER_SIZE + chunk_size)) {
- return PARSE_ERROR;
- }
- }
- Skip(mem, chunk_size_padded);
- } else {
- status = PARSE_NEED_MORE_DATA;
- }
- }
- }
-
- if (mem->start_ == mem->riff_end_) {
- break;
- } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
- status = PARSE_NEED_MORE_DATA;
- }
- } while (status == PARSE_OK);
-
- return status;
-}
-
-// -----------------------------------------------------------------------------
-// Format validation
-
-static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
- const Frame* const frame = dmux->frames_;
- if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
-
- if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
- if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
-
- if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
- return 1;
-}
-
-static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
- const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
- const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
- const Frame* f;
-
- if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
-
- if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
- if (dmux->loop_count_ < 0) return 0;
- if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
-
- for (f = dmux->frames_; f != NULL; f = f->next_) {
- const int cur_frame_set = f->frame_num_;
- int frame_count = 0, fragment_count = 0;
-
- // Check frame properties and if the image is composed of fragments that
- // each fragment came from a fragment.
- for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
- const ChunkData* const image = f->img_components_;
- const ChunkData* const alpha = f->img_components_ + 1;
-
- if (!has_fragments && f->is_fragment_) return 0;
- if (!has_frames && f->frame_num_ > 1) return 0;
- if (f->x_offset_ < 0 || f->y_offset_ < 0) return 0;
- if (f->complete_) {
- if (alpha->size_ == 0 && image->size_ == 0) return 0;
- // Ensure alpha precedes image bitstream.
- if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
- return 0;
- }
-
- if (f->width_ <= 0 || f->height_ <= 0) return 0;
- } else {
- // There shouldn't be a partial frame in a complete file.
- if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
-
- // Ensure alpha precedes image bitstream.
- if (alpha->size_ > 0 && image->size_ > 0 &&
- alpha->offset_ > image->offset_) {
- return 0;
- }
- // There shouldn't be any frames after an incomplete one.
- if (f->next_ != NULL) return 0;
- }
-
- fragment_count += f->is_fragment_;
- ++frame_count;
- }
- if (!has_fragments && frame_count > 1) return 0;
- if (fragment_count > 0 && frame_count != fragment_count) return 0;
- if (f == NULL) break;
- }
- return 1;
-}
-
-// -----------------------------------------------------------------------------
-// WebPDemuxer object
-
-static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
- dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
- dmux->loop_count_ = 1;
- dmux->bgcolor_ = 0xFFFFFFFF; // White background by default.
- dmux->canvas_width_ = -1;
- dmux->canvas_height_ = -1;
- dmux->frames_tail_ = &dmux->frames_;
- dmux->mem_ = *mem;
-}
-
-WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
- WebPDemuxState* state, int version) {
- const ChunkParser* parser;
- int partial;
- ParseStatus status = PARSE_ERROR;
- MemBuffer mem;
- WebPDemuxer* dmux;
-
- if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
- if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
-
- if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
- if (!ReadHeader(&mem)) return NULL;
-
- partial = (mem.buf_size_ < mem.riff_end_);
- if (!allow_partial && partial) return NULL;
-
- dmux = (WebPDemuxer*)SbMemoryCalloc(1, sizeof(*dmux));
- if (dmux == NULL) return NULL;
- InitDemux(dmux, &mem);
-
- for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
- if (!SbMemoryCompare(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
- status = parser->parse(dmux);
- if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
- if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
- if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
- break;
- }
- }
- if (state) *state = dmux->state_;
-
- if (status == PARSE_ERROR) {
- WebPDemuxDelete(dmux);
- return NULL;
- }
- return dmux;
-}
-
-void WebPDemuxDelete(WebPDemuxer* dmux) {
- Chunk* c;
- Frame* f;
- if (dmux == NULL) return;
-
- for (f = dmux->frames_; f != NULL;) {
- Frame* const cur_frame = f;
- f = f->next_;
- SbMemoryDeallocate(cur_frame);
- }
- for (c = dmux->chunks_; c != NULL;) {
- Chunk* const cur_chunk = c;
- c = c->next_;
- SbMemoryDeallocate(cur_chunk);
- }
- SbMemoryDeallocate(dmux);
-}
-
-// -----------------------------------------------------------------------------
-
-uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
- if (dmux == NULL) return 0;
-
- switch (feature) {
- case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags_;
- case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_;
- case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_;
- case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_;
- case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
- case WEBP_FF_FRAME_COUNT: return (uint32_t)dmux->num_frames_;
- }
- return 0;
-}
-
-// -----------------------------------------------------------------------------
-// Frame iteration
-
-// Find the first 'frame_num' frame. There may be multiple such frames in a
-// fragmented frame.
-static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
- const Frame* f;
- for (f = dmux->frames_; f != NULL; f = f->next_) {
- if (frame_num == f->frame_num_) break;
- }
- return f;
-}
-
-// Returns fragment 'fragment_num' and the total count.
-static const Frame* GetFragment(
- const Frame* const frame_set, int fragment_num, int* const count) {
- const int this_frame = frame_set->frame_num_;
- const Frame* f = frame_set;
- const Frame* fragment = NULL;
- int total;
-
- for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
- if (++total == fragment_num) fragment = f;
- }
- *count = total;
- return fragment;
-}
-
-static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
- const Frame* const frame,
- size_t* const data_size) {
- *data_size = 0;
- if (frame != NULL) {
- const ChunkData* const image = frame->img_components_;
- const ChunkData* const alpha = frame->img_components_ + 1;
- size_t start_offset = image->offset_;
- *data_size = image->size_;
-
- // if alpha exists it precedes image, update the size allowing for
- // intervening chunks.
- if (alpha->size_ > 0) {
- const size_t inter_size = (image->offset_ > 0)
- ? image->offset_ - (alpha->offset_ + alpha->size_)
- : 0;
- start_offset = alpha->offset_;
- *data_size += alpha->size_ + inter_size;
- }
- return mem_buf + start_offset;
- }
- return NULL;
-}
-
-// Create a whole 'frame' from VP8 (+ alpha) or lossless.
-static int SynthesizeFrame(const WebPDemuxer* const dmux,
- const Frame* const first_frame,
- int fragment_num, WebPIterator* const iter) {
- const uint8_t* const mem_buf = dmux->mem_.buf_;
- int num_fragments;
- size_t payload_size = 0;
- const Frame* const fragment =
- GetFragment(first_frame, fragment_num, &num_fragments);
- const uint8_t* const payload =
- GetFramePayload(mem_buf, fragment, &payload_size);
- if (payload == NULL) return 0;
- SB_DCHECK(first_frame != NULL);
-
- iter->frame_num = first_frame->frame_num_;
- iter->num_frames = dmux->num_frames_;
- iter->fragment_num = fragment_num;
- iter->num_fragments = num_fragments;
- iter->x_offset = fragment->x_offset_;
- iter->y_offset = fragment->y_offset_;
- iter->width = fragment->width_;
- iter->height = fragment->height_;
- iter->duration = fragment->duration_;
- iter->dispose_method = fragment->dispose_method_;
- iter->complete = fragment->complete_;
- iter->fragment.bytes = payload;
- iter->fragment.size = payload_size;
- // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
- return 1;
-}
-
-static int SetFrame(int frame_num, WebPIterator* const iter) {
- const Frame* frame;
- const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
- if (dmux == NULL || frame_num < 0) return 0;
- if (frame_num > dmux->num_frames_) return 0;
- if (frame_num == 0) frame_num = dmux->num_frames_;
-
- frame = GetFrame(dmux, frame_num);
- if (frame == NULL) return 0;
-
- return SynthesizeFrame(dmux, frame, 1, iter);
-}
-
-int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
- if (iter == NULL) return 0;
-
- SbMemorySet(iter, 0, sizeof(*iter));
- iter->private_ = (void*)dmux;
- return SetFrame(frame, iter);
-}
-
-int WebPDemuxNextFrame(WebPIterator* iter) {
- if (iter == NULL) return 0;
- return SetFrame(iter->frame_num + 1, iter);
-}
-
-int WebPDemuxPrevFrame(WebPIterator* iter) {
- if (iter == NULL) return 0;
- if (iter->frame_num <= 1) return 0;
- return SetFrame(iter->frame_num - 1, iter);
-}
-
-int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
- if (iter != NULL && iter->private_ != NULL && fragment_num > 0) {
- const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
- const Frame* const frame = GetFrame(dmux, iter->frame_num);
- if (frame == NULL) return 0;
-
- return SynthesizeFrame(dmux, frame, fragment_num, iter);
- }
- return 0;
-}
-
-void WebPDemuxReleaseIterator(WebPIterator* iter) {
- (void)iter;
-}
-
-// -----------------------------------------------------------------------------
-// Chunk iteration
-
-static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
- const uint8_t* const mem_buf = dmux->mem_.buf_;
- const Chunk* c;
- int count = 0;
- for (c = dmux->chunks_; c != NULL; c = c->next_) {
- const uint8_t* const header = mem_buf + c->data_.offset_;
- if (!SbMemoryCompare(header, fourcc, TAG_SIZE)) ++count;
- }
- return count;
-}
-
-static const Chunk* GetChunk(const WebPDemuxer* const dmux,
- const char fourcc[4], int chunk_num) {
- const uint8_t* const mem_buf = dmux->mem_.buf_;
- const Chunk* c;
- int count = 0;
- for (c = dmux->chunks_; c != NULL; c = c->next_) {
- const uint8_t* const header = mem_buf + c->data_.offset_;
- if (!SbMemoryCompare(header, fourcc, TAG_SIZE)) ++count;
- if (count == chunk_num) break;
- }
- return c;
-}
-
-static int SetChunk(const char fourcc[4], int chunk_num,
- WebPChunkIterator* const iter) {
- const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
- int count;
-
- if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
- count = ChunkCount(dmux, fourcc);
- if (count == 0) return 0;
- if (chunk_num == 0) chunk_num = count;
-
- if (chunk_num <= count) {
- const uint8_t* const mem_buf = dmux->mem_.buf_;
- const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
- iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
- iter->chunk.size = chunk->data_.size_ - CHUNK_HEADER_SIZE;
- iter->num_chunks = count;
- iter->chunk_num = chunk_num;
- return 1;
- }
- return 0;
-}
-
-int WebPDemuxGetChunk(const WebPDemuxer* dmux,
- const char fourcc[4], int chunk_num,
- WebPChunkIterator* iter) {
- if (iter == NULL) return 0;
-
- SbMemorySet(iter, 0, sizeof(*iter));
- iter->private_ = (void*)dmux;
- return SetChunk(fourcc, chunk_num, iter);
-}
-
-int WebPDemuxNextChunk(WebPChunkIterator* iter) {
- if (iter != NULL) {
- const char* const fourcc =
- (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
- return SetChunk(fourcc, iter->chunk_num + 1, iter);
- }
- return 0;
-}
-
-int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
- if (iter != NULL && iter->chunk_num > 1) {
- const char* const fourcc =
- (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
- return SetChunk(fourcc, iter->chunk_num - 1, iter);
- }
- return 0;
-}
-
-void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
- (void)iter;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/doc/README b/src/third_party/libwebp/doc/README
new file mode 100644
index 0000000..7f23e0c
--- /dev/null
+++ b/src/third_party/libwebp/doc/README
@@ -0,0 +1,29 @@
+
+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/src/third_party/libwebp/doc/TODO b/src/third_party/libwebp/doc/TODO
new file mode 100644
index 0000000..b0a9382
--- /dev/null
+++ b/src/third_party/libwebp/doc/TODO
@@ -0,0 +1,13 @@
+<louquillio@google.com>, 20111004
+
+* Determine that normative RFC 2119 terms (MUST, SHOULD, MAY, etc.) are
+ truly intended in all cases where capitalized.
+
+* Several passages could be made clearer.
+
+ * Overall edit for scope. Portions are phrased as an introduction to
+ the 0.1.3 RIFF container additions, rather than a holistic guide to
+ WebP.
+
+ * To wit, suggest s/[spec|specification]/guide/g . "Spec" can imply a
+ standards track; in any case it's too formal for a work in progress.
diff --git a/src/third_party/libwebp/doc/template.html b/src/third_party/libwebp/doc/template.html
new file mode 100644
index 0000000..5dbc289
--- /dev/null
+++ b/src/third_party/libwebp/doc/template.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <title>WebP Container Specification</title>
+ <meta name="generator" content="kramdown <%= ::Kramdown::VERSION %>" />
+ <style type="text/css">
+ body {
+ color: #000;
+ background-color: #fff;
+ margin: 10%;
+ font-family: "Liberation Sans", "DejaVu Sans", "Bitstream Vera Sans", Arial, sans-serif;
+ line-height: 1.4;
+ }
+ h2 {
+ border-bottom: 1px solid #ccc;
+ padding-bottom: 0;
+ }
+ table {
+ border-collapse: collapse;
+ }
+ th, td {
+ border: 1px solid #999;
+ padding: .5em .7em;;
+ }
+ th {
+ color: #fff;
+ background-color: #000;
+ }
+ td {
+ }
+ hr {
+ }
+ code {
+ color: #000;
+ background-color: #f7f7f7;
+ padding: 0 3px;
+ font-family: "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Consolata, monospace;
+ }
+ pre {
+ background-color: #f7f7f7;
+ padding: 1em;
+ border: 1px solid #ccc;
+ width: 42em;
+ overflow: auto;
+ font-family: "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Consolata, monospace;
+ }
+ pre code {
+ background-color: #f7f7f7;
+ padding: 0; /* Only want padding on inline code, not blocks */
+ }
+ pre.terminal {
+ color: #fff;
+ background-color: #000;
+ border: 1px solid #ccc;
+ max-height: 30em;
+ }
+ pre.terminal code {
+ color: #fff;
+ background-color: #000;
+ font-size: smaller;
+ }
+ #markdown-toc ul {
+ list-style-type: disc;
+ }
+ ul#markdown-toc {
+ margin-top: -1em;
+ visibility: hidden;
+ -webkit-padding-start: 0;
+ }
+ ul#markdown-toc ul {
+ visibility: visible;
+ }
+ ul#markdown-toc ul ul{
+ visibility: visible;
+ }
+ ul#markdown-toc + hr {
+ margin-bottom: 4em;
+ }
+ ol ol { /* Format nested ordered lists */
+ list-style-type: lower-alpha;
+ }
+ dt {
+ font-style: italic;
+ font-weight: bold;
+ }
+ .caption {
+ }
+ </style>
+</head>
+<body>
+<%= @body %>
+</body>
+</html>
diff --git a/src/third_party/libwebp/doc/webp-container-spec.txt b/src/third_party/libwebp/doc/webp-container-spec.txt
new file mode 100644
index 0000000..94a7ca2
--- /dev/null
+++ b/src/third_party/libwebp/doc/webp-container-spec.txt
@@ -0,0 +1,823 @@
+<!--
+
+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.
+
+See the accompanying README. External link targets are referenced at the
+end of this file.
+
+-->
+
+
+WebP Container Specification
+============================
+
+* TOC placeholder
+{:toc}
+
+
+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.
+
+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:
+
+ * **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.
+
+ * **Transparency.** An image may have transparency, i.e., an alpha channel.
+
+ * **Color Profile.** An image may have an embedded ICC profile as described
+ by the [International Color Consortium][iccspec].
+
+ * **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][].
+
+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
+------------------------
+
+A WebP file contains either a still image (i.e., 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.
+
+Below are additional terms used throughout this document:
+
+_Reader/Writer_
+
+: Code that reads WebP files is referred to as a _reader_, while code that
+ writes them is referred to as a _writer_.
+
+_uint16_
+
+: A 16-bit, little-endian, unsigned integer.
+
+_uint24_
+
+: A 24-bit, little-endian, unsigned integer.
+
+_uint32_
+
+: A 32-bit, little-endian, unsigned integer.
+
+_FourCC_
+
+: A _FourCC_ (four-character code) is a _uint32_ created by concatenating four
+ ASCII characters in little-endian order.
+
+_1-based_
+
+: An unsigned integer field storing values offset by `-1`. e.g., Such a field
+ would store value _25_ as _24_.
+
+
+RIFF 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:
+
+ 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
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Chunk FourCC |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Chunk Size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Chunk Payload |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Chunk FourCC: 32 bits
+
+: ASCII four-character code used for chunk identification.
+
+Chunk Size: 32 bits (_uint32_)
+
+: The size of the chunk 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.
+
+**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
+format are all lowercase. WebP does not follow this convention.
+
+
+WebP File Header
+----------------
+
+ 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
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 'R' | 'I' | 'F' | 'F' |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | File Size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 'W' | 'E' | 'B' | 'P' |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+'RIFF': 32 bits
+
+: The ASCII characters 'R' 'I' 'F' 'F'.
+
+File Size: 32 bits (_uint32_)
+
+: The size of the file in bytes starting at offset 8. The maximum value of
+ this field is 2^32 minus 10 bytes and thus the size of the whole file is at
+ most 4GiB minus 2 bytes.
+
+'WEBP': 32 bits
+
+: The ASCII characters 'W' 'E' 'B' 'P'.
+
+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.
+
+
+Simple File Format (Lossy)
+--------------------------
+
+This layout SHOULD be used if the image requires _lossy_ encoding and does not
+require transparency or other advanced features provided by the extended format.
+Files with this layout are smaller and supported by older software.
+
+Simple WebP (lossy) file format:
+
+ 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:
+
+ 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('VP8 ') |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | VP8 data |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+VP8 data: _Chunk Size_ bytes
+
+: VP8 bitstream data.
+
+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.
+
+
+Simple File Format (Lossless)
+-----------------------------
+
+**Note:** Older readers may not support files using the lossless format.
+
+This layout SHOULD be used if the image requires _lossless_ encoding (with an
+optional transparency channel) and does not require advanced features provided
+by the extended format.
+
+Simple WebP (lossless) file format:
+
+ 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:
+
+ 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('VP8L') |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | VP8L data |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+VP8L data: _Chunk Size_ bytes
+
+: VP8L bitstream data.
+
+The current specification of the VP8L bitstream can be found at
+[WebP Lossless Bitstream Format][webpllspec]. Note that the VP8L header
+contains the VP8L image width and height. That is assumed to be the width
+and height of the canvas.
+
+
+Extended File Format
+--------------------
+
+**Note:** Older readers may not support files using the extended format.
+
+An extended format file consists of:
+
+ * A 'VP8X' chunk with information about features used in the file.
+
+ * An optional 'ICCP' chunk with color profile.
+
+ * An optional 'ANIM' chunk with animation control data.
+
+ * Image data.
+
+ * An optional 'EXIF' chunk with EXIF metadata.
+
+ * An optional 'XMP ' chunk with XMP metadata.
+
+ * An optional list of [unknown chunks](#unknown-chunks). _\[status: experimental\]_
+
+For a _still image_, the _image data_ consists of a single frame, which is made
+up of:
+
+ * An optional [alpha subchunk](#alpha).
+
+ * A [bitstream subchunk](#bitstream-vp8vp8l).
+
+For an _animated image_, the _image data_ consists of multiple frames. More
+details about frames can be found in the [Animation](#animation) section.
+
+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.
+
+**Rationale:** Setting the order of chunks should allow quicker file
+parsing. For example, if an 'ALPH' chunk does not appear in its required
+position, a decoder can choose to stop searching for it. The rule of
+ignoring late chunks should make programs that need to do a full search
+give the same results as the ones stopping early.
+
+Extended WebP file header:
+{:#extended_header}
+
+ 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 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Canvas Width Minus One | ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ... Canvas Height Minus One |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Reserved (Rsv): 2 bits
+
+: SHOULD be `0`.
+
+ICC profile (I): 1 bit
+
+: Set if the file contains an ICC profile.
+
+Alpha (L): 1 bit
+
+: Set if any of the frames of the image contain transparency information
+ ("alpha").
+
+EXIF metadata (E): 1 bit
+
+: Set if the file contains EXIF metadata.
+
+XMP metadata (X): 1 bit
+
+: Set if the file contains XMP metadata.
+
+Animation (A): 1 bit
+
+: Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be
+ used to control the animation.
+
+Reserved (R): 1 bit
+
+: SHOULD be `0`.
+
+Reserved: 24 bits
+
+: SHOULD be `0`.
+
+Canvas Width Minus One: 24 bits
+
+: _1-based_ width of the canvas in pixels.
+ 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 product of _Canvas Width_ and _Canvas Height_ MUST be at most `2^32 - 1`.
+
+Future specifications MAY add more fields.
+
+### Chunks
+
+#### Animation
+
+An animation is controlled by ANIM and ANMF chunks.
+
+ANIM Chunk:
+{:#anim_chunk}
+
+For an animated image, this chunk contains the _global parameters_ of the
+animation.
+
+ 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('ANIM') |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Background Color |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Loop Count |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Background Color: 32 bits (_uint32_)
+
+: The default background color of the canvas in \[Blue, Green, Red, Alpha\]
+ byte order. This color MAY be used to fill the unused space on the canvas
+ around the frames, as well as the transparent pixels of the first frame.
+ Background color is also used when disposal method is `1`.
+
+**Note**:
+
+ * Background color MAY contain a transparency value (alpha), 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.
+
+ * The canvas is cleared at the start of each loop. The background color MAY be
+ used to achieve this.
+
+Loop Count: 16 bits (_uint16_)
+
+: 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.
+
+ANMF chunk:
+
+For animated images, this chunk contains information about a _single_ frame.
+If the _Animation flag_ is not set, then this chunk SHOULD NOT be present.
+
+ 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('ANMF') |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Frame X | ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ... Frame Y | Frame Width Minus One ...
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ... | Frame Height Minus One |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Frame Duration | Reserved |B|D|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Frame Data |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Frame X: 24 bits (_uint24_)
+
+: 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`
+
+Frame Width Minus One: 24 bits (_uint24_)
+
+: The _1-based_ width of the frame.
+ 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`
+
+Frame Duration: 24 bits (_uint24_)
+
+: The time to wait before displaying the next frame, in 1 millisecond units.
+ Note the interpretation of frame duration of 0 (and often <= 10) is
+ implementation defined. Many tools and browsers assign a minimum duration
+ similar to GIF.
+
+Reserved: 6 bits
+
+: SHOULD be 0.
+
+Blending method (B): 1 bit
+
+: Indicates how transparent pixels of _the current frame_ are to be blended
+ with corresponding pixels of the previous canvas:
+
+ * `0`: Use alpha blending. After disposing of the previous frame, render the
+ current frame on the canvas using [alpha-blending](#alpha-blending). If
+ the current frame does not have an alpha channel, assume alpha value of
+ 255, effectively replacing the rectangle.
+
+ * `1`: Do not blend. After disposing of the previous frame, render the
+ current frame on the canvas by overwriting the rectangle covered by the
+ current frame.
+
+Disposal method (D): 1 bit
+
+: Indicates how _the current frame_ is to be treated after it has been
+ displayed (before rendering the next frame) on the canvas:
+
+ * `0`: Do not dispose. Leave the canvas as is.
+
+ * `1`: Dispose to background color. Fill the _rectangle_ on the canvas
+ covered by the _current frame_ with background color specified in the
+ [ANIM chunk](#anim_chunk).
+
+**Notes**:
+
+ * The frame disposal only applies to the _frame rectangle_, that is, the
+ rectangle defined by _Frame X_, _Frame Y_, _frame width_ and _frame height_.
+ It may or may not cover the whole canvas.
+
+{:#alpha-blending}
+ * **Alpha-blending**:
+
+ Given that each of the R, G, B and A channels is 8-bit, and the RGB
+ channels are _not premultiplied_ by alpha, the formula for blending
+ 'dst' onto 'src' is:
+
+~~~~~
+ blend.A = src.A + dst.A * (1 - src.A / 255)
+ 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
+~~~~~
+
+ * Alpha-blending SHOULD be done in linear color space, by taking into account
+ the [color profile](#color-profile) of the image. If the color profile is
+ not present, sRGB is to be assumed. (Note that sRGB also needs to be
+ linearized due to a gamma of ~2.2).
+
+Frame Data: _Chunk Size_ - `16` bytes
+
+: Consists of:
+
+ * An optional [alpha subchunk](#alpha) for the frame.
+
+ * A [bitstream subchunk](#bitstream-vp8vp8l) for the frame.
+
+ * An optional list of [unknown chunks](#unknown-chunks).
+
+**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual
+_padded_ chunks as described by the [RIFF file format](#riff-file-format).
+
+#### Alpha
+
+ 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('ALPH') |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Rsv| P | F | C | Alpha Bitstream... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Reserved (Rsv): 2 bits
+
+: SHOULD be `0`.
+
+Pre-processing (P): 2 bits
+
+: 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.
+
+ * `0`: no pre-processing
+ * `1`: level reduction
+
+Filtering method (F): 2 bits
+
+: The filtering method used:
+
+ * `0`: None.
+ * `1`: Horizontal filter.
+ * `2`: Vertical filter.
+ * `3`: Gradient filter.
+
+For each pixel, filtering is performed using the following calculations.
+Assume the alpha values surrounding the current `X` position are labeled as:
+
+ C | B |
+ ---+---+
+ A | X |
+
+We seek to compute the alpha value at position `X`. First, a prediction is
+made depending on the filtering method:
+
+ * Method `0`: predictor = 0
+ * Method `1`: predictor = A
+ * Method `2`: predictor = B
+ * Method `3`: predictor = clip(A + B - C)
+
+where `clip(v)` is equal to:
+
+ * 0 if v < 0
+ * 255 if v > 255
+ * 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:
+
+`alpha = (predictor + X) % 256`
+
+There are special cases for left-most and top-most pixel positions:
+
+ * 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
+ location (x, 0) are predicted using the location (x-1, 0) on the left.
+
+
+Decoders are not required to use this information in any specified way.
+
+Compression method (C): 2 bits
+
+: The compression method used:
+
+ * `0`: No compression.
+ * `1`: Compressed using the WebP lossless format.
+
+Alpha bitstream: _Chunk Size_ - `1` bytes
+
+: Encoded alpha bitstream.
+
+This optional chunk contains encoded alpha data for this frame. A frame
+containing a 'VP8L' chunk SHOULD NOT contain this chunk.
+
+**Rationale**: The transparency information is already part of the 'VP8L'
+chunk.
+
+The alpha channel data is stored as uncompressed raw data (when
+compression method is '0') or compressed using the lossless format
+(when the compression method is '1').
+
+ * Raw data: consists of a byte sequence of length width * height,
+ containing all the 8-bit transparency values in scan order.
+
+ * Lossless format compression: the byte sequence is a compressed
+ image-stream (as described in the [WebP Lossless Bitstream Format]
+ [webpllspec]) of implicit dimension width x height. That is, this
+ image-stream does NOT contain any headers describing the image dimension.
+
+ **Rationale**: the dimension is already known from other sources,
+ so storing it again would be redundant and error-prone.
+
+ Once the image-stream is decoded into ARGB color values, following
+ the process described in the lossless format specification, the
+ transparency information must be extracted from the *green* channel
+ of the ARGB quadruplet.
+
+ **Rationale**: the green channel is allowed extra transformation
+ steps in the specification -- unlike the other channels -- that can
+ improve compression.
+
+#### Bitstream (VP8/VP8L)
+
+This chunk contains compressed bitstream data for a single frame.
+
+A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
+significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
+"VP8L" as its tag.
+
+The formats of VP8 and VP8L chunks are as described in sections
+[Simple File Format (Lossy)](#simple-file-format-lossy)
+and [Simple File Format (Lossless)](#simple-file-format-lossless) respectively.
+
+#### 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: _Chunk Size_ bytes
+
+: ICC profile.
+
+This chunk MUST appear before the image data.
+
+There SHOULD be at most one such chunk. If there are more such chunks, readers
+MAY ignore all except the first one.
+See the [ICC Specification][iccspec] for details.
+
+If this chunk is not present, sRGB SHOULD be assumed.
+
+#### Metadata
+
+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.
+
+The chunks are defined as follows:
+
+EXIF chunk:
+
+ 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('EXIF') |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | EXIF Metadata |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+EXIF Metadata: _Chunk Size_ bytes
+
+: image metadata in EXIF format.
+
+XMP chunk:
+
+ 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('XMP ') |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | XMP Metadata |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+XMP Metadata: _Chunk Size_ bytes
+
+: image metadata in XMP format.
+
+Additional guidance about handling metadata can be found in the
+Metadata Working Group's [Guidelines for Handling Metadata][metadata].
+
+#### Unknown Chunks _\[status: experimental\]_
+
+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
+considered an _unknown chunk_.
+
+**Rationale**: Allowing unknown chunks gives a provision for future extension
+of the format, and also allows storage of any application-specific data.
+
+A file MAY contain unknown chunks:
+
+ * At the end of the file as described in [Extended WebP file
+ header](#extended_header) section.
+ * At the end of ANMF chunks as described in the
+ [Animation](#animation) section.
+
+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
+
+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.
+
+Displaying an _animated image_ canvas MUST be equivalent to the following
+pseudocode:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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
+if loop_count == 0:
+ 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.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+Example File Layouts
+--------------------
+
+A lossy encoded image with alpha may look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- ALPH (alpha bitstream)
++- VP8 (bitstream)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A losslessly encoded image may look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- XYZW (unknown chunk)
++- VP8L (lossless bitstream)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A lossless image with ICC profile and XMP metadata may
+look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- ICCP (color profile)
++- VP8L (lossless bitstream)
++- XMP (metadata)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An animated image with EXIF metadata may look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- ANIM (global animation parameters)
++- ANMF (frame1 parameters + data)
++- ANMF (frame2 parameters + data)
++- ANMF (frame3 parameters + data)
++- ANMF (frame4 parameters + data)
++- 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
diff --git a/src/third_party/libwebp/doc/webp-lossless-bitstream-spec.txt b/src/third_party/libwebp/doc/webp-lossless-bitstream-spec.txt
new file mode 100644
index 0000000..2d2dde1
--- /dev/null
+++ b/src/third_party/libwebp/doc/webp-lossless-bitstream-spec.txt
@@ -0,0 +1,1090 @@
+<!--
+
+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.
+
+See the accompanying README. 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.
+
+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.
+
+
+* 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.
+
+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);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+is equivalent with the two statements below:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+b = ReadBits(1);
+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.
+
+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
+-------------
+
+The beginning of the header has the RIFF container. This consists of the
+following 21 bytes:
+
+ 1. String "RIFF"
+ 2. A little-endian 32 bit value of the block length, the whole size
+ of the block controlled by the RIFF header. Normally this equals
+ the payload size (file size minus 8 bytes: 4 bytes for the 'RIFF'
+ identifier and 4 bytes for storing the value itself).
+ 3. String "WEBP" (RIFF container name).
+ 4. String "VP8L" (chunk tag for lossless encoded image data).
+ 5. A little-endian 32-bit value of the number of bytes in the
+ 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:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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 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\]
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int version_number = ReadBits(3);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+3 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.
+
+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.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+while (ReadBits(1)) { // Transform present.
+ // Decode transform type.
+ enum TransformType transform_type = ReadBits(2);
+ // Decode transform data.
+ ...
+}
+
+// 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.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+enum TransformType {
+ PREDICTOR_TRANSFORM = 0,
+ COLOR_TRANSFORM = 1,
+ SUBTRACT_GREEN = 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.
+
+
+### 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 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))
+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).
+
+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.
+
+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
+O O O O O O O O O O O
+O O O O TL T TR O O O O
+O O O O L P X X X X X
+X X X X X X X X X X X
+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.
+
+Given the above neighboring pixels, the different prediction modes are
+defined as follows.
+
+| Mode | Predicted value of each channel of the current pixel |
+| ------ | ------------------------------------------------------- |
+| 0 | 0xff000000 (represents solid black color in ARGB) |
+| 1 | L |
+| 2 | T |
+| 3 | TR |
+| 4 | TL |
+| 5 | Average2(Average2(L, TR), T) |
+| 6 | Average2(L, TL) |
+| 7 | Average2(L, T) |
+| 8 | Average2(TL, T) |
+| 9 | Average2(T, TR) |
+| 10 | Average2(Average2(L, TL), Average2(T, TR)) |
+| 11 | Select(L, T, TL) |
+| 12 | ClampAddSubtractFull(L, T, TL) |
+| 13 | ClampAddSubtractHalf(Average2(L, T), TL) |
+
+
+`Average2` is defined as follows for each ARGB component:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+uint8 Average2(uint8 a, uint8 b) {
+ return (a + b) / 2;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Select predictor is defined as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+uint32 Select(uint32 L, uint32 T, uint32 TL) {
+ // L = left pixel, T = top pixel, TL = top left pixel.
+
+ // ARGB component estimates for prediction.
+ int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
+ int pRed = RED(L) + RED(T) - RED(TL);
+ int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
+ int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);
+
+ // Manhattan distances to estimates for left and top pixels.
+ int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
+ abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
+ int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
+ abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
+
+ // Return either left or top, the one closer to the prediction.
+ if (pL < pT) { // \[AMENDED\]
+ return L;
+ } else {
+ return T;
+ }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The functions `ClampAddSubtractFull` and `ClampAddSubtractHalf` are
+performed for each ARGB component as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Clamp the input value between 0 and 255.
+int Clamp(int a) {
+ return (a < 0) ? 0 : (a > 255) ? 255 : a;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int ClampAddSubtractFull(int a, int b, int c) {
+ return Clamp(a + b - c);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int ClampAddSubtractHalf(int a, int b) {
+ return Clamp(a + (a - b) / 2);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+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.
+
+
+### 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.
+
+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 {
+ uint8 green_to_red;
+ uint8 green_to_blue;
+ uint8 red_to_blue;
+} 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 color transform function is defined as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void ColorTransform(uint8 red, uint8 blue, uint8 green,
+ ColorTransformElement *trans,
+ uint8 *new_red, uint8 *new_blue) {
+ // Transformed values of red and blue components
+ uint32 tmp_red = red;
+ uint32 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);
+
+ *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:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int8 ColorTransformDelta(int8 t, int8 c) {
+ return (t * c) >> 5;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+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;
+int block_width = 1 << size_bits;
+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).
+
+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.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void InverseTransform(uint8 red, uint8 green, uint8 blue,
+ ColorTransformElement *p,
+ 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);
+
+ *new_red = red & 0xff;
+ *new_blue = blue & 0xff;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+### 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:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
+ *red = (*red + green) & 0xff;
+ *blue = (*blue + green) & 0xff;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+
+### 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.)
+
+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:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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 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\]
+
+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.
+
+`color_table_size` specifies how many pixels are combined together:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int width_bits;
+if (color_table_size <= 2) {
+ width_bits = 3;
+} else if (color_table_size <= 4) {
+ width_bits = 2;
+} else if (color_table_size <= 16) {
+ width_bits = 1;
+} else {
+ width_bits = 0;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`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.
+
+The values are packed into the green component as follows:
+
+ * `width_bits` = 1: for every x value where x ≡ 0 (mod 2), a green
+ value at x is positioned into the 4 least-significant bits of the
+ green value at x / 2, a green value at x + 1 is positioned into the
+ 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.
+ * `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.
+
+
+4 Image Data
+------------
+
+Image data is an array of pixel values in scan-line order.
+
+### 4.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
+ 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
+ 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
+ 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
+
+The encoding of image data is independent of its role.
+
+The image is first divided into a set of fixed-size blocks (typically 16x16
+blocks). Each of these blocks are modeled using their own entropy codes. Also,
+several blocks may share the same entropy codes.
+
+**Rationale:** Storing an entropy code incurs a cost. This cost can be minimized
+if statistically similar blocks share an entropy code, thereby storing that code
+only once. For example, an encoder can find similar blocks by clustering them
+using their statistical properties, or by repeatedly joining a pair of randomly
+selected clusters when it reduces the overall amount of bits needed to encode
+the image.
+
+Each pixel is encoded using one of the three possible methods:
+
+ 1. Huffman 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.
+
+#### 4.2.1 Huffman 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.
+
+#### 4.2.2 LZ77 Backward Reference
+
+Backward references are tuples of _length_ and _distance code_:
+
+ * Length indicates how many pixels in scan-line order are to be copied.
+ * Distance code is a number indicating the position of a previously seen
+ pixel, from which the pixels are to be copied. The exact mapping is
+ described [below](#distance-mapping).
+
+The length and distance values are stored using **LZ77 prefix coding**.
+
+LZ77 prefix coding divides large integer values into two parts: the _prefix
+code_ and the _extra bits_: the prefix code is stored using an entropy code,
+while the extra bits are stored as they are (without an entropy code).
+
+**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.
+
+The following table denotes the prefix codes and extra bits used for storing
+different range 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
+values. For distance values, however, all the 40 prefix codes are valid.
+
+| Value range | Prefix code | Extra bits |
+| --------------- | ----------- | ---------- |
+| 1 | 0 | 0 |
+| 2 | 1 | 0 |
+| 3 | 2 | 0 |
+| 4 | 3 | 0 |
+| 5..6 | 4 | 1 |
+| 7..8 | 5 | 1 |
+| 9..12 | 6 | 2 |
+| 13..16 | 7 | 2 |
+| ... | ... | ... |
+| 3072..4096 | 23 | 10 |
+| ... | ... | ... |
+| 524289..786432 | 38 | 18 |
+| 786433..1048576 | 39 | 18 |
+
+The pseudocode to obtain a (length or distance) value from the prefix code is
+as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+if (prefix_code < 4) {
+ return prefix_code + 1;
+}
+int extra_bits = (prefix_code - 2) >> 1;
+int offset = (2 + (prefix_code & 1)) << extra_bits;
+return offset + ReadBits(extra_bits) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**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
+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.
+
+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:
+
+ * Pixels that are 1 to 7 rows above the current pixel, and are up to 8 columns
+ to the left or up to 7 columns to the right of the current pixel. \[Total
+ such pixels = `7 * (8 + 1 + 7) = 112`\].
+ * Pixels that are in same row as the current pixel, and are up to 8 columns to
+ the left of the current pixel. \[`8` such pixels\].
+
+The mapping between distance code `i` and the neighboring pixel offset
+`(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)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+The decoder can convert a distances code 'i' to a scan-line order distance
+'dist' as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(xi, yi) = distance_map[i]
+dist = x + y * xsize
+if (dist < 1) {
+ dist = 1
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where 'distance_map' is the mapping noted above and `xsize` is the width of the
+image in pixels.
+
+
+#### 4.2.3 Color Cache Coding
+
+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)).
+
+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
+symbols and the length prefix codes. However, if this bit is 1, the color cache
+size is read next:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int color_cache_code_bits = ReadBits(4);
+int color_cache_size = 1 << color_cache_code_bits;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`color_cache_code_bits` defines the size of the color_cache by (1 <<
+`color_cache_code_bits`). The range of allowed values for
+`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.
+
+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
+--------------
+
+### 5.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_.
+
+In particular, the format uses **spatially-variant Huffman 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.
+
+### 5.2 Details
+
+The encoded image data consists of two parts:
+
+ 1. Meta Huffman codes
+ 1. Entropy-coded image data
+
+#### 5.2.1 Decoding of Meta Huffman 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.
+
+Meta Huffman 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
+value:
+
+ * If this bit is zero, there is only one meta Huffman 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).
+
+**Entropy image:**
+
+The entropy image defines which Huffman 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'.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where `DIV_ROUND_UP` is as defined [earlier](#predictor-transform).
+
+Next bits contain an entropy image of width `huffman_xsize` and height
+`huffman_ysize`.
+
+**Interpretation of Meta Huffman Codes:**
+
+For any given pixel (x, y), there is a set of five Huffman 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
+ respectively.
+ * **Huffman code #5**: used for backward-reference distance.
+
+From here on, we refer to this set as a **Huffman 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:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_huff_groups = max(entropy image) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+where `max(entropy image)` indicates the largest Huffman code stored in the
+entropy image.
+
+As each Huffman code groups contains five Huffman codes, the total number of
+Huffman codes is:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_huff_codes = 5 * num_huff_groups;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given a pixel (x, y) in the ARGB image, we can obtain the corresponding Huffman
+codes to be used as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int position = (y >> huffman_bits) * huffman_xsize + (x >> huffman_bits);
+int meta_huff_code = (entropy_image[pos] >> 8) & 0xffff;
+HuffmanCodeGroup huff_group = huffman_code_groups[meta_huff_code];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where, we have assumed the existence of `HuffmanCodeGroup` structure, which
+represents a set of five Huffman codes. Also, `huffman_code_groups` is an array
+of `HuffmanCodeGroup` (of size `num_huff_groups`).
+
+The decoder then uses Huffman code group `huff_group` to decode the pixel
+(x, y) as explained in the [next section](#decoding-entropy-coded-image-data).
+
+#### 5.2.2 Decoding Entropy-coded Image Data
+
+For the current position (x, y) in the image, the decoder first identifies the
+corresponding Huffman code group (as explained in the last section). Given the
+Huffman code group, the pixel is read and decoded as follows:
+
+Read next symbol S from the bitstream using Huffman code #1. \[See
+[next section](#decoding-the-code-lengths) for details on decoding the Huffman
+code lengths\]. Note that S is any integer in the range `0` to
+`(256 + 24 + ` [`color_cache_size`](#color-cache-code)`- 1)`.
+
+The interpretation of S depends on its value:
+
+ 1. if S < 256
+ 1. Use S as the green component
+ 1. Read red from the bitstream using Huffman code #2
+ 1. Read blue from the bitstream using Huffman code #3
+ 1. Read alpha from the bitstream using Huffman code #4
+ 1. if S < 256 + 24
+ 1. Use S - 256 as a length prefix code
+ 1. Read extra bits for length from the bitstream
+ 1. Determine backward-reference length L from length prefix code and the
+ extra bits read.
+ 1. Read distance prefix code from the bitstream using Huffman code #5
+ 1. Read extra bits for distance from the bitstream
+ 1. Determine backward-reference distance D from distance prefix code and
+ the extra bits read.
+ 1. Copy the L pixels (in scan-line order) from the sequence of pixels
+ prior to them by D pixels.
+ 1. if S >= 256 + 24
+ 1. Use S - (256 + 24) as the index into the color cache.
+ 1. Get ARGB color from the color cache at that index.
+
+
+**Decoding the Code Lengths:**
+{:#decoding-the-code-lengths}
+
+This section describes the details about reading a symbol from the bitstream by
+decoding the Huffman code length.
+
+The Huffman 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_.
+
+**(i) Simple Code Length Code:**
+
+This variant is used in the special case when only 1 or 2 Huffman code lengths
+are non-zero, and are in the range of \[0, 255\]. All other Huffman code lengths
+are implicitly zeros.
+
+The first bit indicates the number of non-zero code lengths:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_code_lengths = ReadBits(1) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first code length is stored either using a 1-bit code for values of 0 and 1,
+or using an 8-bit code for values in range \[0, 255\]. The second code length,
+when present, is coded as an 8-bit code.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int is_first_8bits = ReadBits(1);
+code_lengths[0] = ReadBits(1 + 7 * is_first_8bits);
+if (num_code_lengths == 2) {
+ code_lengths[1] = ReadBits(8);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Note:** Another special case is when _all_ Huffman code lengths are _zeros_
+(an empty Huffman code). For example, a Huffman code for distance can be empty
+if there are no backward references. Similarly, Huffman codes for alpha, red,
+and blue can be empty if all pixels within the same meta Huffman code are
+produced using the color cache. However, this case doesn't need a special
+handling, as empty Huffman codes can be coded as those containing a single
+symbol `0`.
+
+**(ii) Normal Code Length Code:**
+
+The code lengths of a Huffman code are read as follows: `num_code_lengths`
+specifies the number of code lengths; the rest of the 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_lengths[kCodeLengthCodes] = { 0 }; // All zeros.
+int num_code_lengths = 4 + ReadBits(4);
+for (i = 0; i < num_code_lengths; ++i) {
+ code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ * Code length 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, i.e.,
+ 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\], i.e., 3 + `ReadBits(3)`
+ times.
+ * Code 18 emits a streak of zeros of length \[11..138\], i.e.,
+ 11 + `ReadBits(7)` times.
+
+
+6 Overall Structure of the Format
+---------------------------------
+
+Below is a view into the format in Backus-Naur form. It does not cover
+all details. End-of-image (EOI) is only implicitly coded into the number
+of pixels (xsize * ysize).
+
+
+#### Basic Structure
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<format> ::= <RIFF header><image size><image stream>
+<image stream> ::= <optional-transform><spatially-coded image>
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+#### Structure of Transforms
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<optional-transform> ::= (1-bit value 1; <transform> <optional-transform>) |
+ 1-bit value 0
+<transform> ::= <predictor-tx> | <color-tx> | <subtract-green-tx> |
+ <color-indexing-tx>
+<predictor-tx> ::= 2-bit value 0; <predictor image>
+<predictor image> ::= 3-bit sub-pixel code ; <entropy-coded image>
+<color-tx> ::= 2-bit value 1; <color image>
+<color image> ::= 3-bit sub-pixel code ; <entropy-coded image>
+<subtract-green-tx> ::= 2-bit value 2
+<color-indexing-tx> ::= 2-bit value 3; <color-indexing image>
+<color-indexing image> ::= 8-bit color count; <entropy-coded image>
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+#### Structure of the Image Data
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<spatially-coded image> ::= <meta huffman><entropy-coded image>
+<entropy-coded image> ::= <color cache info><huffman codes><lz77-coded image>
+<meta huffman> ::= 1-bit value 0 |
+ (1-bit value 1; <entropy image>)
+<entropy image> ::= 3-bit subsample value; <entropy-coded image>
+<color cache info> ::= 1 bit value 0 |
+ (1-bit value 1; 4-bit value for color cache size)
+<huffman codes> ::= <huffman code group> | <huffman code group><huffman codes>
+<huffman code group> ::= <huffman code><huffman code><huffman code>
+ <huffman code><huffman code>
+ See "Interpretation of Meta Huffman codes" to
+ understand what each of these five Huffman codes are
+ for.
+<huffman code> ::= <simple huffman code> | <normal huffman code>
+<simple huffman code> ::= see "Simple code length code" for details
+<normal huffman code> ::= <code length code>; encoded code lengths
+<code length code> ::= see section "Normal code length code"
+<lz77-coded image> ::= ((<argb-pixel> | <lz77-copy> | <color-cache-code>)
+ <lz77-coded image>) | ""
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A possible example sequence:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<RIFF header><image size>1-bit value 1<subtract-green-tx>
+1-bit value 1<predictor-tx>1-bit value 0<meta huffman>
+<color cache info><huffman codes>
+<lz77-coded image>
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[canonical_huff]: http://en.wikipedia.org/wiki/Canonical_Huffman_code
diff --git a/src/third_party/libwebp/dsp/cpu.c b/src/third_party/libwebp/dsp/cpu.c
deleted file mode 100644
index 179901e..0000000
--- a/src/third_party/libwebp/dsp/cpu.c
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// CPU detection
-//
-// Author: Christian Duvivier (cduvivier@google.com)
-
-#include "./dsp.h"
-
-#if defined(__ANDROID__)
-#include <cpu-features.h>
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// SSE2 detection.
-//
-
-// apple/darwin gcc-4.0.1 defines __PIC__, but not __pic__ with -fPIC.
-#if (defined(__pic__) || defined(__PIC__)) && defined(__i386__)
-static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
- __asm__ volatile (
- "mov %%ebx, %%edi\n"
- "cpuid\n"
- "xchg %%edi, %%ebx\n"
- : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
- : "a"(info_type));
-}
-#elif defined(__i386__) || defined(__x86_64__)
-static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
- __asm__ volatile (
- "cpuid\n"
- : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
- : "a"(info_type));
-}
-#elif defined(WEBP_MSC_SSE2)
-#define GetCPUInfo __cpuid
-#endif
-
-#if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2)
-static int x86CPUInfo(CPUFeature feature) {
- int cpu_info[4];
- GetCPUInfo(cpu_info, 1);
- if (feature == kSSE2) {
- return 0 != (cpu_info[3] & 0x04000000);
- }
- if (feature == kSSE3) {
- return 0 != (cpu_info[2] & 0x00000001);
- }
- return 0;
-}
-VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
-#elif defined(WEBP_ANDROID_NEON)
-static int AndroidCPUInfo(CPUFeature feature) {
- const AndroidCpuFamily cpu_family = android_getCpuFamily();
- const uint64_t cpu_features = android_getCpuFeatures();
- if (feature == kNEON) {
- return (cpu_family == ANDROID_CPU_FAMILY_ARM &&
- 0 != (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON));
- }
- return 0;
-}
-VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
-#elif defined(__ARM_NEON__)
-// define a dummy function to enable turning off NEON at runtime by setting
-// VP8DecGetCPUInfo = NULL
-static int armCPUInfo(CPUFeature feature) {
- (void)feature;
- return 1;
-}
-VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
-#else
-VP8CPUInfo VP8GetCPUInfo = NULL;
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/dec.c b/src/third_party/libwebp/dsp/dec.c
deleted file mode 100644
index ce32ee3..0000000
--- a/src/third_party/libwebp/dsp/dec.c
+++ /dev/null
@@ -1,743 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Speed-critical decoding functions.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "./dsp.h"
-#include "../dec/vp8i.h"
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// run-time tables (~4k)
-
-static uint8_t abs0[255 + 255 + 1]; // abs(i)
-static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1
-static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127]
-static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15]
-static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
-
-// We declare this variable 'volatile' to prevent instruction reordering
-// and make sure it's set to true _last_ (so as to be thread-safe)
-static volatile int tables_ok = 0;
-
-static void DspInitTables(void) {
- if (!tables_ok) {
- int i;
- for (i = -255; i <= 255; ++i) {
- abs0[255 + i] = (i < 0) ? -i : i;
- abs1[255 + i] = abs0[255 + i] >> 1;
- }
- for (i = -1020; i <= 1020; ++i) {
- sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
- }
- for (i = -112; i <= 112; ++i) {
- sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
- }
- for (i = -255; i <= 255 + 255; ++i) {
- clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
- }
- tables_ok = 1;
- }
-}
-
-static WEBP_INLINE uint8_t clip_8b(int v) {
- return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
-}
-
-//------------------------------------------------------------------------------
-// Transforms (Paragraph 14.4)
-
-#define STORE(x, y, v) \
- dst[x + y * BPS] = clip_8b(dst[x + y * BPS] + ((v) >> 3))
-
-static const int kC1 = 20091 + (1 << 16);
-static const int kC2 = 35468;
-#define MUL(a, b) (((a) * (b)) >> 16)
-
-static void TransformOne(const int16_t* in, uint8_t* dst) {
- int C[4 * 4], *tmp;
- int i;
- tmp = C;
- for (i = 0; i < 4; ++i) { // vertical pass
- const int a = in[0] + in[8]; // [-4096, 4094]
- const int b = in[0] - in[8]; // [-4095, 4095]
- const int c = MUL(in[4], kC2) - MUL(in[12], kC1); // [-3783, 3783]
- const int d = MUL(in[4], kC1) + MUL(in[12], kC2); // [-3785, 3781]
- tmp[0] = a + d; // [-7881, 7875]
- tmp[1] = b + c; // [-7878, 7878]
- tmp[2] = b - c; // [-7878, 7878]
- tmp[3] = a - d; // [-7877, 7879]
- tmp += 4;
- in++;
- }
- // Each pass is expanding the dynamic range by ~3.85 (upper bound).
- // The exact value is (2. + (kC1 + kC2) / 65536).
- // After the second pass, maximum interval is [-3794, 3794], assuming
- // an input in [-2048, 2047] interval. We then need to add a dst value
- // in the [0, 255] range.
- // In the worst case scenario, the input to clip_8b() can be as large as
- // [-60713, 60968].
- tmp = C;
- for (i = 0; i < 4; ++i) { // horizontal pass
- const int dc = tmp[0] + 4;
- const int a = dc + tmp[8];
- const int b = dc - tmp[8];
- const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
- const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
- STORE(0, 0, a + d);
- STORE(1, 0, b + c);
- STORE(2, 0, b - c);
- STORE(3, 0, a - d);
- tmp++;
- dst += BPS;
- }
-}
-#undef MUL
-
-static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) {
- TransformOne(in, dst);
- if (do_two) {
- TransformOne(in + 16, dst + 4);
- }
-}
-
-static void TransformUV(const int16_t* in, uint8_t* dst) {
- VP8Transform(in + 0 * 16, dst, 1);
- VP8Transform(in + 2 * 16, dst + 4 * BPS, 1);
-}
-
-static void TransformDC(const int16_t *in, uint8_t* dst) {
- const int DC = in[0] + 4;
- int i, j;
- for (j = 0; j < 4; ++j) {
- for (i = 0; i < 4; ++i) {
- STORE(i, j, DC);
- }
- }
-}
-
-static void TransformDCUV(const int16_t* in, uint8_t* dst) {
- if (in[0 * 16]) TransformDC(in + 0 * 16, dst);
- if (in[1 * 16]) TransformDC(in + 1 * 16, dst + 4);
- if (in[2 * 16]) TransformDC(in + 2 * 16, dst + 4 * BPS);
- if (in[3 * 16]) TransformDC(in + 3 * 16, dst + 4 * BPS + 4);
-}
-
-#undef STORE
-
-//------------------------------------------------------------------------------
-// Paragraph 14.3
-
-static void TransformWHT(const int16_t* in, int16_t* out) {
- int tmp[16];
- int i;
- for (i = 0; i < 4; ++i) {
- const int a0 = in[0 + i] + in[12 + i];
- const int a1 = in[4 + i] + in[ 8 + i];
- const int a2 = in[4 + i] - in[ 8 + i];
- const int a3 = in[0 + i] - in[12 + i];
- tmp[0 + i] = a0 + a1;
- tmp[8 + i] = a0 - a1;
- tmp[4 + i] = a3 + a2;
- tmp[12 + i] = a3 - a2;
- }
- for (i = 0; i < 4; ++i) {
- const int dc = tmp[0 + i * 4] + 3; // w/ rounder
- const int a0 = dc + tmp[3 + i * 4];
- const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
- const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
- const int a3 = dc - tmp[3 + i * 4];
- out[ 0] = (a0 + a1) >> 3;
- out[16] = (a3 + a2) >> 3;
- out[32] = (a0 - a1) >> 3;
- out[48] = (a3 - a2) >> 3;
- out += 64;
- }
-}
-
-void (*VP8TransformWHT)(const int16_t* in, int16_t* out) = TransformWHT;
-
-//------------------------------------------------------------------------------
-// Intra predictions
-
-#define DST(x, y) dst[(x) + (y) * BPS]
-
-static WEBP_INLINE void TrueMotion(uint8_t *dst, int size) {
- const uint8_t* top = dst - BPS;
- const uint8_t* const clip0 = clip1 + 255 - top[-1];
- int y;
- for (y = 0; y < size; ++y) {
- const uint8_t* const clip = clip0 + dst[-1];
- int x;
- for (x = 0; x < size; ++x) {
- dst[x] = clip[top[x]];
- }
- dst += BPS;
- }
-}
-static void TM4(uint8_t *dst) { TrueMotion(dst, 4); }
-static void TM8uv(uint8_t *dst) { TrueMotion(dst, 8); }
-static void TM16(uint8_t *dst) { TrueMotion(dst, 16); }
-
-//------------------------------------------------------------------------------
-// 16x16
-
-static void VE16(uint8_t *dst) { // vertical
- int j;
- for (j = 0; j < 16; ++j) {
- SbMemoryCopy(dst + j * BPS, dst - BPS, 16);
- }
-}
-
-static void HE16(uint8_t *dst) { // horizontal
- int j;
- for (j = 16; j > 0; --j) {
- SbMemorySet(dst, dst[-1], 16);
- dst += BPS;
- }
-}
-
-static WEBP_INLINE void Put16(int v, uint8_t* dst) {
- int j;
- for (j = 0; j < 16; ++j) {
- SbMemorySet(dst + j * BPS, v, 16);
- }
-}
-
-static void DC16(uint8_t *dst) { // DC
- int DC = 16;
- int j;
- for (j = 0; j < 16; ++j) {
- DC += dst[-1 + j * BPS] + dst[j - BPS];
- }
- Put16(DC >> 5, dst);
-}
-
-static void DC16NoTop(uint8_t *dst) { // DC with top samples not available
- int DC = 8;
- int j;
- for (j = 0; j < 16; ++j) {
- DC += dst[-1 + j * BPS];
- }
- Put16(DC >> 4, dst);
-}
-
-static void DC16NoLeft(uint8_t *dst) { // DC with left samples not available
- int DC = 8;
- int i;
- for (i = 0; i < 16; ++i) {
- DC += dst[i - BPS];
- }
- Put16(DC >> 4, dst);
-}
-
-static void DC16NoTopLeft(uint8_t *dst) { // DC with no top and left samples
- Put16(0x80, dst);
-}
-
-//------------------------------------------------------------------------------
-// 4x4
-
-#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
-#define AVG2(a, b) (((a) + (b) + 1) >> 1)
-
-static void VE4(uint8_t *dst) { // vertical
- const uint8_t* top = dst - BPS;
- const uint8_t vals[4] = {
- AVG3(top[-1], top[0], top[1]),
- AVG3(top[ 0], top[1], top[2]),
- AVG3(top[ 1], top[2], top[3]),
- AVG3(top[ 2], top[3], top[4])
- };
- int i;
- for (i = 0; i < 4; ++i) {
- SbMemoryCopy(dst + i * BPS, vals, sizeof(vals));
- }
-}
-
-static void HE4(uint8_t *dst) { // horizontal
- const int A = dst[-1 - BPS];
- const int B = dst[-1];
- const int C = dst[-1 + BPS];
- const int D = dst[-1 + 2 * BPS];
- const int E = dst[-1 + 3 * BPS];
- *(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(A, B, C);
- *(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(B, C, D);
- *(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(C, D, E);
- *(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(D, E, E);
-}
-
-static void DC4(uint8_t *dst) { // DC
- uint32_t dc = 4;
- int i;
- for (i = 0; i < 4; ++i) dc += dst[i - BPS] + dst[-1 + i * BPS];
- dc >>= 3;
- for (i = 0; i < 4; ++i) SbMemorySet(dst + i * BPS, dc, 4);
-}
-
-static void RD4(uint8_t *dst) { // Down-right
- const int I = dst[-1 + 0 * BPS];
- const int J = dst[-1 + 1 * BPS];
- const int K = dst[-1 + 2 * BPS];
- const int L = dst[-1 + 3 * BPS];
- const int X = dst[-1 - BPS];
- const int A = dst[0 - BPS];
- const int B = dst[1 - BPS];
- const int C = dst[2 - BPS];
- const int D = dst[3 - BPS];
- DST(0, 3) = AVG3(J, K, L);
- DST(0, 2) = DST(1, 3) = AVG3(I, J, K);
- DST(0, 1) = DST(1, 2) = DST(2, 3) = AVG3(X, I, J);
- DST(0, 0) = DST(1, 1) = DST(2, 2) = DST(3, 3) = AVG3(A, X, I);
- DST(1, 0) = DST(2, 1) = DST(3, 2) = AVG3(B, A, X);
- DST(2, 0) = DST(3, 1) = AVG3(C, B, A);
- DST(3, 0) = AVG3(D, C, B);
-}
-
-static void LD4(uint8_t *dst) { // Down-Left
- const int A = dst[0 - BPS];
- const int B = dst[1 - BPS];
- const int C = dst[2 - BPS];
- const int D = dst[3 - BPS];
- const int E = dst[4 - BPS];
- const int F = dst[5 - BPS];
- const int G = dst[6 - BPS];
- const int H = dst[7 - BPS];
- DST(0, 0) = AVG3(A, B, C);
- DST(1, 0) = DST(0, 1) = AVG3(B, C, D);
- DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E);
- DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F);
- DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G);
- DST(3, 2) = DST(2, 3) = AVG3(F, G, H);
- DST(3, 3) = AVG3(G, H, H);
-}
-
-static void VR4(uint8_t *dst) { // Vertical-Right
- const int I = dst[-1 + 0 * BPS];
- const int J = dst[-1 + 1 * BPS];
- const int K = dst[-1 + 2 * BPS];
- const int X = dst[-1 - BPS];
- const int A = dst[0 - BPS];
- const int B = dst[1 - BPS];
- const int C = dst[2 - BPS];
- const int D = dst[3 - BPS];
- DST(0, 0) = DST(1, 2) = AVG2(X, A);
- DST(1, 0) = DST(2, 2) = AVG2(A, B);
- DST(2, 0) = DST(3, 2) = AVG2(B, C);
- DST(3, 0) = AVG2(C, D);
-
- DST(0, 3) = AVG3(K, J, I);
- DST(0, 2) = AVG3(J, I, X);
- DST(0, 1) = DST(1, 3) = AVG3(I, X, A);
- DST(1, 1) = DST(2, 3) = AVG3(X, A, B);
- DST(2, 1) = DST(3, 3) = AVG3(A, B, C);
- DST(3, 1) = AVG3(B, C, D);
-}
-
-static void VL4(uint8_t *dst) { // Vertical-Left
- const int A = dst[0 - BPS];
- const int B = dst[1 - BPS];
- const int C = dst[2 - BPS];
- const int D = dst[3 - BPS];
- const int E = dst[4 - BPS];
- const int F = dst[5 - BPS];
- const int G = dst[6 - BPS];
- const int H = dst[7 - BPS];
- DST(0, 0) = AVG2(A, B);
- DST(1, 0) = DST(0, 2) = AVG2(B, C);
- DST(2, 0) = DST(1, 2) = AVG2(C, D);
- DST(3, 0) = DST(2, 2) = AVG2(D, E);
-
- DST(0, 1) = AVG3(A, B, C);
- DST(1, 1) = DST(0, 3) = AVG3(B, C, D);
- DST(2, 1) = DST(1, 3) = AVG3(C, D, E);
- DST(3, 1) = DST(2, 3) = AVG3(D, E, F);
- DST(3, 2) = AVG3(E, F, G);
- DST(3, 3) = AVG3(F, G, H);
-}
-
-static void HU4(uint8_t *dst) { // Horizontal-Up
- const int I = dst[-1 + 0 * BPS];
- const int J = dst[-1 + 1 * BPS];
- const int K = dst[-1 + 2 * BPS];
- const int L = dst[-1 + 3 * BPS];
- DST(0, 0) = AVG2(I, J);
- DST(2, 0) = DST(0, 1) = AVG2(J, K);
- DST(2, 1) = DST(0, 2) = AVG2(K, L);
- DST(1, 0) = AVG3(I, J, K);
- DST(3, 0) = DST(1, 1) = AVG3(J, K, L);
- DST(3, 1) = DST(1, 2) = AVG3(K, L, L);
- DST(3, 2) = DST(2, 2) =
- DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
-}
-
-static void HD4(uint8_t *dst) { // Horizontal-Down
- const int I = dst[-1 + 0 * BPS];
- const int J = dst[-1 + 1 * BPS];
- const int K = dst[-1 + 2 * BPS];
- const int L = dst[-1 + 3 * BPS];
- const int X = dst[-1 - BPS];
- const int A = dst[0 - BPS];
- const int B = dst[1 - BPS];
- const int C = dst[2 - BPS];
-
- DST(0, 0) = DST(2, 1) = AVG2(I, X);
- DST(0, 1) = DST(2, 2) = AVG2(J, I);
- DST(0, 2) = DST(2, 3) = AVG2(K, J);
- DST(0, 3) = AVG2(L, K);
-
- DST(3, 0) = AVG3(A, B, C);
- DST(2, 0) = AVG3(X, A, B);
- DST(1, 0) = DST(3, 1) = AVG3(I, X, A);
- DST(1, 1) = DST(3, 2) = AVG3(J, I, X);
- DST(1, 2) = DST(3, 3) = AVG3(K, J, I);
- DST(1, 3) = AVG3(L, K, J);
-}
-
-#undef DST
-#undef AVG3
-#undef AVG2
-
-//------------------------------------------------------------------------------
-// Chroma
-
-static void VE8uv(uint8_t *dst) { // vertical
- int j;
- for (j = 0; j < 8; ++j) {
- SbMemoryCopy(dst + j * BPS, dst - BPS, 8);
- }
-}
-
-static void HE8uv(uint8_t *dst) { // horizontal
- int j;
- for (j = 0; j < 8; ++j) {
- SbMemorySet(dst, dst[-1], 8);
- dst += BPS;
- }
-}
-
-// helper for chroma-DC predictions
-static WEBP_INLINE void Put8x8uv(uint8_t value, uint8_t* dst) {
- int j;
-#ifndef WEBP_REFERENCE_IMPLEMENTATION
- const uint64_t v = (uint64_t)value * 0x0101010101010101ULL;
- for (j = 0; j < 8; ++j) {
- *(uint64_t*)(dst + j * BPS) = v;
- }
-#else
- for (j = 0; j < 8; ++j) SbMemorySet(dst + j * BPS, value, 8);
-#endif
-}
-
-static void DC8uv(uint8_t *dst) { // DC
- int dc0 = 8;
- int i;
- for (i = 0; i < 8; ++i) {
- dc0 += dst[i - BPS] + dst[-1 + i * BPS];
- }
- Put8x8uv(dc0 >> 4, dst);
-}
-
-static void DC8uvNoLeft(uint8_t *dst) { // DC with no left samples
- int dc0 = 4;
- int i;
- for (i = 0; i < 8; ++i) {
- dc0 += dst[i - BPS];
- }
- Put8x8uv(dc0 >> 3, dst);
-}
-
-static void DC8uvNoTop(uint8_t *dst) { // DC with no top samples
- int dc0 = 4;
- int i;
- for (i = 0; i < 8; ++i) {
- dc0 += dst[-1 + i * BPS];
- }
- Put8x8uv(dc0 >> 3, dst);
-}
-
-static void DC8uvNoTopLeft(uint8_t *dst) { // DC with nothing
- Put8x8uv(0x80, dst);
-}
-
-//------------------------------------------------------------------------------
-// default C implementations
-
-const VP8PredFunc VP8PredLuma4[NUM_BMODES] = {
- DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4
-};
-
-const VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES] = {
- DC16, TM16, VE16, HE16,
- DC16NoTop, DC16NoLeft, DC16NoTopLeft
-};
-
-const VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES] = {
- DC8uv, TM8uv, VE8uv, HE8uv,
- DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft
-};
-
-//------------------------------------------------------------------------------
-// Edge filtering functions
-
-// 4 pixels in, 2 pixels out
-static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
- const int a1 = sclip2[112 + ((a + 4) >> 3)];
- const int a2 = sclip2[112 + ((a + 3) >> 3)];
- p[-step] = clip1[255 + p0 + a2];
- p[ 0] = clip1[255 + q0 - a1];
-}
-
-// 4 pixels in, 4 pixels out
-static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- const int a = 3 * (q0 - p0);
- const int a1 = sclip2[112 + ((a + 4) >> 3)];
- const int a2 = sclip2[112 + ((a + 3) >> 3)];
- const int a3 = (a1 + 1) >> 1;
- p[-2*step] = clip1[255 + p1 + a3];
- p[- step] = clip1[255 + p0 + a2];
- p[ 0] = clip1[255 + q0 - a1];
- p[ step] = clip1[255 + q1 - a3];
-}
-
-// 6 pixels in, 6 pixels out
-static WEBP_INLINE void do_filter6(uint8_t* p, int step) {
- const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
- const int q0 = p[0], q1 = p[step], q2 = p[2*step];
- const int a = sclip1[1020 + 3 * (q0 - p0) + sclip1[1020 + p1 - q1]];
- const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7
- const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7
- const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7
- p[-3*step] = clip1[255 + p2 + a3];
- p[-2*step] = clip1[255 + p1 + a2];
- p[- step] = clip1[255 + p0 + a1];
- p[ 0] = clip1[255 + q0 - a1];
- p[ step] = clip1[255 + q1 - a2];
- p[ 2*step] = clip1[255 + q2 - a3];
-}
-
-static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
-}
-
-static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
-}
-
-static WEBP_INLINE int needs_filter2(const uint8_t* p,
- int step, int t, int it) {
- const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
- const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
- if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
- return 0;
- return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
- abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
- abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
-}
-
-//------------------------------------------------------------------------------
-// Simple In-loop filtering (Paragraph 15.2)
-
-static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
- int i;
- for (i = 0; i < 16; ++i) {
- if (needs_filter(p + i, stride, thresh)) {
- do_filter2(p + i, stride);
- }
- }
-}
-
-static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
- int i;
- for (i = 0; i < 16; ++i) {
- if (needs_filter(p + i * stride, 1, thresh)) {
- do_filter2(p + i * stride, 1);
- }
- }
-}
-
-static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4 * stride;
- SimpleVFilter16(p, stride, thresh);
- }
-}
-
-static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4;
- SimpleHFilter16(p, stride, thresh);
- }
-}
-
-//------------------------------------------------------------------------------
-// Complex In-loop filtering (Paragraph 15.3)
-
-static WEBP_INLINE void FilterLoop26(uint8_t* p,
- int hstride, int vstride, int size,
- int thresh, int ithresh, int hev_thresh) {
- while (size-- > 0) {
- if (needs_filter2(p, hstride, thresh, ithresh)) {
- if (hev(p, hstride, hev_thresh)) {
- do_filter2(p, hstride);
- } else {
- do_filter6(p, hstride);
- }
- }
- p += vstride;
- }
-}
-
-static WEBP_INLINE void FilterLoop24(uint8_t* p,
- int hstride, int vstride, int size,
- int thresh, int ithresh, int hev_thresh) {
- while (size-- > 0) {
- if (needs_filter2(p, hstride, thresh, ithresh)) {
- if (hev(p, hstride, hev_thresh)) {
- do_filter2(p, hstride);
- } else {
- do_filter4(p, hstride);
- }
- }
- p += vstride;
- }
-}
-
-// on macroblock edges
-static void VFilter16(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh);
-}
-
-static void HFilter16(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh);
-}
-
-// on three inner edges
-static void VFilter16i(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4 * stride;
- FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh);
- }
-}
-
-static void HFilter16i(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4;
- FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh);
- }
-}
-
-// 8-pixels wide variant, for chroma filtering
-static void VFilter8(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh);
- FilterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh);
-}
-
-static void HFilter8(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh);
- FilterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh);
-}
-
-static void VFilter8i(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
- FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
-}
-
-static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
- FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
-}
-
-//------------------------------------------------------------------------------
-
-VP8DecIdct2 VP8Transform;
-VP8DecIdct VP8TransformUV;
-VP8DecIdct VP8TransformDC;
-VP8DecIdct VP8TransformDCUV;
-
-VP8LumaFilterFunc VP8VFilter16;
-VP8LumaFilterFunc VP8HFilter16;
-VP8ChromaFilterFunc VP8VFilter8;
-VP8ChromaFilterFunc VP8HFilter8;
-VP8LumaFilterFunc VP8VFilter16i;
-VP8LumaFilterFunc VP8HFilter16i;
-VP8ChromaFilterFunc VP8VFilter8i;
-VP8ChromaFilterFunc VP8HFilter8i;
-VP8SimpleFilterFunc VP8SimpleVFilter16;
-VP8SimpleFilterFunc VP8SimpleHFilter16;
-VP8SimpleFilterFunc VP8SimpleVFilter16i;
-VP8SimpleFilterFunc VP8SimpleHFilter16i;
-
-extern void VP8DspInitSSE2(void);
-extern void VP8DspInitNEON(void);
-
-void VP8DspInit(void) {
- DspInitTables();
-
- VP8Transform = TransformTwo;
- VP8TransformUV = TransformUV;
- VP8TransformDC = TransformDC;
- VP8TransformDCUV = TransformDCUV;
-
- VP8VFilter16 = VFilter16;
- VP8HFilter16 = HFilter16;
- VP8VFilter8 = VFilter8;
- VP8HFilter8 = HFilter8;
- VP8VFilter16i = VFilter16i;
- VP8HFilter16i = HFilter16i;
- VP8VFilter8i = VFilter8i;
- VP8HFilter8i = HFilter8i;
- VP8SimpleVFilter16 = SimpleVFilter16;
- VP8SimpleHFilter16 = SimpleHFilter16;
- VP8SimpleVFilter16i = SimpleVFilter16i;
- VP8SimpleHFilter16i = SimpleHFilter16i;
-
- // If defined, use CPUInfo() to overwrite some pointers with faster versions.
- if (VP8GetCPUInfo) {
-#if defined(WEBP_USE_SSE2)
- if (VP8GetCPUInfo(kSSE2)) {
- VP8DspInitSSE2();
- }
-#elif defined(WEBP_USE_NEON)
- if (VP8GetCPUInfo(kNEON)) {
- VP8DspInitNEON();
- }
-#endif
- }
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/dec_neon.c b/src/third_party/libwebp/dsp/dec_neon.c
deleted file mode 100644
index 5dcd3b7..0000000
--- a/src/third_party/libwebp/dsp/dec_neon.c
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// ARM NEON version of dsp functions and loop filtering.
-//
-// Authors: Somnath Banerjee (somnath@google.com)
-// Johann Koenig (johannkoenig@google.com)
-
-#include "./dsp.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if defined(WEBP_USE_NEON)
-
-#include "../dec/vp8i.h"
-
-#define QRegs "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", \
- "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
-
-#define FLIP_SIGN_BIT2(a, b, s) \
- "veor " #a "," #a "," #s " \n" \
- "veor " #b "," #b "," #s " \n" \
-
-#define FLIP_SIGN_BIT4(a, b, c, d, s) \
- FLIP_SIGN_BIT2(a, b, s) \
- FLIP_SIGN_BIT2(c, d, s) \
-
-#define NEEDS_FILTER(p1, p0, q0, q1, thresh, mask) \
- "vabd.u8 q15," #p0 "," #q0 " \n" /* abs(p0 - q0) */ \
- "vabd.u8 q14," #p1 "," #q1 " \n" /* abs(p1 - q1) */ \
- "vqadd.u8 q15, q15, q15 \n" /* abs(p0 - q0) * 2 */ \
- "vshr.u8 q14, q14, #1 \n" /* abs(p1 - q1) / 2 */ \
- "vqadd.u8 q15, q15, q14 \n" /* abs(p0 - q0) * 2 + abs(p1 - q1) / 2 */ \
- "vdup.8 q14, " #thresh " \n" \
- "vcge.u8 " #mask ", q14, q15 \n" /* mask <= thresh */
-
-#define GET_BASE_DELTA(p1, p0, q0, q1, o) \
- "vqsub.s8 q15," #q0 "," #p0 " \n" /* (q0 - p0) */ \
- "vqsub.s8 " #o "," #p1 "," #q1 " \n" /* (p1 - q1) */ \
- "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 1 * (p0 - q0) */ \
- "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 2 * (p0 - q0) */ \
- "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 3 * (p0 - q0) */
-
-#define DO_SIMPLE_FILTER(p0, q0, fl) \
- "vmov.i8 q15, #0x03 \n" \
- "vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 3 */ \
- "vshr.s8 q15, q15, #3 \n" /* filter1 >> 3 */ \
- "vqadd.s8 " #p0 "," #p0 ", q15 \n" /* p0 += filter1 */ \
- \
- "vmov.i8 q15, #0x04 \n" \
- "vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 4 */ \
- "vshr.s8 q15, q15, #3 \n" /* filter2 >> 3 */ \
- "vqsub.s8 " #q0 "," #q0 ", q15 \n" /* q0 -= filter2 */
-
-// Applies filter on 2 pixels (p0 and q0)
-#define DO_FILTER2(p1, p0, q0, q1, thresh) \
- NEEDS_FILTER(p1, p0, q0, q1, thresh, q9) /* filter mask in q9 */ \
- "vmov.i8 q10, #0x80 \n" /* sign bit */ \
- FLIP_SIGN_BIT4(p1, p0, q0, q1, q10) /* convert to signed value */ \
- GET_BASE_DELTA(p1, p0, q0, q1, q11) /* get filter level */ \
- "vand q9, q9, q11 \n" /* apply filter mask */ \
- DO_SIMPLE_FILTER(p0, q0, q9) /* apply filter */ \
- FLIP_SIGN_BIT2(p0, q0, q10)
-
-// Load/Store vertical edge
-#define LOAD8x4(c1, c2, c3, c4, b1, b2, stride) \
- "vld4.8 {" #c1"[0], " #c2"[0], " #c3"[0], " #c4"[0]}," #b1 "," #stride"\n" \
- "vld4.8 {" #c1"[1], " #c2"[1], " #c3"[1], " #c4"[1]}," #b2 "," #stride"\n" \
- "vld4.8 {" #c1"[2], " #c2"[2], " #c3"[2], " #c4"[2]}," #b1 "," #stride"\n" \
- "vld4.8 {" #c1"[3], " #c2"[3], " #c3"[3], " #c4"[3]}," #b2 "," #stride"\n" \
- "vld4.8 {" #c1"[4], " #c2"[4], " #c3"[4], " #c4"[4]}," #b1 "," #stride"\n" \
- "vld4.8 {" #c1"[5], " #c2"[5], " #c3"[5], " #c4"[5]}," #b2 "," #stride"\n" \
- "vld4.8 {" #c1"[6], " #c2"[6], " #c3"[6], " #c4"[6]}," #b1 "," #stride"\n" \
- "vld4.8 {" #c1"[7], " #c2"[7], " #c3"[7], " #c4"[7]}," #b2 "," #stride"\n"
-
-#define STORE8x2(c1, c2, p, stride) \
- "vst2.8 {" #c1"[0], " #c2"[0]}," #p "," #stride " \n" \
- "vst2.8 {" #c1"[1], " #c2"[1]}," #p "," #stride " \n" \
- "vst2.8 {" #c1"[2], " #c2"[2]}," #p "," #stride " \n" \
- "vst2.8 {" #c1"[3], " #c2"[3]}," #p "," #stride " \n" \
- "vst2.8 {" #c1"[4], " #c2"[4]}," #p "," #stride " \n" \
- "vst2.8 {" #c1"[5], " #c2"[5]}," #p "," #stride " \n" \
- "vst2.8 {" #c1"[6], " #c2"[6]}," #p "," #stride " \n" \
- "vst2.8 {" #c1"[7], " #c2"[7]}," #p "," #stride " \n"
-
-//-----------------------------------------------------------------------------
-// Simple In-loop filtering (Paragraph 15.2)
-
-static void SimpleVFilter16NEON(uint8_t* p, int stride, int thresh) {
- __asm__ volatile (
- "sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride
-
- "vld1.u8 {q1}, [%[p]], %[stride] \n" // p1
- "vld1.u8 {q2}, [%[p]], %[stride] \n" // p0
- "vld1.u8 {q3}, [%[p]], %[stride] \n" // q0
- "vld1.u8 {q4}, [%[p]] \n" // q1
-
- DO_FILTER2(q1, q2, q3, q4, %[thresh])
-
- "sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride
-
- "vst1.u8 {q2}, [%[p]], %[stride] \n" // store op0
- "vst1.u8 {q3}, [%[p]] \n" // store oq0
- : [p] "+r"(p)
- : [stride] "r"(stride), [thresh] "r"(thresh)
- : "memory", QRegs
- );
-}
-
-static void SimpleHFilter16NEON(uint8_t* p, int stride, int thresh) {
- __asm__ volatile (
- "sub r4, %[p], #2 \n" // base1 = p - 2
- "lsl r6, %[stride], #1 \n" // r6 = 2 * stride
- "add r5, r4, %[stride] \n" // base2 = base1 + stride
-
- LOAD8x4(d2, d3, d4, d5, [r4], [r5], r6)
- LOAD8x4(d6, d7, d8, d9, [r4], [r5], r6)
- "vswp d3, d6 \n" // p1:q1 p0:q3
- "vswp d5, d8 \n" // q0:q2 q1:q4
- "vswp q2, q3 \n" // p1:q1 p0:q2 q0:q3 q1:q4
-
- DO_FILTER2(q1, q2, q3, q4, %[thresh])
-
- "sub %[p], %[p], #1 \n" // p - 1
-
- "vswp d5, d6 \n"
- STORE8x2(d4, d5, [%[p]], %[stride])
- STORE8x2(d6, d7, [%[p]], %[stride])
-
- : [p] "+r"(p)
- : [stride] "r"(stride), [thresh] "r"(thresh)
- : "memory", "r4", "r5", "r6", QRegs
- );
-}
-
-static void SimpleVFilter16iNEON(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4 * stride;
- SimpleVFilter16NEON(p, stride, thresh);
- }
-}
-
-static void SimpleHFilter16iNEON(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4;
- SimpleHFilter16NEON(p, stride, thresh);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Inverse transforms (Paragraph 14.4)
-
-static void TransformOneNEON(const int16_t *in, uint8_t *dst) {
- const int kBPS = BPS;
- const int16_t constants[] = {20091, 17734, 0, 0};
- /* kC1, kC2. Padded because vld1.16 loads 8 bytes
- * Technically these are unsigned but vqdmulh is only available in signed.
- * vqdmulh returns high half (effectively >> 16) but also doubles the value,
- * changing the >> 16 to >> 15 and requiring an additional >> 1.
- * We use this to our advantage with kC2. The canonical value is 35468.
- * However, the high bit is set so treating it as signed will give incorrect
- * results. We avoid this by down shifting by 1 here to clear the highest bit.
- * Combined with the doubling effect of vqdmulh we get >> 16.
- * This can not be applied to kC1 because the lowest bit is set. Down shifting
- * the constant would reduce precision.
- */
-
- /* libwebp uses a trick to avoid some extra addition that libvpx does.
- * Instead of:
- * temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1) >> 16);
- * libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the
- * same issue with kC1 and vqdmulh that we work around by down shifting kC2
- */
-
- /* Adapted from libvpx: vp8/common/arm/neon/shortidct4x4llm_neon.asm */
- __asm__ volatile (
- "vld1.16 {q1, q2}, [%[in]] \n"
- "vld1.16 {d0}, [%[constants]] \n"
-
- /* d2: in[0]
- * d3: in[8]
- * d4: in[4]
- * d5: in[12]
- */
- "vswp d3, d4 \n"
-
- /* q8 = {in[4], in[12]} * kC1 * 2 >> 16
- * q9 = {in[4], in[12]} * kC2 >> 16
- */
- "vqdmulh.s16 q8, q2, d0[0] \n"
- "vqdmulh.s16 q9, q2, d0[1] \n"
-
- /* d22 = a = in[0] + in[8]
- * d23 = b = in[0] - in[8]
- */
- "vqadd.s16 d22, d2, d3 \n"
- "vqsub.s16 d23, d2, d3 \n"
-
- /* The multiplication should be x * kC1 >> 16
- * However, with vqdmulh we get x * kC1 * 2 >> 16
- * (multiply, double, return high half)
- * We avoided this in kC2 by pre-shifting the constant.
- * q8 = in[4]/[12] * kC1 >> 16
- */
- "vshr.s16 q8, q8, #1 \n"
-
- /* Add {in[4], in[12]} back after the multiplication. This is handled by
- * adding 1 << 16 to kC1 in the libwebp C code.
- */
- "vqadd.s16 q8, q2, q8 \n"
-
- /* d20 = c = in[4]*kC2 - in[12]*kC1
- * d21 = d = in[4]*kC1 + in[12]*kC2
- */
- "vqsub.s16 d20, d18, d17 \n"
- "vqadd.s16 d21, d19, d16 \n"
-
- /* d2 = tmp[0] = a + d
- * d3 = tmp[1] = b + c
- * d4 = tmp[2] = b - c
- * d5 = tmp[3] = a - d
- */
- "vqadd.s16 d2, d22, d21 \n"
- "vqadd.s16 d3, d23, d20 \n"
- "vqsub.s16 d4, d23, d20 \n"
- "vqsub.s16 d5, d22, d21 \n"
-
- "vzip.16 q1, q2 \n"
- "vzip.16 q1, q2 \n"
-
- "vswp d3, d4 \n"
-
- /* q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16
- * q9 = {tmp[4], tmp[12]} * kC2 >> 16
- */
- "vqdmulh.s16 q8, q2, d0[0] \n"
- "vqdmulh.s16 q9, q2, d0[1] \n"
-
- /* d22 = a = tmp[0] + tmp[8]
- * d23 = b = tmp[0] - tmp[8]
- */
- "vqadd.s16 d22, d2, d3 \n"
- "vqsub.s16 d23, d2, d3 \n"
-
- /* See long winded explanations prior */
- "vshr.s16 q8, q8, #1 \n"
- "vqadd.s16 q8, q2, q8 \n"
-
- /* d20 = c = in[4]*kC2 - in[12]*kC1
- * d21 = d = in[4]*kC1 + in[12]*kC2
- */
- "vqsub.s16 d20, d18, d17 \n"
- "vqadd.s16 d21, d19, d16 \n"
-
- /* d2 = tmp[0] = a + d
- * d3 = tmp[1] = b + c
- * d4 = tmp[2] = b - c
- * d5 = tmp[3] = a - d
- */
- "vqadd.s16 d2, d22, d21 \n"
- "vqadd.s16 d3, d23, d20 \n"
- "vqsub.s16 d4, d23, d20 \n"
- "vqsub.s16 d5, d22, d21 \n"
-
- "vld1.32 d6[0], [%[dst]], %[kBPS] \n"
- "vld1.32 d6[1], [%[dst]], %[kBPS] \n"
- "vld1.32 d7[0], [%[dst]], %[kBPS] \n"
- "vld1.32 d7[1], [%[dst]], %[kBPS] \n"
-
- "sub %[dst], %[dst], %[kBPS], lsl #2 \n"
-
- /* (val) + 4 >> 3 */
- "vrshr.s16 d2, d2, #3 \n"
- "vrshr.s16 d3, d3, #3 \n"
- "vrshr.s16 d4, d4, #3 \n"
- "vrshr.s16 d5, d5, #3 \n"
-
- "vzip.16 q1, q2 \n"
- "vzip.16 q1, q2 \n"
-
- /* Must accumulate before saturating */
- "vmovl.u8 q8, d6 \n"
- "vmovl.u8 q9, d7 \n"
-
- "vqadd.s16 q1, q1, q8 \n"
- "vqadd.s16 q2, q2, q9 \n"
-
- "vqmovun.s16 d0, q1 \n"
- "vqmovun.s16 d1, q2 \n"
-
- "vst1.32 d0[0], [%[dst]], %[kBPS] \n"
- "vst1.32 d0[1], [%[dst]], %[kBPS] \n"
- "vst1.32 d1[0], [%[dst]], %[kBPS] \n"
- "vst1.32 d1[1], [%[dst]] \n"
-
- : [in] "+r"(in), [dst] "+r"(dst) /* modified registers */
- : [kBPS] "r"(kBPS), [constants] "r"(constants) /* constants */
- : "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" /* clobbered */
- );
-}
-
-static void TransformTwoNEON(const int16_t* in, uint8_t* dst, int do_two) {
- TransformOneNEON(in, dst);
- if (do_two) {
- TransformOneNEON(in + 16, dst + 4);
- }
-}
-
-static void TransformWHT(const int16_t* in, int16_t* out) {
- const int kStep = 32; // The store is only incrementing the pointer as if we
- // had stored a single byte.
- __asm__ volatile (
- // part 1
- // load data into q0, q1
- "vld1.16 {q0, q1}, [%[in]] \n"
-
- "vaddl.s16 q2, d0, d3 \n" // a0 = in[0] + in[12]
- "vaddl.s16 q3, d1, d2 \n" // a1 = in[4] + in[8]
- "vsubl.s16 q4, d1, d2 \n" // a2 = in[4] - in[8]
- "vsubl.s16 q5, d0, d3 \n" // a3 = in[0] - in[12]
-
- "vadd.s32 q0, q2, q3 \n" // tmp[0] = a0 + a1
- "vsub.s32 q2, q2, q3 \n" // tmp[8] = a0 - a1
- "vadd.s32 q1, q5, q4 \n" // tmp[4] = a3 + a2
- "vsub.s32 q3, q5, q4 \n" // tmp[12] = a3 - a2
-
- // Transpose
- // q0 = tmp[0, 4, 8, 12], q1 = tmp[2, 6, 10, 14]
- // q2 = tmp[1, 5, 9, 13], q3 = tmp[3, 7, 11, 15]
- "vswp d1, d4 \n" // vtrn.64 q0, q2
- "vswp d3, d6 \n" // vtrn.64 q1, q3
- "vtrn.32 q0, q1 \n"
- "vtrn.32 q2, q3 \n"
-
- "vmov.s32 q4, #3 \n" // dc = 3
- "vadd.s32 q0, q0, q4 \n" // dc = tmp[0] + 3
- "vadd.s32 q6, q0, q3 \n" // a0 = dc + tmp[3]
- "vadd.s32 q7, q1, q2 \n" // a1 = tmp[1] + tmp[2]
- "vsub.s32 q8, q1, q2 \n" // a2 = tmp[1] - tmp[2]
- "vsub.s32 q9, q0, q3 \n" // a3 = dc - tmp[3]
-
- "vadd.s32 q0, q6, q7 \n"
- "vshrn.s32 d0, q0, #3 \n" // (a0 + a1) >> 3
- "vadd.s32 q1, q9, q8 \n"
- "vshrn.s32 d1, q1, #3 \n" // (a3 + a2) >> 3
- "vsub.s32 q2, q6, q7 \n"
- "vshrn.s32 d2, q2, #3 \n" // (a0 - a1) >> 3
- "vsub.s32 q3, q9, q8 \n"
- "vshrn.s32 d3, q3, #3 \n" // (a3 - a2) >> 3
-
- // set the results to output
- "vst1.16 d0[0], [%[out]], %[kStep] \n"
- "vst1.16 d1[0], [%[out]], %[kStep] \n"
- "vst1.16 d2[0], [%[out]], %[kStep] \n"
- "vst1.16 d3[0], [%[out]], %[kStep] \n"
- "vst1.16 d0[1], [%[out]], %[kStep] \n"
- "vst1.16 d1[1], [%[out]], %[kStep] \n"
- "vst1.16 d2[1], [%[out]], %[kStep] \n"
- "vst1.16 d3[1], [%[out]], %[kStep] \n"
- "vst1.16 d0[2], [%[out]], %[kStep] \n"
- "vst1.16 d1[2], [%[out]], %[kStep] \n"
- "vst1.16 d2[2], [%[out]], %[kStep] \n"
- "vst1.16 d3[2], [%[out]], %[kStep] \n"
- "vst1.16 d0[3], [%[out]], %[kStep] \n"
- "vst1.16 d1[3], [%[out]], %[kStep] \n"
- "vst1.16 d2[3], [%[out]], %[kStep] \n"
- "vst1.16 d3[3], [%[out]], %[kStep] \n"
-
- : [out] "+r"(out) // modified registers
- : [in] "r"(in), [kStep] "r"(kStep) // constants
- : "memory", "q0", "q1", "q2", "q3", "q4",
- "q5", "q6", "q7", "q8", "q9" // clobbered
- );
-}
-
-#endif // WEBP_USE_NEON
-
-//------------------------------------------------------------------------------
-// Entry point
-
-extern void VP8DspInitNEON(void);
-
-void VP8DspInitNEON(void) {
-#if defined(WEBP_USE_NEON)
- VP8Transform = TransformTwoNEON;
- VP8TransformWHT = TransformWHT;
-
- VP8SimpleVFilter16 = SimpleVFilter16NEON;
- VP8SimpleHFilter16 = SimpleHFilter16NEON;
- VP8SimpleVFilter16i = SimpleVFilter16iNEON;
- VP8SimpleHFilter16i = SimpleHFilter16iNEON;
-#endif // WEBP_USE_NEON
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/dec_sse2.c b/src/third_party/libwebp/dsp/dec_sse2.c
deleted file mode 100644
index 6be9467..0000000
--- a/src/third_party/libwebp/dsp/dec_sse2.c
+++ /dev/null
@@ -1,910 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// SSE2 version of some decoding functions (idct, loop filtering).
-//
-// Author: somnath@google.com (Somnath Banerjee)
-// cduvivier@google.com (Christian Duvivier)
-
-#include "./dsp.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if defined(WEBP_USE_SSE2)
-
-#include <emmintrin.h>
-#include "../dec/vp8i.h"
-
-//------------------------------------------------------------------------------
-// Transforms (Paragraph 14.4)
-
-static void TransformSSE2(const int16_t* in, uint8_t* dst, int do_two) {
- // This implementation makes use of 16-bit fixed point versions of two
- // multiply constants:
- // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
- // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
- //
- // To be able to use signed 16-bit integers, we use the following trick to
- // have constants within range:
- // - Associated constants are obtained by subtracting the 16-bit fixed point
- // version of one:
- // k = K - (1 << 16) => K = k + (1 << 16)
- // K1 = 85267 => k1 = 20091
- // K2 = 35468 => k2 = -30068
- // - The multiplication of a variable by a constant become the sum of the
- // variable and the multiplication of that variable by the associated
- // constant:
- // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
- const __m128i k1 = _mm_set1_epi16(20091);
- const __m128i k2 = _mm_set1_epi16(-30068);
- __m128i T0, T1, T2, T3;
-
- // Load and concatenate the transform coefficients (we'll do two transforms
- // in parallel). In the case of only one transform, the second half of the
- // vectors will just contain random value we'll never use nor store.
- __m128i in0, in1, in2, in3;
- {
- in0 = _mm_loadl_epi64((__m128i*)&in[0]);
- in1 = _mm_loadl_epi64((__m128i*)&in[4]);
- in2 = _mm_loadl_epi64((__m128i*)&in[8]);
- in3 = _mm_loadl_epi64((__m128i*)&in[12]);
- // a00 a10 a20 a30 x x x x
- // a01 a11 a21 a31 x x x x
- // a02 a12 a22 a32 x x x x
- // a03 a13 a23 a33 x x x x
- if (do_two) {
- const __m128i inB0 = _mm_loadl_epi64((__m128i*)&in[16]);
- const __m128i inB1 = _mm_loadl_epi64((__m128i*)&in[20]);
- const __m128i inB2 = _mm_loadl_epi64((__m128i*)&in[24]);
- const __m128i inB3 = _mm_loadl_epi64((__m128i*)&in[28]);
- in0 = _mm_unpacklo_epi64(in0, inB0);
- in1 = _mm_unpacklo_epi64(in1, inB1);
- in2 = _mm_unpacklo_epi64(in2, inB2);
- in3 = _mm_unpacklo_epi64(in3, inB3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
- }
-
- // Vertical pass and subsequent transpose.
- {
- // First pass, c and d calculations are longer because of the "trick"
- // multiplications.
- const __m128i a = _mm_add_epi16(in0, in2);
- const __m128i b = _mm_sub_epi16(in0, in2);
- // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
- const __m128i c1 = _mm_mulhi_epi16(in1, k2);
- const __m128i c2 = _mm_mulhi_epi16(in3, k1);
- const __m128i c3 = _mm_sub_epi16(in1, in3);
- const __m128i c4 = _mm_sub_epi16(c1, c2);
- const __m128i c = _mm_add_epi16(c3, c4);
- // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
- const __m128i d1 = _mm_mulhi_epi16(in1, k1);
- const __m128i d2 = _mm_mulhi_epi16(in3, k2);
- const __m128i d3 = _mm_add_epi16(in1, in3);
- const __m128i d4 = _mm_add_epi16(d1, d2);
- const __m128i d = _mm_add_epi16(d3, d4);
-
- // Second pass.
- const __m128i tmp0 = _mm_add_epi16(a, d);
- const __m128i tmp1 = _mm_add_epi16(b, c);
- const __m128i tmp2 = _mm_sub_epi16(b, c);
- const __m128i tmp3 = _mm_sub_epi16(a, d);
-
- // Transpose the two 4x4.
- // a00 a01 a02 a03 b00 b01 b02 b03
- // a10 a11 a12 a13 b10 b11 b12 b13
- // a20 a21 a22 a23 b20 b21 b22 b23
- // a30 a31 a32 a33 b30 b31 b32 b33
- const __m128i transpose0_0 = _mm_unpacklo_epi16(tmp0, tmp1);
- const __m128i transpose0_1 = _mm_unpacklo_epi16(tmp2, tmp3);
- const __m128i transpose0_2 = _mm_unpackhi_epi16(tmp0, tmp1);
- const __m128i transpose0_3 = _mm_unpackhi_epi16(tmp2, tmp3);
- // a00 a10 a01 a11 a02 a12 a03 a13
- // a20 a30 a21 a31 a22 a32 a23 a33
- // b00 b10 b01 b11 b02 b12 b03 b13
- // b20 b30 b21 b31 b22 b32 b23 b33
- const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
- const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
- // a00 a10 a20 a30 a01 a11 a21 a31
- // b00 b10 b20 b30 b01 b11 b21 b31
- // a02 a12 a22 a32 a03 a13 a23 a33
- // b02 b12 a22 b32 b03 b13 b23 b33
- T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
- T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
- T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
- T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
-
- // Horizontal pass and subsequent transpose.
- {
- // First pass, c and d calculations are longer because of the "trick"
- // multiplications.
- const __m128i four = _mm_set1_epi16(4);
- const __m128i dc = _mm_add_epi16(T0, four);
- const __m128i a = _mm_add_epi16(dc, T2);
- const __m128i b = _mm_sub_epi16(dc, T2);
- // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
- const __m128i c1 = _mm_mulhi_epi16(T1, k2);
- const __m128i c2 = _mm_mulhi_epi16(T3, k1);
- const __m128i c3 = _mm_sub_epi16(T1, T3);
- const __m128i c4 = _mm_sub_epi16(c1, c2);
- const __m128i c = _mm_add_epi16(c3, c4);
- // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
- const __m128i d1 = _mm_mulhi_epi16(T1, k1);
- const __m128i d2 = _mm_mulhi_epi16(T3, k2);
- const __m128i d3 = _mm_add_epi16(T1, T3);
- const __m128i d4 = _mm_add_epi16(d1, d2);
- const __m128i d = _mm_add_epi16(d3, d4);
-
- // Second pass.
- const __m128i tmp0 = _mm_add_epi16(a, d);
- const __m128i tmp1 = _mm_add_epi16(b, c);
- const __m128i tmp2 = _mm_sub_epi16(b, c);
- const __m128i tmp3 = _mm_sub_epi16(a, d);
- const __m128i shifted0 = _mm_srai_epi16(tmp0, 3);
- const __m128i shifted1 = _mm_srai_epi16(tmp1, 3);
- const __m128i shifted2 = _mm_srai_epi16(tmp2, 3);
- const __m128i shifted3 = _mm_srai_epi16(tmp3, 3);
-
- // Transpose the two 4x4.
- // a00 a01 a02 a03 b00 b01 b02 b03
- // a10 a11 a12 a13 b10 b11 b12 b13
- // a20 a21 a22 a23 b20 b21 b22 b23
- // a30 a31 a32 a33 b30 b31 b32 b33
- const __m128i transpose0_0 = _mm_unpacklo_epi16(shifted0, shifted1);
- const __m128i transpose0_1 = _mm_unpacklo_epi16(shifted2, shifted3);
- const __m128i transpose0_2 = _mm_unpackhi_epi16(shifted0, shifted1);
- const __m128i transpose0_3 = _mm_unpackhi_epi16(shifted2, shifted3);
- // a00 a10 a01 a11 a02 a12 a03 a13
- // a20 a30 a21 a31 a22 a32 a23 a33
- // b00 b10 b01 b11 b02 b12 b03 b13
- // b20 b30 b21 b31 b22 b32 b23 b33
- const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
- const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
- // a00 a10 a20 a30 a01 a11 a21 a31
- // b00 b10 b20 b30 b01 b11 b21 b31
- // a02 a12 a22 a32 a03 a13 a23 a33
- // b02 b12 a22 b32 b03 b13 b23 b33
- T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
- T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
- T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
- T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
-
- // Add inverse transform to 'dst' and store.
- {
- const __m128i zero = _mm_setzero_si128();
- // Load the reference(s).
- __m128i dst0, dst1, dst2, dst3;
- if (do_two) {
- // Load eight bytes/pixels per line.
- dst0 = _mm_loadl_epi64((__m128i*)&dst[0 * BPS]);
- dst1 = _mm_loadl_epi64((__m128i*)&dst[1 * BPS]);
- dst2 = _mm_loadl_epi64((__m128i*)&dst[2 * BPS]);
- dst3 = _mm_loadl_epi64((__m128i*)&dst[3 * BPS]);
- } else {
- // Load four bytes/pixels per line.
- dst0 = _mm_cvtsi32_si128(*(int*)&dst[0 * BPS]);
- dst1 = _mm_cvtsi32_si128(*(int*)&dst[1 * BPS]);
- dst2 = _mm_cvtsi32_si128(*(int*)&dst[2 * BPS]);
- dst3 = _mm_cvtsi32_si128(*(int*)&dst[3 * BPS]);
- }
- // Convert to 16b.
- dst0 = _mm_unpacklo_epi8(dst0, zero);
- dst1 = _mm_unpacklo_epi8(dst1, zero);
- dst2 = _mm_unpacklo_epi8(dst2, zero);
- dst3 = _mm_unpacklo_epi8(dst3, zero);
- // Add the inverse transform(s).
- dst0 = _mm_add_epi16(dst0, T0);
- dst1 = _mm_add_epi16(dst1, T1);
- dst2 = _mm_add_epi16(dst2, T2);
- dst3 = _mm_add_epi16(dst3, T3);
- // Unsigned saturate to 8b.
- dst0 = _mm_packus_epi16(dst0, dst0);
- dst1 = _mm_packus_epi16(dst1, dst1);
- dst2 = _mm_packus_epi16(dst2, dst2);
- dst3 = _mm_packus_epi16(dst3, dst3);
- // Store the results.
- if (do_two) {
- // Store eight bytes/pixels per line.
- _mm_storel_epi64((__m128i*)&dst[0 * BPS], dst0);
- _mm_storel_epi64((__m128i*)&dst[1 * BPS], dst1);
- _mm_storel_epi64((__m128i*)&dst[2 * BPS], dst2);
- _mm_storel_epi64((__m128i*)&dst[3 * BPS], dst3);
- } else {
- // Store four bytes/pixels per line.
- *((int32_t *)&dst[0 * BPS]) = _mm_cvtsi128_si32(dst0);
- *((int32_t *)&dst[1 * BPS]) = _mm_cvtsi128_si32(dst1);
- *((int32_t *)&dst[2 * BPS]) = _mm_cvtsi128_si32(dst2);
- *((int32_t *)&dst[3 * BPS]) = _mm_cvtsi128_si32(dst3);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// Loop Filter (Paragraph 15)
-
-// Compute abs(p - q) = subs(p - q) OR subs(q - p)
-#define MM_ABS(p, q) _mm_or_si128( \
- _mm_subs_epu8((q), (p)), \
- _mm_subs_epu8((p), (q)))
-
-// Shift each byte of "a" by N bits while preserving by the sign bit.
-//
-// It first shifts the lower bytes of the words and then the upper bytes and
-// then merges the results together.
-#define SIGNED_SHIFT_N(a, N) { \
- __m128i t = a; \
- t = _mm_slli_epi16(t, 8); \
- t = _mm_srai_epi16(t, N); \
- t = _mm_srli_epi16(t, 8); \
- \
- a = _mm_srai_epi16(a, N + 8); \
- a = _mm_slli_epi16(a, 8); \
- \
- a = _mm_or_si128(t, a); \
-}
-
-#define FLIP_SIGN_BIT2(a, b) { \
- a = _mm_xor_si128(a, sign_bit); \
- b = _mm_xor_si128(b, sign_bit); \
-}
-
-#define FLIP_SIGN_BIT4(a, b, c, d) { \
- FLIP_SIGN_BIT2(a, b); \
- FLIP_SIGN_BIT2(c, d); \
-}
-
-#define GET_NOTHEV(p1, p0, q0, q1, hev_thresh, not_hev) { \
- const __m128i zero = _mm_setzero_si128(); \
- const __m128i t_1 = MM_ABS(p1, p0); \
- const __m128i t_2 = MM_ABS(q1, q0); \
- \
- const __m128i h = _mm_set1_epi8(hev_thresh); \
- const __m128i t_3 = _mm_subs_epu8(t_1, h); /* abs(p1 - p0) - hev_tresh */ \
- const __m128i t_4 = _mm_subs_epu8(t_2, h); /* abs(q1 - q0) - hev_tresh */ \
- \
- not_hev = _mm_or_si128(t_3, t_4); \
- not_hev = _mm_cmpeq_epi8(not_hev, zero); /* not_hev <= t1 && not_hev <= t2 */\
-}
-
-#define GET_BASE_DELTA(p1, p0, q0, q1, o) { \
- const __m128i qp0 = _mm_subs_epi8(q0, p0); /* q0 - p0 */ \
- o = _mm_subs_epi8(p1, q1); /* p1 - q1 */ \
- o = _mm_adds_epi8(o, qp0); /* p1 - q1 + 1 * (q0 - p0) */ \
- o = _mm_adds_epi8(o, qp0); /* p1 - q1 + 2 * (q0 - p0) */ \
- o = _mm_adds_epi8(o, qp0); /* p1 - q1 + 3 * (q0 - p0) */ \
-}
-
-#define DO_SIMPLE_FILTER(p0, q0, fl) { \
- const __m128i three = _mm_set1_epi8(3); \
- const __m128i four = _mm_set1_epi8(4); \
- __m128i v3 = _mm_adds_epi8(fl, three); \
- __m128i v4 = _mm_adds_epi8(fl, four); \
- \
- /* Do +4 side */ \
- SIGNED_SHIFT_N(v4, 3); /* v4 >> 3 */ \
- q0 = _mm_subs_epi8(q0, v4); /* q0 -= v4 */ \
- \
- /* Now do +3 side */ \
- SIGNED_SHIFT_N(v3, 3); /* v3 >> 3 */ \
- p0 = _mm_adds_epi8(p0, v3); /* p0 += v3 */ \
-}
-
-// Updates values of 2 pixels at MB edge during complex filtering.
-// Update operations:
-// q = q - delta and p = p + delta; where delta = [(a_hi >> 7), (a_lo >> 7)]
-#define UPDATE_2PIXELS(pi, qi, a_lo, a_hi) { \
- const __m128i a_lo7 = _mm_srai_epi16(a_lo, 7); \
- const __m128i a_hi7 = _mm_srai_epi16(a_hi, 7); \
- const __m128i delta = _mm_packs_epi16(a_lo7, a_hi7); \
- pi = _mm_adds_epi8(pi, delta); \
- qi = _mm_subs_epi8(qi, delta); \
-}
-
-static void NeedsFilter(const __m128i* p1, const __m128i* p0, const __m128i* q0,
- const __m128i* q1, int thresh, __m128i *mask) {
- __m128i t1 = MM_ABS(*p1, *q1); // abs(p1 - q1)
- *mask = _mm_set1_epi8(0xFE);
- t1 = _mm_and_si128(t1, *mask); // set lsb of each byte to zero
- t1 = _mm_srli_epi16(t1, 1); // abs(p1 - q1) / 2
-
- *mask = MM_ABS(*p0, *q0); // abs(p0 - q0)
- *mask = _mm_adds_epu8(*mask, *mask); // abs(p0 - q0) * 2
- *mask = _mm_adds_epu8(*mask, t1); // abs(p0 - q0) * 2 + abs(p1 - q1) / 2
-
- t1 = _mm_set1_epi8(thresh);
- *mask = _mm_subs_epu8(*mask, t1); // mask <= thresh
- *mask = _mm_cmpeq_epi8(*mask, _mm_setzero_si128());
-}
-
-//------------------------------------------------------------------------------
-// Edge filtering functions
-
-// Applies filter on 2 pixels (p0 and q0)
-static WEBP_INLINE void DoFilter2(const __m128i* p1, __m128i* p0, __m128i* q0,
- const __m128i* q1, int thresh) {
- __m128i a, mask;
- const __m128i sign_bit = _mm_set1_epi8(0x80);
- const __m128i p1s = _mm_xor_si128(*p1, sign_bit);
- const __m128i q1s = _mm_xor_si128(*q1, sign_bit);
-
- NeedsFilter(p1, p0, q0, q1, thresh, &mask);
-
- // convert to signed values
- FLIP_SIGN_BIT2(*p0, *q0);
-
- GET_BASE_DELTA(p1s, *p0, *q0, q1s, a);
- a = _mm_and_si128(a, mask); // mask filter values we don't care about
- DO_SIMPLE_FILTER(*p0, *q0, a);
-
- // unoffset
- FLIP_SIGN_BIT2(*p0, *q0);
-}
-
-// Applies filter on 4 pixels (p1, p0, q0 and q1)
-static WEBP_INLINE void DoFilter4(__m128i* p1, __m128i *p0,
- __m128i* q0, __m128i* q1,
- const __m128i* mask, int hev_thresh) {
- __m128i not_hev;
- __m128i t1, t2, t3;
- const __m128i sign_bit = _mm_set1_epi8(0x80);
-
- // compute hev mask
- GET_NOTHEV(*p1, *p0, *q0, *q1, hev_thresh, not_hev);
-
- // convert to signed values
- FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
-
- t1 = _mm_subs_epi8(*p1, *q1); // p1 - q1
- t1 = _mm_andnot_si128(not_hev, t1); // hev(p1 - q1)
- t2 = _mm_subs_epi8(*q0, *p0); // q0 - p0
- t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 1 * (q0 - p0)
- t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 2 * (q0 - p0)
- t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 3 * (q0 - p0)
- t1 = _mm_and_si128(t1, *mask); // mask filter values we don't care about
-
- // Do +4 side
- t2 = _mm_set1_epi8(4);
- t2 = _mm_adds_epi8(t1, t2); // 3 * (q0 - p0) + (p1 - q1) + 4
- SIGNED_SHIFT_N(t2, 3); // (3 * (q0 - p0) + hev(p1 - q1) + 4) >> 3
- t3 = t2; // save t2
- *q0 = _mm_subs_epi8(*q0, t2); // q0 -= t2
-
- // Now do +3 side
- t2 = _mm_set1_epi8(3);
- t2 = _mm_adds_epi8(t1, t2); // +3 instead of +4
- SIGNED_SHIFT_N(t2, 3); // (3 * (q0 - p0) + hev(p1 - q1) + 3) >> 3
- *p0 = _mm_adds_epi8(*p0, t2); // p0 += t2
-
- t2 = _mm_set1_epi8(1);
- t3 = _mm_adds_epi8(t3, t2);
- SIGNED_SHIFT_N(t3, 1); // (3 * (q0 - p0) + hev(p1 - q1) + 4) >> 4
-
- t3 = _mm_and_si128(not_hev, t3); // if !hev
- *q1 = _mm_subs_epi8(*q1, t3); // q1 -= t3
- *p1 = _mm_adds_epi8(*p1, t3); // p1 += t3
-
- // unoffset
- FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
-}
-
-// Applies filter on 6 pixels (p2, p1, p0, q0, q1 and q2)
-static WEBP_INLINE void DoFilter6(__m128i *p2, __m128i* p1, __m128i *p0,
- __m128i* q0, __m128i* q1, __m128i *q2,
- const __m128i* mask, int hev_thresh) {
- __m128i a, not_hev;
- const __m128i sign_bit = _mm_set1_epi8(0x80);
-
- // compute hev mask
- GET_NOTHEV(*p1, *p0, *q0, *q1, hev_thresh, not_hev);
-
- // convert to signed values
- FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
- FLIP_SIGN_BIT2(*p2, *q2);
-
- GET_BASE_DELTA(*p1, *p0, *q0, *q1, a);
-
- { // do simple filter on pixels with hev
- const __m128i m = _mm_andnot_si128(not_hev, *mask);
- const __m128i f = _mm_and_si128(a, m);
- DO_SIMPLE_FILTER(*p0, *q0, f);
- }
- { // do strong filter on pixels with not hev
- const __m128i zero = _mm_setzero_si128();
- const __m128i nine = _mm_set1_epi16(0x0900);
- const __m128i sixty_three = _mm_set1_epi16(63);
-
- const __m128i m = _mm_and_si128(not_hev, *mask);
- const __m128i f = _mm_and_si128(a, m);
- const __m128i f_lo = _mm_unpacklo_epi8(zero, f);
- const __m128i f_hi = _mm_unpackhi_epi8(zero, f);
-
- const __m128i f9_lo = _mm_mulhi_epi16(f_lo, nine); // Filter (lo) * 9
- const __m128i f9_hi = _mm_mulhi_epi16(f_hi, nine); // Filter (hi) * 9
- const __m128i f18_lo = _mm_add_epi16(f9_lo, f9_lo); // Filter (lo) * 18
- const __m128i f18_hi = _mm_add_epi16(f9_hi, f9_hi); // Filter (hi) * 18
-
- const __m128i a2_lo = _mm_add_epi16(f9_lo, sixty_three); // Filter * 9 + 63
- const __m128i a2_hi = _mm_add_epi16(f9_hi, sixty_three); // Filter * 9 + 63
-
- const __m128i a1_lo = _mm_add_epi16(f18_lo, sixty_three); // F... * 18 + 63
- const __m128i a1_hi = _mm_add_epi16(f18_hi, sixty_three); // F... * 18 + 63
-
- const __m128i a0_lo = _mm_add_epi16(f18_lo, a2_lo); // Filter * 27 + 63
- const __m128i a0_hi = _mm_add_epi16(f18_hi, a2_hi); // Filter * 27 + 63
-
- UPDATE_2PIXELS(*p2, *q2, a2_lo, a2_hi);
- UPDATE_2PIXELS(*p1, *q1, a1_lo, a1_hi);
- UPDATE_2PIXELS(*p0, *q0, a0_lo, a0_hi);
- }
-
- // unoffset
- FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
- FLIP_SIGN_BIT2(*p2, *q2);
-}
-
-// reads 8 rows across a vertical edge.
-//
-// TODO(somnath): Investigate _mm_shuffle* also see if it can be broken into
-// two Load4x4() to avoid code duplication.
-static WEBP_INLINE void Load8x4(const uint8_t* b, int stride,
- __m128i* p, __m128i* q) {
- __m128i t1, t2;
-
- // Load 0th, 1st, 4th and 5th rows
- __m128i r0 = _mm_cvtsi32_si128(*((int*)&b[0 * stride])); // 03 02 01 00
- __m128i r1 = _mm_cvtsi32_si128(*((int*)&b[1 * stride])); // 13 12 11 10
- __m128i r4 = _mm_cvtsi32_si128(*((int*)&b[4 * stride])); // 43 42 41 40
- __m128i r5 = _mm_cvtsi32_si128(*((int*)&b[5 * stride])); // 53 52 51 50
-
- r0 = _mm_unpacklo_epi32(r0, r4); // 43 42 41 40 03 02 01 00
- r1 = _mm_unpacklo_epi32(r1, r5); // 53 52 51 50 13 12 11 10
-
- // t1 = 53 43 52 42 51 41 50 40 13 03 12 02 11 01 10 00
- t1 = _mm_unpacklo_epi8(r0, r1);
-
- // Load 2nd, 3rd, 6th and 7th rows
- r0 = _mm_cvtsi32_si128(*((int*)&b[2 * stride])); // 23 22 21 22
- r1 = _mm_cvtsi32_si128(*((int*)&b[3 * stride])); // 33 32 31 30
- r4 = _mm_cvtsi32_si128(*((int*)&b[6 * stride])); // 63 62 61 60
- r5 = _mm_cvtsi32_si128(*((int*)&b[7 * stride])); // 73 72 71 70
-
- r0 = _mm_unpacklo_epi32(r0, r4); // 63 62 61 60 23 22 21 20
- r1 = _mm_unpacklo_epi32(r1, r5); // 73 72 71 70 33 32 31 30
-
- // t2 = 73 63 72 62 71 61 70 60 33 23 32 22 31 21 30 20
- t2 = _mm_unpacklo_epi8(r0, r1);
-
- // t1 = 33 23 13 03 32 22 12 02 31 21 11 01 30 20 10 00
- // t2 = 73 63 53 43 72 62 52 42 71 61 51 41 70 60 50 40
- r0 = t1;
- t1 = _mm_unpacklo_epi16(t1, t2);
- t2 = _mm_unpackhi_epi16(r0, t2);
-
- // *p = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00
- // *q = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02
- *p = _mm_unpacklo_epi32(t1, t2);
- *q = _mm_unpackhi_epi32(t1, t2);
-}
-
-static WEBP_INLINE void Load16x4(const uint8_t* r0, const uint8_t* r8,
- int stride,
- __m128i* p1, __m128i* p0,
- __m128i* q0, __m128i* q1) {
- __m128i t1, t2;
- // Assume the pixels around the edge (|) are numbered as follows
- // 00 01 | 02 03
- // 10 11 | 12 13
- // ... | ...
- // e0 e1 | e2 e3
- // f0 f1 | f2 f3
- //
- // r0 is pointing to the 0th row (00)
- // r8 is pointing to the 8th row (80)
-
- // Load
- // p1 = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00
- // q0 = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02
- // p0 = f1 e1 d1 c1 b1 a1 91 81 f0 e0 d0 c0 b0 a0 90 80
- // q1 = f3 e3 d3 c3 b3 a3 93 83 f2 e2 d2 c2 b2 a2 92 82
- Load8x4(r0, stride, p1, q0);
- Load8x4(r8, stride, p0, q1);
-
- t1 = *p1;
- t2 = *q0;
- // p1 = f0 e0 d0 c0 b0 a0 90 80 70 60 50 40 30 20 10 00
- // p0 = f1 e1 d1 c1 b1 a1 91 81 71 61 51 41 31 21 11 01
- // q0 = f2 e2 d2 c2 b2 a2 92 82 72 62 52 42 32 22 12 02
- // q1 = f3 e3 d3 c3 b3 a3 93 83 73 63 53 43 33 23 13 03
- *p1 = _mm_unpacklo_epi64(t1, *p0);
- *p0 = _mm_unpackhi_epi64(t1, *p0);
- *q0 = _mm_unpacklo_epi64(t2, *q1);
- *q1 = _mm_unpackhi_epi64(t2, *q1);
-}
-
-static WEBP_INLINE void Store4x4(__m128i* x, uint8_t* dst, int stride) {
- int i;
- for (i = 0; i < 4; ++i, dst += stride) {
- *((int32_t*)dst) = _mm_cvtsi128_si32(*x);
- *x = _mm_srli_si128(*x, 4);
- }
-}
-
-// Transpose back and store
-static WEBP_INLINE void Store16x4(uint8_t* r0, uint8_t* r8, int stride,
- __m128i* p1, __m128i* p0,
- __m128i* q0, __m128i* q1) {
- __m128i t1;
-
- // p0 = 71 70 61 60 51 50 41 40 31 30 21 20 11 10 01 00
- // p1 = f1 f0 e1 e0 d1 d0 c1 c0 b1 b0 a1 a0 91 90 81 80
- t1 = *p0;
- *p0 = _mm_unpacklo_epi8(*p1, t1);
- *p1 = _mm_unpackhi_epi8(*p1, t1);
-
- // q0 = 73 72 63 62 53 52 43 42 33 32 23 22 13 12 03 02
- // q1 = f3 f2 e3 e2 d3 d2 c3 c2 b3 b2 a3 a2 93 92 83 82
- t1 = *q0;
- *q0 = _mm_unpacklo_epi8(t1, *q1);
- *q1 = _mm_unpackhi_epi8(t1, *q1);
-
- // p0 = 33 32 31 30 23 22 21 20 13 12 11 10 03 02 01 00
- // q0 = 73 72 71 70 63 62 61 60 53 52 51 50 43 42 41 40
- t1 = *p0;
- *p0 = _mm_unpacklo_epi16(t1, *q0);
- *q0 = _mm_unpackhi_epi16(t1, *q0);
-
- // p1 = b3 b2 b1 b0 a3 a2 a1 a0 93 92 91 90 83 82 81 80
- // q1 = f3 f2 f1 f0 e3 e2 e1 e0 d3 d2 d1 d0 c3 c2 c1 c0
- t1 = *p1;
- *p1 = _mm_unpacklo_epi16(t1, *q1);
- *q1 = _mm_unpackhi_epi16(t1, *q1);
-
- Store4x4(p0, r0, stride);
- r0 += 4 * stride;
- Store4x4(q0, r0, stride);
-
- Store4x4(p1, r8, stride);
- r8 += 4 * stride;
- Store4x4(q1, r8, stride);
-}
-
-//------------------------------------------------------------------------------
-// Simple In-loop filtering (Paragraph 15.2)
-
-static void SimpleVFilter16SSE2(uint8_t* p, int stride, int thresh) {
- // Load
- __m128i p1 = _mm_loadu_si128((__m128i*)&p[-2 * stride]);
- __m128i p0 = _mm_loadu_si128((__m128i*)&p[-stride]);
- __m128i q0 = _mm_loadu_si128((__m128i*)&p[0]);
- __m128i q1 = _mm_loadu_si128((__m128i*)&p[stride]);
-
- DoFilter2(&p1, &p0, &q0, &q1, thresh);
-
- // Store
- _mm_storeu_si128((__m128i*)&p[-stride], p0);
- _mm_storeu_si128((__m128i*)p, q0);
-}
-
-static void SimpleHFilter16SSE2(uint8_t* p, int stride, int thresh) {
- __m128i p1, p0, q0, q1;
-
- p -= 2; // beginning of p1
-
- Load16x4(p, p + 8 * stride, stride, &p1, &p0, &q0, &q1);
- DoFilter2(&p1, &p0, &q0, &q1, thresh);
- Store16x4(p, p + 8 * stride, stride, &p1, &p0, &q0, &q1);
-}
-
-static void SimpleVFilter16iSSE2(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4 * stride;
- SimpleVFilter16SSE2(p, stride, thresh);
- }
-}
-
-static void SimpleHFilter16iSSE2(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4;
- SimpleHFilter16SSE2(p, stride, thresh);
- }
-}
-
-//------------------------------------------------------------------------------
-// Complex In-loop filtering (Paragraph 15.3)
-
-#define MAX_DIFF1(p3, p2, p1, p0, m) { \
- m = MM_ABS(p3, p2); \
- m = _mm_max_epu8(m, MM_ABS(p2, p1)); \
- m = _mm_max_epu8(m, MM_ABS(p1, p0)); \
-}
-
-#define MAX_DIFF2(p3, p2, p1, p0, m) { \
- m = _mm_max_epu8(m, MM_ABS(p3, p2)); \
- m = _mm_max_epu8(m, MM_ABS(p2, p1)); \
- m = _mm_max_epu8(m, MM_ABS(p1, p0)); \
-}
-
-#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) { \
- e1 = _mm_loadu_si128((__m128i*)&(p)[0 * stride]); \
- e2 = _mm_loadu_si128((__m128i*)&(p)[1 * stride]); \
- e3 = _mm_loadu_si128((__m128i*)&(p)[2 * stride]); \
- e4 = _mm_loadu_si128((__m128i*)&(p)[3 * stride]); \
-}
-
-#define LOADUV_H_EDGE(p, u, v, stride) { \
- p = _mm_loadl_epi64((__m128i*)&(u)[(stride)]); \
- p = _mm_unpacklo_epi64(p, _mm_loadl_epi64((__m128i*)&(v)[(stride)])); \
-}
-
-#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) { \
- LOADUV_H_EDGE(e1, u, v, 0 * stride); \
- LOADUV_H_EDGE(e2, u, v, 1 * stride); \
- LOADUV_H_EDGE(e3, u, v, 2 * stride); \
- LOADUV_H_EDGE(e4, u, v, 3 * stride); \
-}
-
-#define STOREUV(p, u, v, stride) { \
- _mm_storel_epi64((__m128i*)&u[(stride)], p); \
- p = _mm_srli_si128(p, 8); \
- _mm_storel_epi64((__m128i*)&v[(stride)], p); \
-}
-
-#define COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask) { \
- __m128i fl_yes; \
- const __m128i it = _mm_set1_epi8(ithresh); \
- mask = _mm_subs_epu8(mask, it); \
- mask = _mm_cmpeq_epi8(mask, _mm_setzero_si128()); \
- NeedsFilter(&p1, &p0, &q0, &q1, thresh, &fl_yes); \
- mask = _mm_and_si128(mask, fl_yes); \
-}
-
-// on macroblock edges
-static void VFilter16SSE2(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- __m128i t1;
- __m128i mask;
- __m128i p2, p1, p0, q0, q1, q2;
-
- // Load p3, p2, p1, p0
- LOAD_H_EDGES4(p - 4 * stride, stride, t1, p2, p1, p0);
- MAX_DIFF1(t1, p2, p1, p0, mask);
-
- // Load q0, q1, q2, q3
- LOAD_H_EDGES4(p, stride, q0, q1, q2, t1);
- MAX_DIFF2(t1, q2, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
-
- // Store
- _mm_storeu_si128((__m128i*)&p[-3 * stride], p2);
- _mm_storeu_si128((__m128i*)&p[-2 * stride], p1);
- _mm_storeu_si128((__m128i*)&p[-1 * stride], p0);
- _mm_storeu_si128((__m128i*)&p[0 * stride], q0);
- _mm_storeu_si128((__m128i*)&p[1 * stride], q1);
- _mm_storeu_si128((__m128i*)&p[2 * stride], q2);
-}
-
-static void HFilter16SSE2(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- __m128i mask;
- __m128i p3, p2, p1, p0, q0, q1, q2, q3;
-
- uint8_t* const b = p - 4;
- Load16x4(b, b + 8 * stride, stride, &p3, &p2, &p1, &p0); // p3, p2, p1, p0
- MAX_DIFF1(p3, p2, p1, p0, mask);
-
- Load16x4(p, p + 8 * stride, stride, &q0, &q1, &q2, &q3); // q0, q1, q2, q3
- MAX_DIFF2(q3, q2, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
-
- Store16x4(b, b + 8 * stride, stride, &p3, &p2, &p1, &p0);
- Store16x4(p, p + 8 * stride, stride, &q0, &q1, &q2, &q3);
-}
-
-// on three inner edges
-static void VFilter16iSSE2(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- int k;
- __m128i mask;
- __m128i t1, t2, p1, p0, q0, q1;
-
- for (k = 3; k > 0; --k) {
- // Load p3, p2, p1, p0
- LOAD_H_EDGES4(p, stride, t2, t1, p1, p0);
- MAX_DIFF1(t2, t1, p1, p0, mask);
-
- p += 4 * stride;
-
- // Load q0, q1, q2, q3
- LOAD_H_EDGES4(p, stride, q0, q1, t1, t2);
- MAX_DIFF2(t2, t1, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
-
- // Store
- _mm_storeu_si128((__m128i*)&p[-2 * stride], p1);
- _mm_storeu_si128((__m128i*)&p[-1 * stride], p0);
- _mm_storeu_si128((__m128i*)&p[0 * stride], q0);
- _mm_storeu_si128((__m128i*)&p[1 * stride], q1);
- }
-}
-
-static void HFilter16iSSE2(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- int k;
- uint8_t* b;
- __m128i mask;
- __m128i t1, t2, p1, p0, q0, q1;
-
- for (k = 3; k > 0; --k) {
- b = p;
- Load16x4(b, b + 8 * stride, stride, &t2, &t1, &p1, &p0); // p3, p2, p1, p0
- MAX_DIFF1(t2, t1, p1, p0, mask);
-
- b += 4; // beginning of q0
- Load16x4(b, b + 8 * stride, stride, &q0, &q1, &t1, &t2); // q0, q1, q2, q3
- MAX_DIFF2(t2, t1, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
-
- b -= 2; // beginning of p1
- Store16x4(b, b + 8 * stride, stride, &p1, &p0, &q0, &q1);
-
- p += 4;
- }
-}
-
-// 8-pixels wide variant, for chroma filtering
-static void VFilter8SSE2(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- __m128i mask;
- __m128i t1, p2, p1, p0, q0, q1, q2;
-
- // Load p3, p2, p1, p0
- LOADUV_H_EDGES4(u - 4 * stride, v - 4 * stride, stride, t1, p2, p1, p0);
- MAX_DIFF1(t1, p2, p1, p0, mask);
-
- // Load q0, q1, q2, q3
- LOADUV_H_EDGES4(u, v, stride, q0, q1, q2, t1);
- MAX_DIFF2(t1, q2, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
-
- // Store
- STOREUV(p2, u, v, -3 * stride);
- STOREUV(p1, u, v, -2 * stride);
- STOREUV(p0, u, v, -1 * stride);
- STOREUV(q0, u, v, 0 * stride);
- STOREUV(q1, u, v, 1 * stride);
- STOREUV(q2, u, v, 2 * stride);
-}
-
-static void HFilter8SSE2(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- __m128i mask;
- __m128i p3, p2, p1, p0, q0, q1, q2, q3;
-
- uint8_t* const tu = u - 4;
- uint8_t* const tv = v - 4;
- Load16x4(tu, tv, stride, &p3, &p2, &p1, &p0); // p3, p2, p1, p0
- MAX_DIFF1(p3, p2, p1, p0, mask);
-
- Load16x4(u, v, stride, &q0, &q1, &q2, &q3); // q0, q1, q2, q3
- MAX_DIFF2(q3, q2, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
-
- Store16x4(tu, tv, stride, &p3, &p2, &p1, &p0);
- Store16x4(u, v, stride, &q0, &q1, &q2, &q3);
-}
-
-static void VFilter8iSSE2(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- __m128i mask;
- __m128i t1, t2, p1, p0, q0, q1;
-
- // Load p3, p2, p1, p0
- LOADUV_H_EDGES4(u, v, stride, t2, t1, p1, p0);
- MAX_DIFF1(t2, t1, p1, p0, mask);
-
- u += 4 * stride;
- v += 4 * stride;
-
- // Load q0, q1, q2, q3
- LOADUV_H_EDGES4(u, v, stride, q0, q1, t1, t2);
- MAX_DIFF2(t2, t1, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
-
- // Store
- STOREUV(p1, u, v, -2 * stride);
- STOREUV(p0, u, v, -1 * stride);
- STOREUV(q0, u, v, 0 * stride);
- STOREUV(q1, u, v, 1 * stride);
-}
-
-static void HFilter8iSSE2(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- __m128i mask;
- __m128i t1, t2, p1, p0, q0, q1;
- Load16x4(u, v, stride, &t2, &t1, &p1, &p0); // p3, p2, p1, p0
- MAX_DIFF1(t2, t1, p1, p0, mask);
-
- u += 4; // beginning of q0
- v += 4;
- Load16x4(u, v, stride, &q0, &q1, &t1, &t2); // q0, q1, q2, q3
- MAX_DIFF2(t2, t1, q1, q0, mask);
-
- COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
- DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
-
- u -= 2; // beginning of p1
- v -= 2;
- Store16x4(u, v, stride, &p1, &p0, &q0, &q1);
-}
-
-#endif // WEBP_USE_SSE2
-
-//------------------------------------------------------------------------------
-// Entry point
-
-extern void VP8DspInitSSE2(void);
-
-void VP8DspInitSSE2(void) {
-#if defined(WEBP_USE_SSE2)
- VP8Transform = TransformSSE2;
-
- VP8VFilter16 = VFilter16SSE2;
- VP8HFilter16 = HFilter16SSE2;
- VP8VFilter8 = VFilter8SSE2;
- VP8HFilter8 = HFilter8SSE2;
- VP8VFilter16i = VFilter16iSSE2;
- VP8HFilter16i = HFilter16iSSE2;
- VP8VFilter8i = VFilter8iSSE2;
- VP8HFilter8i = HFilter8iSSE2;
-
- VP8SimpleVFilter16 = SimpleVFilter16SSE2;
- VP8SimpleHFilter16 = SimpleHFilter16SSE2;
- VP8SimpleVFilter16i = SimpleVFilter16iSSE2;
- VP8SimpleHFilter16i = SimpleHFilter16iSSE2;
-#endif // WEBP_USE_SSE2
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/dsp.h b/src/third_party/libwebp/dsp/dsp.h
deleted file mode 100644
index 03bee54..0000000
--- a/src/third_party/libwebp/dsp/dsp.h
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Speed-critical functions.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_DSP_DSP_H_
-#define WEBP_DSP_DSP_H_
-
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// CPU detection
-
-#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
-#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
-#endif
-
-#if defined(__SSE2__) || defined(WEBP_MSC_SSE2)
-#define WEBP_USE_SSE2
-#endif
-
-#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
-#define WEBP_ANDROID_NEON // Android targets that might support NEON
-#endif
-
-#if (defined(__ARM_NEON__) || defined(WEBP_ANDROID_NEON)) && !defined(__aarch64__)
-#define WEBP_USE_NEON
-#endif
-
-typedef enum {
- kSSE2,
- kSSE3,
- kNEON
-} CPUFeature;
-// returns true if the CPU supports the feature.
-typedef int (*VP8CPUInfo)(CPUFeature feature);
-extern VP8CPUInfo VP8GetCPUInfo;
-
-//------------------------------------------------------------------------------
-// Encoding
-
-// Transforms
-// VP8Idct: Does one of two inverse transforms. If do_two is set, the transforms
-// will be done for (ref, in, dst) and (ref + 4, in + 16, dst + 4).
-typedef void (*VP8Idct)(const uint8_t* ref, const int16_t* in, uint8_t* dst,
- int do_two);
-typedef void (*VP8Fdct)(const uint8_t* src, const uint8_t* ref, int16_t* out);
-typedef void (*VP8WHT)(const int16_t* in, int16_t* out);
-extern VP8Idct VP8ITransform;
-extern VP8Fdct VP8FTransform;
-extern VP8WHT VP8ITransformWHT;
-extern VP8WHT VP8FTransformWHT;
-// Predictions
-// *dst is the destination block. *top and *left can be NULL.
-typedef void (*VP8IntraPreds)(uint8_t *dst, const uint8_t* left,
- const uint8_t* top);
-typedef void (*VP8Intra4Preds)(uint8_t *dst, const uint8_t* top);
-extern VP8Intra4Preds VP8EncPredLuma4;
-extern VP8IntraPreds VP8EncPredLuma16;
-extern VP8IntraPreds VP8EncPredChroma8;
-
-typedef int (*VP8Metric)(const uint8_t* pix, const uint8_t* ref);
-extern VP8Metric VP8SSE16x16, VP8SSE16x8, VP8SSE8x8, VP8SSE4x4;
-typedef int (*VP8WMetric)(const uint8_t* pix, const uint8_t* ref,
- const uint16_t* const weights);
-extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16;
-
-typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
-extern VP8BlockCopy VP8Copy4x4;
-// Quantization
-struct VP8Matrix; // forward declaration
-typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16],
- int n, const struct VP8Matrix* const mtx);
-extern VP8QuantizeBlock VP8EncQuantizeBlock;
-
-// Collect histogram for susceptibility calculation and accumulate in histo[].
-struct VP8Histogram;
-typedef void (*VP8CHisto)(const uint8_t* ref, const uint8_t* pred,
- int start_block, int end_block,
- struct VP8Histogram* const histo);
-extern const int VP8DspScan[16 + 4 + 4];
-extern VP8CHisto VP8CollectHistogram;
-
-void VP8EncDspInit(void); // must be called before using any of the above
-
-//------------------------------------------------------------------------------
-// Decoding
-
-typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst);
-// when doing two transforms, coeffs is actually int16_t[2][16].
-typedef void (*VP8DecIdct2)(const int16_t* coeffs, uint8_t* dst, int do_two);
-extern VP8DecIdct2 VP8Transform;
-extern VP8DecIdct VP8TransformUV;
-extern VP8DecIdct VP8TransformDC;
-extern VP8DecIdct VP8TransformDCUV;
-extern VP8WHT VP8TransformWHT;
-
-// *dst is the destination block, with stride BPS. Boundary samples are
-// assumed accessible when needed.
-typedef void (*VP8PredFunc)(uint8_t* dst);
-extern const VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
-extern const VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
-extern const VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
-
-// simple filter (only for luma)
-typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh);
-extern VP8SimpleFilterFunc VP8SimpleVFilter16;
-extern VP8SimpleFilterFunc VP8SimpleHFilter16;
-extern VP8SimpleFilterFunc VP8SimpleVFilter16i; // filter 3 inner edges
-extern VP8SimpleFilterFunc VP8SimpleHFilter16i;
-
-// regular filter (on both macroblock edges and inner edges)
-typedef void (*VP8LumaFilterFunc)(uint8_t* luma, int stride,
- int thresh, int ithresh, int hev_t);
-typedef void (*VP8ChromaFilterFunc)(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_t);
-// on outer edge
-extern VP8LumaFilterFunc VP8VFilter16;
-extern VP8LumaFilterFunc VP8HFilter16;
-extern VP8ChromaFilterFunc VP8VFilter8;
-extern VP8ChromaFilterFunc VP8HFilter8;
-
-// on inner edge
-extern VP8LumaFilterFunc VP8VFilter16i; // filtering 3 inner edges altogether
-extern VP8LumaFilterFunc VP8HFilter16i;
-extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether
-extern VP8ChromaFilterFunc VP8HFilter8i;
-
-// must be called before anything using the above
-void VP8DspInit(void);
-
-//------------------------------------------------------------------------------
-// WebP I/O
-
-#define FANCY_UPSAMPLING // undefined to remove fancy upsampling support
-
-typedef void (*WebPUpsampleLinePairFunc)(
- const uint8_t* top_y, const uint8_t* bottom_y,
- const uint8_t* top_u, const uint8_t* top_v,
- const uint8_t* cur_u, const uint8_t* cur_v,
- uint8_t* top_dst, uint8_t* bottom_dst, int len);
-
-#ifdef FANCY_UPSAMPLING
-
-// Fancy upsampling functions to convert YUV to RGB(A) modes
-extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
-
-// Initializes SSE2 version of the fancy upsamplers.
-void WebPInitUpsamplersSSE2(void);
-
-// NEON version
-void WebPInitUpsamplersNEON(void);
-
-#endif // FANCY_UPSAMPLING
-
-// Point-sampling methods.
-typedef void (*WebPSampleLinePairFunc)(
- const uint8_t* top_y, const uint8_t* bottom_y,
- const uint8_t* u, const uint8_t* v,
- uint8_t* top_dst, uint8_t* bottom_dst, int len);
-
-extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */];
-
-// General function for converting two lines of ARGB or RGBA.
-// 'alpha_is_last' should be true if 0xff000000 is stored in memory as
-// as 0x00, 0x00, 0x00, 0xff (little endian).
-WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last);
-
-// YUV444->RGB converters
-typedef void (*WebPYUV444Converter)(const uint8_t* y,
- const uint8_t* u, const uint8_t* v,
- uint8_t* dst, int len);
-
-extern const WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
-
-// Main function to be called
-void WebPInitUpsamplers(void);
-
-//------------------------------------------------------------------------------
-// Pre-multiply planes with alpha values
-
-// Apply alpha pre-multiply on an rgba, bgra or argb plane of size w * h.
-// alpha_first should be 0 for argb, 1 for rgba or bgra (where alpha is last).
-extern void (*WebPApplyAlphaMultiply)(
- uint8_t* rgba, int alpha_first, int w, int h, int stride);
-
-// Same, buf specifically for RGBA4444 format
-extern void (*WebPApplyAlphaMultiply4444)(
- uint8_t* rgba4444, int w, int h, int stride);
-
-// To be called first before using the above.
-void WebPInitPremultiply(void);
-
-void WebPInitPremultiplySSE2(void); // should not be called directly.
-void WebPInitPremultiplyNEON(void);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_DSP_DSP_H_ */
diff --git a/src/third_party/libwebp/dsp/enc.c b/src/third_party/libwebp/dsp/enc.c
deleted file mode 100644
index fae8d93..0000000
--- a/src/third_party/libwebp/dsp/enc.c
+++ /dev/null
@@ -1,737 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Speed-critical encoding functions.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/client_porting/poem/stdlib_poem.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h> // for abs()
-#endif
-
-#include "./dsp.h"
-#include "../enc/vp8enci.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-static WEBP_INLINE uint8_t clip_8b(int v) {
- return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
-}
-
-static WEBP_INLINE int clip_max(int v, int max) {
- return (v > max) ? max : v;
-}
-
-//------------------------------------------------------------------------------
-// Compute susceptibility based on DCT-coeff histograms:
-// the higher, the "easier" the macroblock is to compress.
-
-const int VP8DspScan[16 + 4 + 4] = {
- // Luma
- 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
- 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
- 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
- 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS,
-
- 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U
- 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V
-};
-
-static void CollectHistogram(const uint8_t* ref, const uint8_t* pred,
- int start_block, int end_block,
- VP8Histogram* const histo) {
- int j;
- for (j = start_block; j < end_block; ++j) {
- int k;
- int16_t out[16];
-
- VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
-
- // Convert coefficients to bin.
- for (k = 0; k < 16; ++k) {
- const int v = abs(out[k]) >> 3; // TODO(skal): add rounding?
- const int clipped_value = clip_max(v, MAX_COEFF_THRESH);
- histo->distribution[clipped_value]++;
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// run-time tables (~4k)
-
-static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
-
-// We declare this variable 'volatile' to prevent instruction reordering
-// and make sure it's set to true _last_ (so as to be thread-safe)
-static volatile int tables_ok = 0;
-
-static void InitTables(void) {
- if (!tables_ok) {
- int i;
- for (i = -255; i <= 255 + 255; ++i) {
- clip1[255 + i] = clip_8b(i);
- }
- tables_ok = 1;
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Transforms (Paragraph 14.4)
-
-#define STORE(x, y, v) \
- dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3))
-
-static const int kC1 = 20091 + (1 << 16);
-static const int kC2 = 35468;
-#define MUL(a, b) (((a) * (b)) >> 16)
-
-static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
- uint8_t* dst) {
- int C[4 * 4], *tmp;
- int i;
- tmp = C;
- for (i = 0; i < 4; ++i) { // vertical pass
- const int a = in[0] + in[8];
- const int b = in[0] - in[8];
- const int c = MUL(in[4], kC2) - MUL(in[12], kC1);
- const int d = MUL(in[4], kC1) + MUL(in[12], kC2);
- tmp[0] = a + d;
- tmp[1] = b + c;
- tmp[2] = b - c;
- tmp[3] = a - d;
- tmp += 4;
- in++;
- }
-
- tmp = C;
- for (i = 0; i < 4; ++i) { // horizontal pass
- const int dc = tmp[0] + 4;
- const int a = dc + tmp[8];
- const int b = dc - tmp[8];
- const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
- const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
- STORE(0, i, a + d);
- STORE(1, i, b + c);
- STORE(2, i, b - c);
- STORE(3, i, a - d);
- tmp++;
- }
-}
-
-static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst,
- int do_two) {
- ITransformOne(ref, in, dst);
- if (do_two) {
- ITransformOne(ref + 4, in + 16, dst + 4);
- }
-}
-
-static void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) {
- int i;
- int tmp[16];
- for (i = 0; i < 4; ++i, src += BPS, ref += BPS) {
- const int d0 = src[0] - ref[0]; // 9bit dynamic range ([-255,255])
- const int d1 = src[1] - ref[1];
- const int d2 = src[2] - ref[2];
- const int d3 = src[3] - ref[3];
- const int a0 = (d0 + d3); // 10b [-510,510]
- const int a1 = (d1 + d2);
- const int a2 = (d1 - d2);
- const int a3 = (d0 - d3);
- tmp[0 + i * 4] = (a0 + a1) * 8; // 14b [-8160,8160]
- tmp[1 + i * 4] = (a2 * 2217 + a3 * 5352 + 1812) >> 9; // [-7536,7542]
- tmp[2 + i * 4] = (a0 - a1) * 8;
- tmp[3 + i * 4] = (a3 * 2217 - a2 * 5352 + 937) >> 9;
- }
- for (i = 0; i < 4; ++i) {
- const int a0 = (tmp[0 + i] + tmp[12 + i]); // 15b
- const int a1 = (tmp[4 + i] + tmp[ 8 + i]);
- const int a2 = (tmp[4 + i] - tmp[ 8 + i]);
- const int a3 = (tmp[0 + i] - tmp[12 + i]);
- out[0 + i] = (a0 + a1 + 7) >> 4; // 12b
- out[4 + i] = ((a2 * 2217 + a3 * 5352 + 12000) >> 16) + (a3 != 0);
- out[8 + i] = (a0 - a1 + 7) >> 4;
- out[12+ i] = ((a3 * 2217 - a2 * 5352 + 51000) >> 16);
- }
-}
-
-static void ITransformWHT(const int16_t* in, int16_t* out) {
- int tmp[16];
- int i;
- for (i = 0; i < 4; ++i) {
- const int a0 = in[0 + i] + in[12 + i];
- const int a1 = in[4 + i] + in[ 8 + i];
- const int a2 = in[4 + i] - in[ 8 + i];
- const int a3 = in[0 + i] - in[12 + i];
- tmp[0 + i] = a0 + a1;
- tmp[8 + i] = a0 - a1;
- tmp[4 + i] = a3 + a2;
- tmp[12 + i] = a3 - a2;
- }
- for (i = 0; i < 4; ++i) {
- const int dc = tmp[0 + i * 4] + 3; // w/ rounder
- const int a0 = dc + tmp[3 + i * 4];
- const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
- const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
- const int a3 = dc - tmp[3 + i * 4];
- out[ 0] = (a0 + a1) >> 3;
- out[16] = (a3 + a2) >> 3;
- out[32] = (a0 - a1) >> 3;
- out[48] = (a3 - a2) >> 3;
- out += 64;
- }
-}
-
-static void FTransformWHT(const int16_t* in, int16_t* out) {
- // input is 12b signed
- int16_t tmp[16];
- int i;
- for (i = 0; i < 4; ++i, in += 64) {
- const int a0 = (in[0 * 16] + in[2 * 16]); // 13b
- const int a1 = (in[1 * 16] + in[3 * 16]);
- const int a2 = (in[1 * 16] - in[3 * 16]);
- const int a3 = (in[0 * 16] - in[2 * 16]);
- tmp[0 + i * 4] = a0 + a1; // 14b
- tmp[1 + i * 4] = a3 + a2;
- tmp[2 + i * 4] = a3 - a2;
- tmp[3 + i * 4] = a0 - a1;
- }
- for (i = 0; i < 4; ++i) {
- const int a0 = (tmp[0 + i] + tmp[8 + i]); // 15b
- const int a1 = (tmp[4 + i] + tmp[12+ i]);
- const int a2 = (tmp[4 + i] - tmp[12+ i]);
- const int a3 = (tmp[0 + i] - tmp[8 + i]);
- const int b0 = a0 + a1; // 16b
- const int b1 = a3 + a2;
- const int b2 = a3 - a2;
- const int b3 = a0 - a1;
- out[ 0 + i] = b0 >> 1; // 15b
- out[ 4 + i] = b1 >> 1;
- out[ 8 + i] = b2 >> 1;
- out[12 + i] = b3 >> 1;
- }
-}
-
-#undef MUL
-#undef STORE
-
-//------------------------------------------------------------------------------
-// Intra predictions
-
-#define DST(x, y) dst[(x) + (y) * BPS]
-
-static WEBP_INLINE void Fill(uint8_t* dst, int value, int size) {
- int j;
- for (j = 0; j < size; ++j) {
- SbMemorySet(dst + j * BPS, value, size);
- }
-}
-
-static WEBP_INLINE void VerticalPred(uint8_t* dst,
- const uint8_t* top, int size) {
- int j;
- if (top) {
- for (j = 0; j < size; ++j) SbMemoryCopy(dst + j * BPS, top, size);
- } else {
- Fill(dst, 127, size);
- }
-}
-
-static WEBP_INLINE void HorizontalPred(uint8_t* dst,
- const uint8_t* left, int size) {
- if (left) {
- int j;
- for (j = 0; j < size; ++j) {
- SbMemorySet(dst + j * BPS, left[j], size);
- }
- } else {
- Fill(dst, 129, size);
- }
-}
-
-static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left,
- const uint8_t* top, int size) {
- int y;
- if (left) {
- if (top) {
- const uint8_t* const clip = clip1 + 255 - left[-1];
- for (y = 0; y < size; ++y) {
- const uint8_t* const clip_table = clip + left[y];
- int x;
- for (x = 0; x < size; ++x) {
- dst[x] = clip_table[top[x]];
- }
- dst += BPS;
- }
- } else {
- HorizontalPred(dst, left, size);
- }
- } else {
- // true motion without left samples (hence: with default 129 value)
- // is equivalent to VE prediction where you just copy the top samples.
- // Note that if top samples are not available, the default value is
- // then 129, and not 127 as in the VerticalPred case.
- if (top) {
- VerticalPred(dst, top, size);
- } else {
- Fill(dst, 129, size);
- }
- }
-}
-
-static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left,
- const uint8_t* top,
- int size, int round, int shift) {
- int DC = 0;
- int j;
- if (top) {
- for (j = 0; j < size; ++j) DC += top[j];
- if (left) { // top and left present
- for (j = 0; j < size; ++j) DC += left[j];
- } else { // top, but no left
- DC += DC;
- }
- DC = (DC + round) >> shift;
- } else if (left) { // left but no top
- for (j = 0; j < size; ++j) DC += left[j];
- DC += DC;
- DC = (DC + round) >> shift;
- } else { // no top, no left, nothing.
- DC = 0x80;
- }
- Fill(dst, DC, size);
-}
-
-//------------------------------------------------------------------------------
-// Chroma 8x8 prediction (paragraph 12.2)
-
-static void IntraChromaPreds(uint8_t* dst, const uint8_t* left,
- const uint8_t* top) {
- // U block
- DCMode(C8DC8 + dst, left, top, 8, 8, 4);
- VerticalPred(C8VE8 + dst, top, 8);
- HorizontalPred(C8HE8 + dst, left, 8);
- TrueMotion(C8TM8 + dst, left, top, 8);
- // V block
- dst += 8;
- if (top) top += 8;
- if (left) left += 16;
- DCMode(C8DC8 + dst, left, top, 8, 8, 4);
- VerticalPred(C8VE8 + dst, top, 8);
- HorizontalPred(C8HE8 + dst, left, 8);
- TrueMotion(C8TM8 + dst, left, top, 8);
-}
-
-//------------------------------------------------------------------------------
-// luma 16x16 prediction (paragraph 12.3)
-
-static void Intra16Preds(uint8_t* dst,
- const uint8_t* left, const uint8_t* top) {
- DCMode(I16DC16 + dst, left, top, 16, 16, 5);
- VerticalPred(I16VE16 + dst, top, 16);
- HorizontalPred(I16HE16 + dst, left, 16);
- TrueMotion(I16TM16 + dst, left, top, 16);
-}
-
-//------------------------------------------------------------------------------
-// luma 4x4 prediction
-
-#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
-#define AVG2(a, b) (((a) + (b) + 1) >> 1)
-
-static void VE4(uint8_t* dst, const uint8_t* top) { // vertical
- const uint8_t vals[4] = {
- AVG3(top[-1], top[0], top[1]),
- AVG3(top[ 0], top[1], top[2]),
- AVG3(top[ 1], top[2], top[3]),
- AVG3(top[ 2], top[3], top[4])
- };
- int i;
- for (i = 0; i < 4; ++i) {
- SbMemoryCopy(dst + i * BPS, vals, 4);
- }
-}
-
-static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal
- const int X = top[-1];
- const int I = top[-2];
- const int J = top[-3];
- const int K = top[-4];
- const int L = top[-5];
- *(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(X, I, J);
- *(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(I, J, K);
- *(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(J, K, L);
- *(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(K, L, L);
-}
-
-static void DC4(uint8_t* dst, const uint8_t* top) {
- uint32_t dc = 4;
- int i;
- for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i];
- Fill(dst, dc >> 3, 4);
-}
-
-static void RD4(uint8_t* dst, const uint8_t* top) {
- const int X = top[-1];
- const int I = top[-2];
- const int J = top[-3];
- const int K = top[-4];
- const int L = top[-5];
- const int A = top[0];
- const int B = top[1];
- const int C = top[2];
- const int D = top[3];
- DST(0, 3) = AVG3(J, K, L);
- DST(0, 2) = DST(1, 3) = AVG3(I, J, K);
- DST(0, 1) = DST(1, 2) = DST(2, 3) = AVG3(X, I, J);
- DST(0, 0) = DST(1, 1) = DST(2, 2) = DST(3, 3) = AVG3(A, X, I);
- DST(1, 0) = DST(2, 1) = DST(3, 2) = AVG3(B, A, X);
- DST(2, 0) = DST(3, 1) = AVG3(C, B, A);
- DST(3, 0) = AVG3(D, C, B);
-}
-
-static void LD4(uint8_t* dst, const uint8_t* top) {
- const int A = top[0];
- const int B = top[1];
- const int C = top[2];
- const int D = top[3];
- const int E = top[4];
- const int F = top[5];
- const int G = top[6];
- const int H = top[7];
- DST(0, 0) = AVG3(A, B, C);
- DST(1, 0) = DST(0, 1) = AVG3(B, C, D);
- DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E);
- DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F);
- DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G);
- DST(3, 2) = DST(2, 3) = AVG3(F, G, H);
- DST(3, 3) = AVG3(G, H, H);
-}
-
-static void VR4(uint8_t* dst, const uint8_t* top) {
- const int X = top[-1];
- const int I = top[-2];
- const int J = top[-3];
- const int K = top[-4];
- const int A = top[0];
- const int B = top[1];
- const int C = top[2];
- const int D = top[3];
- DST(0, 0) = DST(1, 2) = AVG2(X, A);
- DST(1, 0) = DST(2, 2) = AVG2(A, B);
- DST(2, 0) = DST(3, 2) = AVG2(B, C);
- DST(3, 0) = AVG2(C, D);
-
- DST(0, 3) = AVG3(K, J, I);
- DST(0, 2) = AVG3(J, I, X);
- DST(0, 1) = DST(1, 3) = AVG3(I, X, A);
- DST(1, 1) = DST(2, 3) = AVG3(X, A, B);
- DST(2, 1) = DST(3, 3) = AVG3(A, B, C);
- DST(3, 1) = AVG3(B, C, D);
-}
-
-static void VL4(uint8_t* dst, const uint8_t* top) {
- const int A = top[0];
- const int B = top[1];
- const int C = top[2];
- const int D = top[3];
- const int E = top[4];
- const int F = top[5];
- const int G = top[6];
- const int H = top[7];
- DST(0, 0) = AVG2(A, B);
- DST(1, 0) = DST(0, 2) = AVG2(B, C);
- DST(2, 0) = DST(1, 2) = AVG2(C, D);
- DST(3, 0) = DST(2, 2) = AVG2(D, E);
-
- DST(0, 1) = AVG3(A, B, C);
- DST(1, 1) = DST(0, 3) = AVG3(B, C, D);
- DST(2, 1) = DST(1, 3) = AVG3(C, D, E);
- DST(3, 1) = DST(2, 3) = AVG3(D, E, F);
- DST(3, 2) = AVG3(E, F, G);
- DST(3, 3) = AVG3(F, G, H);
-}
-
-static void HU4(uint8_t* dst, const uint8_t* top) {
- const int I = top[-2];
- const int J = top[-3];
- const int K = top[-4];
- const int L = top[-5];
- DST(0, 0) = AVG2(I, J);
- DST(2, 0) = DST(0, 1) = AVG2(J, K);
- DST(2, 1) = DST(0, 2) = AVG2(K, L);
- DST(1, 0) = AVG3(I, J, K);
- DST(3, 0) = DST(1, 1) = AVG3(J, K, L);
- DST(3, 1) = DST(1, 2) = AVG3(K, L, L);
- DST(3, 2) = DST(2, 2) =
- DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
-}
-
-static void HD4(uint8_t* dst, const uint8_t* top) {
- const int X = top[-1];
- const int I = top[-2];
- const int J = top[-3];
- const int K = top[-4];
- const int L = top[-5];
- const int A = top[0];
- const int B = top[1];
- const int C = top[2];
-
- DST(0, 0) = DST(2, 1) = AVG2(I, X);
- DST(0, 1) = DST(2, 2) = AVG2(J, I);
- DST(0, 2) = DST(2, 3) = AVG2(K, J);
- DST(0, 3) = AVG2(L, K);
-
- DST(3, 0) = AVG3(A, B, C);
- DST(2, 0) = AVG3(X, A, B);
- DST(1, 0) = DST(3, 1) = AVG3(I, X, A);
- DST(1, 1) = DST(3, 2) = AVG3(J, I, X);
- DST(1, 2) = DST(3, 3) = AVG3(K, J, I);
- DST(1, 3) = AVG3(L, K, J);
-}
-
-static void TM4(uint8_t* dst, const uint8_t* top) {
- int x, y;
- const uint8_t* const clip = clip1 + 255 - top[-1];
- for (y = 0; y < 4; ++y) {
- const uint8_t* const clip_table = clip + top[-2 - y];
- for (x = 0; x < 4; ++x) {
- dst[x] = clip_table[top[x]];
- }
- dst += BPS;
- }
-}
-
-#undef DST
-#undef AVG3
-#undef AVG2
-
-// Left samples are top[-5 .. -2], top_left is top[-1], top are
-// located at top[0..3], and top right is top[4..7]
-static void Intra4Preds(uint8_t* dst, const uint8_t* top) {
- DC4(I4DC4 + dst, top);
- TM4(I4TM4 + dst, top);
- VE4(I4VE4 + dst, top);
- HE4(I4HE4 + dst, top);
- RD4(I4RD4 + dst, top);
- VR4(I4VR4 + dst, top);
- LD4(I4LD4 + dst, top);
- VL4(I4VL4 + dst, top);
- HD4(I4HD4 + dst, top);
- HU4(I4HU4 + dst, top);
-}
-
-//------------------------------------------------------------------------------
-// Metric
-
-static WEBP_INLINE int GetSSE(const uint8_t* a, const uint8_t* b,
- int w, int h) {
- int count = 0;
- int y, x;
- for (y = 0; y < h; ++y) {
- for (x = 0; x < w; ++x) {
- const int diff = (int)a[x] - b[x];
- count += diff * diff;
- }
- a += BPS;
- b += BPS;
- }
- return count;
-}
-
-static int SSE16x16(const uint8_t* a, const uint8_t* b) {
- return GetSSE(a, b, 16, 16);
-}
-static int SSE16x8(const uint8_t* a, const uint8_t* b) {
- return GetSSE(a, b, 16, 8);
-}
-static int SSE8x8(const uint8_t* a, const uint8_t* b) {
- return GetSSE(a, b, 8, 8);
-}
-static int SSE4x4(const uint8_t* a, const uint8_t* b) {
- return GetSSE(a, b, 4, 4);
-}
-
-//------------------------------------------------------------------------------
-// Texture distortion
-//
-// We try to match the spectral content (weighted) between source and
-// reconstructed samples.
-
-// Hadamard transform
-// Returns the weighted sum of the absolute value of transformed coefficients.
-static int TTransform(const uint8_t* in, const uint16_t* w) {
- int sum = 0;
- int tmp[16];
- int i;
- // horizontal pass
- for (i = 0; i < 4; ++i, in += BPS) {
- const int a0 = in[0] + in[2];
- const int a1 = in[1] + in[3];
- const int a2 = in[1] - in[3];
- const int a3 = in[0] - in[2];
- tmp[0 + i * 4] = a0 + a1;
- tmp[1 + i * 4] = a3 + a2;
- tmp[2 + i * 4] = a3 - a2;
- tmp[3 + i * 4] = a0 - a1;
- }
- // vertical pass
- for (i = 0; i < 4; ++i, ++w) {
- const int a0 = tmp[0 + i] + tmp[8 + i];
- const int a1 = tmp[4 + i] + tmp[12+ i];
- const int a2 = tmp[4 + i] - tmp[12+ i];
- const int a3 = tmp[0 + i] - tmp[8 + i];
- const int b0 = a0 + a1;
- const int b1 = a3 + a2;
- const int b2 = a3 - a2;
- const int b3 = a0 - a1;
-
- sum += w[ 0] * abs(b0);
- sum += w[ 4] * abs(b1);
- sum += w[ 8] * abs(b2);
- sum += w[12] * abs(b3);
- }
- return sum;
-}
-
-static int Disto4x4(const uint8_t* const a, const uint8_t* const b,
- const uint16_t* const w) {
- const int sum1 = TTransform(a, w);
- const int sum2 = TTransform(b, w);
- return abs(sum2 - sum1) >> 5;
-}
-
-static int Disto16x16(const uint8_t* const a, const uint8_t* const b,
- const uint16_t* const w) {
- int D = 0;
- int x, y;
- for (y = 0; y < 16 * BPS; y += 4 * BPS) {
- for (x = 0; x < 16; x += 4) {
- D += Disto4x4(a + x + y, b + x + y, w);
- }
- }
- return D;
-}
-
-//------------------------------------------------------------------------------
-// Quantization
-//
-
-static const uint8_t kZigzag[16] = {
- 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
-};
-
-// Simple quantization
-static int QuantizeBlock(int16_t in[16], int16_t out[16],
- int n, const VP8Matrix* const mtx) {
- int last = -1;
- for (; n < 16; ++n) {
- const int j = kZigzag[n];
- const int sign = (in[j] < 0);
- const int coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
- if (coeff > mtx->zthresh_[j]) {
- const int Q = mtx->q_[j];
- const int iQ = mtx->iq_[j];
- const int B = mtx->bias_[j];
- out[n] = QUANTDIV(coeff, iQ, B);
- if (out[n] > MAX_LEVEL) out[n] = MAX_LEVEL;
- if (sign) out[n] = -out[n];
- in[j] = out[n] * Q;
- if (out[n]) last = n;
- } else {
- out[n] = 0;
- in[j] = 0;
- }
- }
- return (last >= 0);
-}
-
-//------------------------------------------------------------------------------
-// Block copy
-
-static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int size) {
- int y;
- for (y = 0; y < size; ++y) {
- SbMemoryCopy(dst, src, size);
- src += BPS;
- dst += BPS;
- }
-}
-
-static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
-
-//------------------------------------------------------------------------------
-// Initialization
-
-// Speed-critical function pointers. We have to initialize them to the default
-// implementations within VP8EncDspInit().
-VP8CHisto VP8CollectHistogram;
-VP8Idct VP8ITransform;
-VP8Fdct VP8FTransform;
-VP8WHT VP8ITransformWHT;
-VP8WHT VP8FTransformWHT;
-VP8Intra4Preds VP8EncPredLuma4;
-VP8IntraPreds VP8EncPredLuma16;
-VP8IntraPreds VP8EncPredChroma8;
-VP8Metric VP8SSE16x16;
-VP8Metric VP8SSE8x8;
-VP8Metric VP8SSE16x8;
-VP8Metric VP8SSE4x4;
-VP8WMetric VP8TDisto4x4;
-VP8WMetric VP8TDisto16x16;
-VP8QuantizeBlock VP8EncQuantizeBlock;
-VP8BlockCopy VP8Copy4x4;
-
-extern void VP8EncDspInitSSE2(void);
-extern void VP8EncDspInitNEON(void);
-
-void VP8EncDspInit(void) {
- InitTables();
-
- // default C implementations
- VP8CollectHistogram = CollectHistogram;
- VP8ITransform = ITransform;
- VP8FTransform = FTransform;
- VP8ITransformWHT = ITransformWHT;
- VP8FTransformWHT = FTransformWHT;
- VP8EncPredLuma4 = Intra4Preds;
- VP8EncPredLuma16 = Intra16Preds;
- VP8EncPredChroma8 = IntraChromaPreds;
- VP8SSE16x16 = SSE16x16;
- VP8SSE8x8 = SSE8x8;
- VP8SSE16x8 = SSE16x8;
- VP8SSE4x4 = SSE4x4;
- VP8TDisto4x4 = Disto4x4;
- VP8TDisto16x16 = Disto16x16;
- VP8EncQuantizeBlock = QuantizeBlock;
- VP8Copy4x4 = Copy4x4;
-
- // If defined, use CPUInfo() to overwrite some pointers with faster versions.
- if (VP8GetCPUInfo) {
-#if defined(WEBP_USE_SSE2)
- if (VP8GetCPUInfo(kSSE2)) {
- VP8EncDspInitSSE2();
- }
-#elif defined(WEBP_USE_NEON)
- if (VP8GetCPUInfo(kNEON)) {
- VP8EncDspInitNEON();
- }
-#endif
- }
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/enc_neon.c b/src/third_party/libwebp/dsp/enc_neon.c
deleted file mode 100644
index eb256e6..0000000
--- a/src/third_party/libwebp/dsp/enc_neon.c
+++ /dev/null
@@ -1,639 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// ARM NEON version of speed-critical encoding functions.
-//
-// adapted from libvpx (http://www.webmproject.org/code/)
-
-#include "./dsp.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if defined(WEBP_USE_NEON)
-
-#include "../enc/vp8enci.h"
-
-//------------------------------------------------------------------------------
-// Transforms (Paragraph 14.4)
-
-// Inverse transform.
-// This code is pretty much the same as TransformOneNEON in the decoder, except
-// for subtraction to *ref. See the comments there for algorithmic explanations.
-static void ITransformOne(const uint8_t* ref,
- const int16_t* in, uint8_t* dst) {
- const int kBPS = BPS;
- const int16_t kC1C2[] = { 20091, 17734, 0, 0 }; // kC1 / (kC2 >> 1) / 0 / 0
-
- __asm__ volatile (
- "vld1.16 {q1, q2}, [%[in]] \n"
- "vld1.16 {d0}, [%[kC1C2]] \n"
-
- // d2: in[0]
- // d3: in[8]
- // d4: in[4]
- // d5: in[12]
- "vswp d3, d4 \n"
-
- // q8 = {in[4], in[12]} * kC1 * 2 >> 16
- // q9 = {in[4], in[12]} * kC2 >> 16
- "vqdmulh.s16 q8, q2, d0[0] \n"
- "vqdmulh.s16 q9, q2, d0[1] \n"
-
- // d22 = a = in[0] + in[8]
- // d23 = b = in[0] - in[8]
- "vqadd.s16 d22, d2, d3 \n"
- "vqsub.s16 d23, d2, d3 \n"
-
- // q8 = in[4]/[12] * kC1 >> 16
- "vshr.s16 q8, q8, #1 \n"
-
- // Add {in[4], in[12]} back after the multiplication.
- "vqadd.s16 q8, q2, q8 \n"
-
- // d20 = c = in[4]*kC2 - in[12]*kC1
- // d21 = d = in[4]*kC1 + in[12]*kC2
- "vqsub.s16 d20, d18, d17 \n"
- "vqadd.s16 d21, d19, d16 \n"
-
- // d2 = tmp[0] = a + d
- // d3 = tmp[1] = b + c
- // d4 = tmp[2] = b - c
- // d5 = tmp[3] = a - d
- "vqadd.s16 d2, d22, d21 \n"
- "vqadd.s16 d3, d23, d20 \n"
- "vqsub.s16 d4, d23, d20 \n"
- "vqsub.s16 d5, d22, d21 \n"
-
- "vzip.16 q1, q2 \n"
- "vzip.16 q1, q2 \n"
-
- "vswp d3, d4 \n"
-
- // q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16
- // q9 = {tmp[4], tmp[12]} * kC2 >> 16
- "vqdmulh.s16 q8, q2, d0[0] \n"
- "vqdmulh.s16 q9, q2, d0[1] \n"
-
- // d22 = a = tmp[0] + tmp[8]
- // d23 = b = tmp[0] - tmp[8]
- "vqadd.s16 d22, d2, d3 \n"
- "vqsub.s16 d23, d2, d3 \n"
-
- "vshr.s16 q8, q8, #1 \n"
- "vqadd.s16 q8, q2, q8 \n"
-
- // d20 = c = in[4]*kC2 - in[12]*kC1
- // d21 = d = in[4]*kC1 + in[12]*kC2
- "vqsub.s16 d20, d18, d17 \n"
- "vqadd.s16 d21, d19, d16 \n"
-
- // d2 = tmp[0] = a + d
- // d3 = tmp[1] = b + c
- // d4 = tmp[2] = b - c
- // d5 = tmp[3] = a - d
- "vqadd.s16 d2, d22, d21 \n"
- "vqadd.s16 d3, d23, d20 \n"
- "vqsub.s16 d4, d23, d20 \n"
- "vqsub.s16 d5, d22, d21 \n"
-
- "vld1.32 d6[0], [%[ref]], %[kBPS] \n"
- "vld1.32 d6[1], [%[ref]], %[kBPS] \n"
- "vld1.32 d7[0], [%[ref]], %[kBPS] \n"
- "vld1.32 d7[1], [%[ref]], %[kBPS] \n"
-
- "sub %[ref], %[ref], %[kBPS], lsl #2 \n"
-
- // (val) + 4 >> 3
- "vrshr.s16 d2, d2, #3 \n"
- "vrshr.s16 d3, d3, #3 \n"
- "vrshr.s16 d4, d4, #3 \n"
- "vrshr.s16 d5, d5, #3 \n"
-
- "vzip.16 q1, q2 \n"
- "vzip.16 q1, q2 \n"
-
- // Must accumulate before saturating
- "vmovl.u8 q8, d6 \n"
- "vmovl.u8 q9, d7 \n"
-
- "vqadd.s16 q1, q1, q8 \n"
- "vqadd.s16 q2, q2, q9 \n"
-
- "vqmovun.s16 d0, q1 \n"
- "vqmovun.s16 d1, q2 \n"
-
- "vst1.32 d0[0], [%[dst]], %[kBPS] \n"
- "vst1.32 d0[1], [%[dst]], %[kBPS] \n"
- "vst1.32 d1[0], [%[dst]], %[kBPS] \n"
- "vst1.32 d1[1], [%[dst]] \n"
-
- : [in] "+r"(in), [dst] "+r"(dst) // modified registers
- : [kBPS] "r"(kBPS), [kC1C2] "r"(kC1C2), [ref] "r"(ref) // constants
- : "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" // clobbered
- );
-}
-
-static void ITransform(const uint8_t* ref,
- const int16_t* in, uint8_t* dst, int do_two) {
- ITransformOne(ref, in, dst);
- if (do_two) {
- ITransformOne(ref + 4, in + 16, dst + 4);
- }
-}
-
-// Same code as dec_neon.c
-static void ITransformWHT(const int16_t* in, int16_t* out) {
- const int kStep = 32; // The store is only incrementing the pointer as if we
- // had stored a single byte.
- __asm__ volatile (
- // part 1
- // load data into q0, q1
- "vld1.16 {q0, q1}, [%[in]] \n"
-
- "vaddl.s16 q2, d0, d3 \n" // a0 = in[0] + in[12]
- "vaddl.s16 q3, d1, d2 \n" // a1 = in[4] + in[8]
- "vsubl.s16 q4, d1, d2 \n" // a2 = in[4] - in[8]
- "vsubl.s16 q5, d0, d3 \n" // a3 = in[0] - in[12]
-
- "vadd.s32 q0, q2, q3 \n" // tmp[0] = a0 + a1
- "vsub.s32 q2, q2, q3 \n" // tmp[8] = a0 - a1
- "vadd.s32 q1, q5, q4 \n" // tmp[4] = a3 + a2
- "vsub.s32 q3, q5, q4 \n" // tmp[12] = a3 - a2
-
- // Transpose
- // q0 = tmp[0, 4, 8, 12], q1 = tmp[2, 6, 10, 14]
- // q2 = tmp[1, 5, 9, 13], q3 = tmp[3, 7, 11, 15]
- "vswp d1, d4 \n" // vtrn.64 q0, q2
- "vswp d3, d6 \n" // vtrn.64 q1, q3
- "vtrn.32 q0, q1 \n"
- "vtrn.32 q2, q3 \n"
-
- "vmov.s32 q4, #3 \n" // dc = 3
- "vadd.s32 q0, q0, q4 \n" // dc = tmp[0] + 3
- "vadd.s32 q6, q0, q3 \n" // a0 = dc + tmp[3]
- "vadd.s32 q7, q1, q2 \n" // a1 = tmp[1] + tmp[2]
- "vsub.s32 q8, q1, q2 \n" // a2 = tmp[1] - tmp[2]
- "vsub.s32 q9, q0, q3 \n" // a3 = dc - tmp[3]
-
- "vadd.s32 q0, q6, q7 \n"
- "vshrn.s32 d0, q0, #3 \n" // (a0 + a1) >> 3
- "vadd.s32 q1, q9, q8 \n"
- "vshrn.s32 d1, q1, #3 \n" // (a3 + a2) >> 3
- "vsub.s32 q2, q6, q7 \n"
- "vshrn.s32 d2, q2, #3 \n" // (a0 - a1) >> 3
- "vsub.s32 q3, q9, q8 \n"
- "vshrn.s32 d3, q3, #3 \n" // (a3 - a2) >> 3
-
- // set the results to output
- "vst1.16 d0[0], [%[out]], %[kStep] \n"
- "vst1.16 d1[0], [%[out]], %[kStep] \n"
- "vst1.16 d2[0], [%[out]], %[kStep] \n"
- "vst1.16 d3[0], [%[out]], %[kStep] \n"
- "vst1.16 d0[1], [%[out]], %[kStep] \n"
- "vst1.16 d1[1], [%[out]], %[kStep] \n"
- "vst1.16 d2[1], [%[out]], %[kStep] \n"
- "vst1.16 d3[1], [%[out]], %[kStep] \n"
- "vst1.16 d0[2], [%[out]], %[kStep] \n"
- "vst1.16 d1[2], [%[out]], %[kStep] \n"
- "vst1.16 d2[2], [%[out]], %[kStep] \n"
- "vst1.16 d3[2], [%[out]], %[kStep] \n"
- "vst1.16 d0[3], [%[out]], %[kStep] \n"
- "vst1.16 d1[3], [%[out]], %[kStep] \n"
- "vst1.16 d2[3], [%[out]], %[kStep] \n"
- "vst1.16 d3[3], [%[out]], %[kStep] \n"
-
- : [out] "+r"(out) // modified registers
- : [in] "r"(in), [kStep] "r"(kStep) // constants
- : "memory", "q0", "q1", "q2", "q3", "q4",
- "q5", "q6", "q7", "q8", "q9" // clobbered
- );
-}
-
-// Forward transform.
-
-// adapted from vp8/encoder/arm/neon/shortfdct_neon.asm
-static const int16_t kCoeff16[] = {
- 5352, 5352, 5352, 5352, 2217, 2217, 2217, 2217
-};
-static const int32_t kCoeff32[] = {
- 1812, 1812, 1812, 1812,
- 937, 937, 937, 937,
- 12000, 12000, 12000, 12000,
- 51000, 51000, 51000, 51000
-};
-
-static void FTransform(const uint8_t* src, const uint8_t* ref,
- int16_t* out) {
- const int kBPS = BPS;
- const uint8_t* src_ptr = src;
- const uint8_t* ref_ptr = ref;
- const int16_t* coeff16 = kCoeff16;
- const int32_t* coeff32 = kCoeff32;
-
- __asm__ volatile (
- // load src into q4, q5 in high half
- "vld1.8 {d8}, [%[src_ptr]], %[kBPS] \n"
- "vld1.8 {d10}, [%[src_ptr]], %[kBPS] \n"
- "vld1.8 {d9}, [%[src_ptr]], %[kBPS] \n"
- "vld1.8 {d11}, [%[src_ptr]] \n"
-
- // load ref into q6, q7 in high half
- "vld1.8 {d12}, [%[ref_ptr]], %[kBPS] \n"
- "vld1.8 {d14}, [%[ref_ptr]], %[kBPS] \n"
- "vld1.8 {d13}, [%[ref_ptr]], %[kBPS] \n"
- "vld1.8 {d15}, [%[ref_ptr]] \n"
-
- // Pack the high values in to q4 and q6
- "vtrn.32 q4, q5 \n"
- "vtrn.32 q6, q7 \n"
-
- // d[0-3] = src - ref
- "vsubl.u8 q0, d8, d12 \n"
- "vsubl.u8 q1, d9, d13 \n"
-
- // load coeff16 into q8(d16=5352, d17=2217)
- "vld1.16 {q8}, [%[coeff16]] \n"
-
- // load coeff32 high half into q9 = 1812, q10 = 937
- "vld1.32 {q9, q10}, [%[coeff32]]! \n"
-
- // load coeff32 low half into q11=12000, q12=51000
- "vld1.32 {q11,q12}, [%[coeff32]] \n"
-
- // part 1
- // Transpose. Register dN is the same as dN in C
- "vtrn.32 d0, d2 \n"
- "vtrn.32 d1, d3 \n"
- "vtrn.16 d0, d1 \n"
- "vtrn.16 d2, d3 \n"
-
- "vadd.s16 d4, d0, d3 \n" // a0 = d0 + d3
- "vadd.s16 d5, d1, d2 \n" // a1 = d1 + d2
- "vsub.s16 d6, d1, d2 \n" // a2 = d1 - d2
- "vsub.s16 d7, d0, d3 \n" // a3 = d0 - d3
-
- "vadd.s16 d0, d4, d5 \n" // a0 + a1
- "vshl.s16 d0, d0, #3 \n" // temp[0+i*4] = (a0+a1) << 3
- "vsub.s16 d2, d4, d5 \n" // a0 - a1
- "vshl.s16 d2, d2, #3 \n" // (temp[2+i*4] = (a0-a1) << 3
-
- "vmlal.s16 q9, d7, d16 \n" // a3*5352 + 1812
- "vmlal.s16 q10, d7, d17 \n" // a3*2217 + 937
- "vmlal.s16 q9, d6, d17 \n" // a2*2217 + a3*5352 + 1812
- "vmlsl.s16 q10, d6, d16 \n" // a3*2217 + 937 - a2*5352
-
- // temp[1+i*4] = (d2*2217 + d3*5352 + 1812) >> 9
- // temp[3+i*4] = (d3*2217 + 937 - d2*5352) >> 9
- "vshrn.s32 d1, q9, #9 \n"
- "vshrn.s32 d3, q10, #9 \n"
-
- // part 2
- // transpose d0=ip[0], d1=ip[4], d2=ip[8], d3=ip[12]
- "vtrn.32 d0, d2 \n"
- "vtrn.32 d1, d3 \n"
- "vtrn.16 d0, d1 \n"
- "vtrn.16 d2, d3 \n"
-
- "vmov.s16 d26, #7 \n"
-
- "vadd.s16 d4, d0, d3 \n" // a1 = ip[0] + ip[12]
- "vadd.s16 d5, d1, d2 \n" // b1 = ip[4] + ip[8]
- "vsub.s16 d6, d1, d2 \n" // c1 = ip[4] - ip[8]
- "vadd.s16 d4, d4, d26 \n" // a1 + 7
- "vsub.s16 d7, d0, d3 \n" // d1 = ip[0] - ip[12]
-
- "vadd.s16 d0, d4, d5 \n" // op[0] = a1 + b1 + 7
- "vsub.s16 d2, d4, d5 \n" // op[8] = a1 - b1 + 7
-
- "vmlal.s16 q11, d7, d16 \n" // d1*5352 + 12000
- "vmlal.s16 q12, d7, d17 \n" // d1*2217 + 51000
-
- "vceq.s16 d4, d7, #0 \n"
-
- "vshr.s16 d0, d0, #4 \n"
- "vshr.s16 d2, d2, #4 \n"
-
- "vmlal.s16 q11, d6, d17 \n" // c1*2217 + d1*5352 + 12000
- "vmlsl.s16 q12, d6, d16 \n" // d1*2217 - c1*5352 + 51000
-
- "vmvn d4, d4 \n" // !(d1 == 0)
- // op[4] = (c1*2217 + d1*5352 + 12000)>>16
- "vshrn.s32 d1, q11, #16 \n"
- // op[4] += (d1!=0)
- "vsub.s16 d1, d1, d4 \n"
- // op[12]= (d1*2217 - c1*5352 + 51000)>>16
- "vshrn.s32 d3, q12, #16 \n"
-
- // set result to out array
- "vst1.16 {q0, q1}, [%[out]] \n"
- : [src_ptr] "+r"(src_ptr), [ref_ptr] "+r"(ref_ptr),
- [coeff32] "+r"(coeff32) // modified registers
- : [kBPS] "r"(kBPS), [coeff16] "r"(coeff16),
- [out] "r"(out) // constants
- : "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9",
- "q10", "q11", "q12", "q13" // clobbered
- );
-}
-
-static void FTransformWHT(const int16_t* in, int16_t* out) {
- const int kStep = 32;
- __asm__ volatile (
- // d0 = in[0 * 16] , d1 = in[1 * 16]
- // d2 = in[2 * 16] , d3 = in[3 * 16]
- "vld1.16 d0[0], [%[in]], %[kStep] \n"
- "vld1.16 d1[0], [%[in]], %[kStep] \n"
- "vld1.16 d2[0], [%[in]], %[kStep] \n"
- "vld1.16 d3[0], [%[in]], %[kStep] \n"
- "vld1.16 d0[1], [%[in]], %[kStep] \n"
- "vld1.16 d1[1], [%[in]], %[kStep] \n"
- "vld1.16 d2[1], [%[in]], %[kStep] \n"
- "vld1.16 d3[1], [%[in]], %[kStep] \n"
- "vld1.16 d0[2], [%[in]], %[kStep] \n"
- "vld1.16 d1[2], [%[in]], %[kStep] \n"
- "vld1.16 d2[2], [%[in]], %[kStep] \n"
- "vld1.16 d3[2], [%[in]], %[kStep] \n"
- "vld1.16 d0[3], [%[in]], %[kStep] \n"
- "vld1.16 d1[3], [%[in]], %[kStep] \n"
- "vld1.16 d2[3], [%[in]], %[kStep] \n"
- "vld1.16 d3[3], [%[in]], %[kStep] \n"
-
- "vaddl.s16 q2, d0, d2 \n" // a0=(in[0*16]+in[2*16])
- "vaddl.s16 q3, d1, d3 \n" // a1=(in[1*16]+in[3*16])
- "vsubl.s16 q4, d1, d3 \n" // a2=(in[1*16]-in[3*16])
- "vsubl.s16 q5, d0, d2 \n" // a3=(in[0*16]-in[2*16])
-
- "vqadd.s32 q6, q2, q3 \n" // a0 + a1
- "vqadd.s32 q7, q5, q4 \n" // a3 + a2
- "vqsub.s32 q8, q5, q4 \n" // a3 - a2
- "vqsub.s32 q9, q2, q3 \n" // a0 - a1
-
- // Transpose
- // q6 = tmp[0, 1, 2, 3] ; q7 = tmp[ 4, 5, 6, 7]
- // q8 = tmp[8, 9, 10, 11] ; q9 = tmp[12, 13, 14, 15]
- "vswp d13, d16 \n" // vtrn.64 q0, q2
- "vswp d15, d18 \n" // vtrn.64 q1, q3
- "vtrn.32 q6, q7 \n"
- "vtrn.32 q8, q9 \n"
-
- "vqadd.s32 q0, q6, q8 \n" // a0 = tmp[0] + tmp[8]
- "vqadd.s32 q1, q7, q9 \n" // a1 = tmp[4] + tmp[12]
- "vqsub.s32 q2, q7, q9 \n" // a2 = tmp[4] - tmp[12]
- "vqsub.s32 q3, q6, q8 \n" // a3 = tmp[0] - tmp[8]
-
- "vqadd.s32 q4, q0, q1 \n" // b0 = a0 + a1
- "vqadd.s32 q5, q3, q2 \n" // b1 = a3 + a2
- "vqsub.s32 q6, q3, q2 \n" // b2 = a3 - a2
- "vqsub.s32 q7, q0, q1 \n" // b3 = a0 - a1
-
- "vshrn.s32 d18, q4, #1 \n" // b0 >> 1
- "vshrn.s32 d19, q5, #1 \n" // b1 >> 1
- "vshrn.s32 d20, q6, #1 \n" // b2 >> 1
- "vshrn.s32 d21, q7, #1 \n" // b3 >> 1
-
- "vst1.16 {q9, q10}, [%[out]] \n"
-
- : [in] "+r"(in)
- : [kStep] "r"(kStep), [out] "r"(out)
- : "memory", "q0", "q1", "q2", "q3", "q4", "q5",
- "q6", "q7", "q8", "q9", "q10" // clobbered
- ) ;
-}
-
-//------------------------------------------------------------------------------
-// Texture distortion
-//
-// We try to match the spectral content (weighted) between source and
-// reconstructed samples.
-
-// Hadamard transform
-// Returns the weighted sum of the absolute value of transformed coefficients.
-// This uses a TTransform helper function in C
-static int Disto4x4(const uint8_t* const a, const uint8_t* const b,
- const uint16_t* const w) {
- const int kBPS = BPS;
- const uint8_t* A = a;
- const uint8_t* B = b;
- const uint16_t* W = w;
- int sum;
- __asm__ volatile (
- "vld1.32 d0[0], [%[a]], %[kBPS] \n"
- "vld1.32 d0[1], [%[a]], %[kBPS] \n"
- "vld1.32 d2[0], [%[a]], %[kBPS] \n"
- "vld1.32 d2[1], [%[a]] \n"
-
- "vld1.32 d1[0], [%[b]], %[kBPS] \n"
- "vld1.32 d1[1], [%[b]], %[kBPS] \n"
- "vld1.32 d3[0], [%[b]], %[kBPS] \n"
- "vld1.32 d3[1], [%[b]] \n"
-
- // a d0/d2, b d1/d3
- // d0/d1: 01 01 01 01
- // d2/d3: 23 23 23 23
- // But: it goes 01 45 23 67
- // Notice the middle values are transposed
- "vtrn.16 q0, q1 \n"
-
- // {a0, a1} = {in[0] + in[2], in[1] + in[3]}
- "vaddl.u8 q2, d0, d2 \n"
- "vaddl.u8 q10, d1, d3 \n"
- // {a3, a2} = {in[0] - in[2], in[1] - in[3]}
- "vsubl.u8 q3, d0, d2 \n"
- "vsubl.u8 q11, d1, d3 \n"
-
- // tmp[0] = a0 + a1
- "vpaddl.s16 q0, q2 \n"
- "vpaddl.s16 q8, q10 \n"
-
- // tmp[1] = a3 + a2
- "vpaddl.s16 q1, q3 \n"
- "vpaddl.s16 q9, q11 \n"
-
- // No pair subtract
- // q2 = {a0, a3}
- // q3 = {a1, a2}
- "vtrn.16 q2, q3 \n"
- "vtrn.16 q10, q11 \n"
-
- // {tmp[3], tmp[2]} = {a0 - a1, a3 - a2}
- "vsubl.s16 q12, d4, d6 \n"
- "vsubl.s16 q13, d5, d7 \n"
- "vsubl.s16 q14, d20, d22 \n"
- "vsubl.s16 q15, d21, d23 \n"
-
- // separate tmp[3] and tmp[2]
- // q12 = tmp[3]
- // q13 = tmp[2]
- "vtrn.32 q12, q13 \n"
- "vtrn.32 q14, q15 \n"
-
- // Transpose tmp for a
- "vswp d1, d26 \n" // vtrn.64
- "vswp d3, d24 \n" // vtrn.64
- "vtrn.32 q0, q1 \n"
- "vtrn.32 q13, q12 \n"
-
- // Transpose tmp for b
- "vswp d17, d30 \n" // vtrn.64
- "vswp d19, d28 \n" // vtrn.64
- "vtrn.32 q8, q9 \n"
- "vtrn.32 q15, q14 \n"
-
- // The first Q register is a, the second b.
- // q0/8 tmp[0-3]
- // q13/15 tmp[4-7]
- // q1/9 tmp[8-11]
- // q12/14 tmp[12-15]
-
- // These are still in 01 45 23 67 order. We fix it easily in the addition
- // case but the subtraction propegates them.
- "vswp d3, d27 \n"
- "vswp d19, d31 \n"
-
- // a0 = tmp[0] + tmp[8]
- "vadd.s32 q2, q0, q1 \n"
- "vadd.s32 q3, q8, q9 \n"
-
- // a1 = tmp[4] + tmp[12]
- "vadd.s32 q10, q13, q12 \n"
- "vadd.s32 q11, q15, q14 \n"
-
- // a2 = tmp[4] - tmp[12]
- "vsub.s32 q13, q13, q12 \n"
- "vsub.s32 q15, q15, q14 \n"
-
- // a3 = tmp[0] - tmp[8]
- "vsub.s32 q0, q0, q1 \n"
- "vsub.s32 q8, q8, q9 \n"
-
- // b0 = a0 + a1
- "vadd.s32 q1, q2, q10 \n"
- "vadd.s32 q9, q3, q11 \n"
-
- // b1 = a3 + a2
- "vadd.s32 q12, q0, q13 \n"
- "vadd.s32 q14, q8, q15 \n"
-
- // b2 = a3 - a2
- "vsub.s32 q0, q0, q13 \n"
- "vsub.s32 q8, q8, q15 \n"
-
- // b3 = a0 - a1
- "vsub.s32 q2, q2, q10 \n"
- "vsub.s32 q3, q3, q11 \n"
-
- "vld1.64 {q10, q11}, [%[w]] \n"
-
- // abs(b0)
- "vabs.s32 q1, q1 \n"
- "vabs.s32 q9, q9 \n"
- // abs(b1)
- "vabs.s32 q12, q12 \n"
- "vabs.s32 q14, q14 \n"
- // abs(b2)
- "vabs.s32 q0, q0 \n"
- "vabs.s32 q8, q8 \n"
- // abs(b3)
- "vabs.s32 q2, q2 \n"
- "vabs.s32 q3, q3 \n"
-
- // expand w before using.
- "vmovl.u16 q13, d20 \n"
- "vmovl.u16 q15, d21 \n"
-
- // w[0] * abs(b0)
- "vmul.u32 q1, q1, q13 \n"
- "vmul.u32 q9, q9, q13 \n"
-
- // w[4] * abs(b1)
- "vmla.u32 q1, q12, q15 \n"
- "vmla.u32 q9, q14, q15 \n"
-
- // expand w before using.
- "vmovl.u16 q13, d22 \n"
- "vmovl.u16 q15, d23 \n"
-
- // w[8] * abs(b1)
- "vmla.u32 q1, q0, q13 \n"
- "vmla.u32 q9, q8, q13 \n"
-
- // w[12] * abs(b1)
- "vmla.u32 q1, q2, q15 \n"
- "vmla.u32 q9, q3, q15 \n"
-
- // Sum the arrays
- "vpaddl.u32 q1, q1 \n"
- "vpaddl.u32 q9, q9 \n"
- "vadd.u64 d2, d3 \n"
- "vadd.u64 d18, d19 \n"
-
- // Hadamard transform needs 4 bits of extra precision (2 bits in each
- // direction) for dynamic raw. Weights w[] are 16bits at max, so the maximum
- // precision for coeff is 8bit of input + 4bits of Hadamard transform +
- // 16bits for w[] + 2 bits of abs() summation.
- //
- // This uses a maximum of 31 bits (signed). Discarding the top 32 bits is
- // A-OK.
-
- // sum2 - sum1
- "vsub.u32 d0, d2, d18 \n"
- // abs(sum2 - sum1)
- "vabs.s32 d0, d0 \n"
- // abs(sum2 - sum1) >> 5
- "vshr.u32 d0, #5 \n"
-
- // It would be better to move the value straight into r0 but I'm not
- // entirely sure how this works with inline assembly.
- "vmov.32 %[sum], d0[0] \n"
-
- : [sum] "=r"(sum), [a] "+r"(A), [b] "+r"(B), [w] "+r"(W)
- : [kBPS] "r"(kBPS)
- : "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9",
- "q10", "q11", "q12", "q13", "q14", "q15" // clobbered
- ) ;
-
- return sum;
-}
-
-static int Disto16x16(const uint8_t* const a, const uint8_t* const b,
- const uint16_t* const w) {
- int D = 0;
- int x, y;
- for (y = 0; y < 16 * BPS; y += 4 * BPS) {
- for (x = 0; x < 16; x += 4) {
- D += Disto4x4(a + x + y, b + x + y, w);
- }
- }
- return D;
-}
-
-#endif // WEBP_USE_NEON
-
-//------------------------------------------------------------------------------
-// Entry point
-
-extern void VP8EncDspInitNEON(void);
-
-void VP8EncDspInitNEON(void) {
-#if defined(WEBP_USE_NEON)
- VP8ITransform = ITransform;
- VP8FTransform = FTransform;
-
- VP8ITransformWHT = ITransformWHT;
- VP8FTransformWHT = FTransformWHT;
-
- VP8TDisto4x4 = Disto4x4;
- VP8TDisto16x16 = Disto16x16;
-#endif // WEBP_USE_NEON
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/enc_sse2.c b/src/third_party/libwebp/dsp/enc_sse2.c
deleted file mode 100644
index 032e990..0000000
--- a/src/third_party/libwebp/dsp/enc_sse2.c
+++ /dev/null
@@ -1,969 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// SSE2 version of speed-critical encoding functions.
-//
-// Author: Christian Duvivier (cduvivier@google.com)
-
-#include "./dsp.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if defined(WEBP_USE_SSE2)
-#include <stdlib.h> // for abs()
-#include <emmintrin.h>
-
-#include "../enc/vp8enci.h"
-
-//------------------------------------------------------------------------------
-// Quite useful macro for debugging. Left here for convenience.
-
-#if 0
-#include <stdio.h>
-static void PrintReg(const __m128i r, const char* const name, int size) {
- int n;
- union {
- __m128i r;
- uint8_t i8[16];
- uint16_t i16[8];
- uint32_t i32[4];
- uint64_t i64[2];
- } tmp;
- tmp.r = r;
- printf("%s\t: ", name);
- if (size == 8) {
- for (n = 0; n < 16; ++n) printf("%.2x ", tmp.i8[n]);
- } else if (size == 16) {
- for (n = 0; n < 8; ++n) printf("%.4x ", tmp.i16[n]);
- } else if (size == 32) {
- for (n = 0; n < 4; ++n) printf("%.8x ", tmp.i32[n]);
- } else {
- for (n = 0; n < 2; ++n) printf("%.16lx ", tmp.i64[n]);
- }
- printf("\n");
-}
-#endif
-
-//------------------------------------------------------------------------------
-// Compute susceptibility based on DCT-coeff histograms:
-// the higher, the "easier" the macroblock is to compress.
-
-static void CollectHistogramSSE2(const uint8_t* ref, const uint8_t* pred,
- int start_block, int end_block,
- VP8Histogram* const histo) {
- const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH);
- int j;
- for (j = start_block; j < end_block; ++j) {
- int16_t out[16];
- int k;
-
- VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
-
- // Convert coefficients to bin (within out[]).
- {
- // Load.
- const __m128i out0 = _mm_loadu_si128((__m128i*)&out[0]);
- const __m128i out1 = _mm_loadu_si128((__m128i*)&out[8]);
- // sign(out) = out >> 15 (0x0000 if positive, 0xffff if negative)
- const __m128i sign0 = _mm_srai_epi16(out0, 15);
- const __m128i sign1 = _mm_srai_epi16(out1, 15);
- // abs(out) = (out ^ sign) - sign
- const __m128i xor0 = _mm_xor_si128(out0, sign0);
- const __m128i xor1 = _mm_xor_si128(out1, sign1);
- const __m128i abs0 = _mm_sub_epi16(xor0, sign0);
- const __m128i abs1 = _mm_sub_epi16(xor1, sign1);
- // v = abs(out) >> 3
- const __m128i v0 = _mm_srai_epi16(abs0, 3);
- const __m128i v1 = _mm_srai_epi16(abs1, 3);
- // bin = min(v, MAX_COEFF_THRESH)
- const __m128i bin0 = _mm_min_epi16(v0, max_coeff_thresh);
- const __m128i bin1 = _mm_min_epi16(v1, max_coeff_thresh);
- // Store.
- _mm_storeu_si128((__m128i*)&out[0], bin0);
- _mm_storeu_si128((__m128i*)&out[8], bin1);
- }
-
- // Convert coefficients to bin.
- for (k = 0; k < 16; ++k) {
- histo->distribution[out[k]]++;
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// Transforms (Paragraph 14.4)
-
-// Does one or two inverse transforms.
-static void ITransformSSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
- int do_two) {
- // This implementation makes use of 16-bit fixed point versions of two
- // multiply constants:
- // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
- // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
- //
- // To be able to use signed 16-bit integers, we use the following trick to
- // have constants within range:
- // - Associated constants are obtained by subtracting the 16-bit fixed point
- // version of one:
- // k = K - (1 << 16) => K = k + (1 << 16)
- // K1 = 85267 => k1 = 20091
- // K2 = 35468 => k2 = -30068
- // - The multiplication of a variable by a constant become the sum of the
- // variable and the multiplication of that variable by the associated
- // constant:
- // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
- const __m128i k1 = _mm_set1_epi16(20091);
- const __m128i k2 = _mm_set1_epi16(-30068);
- __m128i T0, T1, T2, T3;
-
- // Load and concatenate the transform coefficients (we'll do two inverse
- // transforms in parallel). In the case of only one inverse transform, the
- // second half of the vectors will just contain random value we'll never
- // use nor store.
- __m128i in0, in1, in2, in3;
- {
- in0 = _mm_loadl_epi64((__m128i*)&in[0]);
- in1 = _mm_loadl_epi64((__m128i*)&in[4]);
- in2 = _mm_loadl_epi64((__m128i*)&in[8]);
- in3 = _mm_loadl_epi64((__m128i*)&in[12]);
- // a00 a10 a20 a30 x x x x
- // a01 a11 a21 a31 x x x x
- // a02 a12 a22 a32 x x x x
- // a03 a13 a23 a33 x x x x
- if (do_two) {
- const __m128i inB0 = _mm_loadl_epi64((__m128i*)&in[16]);
- const __m128i inB1 = _mm_loadl_epi64((__m128i*)&in[20]);
- const __m128i inB2 = _mm_loadl_epi64((__m128i*)&in[24]);
- const __m128i inB3 = _mm_loadl_epi64((__m128i*)&in[28]);
- in0 = _mm_unpacklo_epi64(in0, inB0);
- in1 = _mm_unpacklo_epi64(in1, inB1);
- in2 = _mm_unpacklo_epi64(in2, inB2);
- in3 = _mm_unpacklo_epi64(in3, inB3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
- }
-
- // Vertical pass and subsequent transpose.
- {
- // First pass, c and d calculations are longer because of the "trick"
- // multiplications.
- const __m128i a = _mm_add_epi16(in0, in2);
- const __m128i b = _mm_sub_epi16(in0, in2);
- // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
- const __m128i c1 = _mm_mulhi_epi16(in1, k2);
- const __m128i c2 = _mm_mulhi_epi16(in3, k1);
- const __m128i c3 = _mm_sub_epi16(in1, in3);
- const __m128i c4 = _mm_sub_epi16(c1, c2);
- const __m128i c = _mm_add_epi16(c3, c4);
- // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
- const __m128i d1 = _mm_mulhi_epi16(in1, k1);
- const __m128i d2 = _mm_mulhi_epi16(in3, k2);
- const __m128i d3 = _mm_add_epi16(in1, in3);
- const __m128i d4 = _mm_add_epi16(d1, d2);
- const __m128i d = _mm_add_epi16(d3, d4);
-
- // Second pass.
- const __m128i tmp0 = _mm_add_epi16(a, d);
- const __m128i tmp1 = _mm_add_epi16(b, c);
- const __m128i tmp2 = _mm_sub_epi16(b, c);
- const __m128i tmp3 = _mm_sub_epi16(a, d);
-
- // Transpose the two 4x4.
- // a00 a01 a02 a03 b00 b01 b02 b03
- // a10 a11 a12 a13 b10 b11 b12 b13
- // a20 a21 a22 a23 b20 b21 b22 b23
- // a30 a31 a32 a33 b30 b31 b32 b33
- const __m128i transpose0_0 = _mm_unpacklo_epi16(tmp0, tmp1);
- const __m128i transpose0_1 = _mm_unpacklo_epi16(tmp2, tmp3);
- const __m128i transpose0_2 = _mm_unpackhi_epi16(tmp0, tmp1);
- const __m128i transpose0_3 = _mm_unpackhi_epi16(tmp2, tmp3);
- // a00 a10 a01 a11 a02 a12 a03 a13
- // a20 a30 a21 a31 a22 a32 a23 a33
- // b00 b10 b01 b11 b02 b12 b03 b13
- // b20 b30 b21 b31 b22 b32 b23 b33
- const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
- const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
- // a00 a10 a20 a30 a01 a11 a21 a31
- // b00 b10 b20 b30 b01 b11 b21 b31
- // a02 a12 a22 a32 a03 a13 a23 a33
- // b02 b12 a22 b32 b03 b13 b23 b33
- T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
- T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
- T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
- T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
-
- // Horizontal pass and subsequent transpose.
- {
- // First pass, c and d calculations are longer because of the "trick"
- // multiplications.
- const __m128i four = _mm_set1_epi16(4);
- const __m128i dc = _mm_add_epi16(T0, four);
- const __m128i a = _mm_add_epi16(dc, T2);
- const __m128i b = _mm_sub_epi16(dc, T2);
- // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
- const __m128i c1 = _mm_mulhi_epi16(T1, k2);
- const __m128i c2 = _mm_mulhi_epi16(T3, k1);
- const __m128i c3 = _mm_sub_epi16(T1, T3);
- const __m128i c4 = _mm_sub_epi16(c1, c2);
- const __m128i c = _mm_add_epi16(c3, c4);
- // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
- const __m128i d1 = _mm_mulhi_epi16(T1, k1);
- const __m128i d2 = _mm_mulhi_epi16(T3, k2);
- const __m128i d3 = _mm_add_epi16(T1, T3);
- const __m128i d4 = _mm_add_epi16(d1, d2);
- const __m128i d = _mm_add_epi16(d3, d4);
-
- // Second pass.
- const __m128i tmp0 = _mm_add_epi16(a, d);
- const __m128i tmp1 = _mm_add_epi16(b, c);
- const __m128i tmp2 = _mm_sub_epi16(b, c);
- const __m128i tmp3 = _mm_sub_epi16(a, d);
- const __m128i shifted0 = _mm_srai_epi16(tmp0, 3);
- const __m128i shifted1 = _mm_srai_epi16(tmp1, 3);
- const __m128i shifted2 = _mm_srai_epi16(tmp2, 3);
- const __m128i shifted3 = _mm_srai_epi16(tmp3, 3);
-
- // Transpose the two 4x4.
- // a00 a01 a02 a03 b00 b01 b02 b03
- // a10 a11 a12 a13 b10 b11 b12 b13
- // a20 a21 a22 a23 b20 b21 b22 b23
- // a30 a31 a32 a33 b30 b31 b32 b33
- const __m128i transpose0_0 = _mm_unpacklo_epi16(shifted0, shifted1);
- const __m128i transpose0_1 = _mm_unpacklo_epi16(shifted2, shifted3);
- const __m128i transpose0_2 = _mm_unpackhi_epi16(shifted0, shifted1);
- const __m128i transpose0_3 = _mm_unpackhi_epi16(shifted2, shifted3);
- // a00 a10 a01 a11 a02 a12 a03 a13
- // a20 a30 a21 a31 a22 a32 a23 a33
- // b00 b10 b01 b11 b02 b12 b03 b13
- // b20 b30 b21 b31 b22 b32 b23 b33
- const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
- const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
- // a00 a10 a20 a30 a01 a11 a21 a31
- // b00 b10 b20 b30 b01 b11 b21 b31
- // a02 a12 a22 a32 a03 a13 a23 a33
- // b02 b12 a22 b32 b03 b13 b23 b33
- T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
- T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
- T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
- T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
-
- // Add inverse transform to 'ref' and store.
- {
- const __m128i zero = _mm_setzero_si128();
- // Load the reference(s).
- __m128i ref0, ref1, ref2, ref3;
- if (do_two) {
- // Load eight bytes/pixels per line.
- ref0 = _mm_loadl_epi64((__m128i*)&ref[0 * BPS]);
- ref1 = _mm_loadl_epi64((__m128i*)&ref[1 * BPS]);
- ref2 = _mm_loadl_epi64((__m128i*)&ref[2 * BPS]);
- ref3 = _mm_loadl_epi64((__m128i*)&ref[3 * BPS]);
- } else {
- // Load four bytes/pixels per line.
- ref0 = _mm_cvtsi32_si128(*(int*)&ref[0 * BPS]);
- ref1 = _mm_cvtsi32_si128(*(int*)&ref[1 * BPS]);
- ref2 = _mm_cvtsi32_si128(*(int*)&ref[2 * BPS]);
- ref3 = _mm_cvtsi32_si128(*(int*)&ref[3 * BPS]);
- }
- // Convert to 16b.
- ref0 = _mm_unpacklo_epi8(ref0, zero);
- ref1 = _mm_unpacklo_epi8(ref1, zero);
- ref2 = _mm_unpacklo_epi8(ref2, zero);
- ref3 = _mm_unpacklo_epi8(ref3, zero);
- // Add the inverse transform(s).
- ref0 = _mm_add_epi16(ref0, T0);
- ref1 = _mm_add_epi16(ref1, T1);
- ref2 = _mm_add_epi16(ref2, T2);
- ref3 = _mm_add_epi16(ref3, T3);
- // Unsigned saturate to 8b.
- ref0 = _mm_packus_epi16(ref0, ref0);
- ref1 = _mm_packus_epi16(ref1, ref1);
- ref2 = _mm_packus_epi16(ref2, ref2);
- ref3 = _mm_packus_epi16(ref3, ref3);
- // Store the results.
- if (do_two) {
- // Store eight bytes/pixels per line.
- _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
- _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
- _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
- _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
- } else {
- // Store four bytes/pixels per line.
- *((int32_t *)&dst[0 * BPS]) = _mm_cvtsi128_si32(ref0);
- *((int32_t *)&dst[1 * BPS]) = _mm_cvtsi128_si32(ref1);
- *((int32_t *)&dst[2 * BPS]) = _mm_cvtsi128_si32(ref2);
- *((int32_t *)&dst[3 * BPS]) = _mm_cvtsi128_si32(ref3);
- }
- }
-}
-
-static void FTransformSSE2(const uint8_t* src, const uint8_t* ref,
- int16_t* out) {
- const __m128i zero = _mm_setzero_si128();
- const __m128i seven = _mm_set1_epi16(7);
- const __m128i k937 = _mm_set1_epi32(937);
- const __m128i k1812 = _mm_set1_epi32(1812);
- const __m128i k51000 = _mm_set1_epi32(51000);
- const __m128i k12000_plus_one = _mm_set1_epi32(12000 + (1 << 16));
- const __m128i k5352_2217 = _mm_set_epi16(5352, 2217, 5352, 2217,
- 5352, 2217, 5352, 2217);
- const __m128i k2217_5352 = _mm_set_epi16(2217, -5352, 2217, -5352,
- 2217, -5352, 2217, -5352);
- const __m128i k88p = _mm_set_epi16(8, 8, 8, 8, 8, 8, 8, 8);
- const __m128i k88m = _mm_set_epi16(-8, 8, -8, 8, -8, 8, -8, 8);
- const __m128i k5352_2217p = _mm_set_epi16(2217, 5352, 2217, 5352,
- 2217, 5352, 2217, 5352);
- const __m128i k5352_2217m = _mm_set_epi16(-5352, 2217, -5352, 2217,
- -5352, 2217, -5352, 2217);
- __m128i v01, v32;
-
-
- // Difference between src and ref and initial transpose.
- {
- // Load src and convert to 16b.
- const __m128i src0 = _mm_loadl_epi64((__m128i*)&src[0 * BPS]);
- const __m128i src1 = _mm_loadl_epi64((__m128i*)&src[1 * BPS]);
- const __m128i src2 = _mm_loadl_epi64((__m128i*)&src[2 * BPS]);
- const __m128i src3 = _mm_loadl_epi64((__m128i*)&src[3 * BPS]);
- const __m128i src_0 = _mm_unpacklo_epi8(src0, zero);
- const __m128i src_1 = _mm_unpacklo_epi8(src1, zero);
- const __m128i src_2 = _mm_unpacklo_epi8(src2, zero);
- const __m128i src_3 = _mm_unpacklo_epi8(src3, zero);
- // Load ref and convert to 16b.
- const __m128i ref0 = _mm_loadl_epi64((__m128i*)&ref[0 * BPS]);
- const __m128i ref1 = _mm_loadl_epi64((__m128i*)&ref[1 * BPS]);
- const __m128i ref2 = _mm_loadl_epi64((__m128i*)&ref[2 * BPS]);
- const __m128i ref3 = _mm_loadl_epi64((__m128i*)&ref[3 * BPS]);
- const __m128i ref_0 = _mm_unpacklo_epi8(ref0, zero);
- const __m128i ref_1 = _mm_unpacklo_epi8(ref1, zero);
- const __m128i ref_2 = _mm_unpacklo_epi8(ref2, zero);
- const __m128i ref_3 = _mm_unpacklo_epi8(ref3, zero);
- // Compute difference. -> 00 01 02 03 00 00 00 00
- const __m128i diff0 = _mm_sub_epi16(src_0, ref_0);
- const __m128i diff1 = _mm_sub_epi16(src_1, ref_1);
- const __m128i diff2 = _mm_sub_epi16(src_2, ref_2);
- const __m128i diff3 = _mm_sub_epi16(src_3, ref_3);
-
-
- // Unpack and shuffle
- // 00 01 02 03 0 0 0 0
- // 10 11 12 13 0 0 0 0
- // 20 21 22 23 0 0 0 0
- // 30 31 32 33 0 0 0 0
- const __m128i shuf01 = _mm_unpacklo_epi32(diff0, diff1);
- const __m128i shuf23 = _mm_unpacklo_epi32(diff2, diff3);
- // 00 01 10 11 02 03 12 13
- // 20 21 30 31 22 23 32 33
- const __m128i shuf01_p =
- _mm_shufflehi_epi16(shuf01, _MM_SHUFFLE(2, 3, 0, 1));
- const __m128i shuf23_p =
- _mm_shufflehi_epi16(shuf23, _MM_SHUFFLE(2, 3, 0, 1));
- // 00 01 10 11 03 02 13 12
- // 20 21 30 31 23 22 33 32
- const __m128i s01 = _mm_unpacklo_epi64(shuf01_p, shuf23_p);
- const __m128i s32 = _mm_unpackhi_epi64(shuf01_p, shuf23_p);
- // 00 01 10 11 20 21 30 31
- // 03 02 13 12 23 22 33 32
- const __m128i a01 = _mm_add_epi16(s01, s32);
- const __m128i a32 = _mm_sub_epi16(s01, s32);
- // [d0 + d3 | d1 + d2 | ...] = [a0 a1 | a0' a1' | ... ]
- // [d0 - d3 | d1 - d2 | ...] = [a3 a2 | a3' a2' | ... ]
-
- const __m128i tmp0 = _mm_madd_epi16(a01, k88p); // [ (a0 + a1) << 3, ... ]
- const __m128i tmp2 = _mm_madd_epi16(a01, k88m); // [ (a0 - a1) << 3, ... ]
- const __m128i tmp1_1 = _mm_madd_epi16(a32, k5352_2217p);
- const __m128i tmp3_1 = _mm_madd_epi16(a32, k5352_2217m);
- const __m128i tmp1_2 = _mm_add_epi32(tmp1_1, k1812);
- const __m128i tmp3_2 = _mm_add_epi32(tmp3_1, k937);
- const __m128i tmp1 = _mm_srai_epi32(tmp1_2, 9);
- const __m128i tmp3 = _mm_srai_epi32(tmp3_2, 9);
- const __m128i s03 = _mm_packs_epi32(tmp0, tmp2);
- const __m128i s12 = _mm_packs_epi32(tmp1, tmp3);
- const __m128i s_lo = _mm_unpacklo_epi16(s03, s12); // 0 1 0 1 0 1...
- const __m128i s_hi = _mm_unpackhi_epi16(s03, s12); // 2 3 2 3 2 3
- const __m128i v23 = _mm_unpackhi_epi32(s_lo, s_hi);
- v01 = _mm_unpacklo_epi32(s_lo, s_hi);
- v32 = _mm_shuffle_epi32(v23, _MM_SHUFFLE(1, 0, 3, 2)); // 3 2 3 2 3 2..
- }
-
- // Second pass
- {
- // Same operations are done on the (0,3) and (1,2) pairs.
- // a0 = v0 + v3
- // a1 = v1 + v2
- // a3 = v0 - v3
- // a2 = v1 - v2
- const __m128i a01 = _mm_add_epi16(v01, v32);
- const __m128i a32 = _mm_sub_epi16(v01, v32);
- const __m128i a11 = _mm_unpackhi_epi64(a01, a01);
- const __m128i a22 = _mm_unpackhi_epi64(a32, a32);
- const __m128i a01_plus_7 = _mm_add_epi16(a01, seven);
-
- // d0 = (a0 + a1 + 7) >> 4;
- // d2 = (a0 - a1 + 7) >> 4;
- const __m128i c0 = _mm_add_epi16(a01_plus_7, a11);
- const __m128i c2 = _mm_sub_epi16(a01_plus_7, a11);
- const __m128i d0 = _mm_srai_epi16(c0, 4);
- const __m128i d2 = _mm_srai_epi16(c2, 4);
-
- // f1 = ((b3 * 5352 + b2 * 2217 + 12000) >> 16)
- // f3 = ((b3 * 2217 - b2 * 5352 + 51000) >> 16)
- const __m128i b23 = _mm_unpacklo_epi16(a22, a32);
- const __m128i c1 = _mm_madd_epi16(b23, k5352_2217);
- const __m128i c3 = _mm_madd_epi16(b23, k2217_5352);
- const __m128i d1 = _mm_add_epi32(c1, k12000_plus_one);
- const __m128i d3 = _mm_add_epi32(c3, k51000);
- const __m128i e1 = _mm_srai_epi32(d1, 16);
- const __m128i e3 = _mm_srai_epi32(d3, 16);
- const __m128i f1 = _mm_packs_epi32(e1, e1);
- const __m128i f3 = _mm_packs_epi32(e3, e3);
- // f1 = f1 + (a3 != 0);
- // The compare will return (0xffff, 0) for (==0, !=0). To turn that into the
- // desired (0, 1), we add one earlier through k12000_plus_one.
- // -> f1 = f1 + 1 - (a3 == 0)
- const __m128i g1 = _mm_add_epi16(f1, _mm_cmpeq_epi16(a32, zero));
-
- _mm_storel_epi64((__m128i*)&out[ 0], d0);
- _mm_storel_epi64((__m128i*)&out[ 4], g1);
- _mm_storel_epi64((__m128i*)&out[ 8], d2);
- _mm_storel_epi64((__m128i*)&out[12], f3);
- }
-}
-
-static void FTransformWHTSSE2(const int16_t* in, int16_t* out) {
- int16_t tmp[16];
- int i;
- for (i = 0; i < 4; ++i, in += 64) {
- const int a0 = (in[0 * 16] + in[2 * 16]);
- const int a1 = (in[1 * 16] + in[3 * 16]);
- const int a2 = (in[1 * 16] - in[3 * 16]);
- const int a3 = (in[0 * 16] - in[2 * 16]);
- tmp[0 + i * 4] = a0 + a1;
- tmp[1 + i * 4] = a3 + a2;
- tmp[2 + i * 4] = a3 - a2;
- tmp[3 + i * 4] = a0 - a1;
- }
- {
- const __m128i src0 = _mm_loadl_epi64((__m128i*)&tmp[0]);
- const __m128i src1 = _mm_loadl_epi64((__m128i*)&tmp[4]);
- const __m128i src2 = _mm_loadl_epi64((__m128i*)&tmp[8]);
- const __m128i src3 = _mm_loadl_epi64((__m128i*)&tmp[12]);
- const __m128i a0 = _mm_add_epi16(src0, src2);
- const __m128i a1 = _mm_add_epi16(src1, src3);
- const __m128i a2 = _mm_sub_epi16(src1, src3);
- const __m128i a3 = _mm_sub_epi16(src0, src2);
- const __m128i b0 = _mm_srai_epi16(_mm_adds_epi16(a0, a1), 1);
- const __m128i b1 = _mm_srai_epi16(_mm_adds_epi16(a3, a2), 1);
- const __m128i b2 = _mm_srai_epi16(_mm_subs_epi16(a3, a2), 1);
- const __m128i b3 = _mm_srai_epi16(_mm_subs_epi16(a0, a1), 1);
- _mm_storel_epi64((__m128i*)&out[ 0], b0);
- _mm_storel_epi64((__m128i*)&out[ 4], b1);
- _mm_storel_epi64((__m128i*)&out[ 8], b2);
- _mm_storel_epi64((__m128i*)&out[12], b3);
- }
-}
-
-//------------------------------------------------------------------------------
-// Metric
-
-static int SSE_Nx4SSE2(const uint8_t* a, const uint8_t* b,
- int num_quads, int do_16) {
- const __m128i zero = _mm_setzero_si128();
- __m128i sum1 = zero;
- __m128i sum2 = zero;
-
- while (num_quads-- > 0) {
- // Note: for the !do_16 case, we read 16 pixels instead of 8 but that's ok,
- // thanks to buffer over-allocation to that effect.
- const __m128i a0 = _mm_loadu_si128((__m128i*)&a[BPS * 0]);
- const __m128i a1 = _mm_loadu_si128((__m128i*)&a[BPS * 1]);
- const __m128i a2 = _mm_loadu_si128((__m128i*)&a[BPS * 2]);
- const __m128i a3 = _mm_loadu_si128((__m128i*)&a[BPS * 3]);
- const __m128i b0 = _mm_loadu_si128((__m128i*)&b[BPS * 0]);
- const __m128i b1 = _mm_loadu_si128((__m128i*)&b[BPS * 1]);
- const __m128i b2 = _mm_loadu_si128((__m128i*)&b[BPS * 2]);
- const __m128i b3 = _mm_loadu_si128((__m128i*)&b[BPS * 3]);
-
- // compute clip0(a-b) and clip0(b-a)
- const __m128i a0p = _mm_subs_epu8(a0, b0);
- const __m128i a0m = _mm_subs_epu8(b0, a0);
- const __m128i a1p = _mm_subs_epu8(a1, b1);
- const __m128i a1m = _mm_subs_epu8(b1, a1);
- const __m128i a2p = _mm_subs_epu8(a2, b2);
- const __m128i a2m = _mm_subs_epu8(b2, a2);
- const __m128i a3p = _mm_subs_epu8(a3, b3);
- const __m128i a3m = _mm_subs_epu8(b3, a3);
-
- // compute |a-b| with 8b arithmetic as clip0(a-b) | clip0(b-a)
- const __m128i diff0 = _mm_or_si128(a0p, a0m);
- const __m128i diff1 = _mm_or_si128(a1p, a1m);
- const __m128i diff2 = _mm_or_si128(a2p, a2m);
- const __m128i diff3 = _mm_or_si128(a3p, a3m);
-
- // unpack (only four operations, instead of eight)
- const __m128i low0 = _mm_unpacklo_epi8(diff0, zero);
- const __m128i low1 = _mm_unpacklo_epi8(diff1, zero);
- const __m128i low2 = _mm_unpacklo_epi8(diff2, zero);
- const __m128i low3 = _mm_unpacklo_epi8(diff3, zero);
-
- // multiply with self
- const __m128i low_madd0 = _mm_madd_epi16(low0, low0);
- const __m128i low_madd1 = _mm_madd_epi16(low1, low1);
- const __m128i low_madd2 = _mm_madd_epi16(low2, low2);
- const __m128i low_madd3 = _mm_madd_epi16(low3, low3);
-
- // collect in a cascading way
- const __m128i low_sum0 = _mm_add_epi32(low_madd0, low_madd1);
- const __m128i low_sum1 = _mm_add_epi32(low_madd2, low_madd3);
- sum1 = _mm_add_epi32(sum1, low_sum0);
- sum2 = _mm_add_epi32(sum2, low_sum1);
-
- if (do_16) { // if necessary, process the higher 8 bytes similarly
- const __m128i hi0 = _mm_unpackhi_epi8(diff0, zero);
- const __m128i hi1 = _mm_unpackhi_epi8(diff1, zero);
- const __m128i hi2 = _mm_unpackhi_epi8(diff2, zero);
- const __m128i hi3 = _mm_unpackhi_epi8(diff3, zero);
-
- const __m128i hi_madd0 = _mm_madd_epi16(hi0, hi0);
- const __m128i hi_madd1 = _mm_madd_epi16(hi1, hi1);
- const __m128i hi_madd2 = _mm_madd_epi16(hi2, hi2);
- const __m128i hi_madd3 = _mm_madd_epi16(hi3, hi3);
- const __m128i hi_sum0 = _mm_add_epi32(hi_madd0, hi_madd1);
- const __m128i hi_sum1 = _mm_add_epi32(hi_madd2, hi_madd3);
- sum1 = _mm_add_epi32(sum1, hi_sum0);
- sum2 = _mm_add_epi32(sum2, hi_sum1);
- }
- a += 4 * BPS;
- b += 4 * BPS;
- }
- {
- int32_t tmp[4];
- const __m128i sum = _mm_add_epi32(sum1, sum2);
- _mm_storeu_si128((__m128i*)tmp, sum);
- return (tmp[3] + tmp[2] + tmp[1] + tmp[0]);
- }
-}
-
-static int SSE16x16SSE2(const uint8_t* a, const uint8_t* b) {
- return SSE_Nx4SSE2(a, b, 4, 1);
-}
-
-static int SSE16x8SSE2(const uint8_t* a, const uint8_t* b) {
- return SSE_Nx4SSE2(a, b, 2, 1);
-}
-
-static int SSE8x8SSE2(const uint8_t* a, const uint8_t* b) {
- return SSE_Nx4SSE2(a, b, 2, 0);
-}
-
-static int SSE4x4SSE2(const uint8_t* a, const uint8_t* b) {
- const __m128i zero = _mm_setzero_si128();
-
- // Load values. Note that we read 8 pixels instead of 4,
- // but the a/b buffers are over-allocated to that effect.
- const __m128i a0 = _mm_loadl_epi64((__m128i*)&a[BPS * 0]);
- const __m128i a1 = _mm_loadl_epi64((__m128i*)&a[BPS * 1]);
- const __m128i a2 = _mm_loadl_epi64((__m128i*)&a[BPS * 2]);
- const __m128i a3 = _mm_loadl_epi64((__m128i*)&a[BPS * 3]);
- const __m128i b0 = _mm_loadl_epi64((__m128i*)&b[BPS * 0]);
- const __m128i b1 = _mm_loadl_epi64((__m128i*)&b[BPS * 1]);
- const __m128i b2 = _mm_loadl_epi64((__m128i*)&b[BPS * 2]);
- const __m128i b3 = _mm_loadl_epi64((__m128i*)&b[BPS * 3]);
-
- // Combine pair of lines and convert to 16b.
- const __m128i a01 = _mm_unpacklo_epi32(a0, a1);
- const __m128i a23 = _mm_unpacklo_epi32(a2, a3);
- const __m128i b01 = _mm_unpacklo_epi32(b0, b1);
- const __m128i b23 = _mm_unpacklo_epi32(b2, b3);
- const __m128i a01s = _mm_unpacklo_epi8(a01, zero);
- const __m128i a23s = _mm_unpacklo_epi8(a23, zero);
- const __m128i b01s = _mm_unpacklo_epi8(b01, zero);
- const __m128i b23s = _mm_unpacklo_epi8(b23, zero);
-
- // Compute differences; (a-b)^2 = (abs(a-b))^2 = (sat8(a-b) + sat8(b-a))^2
- // TODO(cduvivier): Dissassemble and figure out why this is fastest. We don't
- // need absolute values, there is no need to do calculation
- // in 8bit as we are already in 16bit, ... Yet this is what
- // benchmarks the fastest!
- const __m128i d0 = _mm_subs_epu8(a01s, b01s);
- const __m128i d1 = _mm_subs_epu8(b01s, a01s);
- const __m128i d2 = _mm_subs_epu8(a23s, b23s);
- const __m128i d3 = _mm_subs_epu8(b23s, a23s);
-
- // Square and add them all together.
- const __m128i madd0 = _mm_madd_epi16(d0, d0);
- const __m128i madd1 = _mm_madd_epi16(d1, d1);
- const __m128i madd2 = _mm_madd_epi16(d2, d2);
- const __m128i madd3 = _mm_madd_epi16(d3, d3);
- const __m128i sum0 = _mm_add_epi32(madd0, madd1);
- const __m128i sum1 = _mm_add_epi32(madd2, madd3);
- const __m128i sum2 = _mm_add_epi32(sum0, sum1);
-
- int32_t tmp[4];
- _mm_storeu_si128((__m128i*)tmp, sum2);
- return (tmp[3] + tmp[2] + tmp[1] + tmp[0]);
-}
-
-//------------------------------------------------------------------------------
-// Texture distortion
-//
-// We try to match the spectral content (weighted) between source and
-// reconstructed samples.
-
-// Hadamard transform
-// Returns the difference between the weighted sum of the absolute value of
-// transformed coefficients.
-static int TTransformSSE2(const uint8_t* inA, const uint8_t* inB,
- const uint16_t* const w) {
- int32_t sum[4];
- __m128i tmp_0, tmp_1, tmp_2, tmp_3;
- const __m128i zero = _mm_setzero_si128();
-
- // Load, combine and tranpose inputs.
- {
- const __m128i inA_0 = _mm_loadl_epi64((__m128i*)&inA[BPS * 0]);
- const __m128i inA_1 = _mm_loadl_epi64((__m128i*)&inA[BPS * 1]);
- const __m128i inA_2 = _mm_loadl_epi64((__m128i*)&inA[BPS * 2]);
- const __m128i inA_3 = _mm_loadl_epi64((__m128i*)&inA[BPS * 3]);
- const __m128i inB_0 = _mm_loadl_epi64((__m128i*)&inB[BPS * 0]);
- const __m128i inB_1 = _mm_loadl_epi64((__m128i*)&inB[BPS * 1]);
- const __m128i inB_2 = _mm_loadl_epi64((__m128i*)&inB[BPS * 2]);
- const __m128i inB_3 = _mm_loadl_epi64((__m128i*)&inB[BPS * 3]);
-
- // Combine inA and inB (we'll do two transforms in parallel).
- const __m128i inAB_0 = _mm_unpacklo_epi8(inA_0, inB_0);
- const __m128i inAB_1 = _mm_unpacklo_epi8(inA_1, inB_1);
- const __m128i inAB_2 = _mm_unpacklo_epi8(inA_2, inB_2);
- const __m128i inAB_3 = _mm_unpacklo_epi8(inA_3, inB_3);
- // a00 b00 a01 b01 a02 b03 a03 b03 0 0 0 0 0 0 0 0
- // a10 b10 a11 b11 a12 b12 a13 b13 0 0 0 0 0 0 0 0
- // a20 b20 a21 b21 a22 b22 a23 b23 0 0 0 0 0 0 0 0
- // a30 b30 a31 b31 a32 b32 a33 b33 0 0 0 0 0 0 0 0
-
- // Transpose the two 4x4, discarding the filling zeroes.
- const __m128i transpose0_0 = _mm_unpacklo_epi8(inAB_0, inAB_2);
- const __m128i transpose0_1 = _mm_unpacklo_epi8(inAB_1, inAB_3);
- // a00 a20 b00 b20 a01 a21 b01 b21 a02 a22 b02 b22 a03 a23 b03 b23
- // a10 a30 b10 b30 a11 a31 b11 b31 a12 a32 b12 b32 a13 a33 b13 b33
- const __m128i transpose1_0 = _mm_unpacklo_epi8(transpose0_0, transpose0_1);
- const __m128i transpose1_1 = _mm_unpackhi_epi8(transpose0_0, transpose0_1);
- // a00 a10 a20 a30 b00 b10 b20 b30 a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32 a03 a13 a23 a33 b03 b13 b23 b33
-
- // Convert to 16b.
- tmp_0 = _mm_unpacklo_epi8(transpose1_0, zero);
- tmp_1 = _mm_unpackhi_epi8(transpose1_0, zero);
- tmp_2 = _mm_unpacklo_epi8(transpose1_1, zero);
- tmp_3 = _mm_unpackhi_epi8(transpose1_1, zero);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
-
- // Horizontal pass and subsequent transpose.
- {
- // Calculate a and b (two 4x4 at once).
- const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2);
- const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3);
- const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3);
- const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2);
- const __m128i b0 = _mm_add_epi16(a0, a1);
- const __m128i b1 = _mm_add_epi16(a3, a2);
- const __m128i b2 = _mm_sub_epi16(a3, a2);
- const __m128i b3 = _mm_sub_epi16(a0, a1);
- // a00 a01 a02 a03 b00 b01 b02 b03
- // a10 a11 a12 a13 b10 b11 b12 b13
- // a20 a21 a22 a23 b20 b21 b22 b23
- // a30 a31 a32 a33 b30 b31 b32 b33
-
- // Transpose the two 4x4.
- const __m128i transpose0_0 = _mm_unpacklo_epi16(b0, b1);
- const __m128i transpose0_1 = _mm_unpacklo_epi16(b2, b3);
- const __m128i transpose0_2 = _mm_unpackhi_epi16(b0, b1);
- const __m128i transpose0_3 = _mm_unpackhi_epi16(b2, b3);
- // a00 a10 a01 a11 a02 a12 a03 a13
- // a20 a30 a21 a31 a22 a32 a23 a33
- // b00 b10 b01 b11 b02 b12 b03 b13
- // b20 b30 b21 b31 b22 b32 b23 b33
- const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
- const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
- const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
- // a00 a10 a20 a30 a01 a11 a21 a31
- // b00 b10 b20 b30 b01 b11 b21 b31
- // a02 a12 a22 a32 a03 a13 a23 a33
- // b02 b12 a22 b32 b03 b13 b23 b33
- tmp_0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
- tmp_1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
- tmp_2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
- tmp_3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
- // a00 a10 a20 a30 b00 b10 b20 b30
- // a01 a11 a21 a31 b01 b11 b21 b31
- // a02 a12 a22 a32 b02 b12 b22 b32
- // a03 a13 a23 a33 b03 b13 b23 b33
- }
-
- // Vertical pass and difference of weighted sums.
- {
- // Load all inputs.
- // TODO(cduvivier): Make variable declarations and allocations aligned so
- // we can use _mm_load_si128 instead of _mm_loadu_si128.
- const __m128i w_0 = _mm_loadu_si128((__m128i*)&w[0]);
- const __m128i w_8 = _mm_loadu_si128((__m128i*)&w[8]);
-
- // Calculate a and b (two 4x4 at once).
- const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2);
- const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3);
- const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3);
- const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2);
- const __m128i b0 = _mm_add_epi16(a0, a1);
- const __m128i b1 = _mm_add_epi16(a3, a2);
- const __m128i b2 = _mm_sub_epi16(a3, a2);
- const __m128i b3 = _mm_sub_epi16(a0, a1);
-
- // Separate the transforms of inA and inB.
- __m128i A_b0 = _mm_unpacklo_epi64(b0, b1);
- __m128i A_b2 = _mm_unpacklo_epi64(b2, b3);
- __m128i B_b0 = _mm_unpackhi_epi64(b0, b1);
- __m128i B_b2 = _mm_unpackhi_epi64(b2, b3);
-
- {
- // sign(b) = b >> 15 (0x0000 if positive, 0xffff if negative)
- const __m128i sign_A_b0 = _mm_srai_epi16(A_b0, 15);
- const __m128i sign_A_b2 = _mm_srai_epi16(A_b2, 15);
- const __m128i sign_B_b0 = _mm_srai_epi16(B_b0, 15);
- const __m128i sign_B_b2 = _mm_srai_epi16(B_b2, 15);
-
- // b = abs(b) = (b ^ sign) - sign
- A_b0 = _mm_xor_si128(A_b0, sign_A_b0);
- A_b2 = _mm_xor_si128(A_b2, sign_A_b2);
- B_b0 = _mm_xor_si128(B_b0, sign_B_b0);
- B_b2 = _mm_xor_si128(B_b2, sign_B_b2);
- A_b0 = _mm_sub_epi16(A_b0, sign_A_b0);
- A_b2 = _mm_sub_epi16(A_b2, sign_A_b2);
- B_b0 = _mm_sub_epi16(B_b0, sign_B_b0);
- B_b2 = _mm_sub_epi16(B_b2, sign_B_b2);
- }
-
- // weighted sums
- A_b0 = _mm_madd_epi16(A_b0, w_0);
- A_b2 = _mm_madd_epi16(A_b2, w_8);
- B_b0 = _mm_madd_epi16(B_b0, w_0);
- B_b2 = _mm_madd_epi16(B_b2, w_8);
- A_b0 = _mm_add_epi32(A_b0, A_b2);
- B_b0 = _mm_add_epi32(B_b0, B_b2);
-
- // difference of weighted sums
- A_b0 = _mm_sub_epi32(A_b0, B_b0);
- _mm_storeu_si128((__m128i*)&sum[0], A_b0);
- }
- return sum[0] + sum[1] + sum[2] + sum[3];
-}
-
-static int Disto4x4SSE2(const uint8_t* const a, const uint8_t* const b,
- const uint16_t* const w) {
- const int diff_sum = TTransformSSE2(a, b, w);
- return abs(diff_sum) >> 5;
-}
-
-static int Disto16x16SSE2(const uint8_t* const a, const uint8_t* const b,
- const uint16_t* const w) {
- int D = 0;
- int x, y;
- for (y = 0; y < 16 * BPS; y += 4 * BPS) {
- for (x = 0; x < 16; x += 4) {
- D += Disto4x4SSE2(a + x + y, b + x + y, w);
- }
- }
- return D;
-}
-
-//------------------------------------------------------------------------------
-// Quantization
-//
-
-// Simple quantization
-static int QuantizeBlockSSE2(int16_t in[16], int16_t out[16],
- int n, const VP8Matrix* const mtx) {
- const __m128i max_coeff_2047 = _mm_set1_epi16(MAX_LEVEL);
- const __m128i zero = _mm_setzero_si128();
- __m128i coeff0, coeff8;
- __m128i out0, out8;
- __m128i packed_out;
-
- // Load all inputs.
- // TODO(cduvivier): Make variable declarations and allocations aligned so that
- // we can use _mm_load_si128 instead of _mm_loadu_si128.
- __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]);
- __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]);
- const __m128i sharpen0 = _mm_loadu_si128((__m128i*)&mtx->sharpen_[0]);
- const __m128i sharpen8 = _mm_loadu_si128((__m128i*)&mtx->sharpen_[8]);
- const __m128i iq0 = _mm_loadu_si128((__m128i*)&mtx->iq_[0]);
- const __m128i iq8 = _mm_loadu_si128((__m128i*)&mtx->iq_[8]);
- const __m128i bias0 = _mm_loadu_si128((__m128i*)&mtx->bias_[0]);
- const __m128i bias8 = _mm_loadu_si128((__m128i*)&mtx->bias_[8]);
- const __m128i q0 = _mm_loadu_si128((__m128i*)&mtx->q_[0]);
- const __m128i q8 = _mm_loadu_si128((__m128i*)&mtx->q_[8]);
- const __m128i zthresh0 = _mm_loadu_si128((__m128i*)&mtx->zthresh_[0]);
- const __m128i zthresh8 = _mm_loadu_si128((__m128i*)&mtx->zthresh_[8]);
-
- // sign(in) = in >> 15 (0x0000 if positive, 0xffff if negative)
- const __m128i sign0 = _mm_srai_epi16(in0, 15);
- const __m128i sign8 = _mm_srai_epi16(in8, 15);
-
- // coeff = abs(in) = (in ^ sign) - sign
- coeff0 = _mm_xor_si128(in0, sign0);
- coeff8 = _mm_xor_si128(in8, sign8);
- coeff0 = _mm_sub_epi16(coeff0, sign0);
- coeff8 = _mm_sub_epi16(coeff8, sign8);
-
- // coeff = abs(in) + sharpen
- coeff0 = _mm_add_epi16(coeff0, sharpen0);
- coeff8 = _mm_add_epi16(coeff8, sharpen8);
-
- // out = (coeff * iQ + B) >> QFIX;
- {
- // doing calculations with 32b precision (QFIX=17)
- // out = (coeff * iQ)
- __m128i coeff_iQ0H = _mm_mulhi_epu16(coeff0, iq0);
- __m128i coeff_iQ0L = _mm_mullo_epi16(coeff0, iq0);
- __m128i coeff_iQ8H = _mm_mulhi_epu16(coeff8, iq8);
- __m128i coeff_iQ8L = _mm_mullo_epi16(coeff8, iq8);
- __m128i out_00 = _mm_unpacklo_epi16(coeff_iQ0L, coeff_iQ0H);
- __m128i out_04 = _mm_unpackhi_epi16(coeff_iQ0L, coeff_iQ0H);
- __m128i out_08 = _mm_unpacklo_epi16(coeff_iQ8L, coeff_iQ8H);
- __m128i out_12 = _mm_unpackhi_epi16(coeff_iQ8L, coeff_iQ8H);
- // expand bias from 16b to 32b
- __m128i bias_00 = _mm_unpacklo_epi16(bias0, zero);
- __m128i bias_04 = _mm_unpackhi_epi16(bias0, zero);
- __m128i bias_08 = _mm_unpacklo_epi16(bias8, zero);
- __m128i bias_12 = _mm_unpackhi_epi16(bias8, zero);
- // out = (coeff * iQ + B)
- out_00 = _mm_add_epi32(out_00, bias_00);
- out_04 = _mm_add_epi32(out_04, bias_04);
- out_08 = _mm_add_epi32(out_08, bias_08);
- out_12 = _mm_add_epi32(out_12, bias_12);
- // out = (coeff * iQ + B) >> QFIX;
- out_00 = _mm_srai_epi32(out_00, QFIX);
- out_04 = _mm_srai_epi32(out_04, QFIX);
- out_08 = _mm_srai_epi32(out_08, QFIX);
- out_12 = _mm_srai_epi32(out_12, QFIX);
-
- // pack result as 16b
- out0 = _mm_packs_epi32(out_00, out_04);
- out8 = _mm_packs_epi32(out_08, out_12);
-
- // if (coeff > 2047) coeff = 2047
- out0 = _mm_min_epi16(out0, max_coeff_2047);
- out8 = _mm_min_epi16(out8, max_coeff_2047);
- }
-
- // get sign back (if (sign[j]) out_n = -out_n)
- out0 = _mm_xor_si128(out0, sign0);
- out8 = _mm_xor_si128(out8, sign8);
- out0 = _mm_sub_epi16(out0, sign0);
- out8 = _mm_sub_epi16(out8, sign8);
-
- // in = out * Q
- in0 = _mm_mullo_epi16(out0, q0);
- in8 = _mm_mullo_epi16(out8, q8);
-
- // if (coeff <= mtx->zthresh_) {in=0; out=0;}
- {
- __m128i cmp0 = _mm_cmpgt_epi16(coeff0, zthresh0);
- __m128i cmp8 = _mm_cmpgt_epi16(coeff8, zthresh8);
- in0 = _mm_and_si128(in0, cmp0);
- in8 = _mm_and_si128(in8, cmp8);
- _mm_storeu_si128((__m128i*)&in[0], in0);
- _mm_storeu_si128((__m128i*)&in[8], in8);
- out0 = _mm_and_si128(out0, cmp0);
- out8 = _mm_and_si128(out8, cmp8);
- }
-
- // zigzag the output before storing it.
- //
- // The zigzag pattern can almost be reproduced with a small sequence of
- // shuffles. After it, we only need to swap the 7th (ending up in third
- // position instead of twelfth) and 8th values.
- {
- __m128i outZ0, outZ8;
- outZ0 = _mm_shufflehi_epi16(out0, _MM_SHUFFLE(2, 1, 3, 0));
- outZ0 = _mm_shuffle_epi32 (outZ0, _MM_SHUFFLE(3, 1, 2, 0));
- outZ0 = _mm_shufflehi_epi16(outZ0, _MM_SHUFFLE(3, 1, 0, 2));
- outZ8 = _mm_shufflelo_epi16(out8, _MM_SHUFFLE(3, 0, 2, 1));
- outZ8 = _mm_shuffle_epi32 (outZ8, _MM_SHUFFLE(3, 1, 2, 0));
- outZ8 = _mm_shufflelo_epi16(outZ8, _MM_SHUFFLE(1, 3, 2, 0));
- _mm_storeu_si128((__m128i*)&out[0], outZ0);
- _mm_storeu_si128((__m128i*)&out[8], outZ8);
- packed_out = _mm_packs_epi16(outZ0, outZ8);
- }
- {
- const int16_t outZ_12 = out[12];
- const int16_t outZ_3 = out[3];
- out[3] = outZ_12;
- out[12] = outZ_3;
- }
-
- // detect if all 'out' values are zeroes or not
- {
- int32_t tmp[4];
- _mm_storeu_si128((__m128i*)tmp, packed_out);
- if (n) {
- tmp[0] &= ~0xff;
- }
- return (tmp[3] || tmp[2] || tmp[1] || tmp[0]);
- }
-}
-
-#endif // WEBP_USE_SSE2
-
-//------------------------------------------------------------------------------
-// Entry point
-
-extern void VP8EncDspInitSSE2(void);
-
-void VP8EncDspInitSSE2(void) {
-#if defined(WEBP_USE_SSE2)
- VP8CollectHistogram = CollectHistogramSSE2;
- VP8EncQuantizeBlock = QuantizeBlockSSE2;
- VP8ITransform = ITransformSSE2;
- VP8FTransform = FTransformSSE2;
- VP8FTransformWHT = FTransformWHTSSE2;
- VP8SSE16x16 = SSE16x16SSE2;
- VP8SSE16x8 = SSE16x8SSE2;
- VP8SSE8x8 = SSE8x8SSE2;
- VP8SSE4x4 = SSE4x4SSE2;
- VP8TDisto4x4 = Disto4x4SSE2;
- VP8TDisto16x16 = Disto16x16SSE2;
-#endif // WEBP_USE_SSE2
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/lossless.c b/src/third_party/libwebp/dsp/lossless.c
deleted file mode 100644
index 97f6a26..0000000
--- a/src/third_party/libwebp/dsp/lossless.c
+++ /dev/null
@@ -1,1393 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Image transforms and color space conversion methods for lossless decoder.
-//
-// Authors: Vikas Arora (vikaas.arora@gmail.com)
-// Jyrki Alakuijala (jyrki@google.com)
-// Urvang Joshi (urvang@google.com)
-
-#include "./dsp.h"
-
-// Define the following if target arch is sure to have SSE2
-// #define WEBP_TARGET_HAS_SSE2
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if defined(WEBP_TARGET_HAS_SSE2)
-#include <emmintrin.h>
-#endif
-
-#if defined(STARBOARD)
-#include "starboard/client_porting/poem/stdlib_poem.h"
-#include "starboard/memory.h"
-#include "starboard/log.h"
-#else
-#include <stdlib.h>
-#endif
-#include <math.h>
-#include "./lossless.h"
-#include "../dec/vp8li.h"
-#include "./yuv.h"
-
-#define MAX_DIFF_COST (1e30f)
-
-// lookup table for small values of log2(int)
-#define APPROX_LOG_MAX 4096
-#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086
-const float kLog2Table[LOG_LOOKUP_IDX_MAX] = {
- 0.0000000000000000f, 0.0000000000000000f,
- 1.0000000000000000f, 1.5849625007211560f,
- 2.0000000000000000f, 2.3219280948873621f,
- 2.5849625007211560f, 2.8073549220576041f,
- 3.0000000000000000f, 3.1699250014423121f,
- 3.3219280948873621f, 3.4594316186372973f,
- 3.5849625007211560f, 3.7004397181410921f,
- 3.8073549220576041f, 3.9068905956085187f,
- 4.0000000000000000f, 4.0874628412503390f,
- 4.1699250014423121f, 4.2479275134435852f,
- 4.3219280948873626f, 4.3923174227787606f,
- 4.4594316186372973f, 4.5235619560570130f,
- 4.5849625007211560f, 4.6438561897747243f,
- 4.7004397181410917f, 4.7548875021634682f,
- 4.8073549220576037f, 4.8579809951275718f,
- 4.9068905956085187f, 4.9541963103868749f,
- 5.0000000000000000f, 5.0443941193584533f,
- 5.0874628412503390f, 5.1292830169449663f,
- 5.1699250014423121f, 5.2094533656289501f,
- 5.2479275134435852f, 5.2854022188622487f,
- 5.3219280948873626f, 5.3575520046180837f,
- 5.3923174227787606f, 5.4262647547020979f,
- 5.4594316186372973f, 5.4918530963296747f,
- 5.5235619560570130f, 5.5545888516776376f,
- 5.5849625007211560f, 5.6147098441152083f,
- 5.6438561897747243f, 5.6724253419714951f,
- 5.7004397181410917f, 5.7279204545631987f,
- 5.7548875021634682f, 5.7813597135246599f,
- 5.8073549220576037f, 5.8328900141647412f,
- 5.8579809951275718f, 5.8826430493618415f,
- 5.9068905956085187f, 5.9307373375628866f,
- 5.9541963103868749f, 5.9772799234999167f,
- 6.0000000000000000f, 6.0223678130284543f,
- 6.0443941193584533f, 6.0660891904577720f,
- 6.0874628412503390f, 6.1085244567781691f,
- 6.1292830169449663f, 6.1497471195046822f,
- 6.1699250014423121f, 6.1898245588800175f,
- 6.2094533656289501f, 6.2288186904958804f,
- 6.2479275134435852f, 6.2667865406949010f,
- 6.2854022188622487f, 6.3037807481771030f,
- 6.3219280948873626f, 6.3398500028846243f,
- 6.3575520046180837f, 6.3750394313469245f,
- 6.3923174227787606f, 6.4093909361377017f,
- 6.4262647547020979f, 6.4429434958487279f,
- 6.4594316186372973f, 6.4757334309663976f,
- 6.4918530963296747f, 6.5077946401986963f,
- 6.5235619560570130f, 6.5391588111080309f,
- 6.5545888516776376f, 6.5698556083309478f,
- 6.5849625007211560f, 6.5999128421871278f,
- 6.6147098441152083f, 6.6293566200796094f,
- 6.6438561897747243f, 6.6582114827517946f,
- 6.6724253419714951f, 6.6865005271832185f,
- 6.7004397181410917f, 6.7142455176661224f,
- 6.7279204545631987f, 6.7414669864011464f,
- 6.7548875021634682f, 6.7681843247769259f,
- 6.7813597135246599f, 6.7944158663501061f,
- 6.8073549220576037f, 6.8201789624151878f,
- 6.8328900141647412f, 6.8454900509443747f,
- 6.8579809951275718f, 6.8703647195834047f,
- 6.8826430493618415f, 6.8948177633079437f,
- 6.9068905956085187f, 6.9188632372745946f,
- 6.9307373375628866f, 6.9425145053392398f,
- 6.9541963103868749f, 6.9657842846620869f,
- 6.9772799234999167f, 6.9886846867721654f,
- 7.0000000000000000f, 7.0112272554232539f,
- 7.0223678130284543f, 7.0334230015374501f,
- 7.0443941193584533f, 7.0552824355011898f,
- 7.0660891904577720f, 7.0768155970508308f,
- 7.0874628412503390f, 7.0980320829605263f,
- 7.1085244567781691f, 7.1189410727235076f,
- 7.1292830169449663f, 7.1395513523987936f,
- 7.1497471195046822f, 7.1598713367783890f,
- 7.1699250014423121f, 7.1799090900149344f,
- 7.1898245588800175f, 7.1996723448363644f,
- 7.2094533656289501f, 7.2191685204621611f,
- 7.2288186904958804f, 7.2384047393250785f,
- 7.2479275134435852f, 7.2573878426926521f,
- 7.2667865406949010f, 7.2761244052742375f,
- 7.2854022188622487f, 7.2946207488916270f,
- 7.3037807481771030f, 7.3128829552843557f,
- 7.3219280948873626f, 7.3309168781146167f,
- 7.3398500028846243f, 7.3487281542310771f,
- 7.3575520046180837f, 7.3663222142458160f,
- 7.3750394313469245f, 7.3837042924740519f,
- 7.3923174227787606f, 7.4008794362821843f,
- 7.4093909361377017f, 7.4178525148858982f,
- 7.4262647547020979f, 7.4346282276367245f,
- 7.4429434958487279f, 7.4512111118323289f,
- 7.4594316186372973f, 7.4676055500829976f,
- 7.4757334309663976f, 7.4838157772642563f,
- 7.4918530963296747f, 7.4998458870832056f,
- 7.5077946401986963f, 7.5156998382840427f,
- 7.5235619560570130f, 7.5313814605163118f,
- 7.5391588111080309f, 7.5468944598876364f,
- 7.5545888516776376f, 7.5622424242210728f,
- 7.5698556083309478f, 7.5774288280357486f,
- 7.5849625007211560f, 7.5924570372680806f,
- 7.5999128421871278f, 7.6073303137496104f,
- 7.6147098441152083f, 7.6220518194563764f,
- 7.6293566200796094f, 7.6366246205436487f,
- 7.6438561897747243f, 7.6510516911789281f,
- 7.6582114827517946f, 7.6653359171851764f,
- 7.6724253419714951f, 7.6794800995054464f,
- 7.6865005271832185f, 7.6934869574993252f,
- 7.7004397181410917f, 7.7073591320808825f,
- 7.7142455176661224f, 7.7210991887071855f,
- 7.7279204545631987f, 7.7347096202258383f,
- 7.7414669864011464f, 7.7481928495894605f,
- 7.7548875021634682f, 7.7615512324444795f,
- 7.7681843247769259f, 7.7747870596011736f,
- 7.7813597135246599f, 7.7879025593914317f,
- 7.7944158663501061f, 7.8008998999203047f,
- 7.8073549220576037f, 7.8137811912170374f,
- 7.8201789624151878f, 7.8265484872909150f,
- 7.8328900141647412f, 7.8392037880969436f,
- 7.8454900509443747f, 7.8517490414160571f,
- 7.8579809951275718f, 7.8641861446542797f,
- 7.8703647195834047f, 7.8765169465649993f,
- 7.8826430493618415f, 7.8887432488982591f,
- 7.8948177633079437f, 7.9008668079807486f,
- 7.9068905956085187f, 7.9128893362299619f,
- 7.9188632372745946f, 7.9248125036057812f,
- 7.9307373375628866f, 7.9366379390025709f,
- 7.9425145053392398f, 7.9483672315846778f,
- 7.9541963103868749f, 7.9600019320680805f,
- 7.9657842846620869f, 7.9715435539507719f,
- 7.9772799234999167f, 7.9829935746943103f,
- 7.9886846867721654f, 7.9943534368588577f
-};
-
-const float kSLog2Table[LOG_LOOKUP_IDX_MAX] = {
- 0.00000000f, 0.00000000f, 2.00000000f, 4.75488750f,
- 8.00000000f, 11.60964047f, 15.50977500f, 19.65148445f,
- 24.00000000f, 28.52932501f, 33.21928095f, 38.05374781f,
- 43.01955001f, 48.10571634f, 53.30296891f, 58.60335893f,
- 64.00000000f, 69.48686830f, 75.05865003f, 80.71062276f,
- 86.43856190f, 92.23866588f, 98.10749561f, 104.04192499f,
- 110.03910002f, 116.09640474f, 122.21143267f, 128.38196256f,
- 134.60593782f, 140.88144886f, 147.20671787f, 153.58008562f,
- 160.00000000f, 166.46500594f, 172.97373660f, 179.52490559f,
- 186.11730005f, 192.74977453f, 199.42124551f, 206.13068654f,
- 212.87712380f, 219.65963219f, 226.47733176f, 233.32938445f,
- 240.21499122f, 247.13338933f, 254.08384998f, 261.06567603f,
- 268.07820003f, 275.12078236f, 282.19280949f, 289.29369244f,
- 296.42286534f, 303.57978409f, 310.76392512f, 317.97478424f,
- 325.21187564f, 332.47473081f, 339.76289772f, 347.07593991f,
- 354.41343574f, 361.77497759f, 369.16017124f, 376.56863518f,
- 384.00000000f, 391.45390785f, 398.93001188f, 406.42797576f,
- 413.94747321f, 421.48818752f, 429.04981119f, 436.63204548f,
- 444.23460010f, 451.85719280f, 459.49954906f, 467.16140179f,
- 474.84249102f, 482.54256363f, 490.26137307f, 497.99867911f,
- 505.75424759f, 513.52785023f, 521.31926438f, 529.12827280f,
- 536.95466351f, 544.79822957f, 552.65876890f, 560.53608414f,
- 568.42998244f, 576.34027536f, 584.26677867f, 592.20931226f,
- 600.16769996f, 608.14176943f, 616.13135206f, 624.13628279f,
- 632.15640007f, 640.19154569f, 648.24156472f, 656.30630539f,
- 664.38561898f, 672.47935976f, 680.58738488f, 688.70955430f,
- 696.84573069f, 704.99577935f, 713.15956818f, 721.33696754f,
- 729.52785023f, 737.73209140f, 745.94956849f, 754.18016116f,
- 762.42375127f, 770.68022275f, 778.94946161f, 787.23135586f,
- 795.52579543f, 803.83267219f, 812.15187982f, 820.48331383f,
- 828.82687147f, 837.18245171f, 845.54995518f, 853.92928416f,
- 862.32034249f, 870.72303558f, 879.13727036f, 887.56295522f,
- 896.00000000f, 904.44831595f, 912.90781569f, 921.37841320f,
- 929.86002376f, 938.35256392f, 946.85595152f, 955.37010560f,
- 963.89494641f, 972.43039537f, 980.97637504f, 989.53280911f,
- 998.09962237f, 1006.67674069f, 1015.26409097f, 1023.86160116f,
- 1032.46920021f, 1041.08681805f, 1049.71438560f, 1058.35183469f,
- 1066.99909811f, 1075.65610955f, 1084.32280357f, 1092.99911564f,
- 1101.68498204f, 1110.38033993f, 1119.08512727f, 1127.79928282f,
- 1136.52274614f, 1145.25545758f, 1153.99735821f, 1162.74838989f,
- 1171.50849518f, 1180.27761738f, 1189.05570047f, 1197.84268914f,
- 1206.63852876f, 1215.44316535f, 1224.25654560f, 1233.07861684f,
- 1241.90932703f, 1250.74862473f, 1259.59645914f, 1268.45278005f,
- 1277.31753781f, 1286.19068338f, 1295.07216828f, 1303.96194457f,
- 1312.85996488f, 1321.76618236f, 1330.68055071f, 1339.60302413f,
- 1348.53355734f, 1357.47210556f, 1366.41862452f, 1375.37307041f,
- 1384.33539991f, 1393.30557020f, 1402.28353887f, 1411.26926400f,
- 1420.26270412f, 1429.26381818f, 1438.27256558f, 1447.28890615f,
- 1456.31280014f, 1465.34420819f, 1474.38309138f, 1483.42941118f,
- 1492.48312945f, 1501.54420843f, 1510.61261078f, 1519.68829949f,
- 1528.77123795f, 1537.86138993f, 1546.95871952f, 1556.06319119f,
- 1565.17476976f, 1574.29342040f, 1583.41910860f, 1592.55180020f,
- 1601.69146137f, 1610.83805860f, 1619.99155871f, 1629.15192882f,
- 1638.31913637f, 1647.49314911f, 1656.67393509f, 1665.86146266f,
- 1675.05570047f, 1684.25661744f, 1693.46418280f, 1702.67836605f,
- 1711.89913698f, 1721.12646563f, 1730.36032233f, 1739.60067768f,
- 1748.84750254f, 1758.10076802f, 1767.36044551f, 1776.62650662f,
- 1785.89892323f, 1795.17766747f, 1804.46271172f, 1813.75402857f,
- 1823.05159087f, 1832.35537170f, 1841.66534438f, 1850.98148244f,
- 1860.30375965f, 1869.63214999f, 1878.96662767f, 1888.30716711f,
- 1897.65374295f, 1907.00633003f, 1916.36490342f, 1925.72943838f,
- 1935.09991037f, 1944.47629506f, 1953.85856831f, 1963.24670620f,
- 1972.64068498f, 1982.04048108f, 1991.44607117f, 2000.85743204f,
- 2010.27454072f, 2019.69737440f, 2029.12591044f, 2038.56012640f
-};
-
-float VP8LFastSLog2Slow(int v) {
- SB_DCHECK(v >= LOG_LOOKUP_IDX_MAX);
- if (v < APPROX_LOG_MAX) {
- int log_cnt = 0;
- const float v_f = (float)v;
- while (v >= LOG_LOOKUP_IDX_MAX) {
- ++log_cnt;
- v = v >> 1;
- }
- return v_f * (kLog2Table[v] + log_cnt);
- } else {
- return (float)(LOG_2_RECIPROCAL * v * log((double)v));
- }
-}
-
-float VP8LFastLog2Slow(int v) {
- SB_DCHECK(v >= LOG_LOOKUP_IDX_MAX);
- if (v < APPROX_LOG_MAX) {
- int log_cnt = 0;
- while (v >= LOG_LOOKUP_IDX_MAX) {
- ++log_cnt;
- v = v >> 1;
- }
- return kLog2Table[v] + log_cnt;
- } else {
- return (float)(LOG_2_RECIPROCAL * log((double)v));
- }
-}
-
-//------------------------------------------------------------------------------
-// Image transforms.
-
-// In-place sum of each component with mod 256.
-static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) {
- const uint32_t alpha_and_green = (*a & 0xff00ff00u) + (b & 0xff00ff00u);
- const uint32_t red_and_blue = (*a & 0x00ff00ffu) + (b & 0x00ff00ffu);
- *a = (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu);
-}
-
-static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) {
- return (((a0 ^ a1) & 0xfefefefeL) >> 1) + (a0 & a1);
-}
-
-static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) {
- return Average2(Average2(a0, a2), a1);
-}
-
-static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1,
- uint32_t a2, uint32_t a3) {
- return Average2(Average2(a0, a1), Average2(a2, a3));
-}
-
-#if defined(WEBP_TARGET_HAS_SSE2)
-static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
- uint32_t c2) {
- const __m128i zero = _mm_setzero_si128();
- const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero);
- const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero);
- const __m128i C2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
- const __m128i V1 = _mm_add_epi16(C0, C1);
- const __m128i V2 = _mm_sub_epi16(V1, C2);
- const __m128i b = _mm_packus_epi16(V2, V2);
- const uint32_t output = _mm_cvtsi128_si32(b);
- return output;
-}
-
-static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
- uint32_t c2) {
- const uint32_t ave = Average2(c0, c1);
- const __m128i zero = _mm_setzero_si128();
- const __m128i A0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(ave), zero);
- const __m128i B0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
- const __m128i A1 = _mm_sub_epi16(A0, B0);
- const __m128i BgtA = _mm_cmpgt_epi16(B0, A0);
- const __m128i A2 = _mm_sub_epi16(A1, BgtA);
- const __m128i A3 = _mm_srai_epi16(A2, 1);
- const __m128i A4 = _mm_add_epi16(A0, A3);
- const __m128i A5 = _mm_packus_epi16(A4, A4);
- const uint32_t output = _mm_cvtsi128_si32(A5);
- return output;
-}
-
-static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
- int pa_minus_pb;
- const __m128i zero = _mm_setzero_si128();
- const __m128i A0 = _mm_cvtsi32_si128(a);
- const __m128i B0 = _mm_cvtsi32_si128(b);
- const __m128i C0 = _mm_cvtsi32_si128(c);
- const __m128i AC0 = _mm_subs_epu8(A0, C0);
- const __m128i CA0 = _mm_subs_epu8(C0, A0);
- const __m128i BC0 = _mm_subs_epu8(B0, C0);
- const __m128i CB0 = _mm_subs_epu8(C0, B0);
- const __m128i AC = _mm_or_si128(AC0, CA0);
- const __m128i BC = _mm_or_si128(BC0, CB0);
- const __m128i pa = _mm_unpacklo_epi8(AC, zero); // |a - c|
- const __m128i pb = _mm_unpacklo_epi8(BC, zero); // |b - c|
- const __m128i diff = _mm_sub_epi16(pb, pa);
- {
- int16_t out[8];
- _mm_storeu_si128((__m128i*)out, diff);
- pa_minus_pb = out[0] + out[1] + out[2] + out[3];
- }
- return (pa_minus_pb <= 0) ? a : b;
-}
-
-#else
-
-static WEBP_INLINE uint32_t Clip255(uint32_t a) {
- if (a < 256) {
- return a;
- }
- // return 0, when a is a negative integer.
- // return 255, when a is positive.
- return ~a >> 24;
-}
-
-static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) {
- return Clip255(a + b - c);
-}
-
-static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
- uint32_t c2) {
- const int a = AddSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24);
- const int r = AddSubtractComponentFull((c0 >> 16) & 0xff,
- (c1 >> 16) & 0xff,
- (c2 >> 16) & 0xff);
- const int g = AddSubtractComponentFull((c0 >> 8) & 0xff,
- (c1 >> 8) & 0xff,
- (c2 >> 8) & 0xff);
- const int b = AddSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff);
- return (a << 24) | (r << 16) | (g << 8) | b;
-}
-
-static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) {
- return Clip255(a + (a - b) / 2);
-}
-
-static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
- uint32_t c2) {
- const uint32_t ave = Average2(c0, c1);
- const int a = AddSubtractComponentHalf(ave >> 24, c2 >> 24);
- const int r = AddSubtractComponentHalf((ave >> 16) & 0xff, (c2 >> 16) & 0xff);
- const int g = AddSubtractComponentHalf((ave >> 8) & 0xff, (c2 >> 8) & 0xff);
- const int b = AddSubtractComponentHalf((ave >> 0) & 0xff, (c2 >> 0) & 0xff);
- return (a << 24) | (r << 16) | (g << 8) | b;
-}
-
-static WEBP_INLINE int Sub3(int a, int b, int c) {
- const int pb = b - c;
- const int pa = a - c;
- return abs(pb) - abs(pa);
-}
-
-static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
- const int pa_minus_pb =
- Sub3((a >> 24) , (b >> 24) , (c >> 24) ) +
- Sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) +
- Sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) +
- Sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff);
- return (pa_minus_pb <= 0) ? a : b;
-}
-#endif
-
-//------------------------------------------------------------------------------
-// Predictors
-
-static uint32_t Predictor0(uint32_t left, const uint32_t* const top) {
- (void)top;
- (void)left;
- return ARGB_BLACK;
-}
-static uint32_t Predictor1(uint32_t left, const uint32_t* const top) {
- (void)top;
- return left;
-}
-static uint32_t Predictor2(uint32_t left, const uint32_t* const top) {
- (void)left;
- return top[0];
-}
-static uint32_t Predictor3(uint32_t left, const uint32_t* const top) {
- (void)left;
- return top[1];
-}
-static uint32_t Predictor4(uint32_t left, const uint32_t* const top) {
- (void)left;
- return top[-1];
-}
-static uint32_t Predictor5(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average3(left, top[0], top[1]);
- return pred;
-}
-static uint32_t Predictor6(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2(left, top[-1]);
- return pred;
-}
-static uint32_t Predictor7(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2(left, top[0]);
- return pred;
-}
-static uint32_t Predictor8(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2(top[-1], top[0]);
- (void)left;
- return pred;
-}
-static uint32_t Predictor9(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average2(top[0], top[1]);
- (void)left;
- return pred;
-}
-static uint32_t Predictor10(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Average4(left, top[-1], top[0], top[1]);
- return pred;
-}
-static uint32_t Predictor11(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = Select(top[0], left, top[-1]);
- return pred;
-}
-static uint32_t Predictor12(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]);
- return pred;
-}
-static uint32_t Predictor13(uint32_t left, const uint32_t* const top) {
- const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]);
- return pred;
-}
-
-typedef uint32_t (*PredictorFunc)(uint32_t left, const uint32_t* const top);
-static const PredictorFunc kPredictors[16] = {
- Predictor0, Predictor1, Predictor2, Predictor3,
- Predictor4, Predictor5, Predictor6, Predictor7,
- Predictor8, Predictor9, Predictor10, Predictor11,
- Predictor12, Predictor13,
- Predictor0, Predictor0 // <- padding security sentinels
-};
-
-// TODO(vikasa): Replace 256 etc with defines.
-static float PredictionCostSpatial(const int* counts,
- int weight_0, double exp_val) {
- const int significant_symbols = 16;
- const double exp_decay_factor = 0.6;
- double bits = weight_0 * counts[0];
- int i;
- for (i = 1; i < significant_symbols; ++i) {
- bits += exp_val * (counts[i] + counts[256 - i]);
- exp_val *= exp_decay_factor;
- }
- return (float)(-0.1 * bits);
-}
-
-// Compute the combined Shanon's entropy for distribution {X} and {X+Y}
-static float CombinedShannonEntropy(const int* const X,
- const int* const Y, int n) {
- int i;
- double retval = 0.;
- int sumX = 0, sumXY = 0;
- for (i = 0; i < n; ++i) {
- const int x = X[i];
- const int xy = X[i] + Y[i];
- if (x != 0) {
- sumX += x;
- retval -= VP8LFastSLog2(x);
- }
- if (xy != 0) {
- sumXY += xy;
- retval -= VP8LFastSLog2(xy);
- }
- }
- retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY);
- return (float)retval;
-}
-
-static float PredictionCostSpatialHistogram(int accumulated[4][256],
- int tile[4][256]) {
- int i;
- double retval = 0;
- for (i = 0; i < 4; ++i) {
- const double kExpValue = 0.94;
- retval += PredictionCostSpatial(tile[i], 1, kExpValue);
- retval += CombinedShannonEntropy(tile[i], accumulated[i], 256);
- }
- return (float)retval;
-}
-
-static int GetBestPredictorForTile(int width, int height,
- int tile_x, int tile_y, int bits,
- int accumulated[4][256],
- const uint32_t* const argb_scratch) {
- const int kNumPredModes = 14;
- const int col_start = tile_x << bits;
- const int row_start = tile_y << bits;
- const int tile_size = 1 << bits;
- const int ymax = (tile_size <= height - row_start) ?
- tile_size : height - row_start;
- const int xmax = (tile_size <= width - col_start) ?
- tile_size : width - col_start;
- int histo[4][256];
- float best_diff = MAX_DIFF_COST;
- int best_mode = 0;
-
- int mode;
- for (mode = 0; mode < kNumPredModes; ++mode) {
- const uint32_t* current_row = argb_scratch;
- const PredictorFunc pred_func = kPredictors[mode];
- float cur_diff;
- int y;
- SbMemorySet(&histo[0][0], 0, sizeof(histo));
- for (y = 0; y < ymax; ++y) {
- int x;
- const int row = row_start + y;
- const uint32_t* const upper_row = current_row;
- current_row = upper_row + width;
- for (x = 0; x < xmax; ++x) {
- const int col = col_start + x;
- uint32_t predict;
- uint32_t predict_diff;
- if (row == 0) {
- predict = (col == 0) ? ARGB_BLACK : current_row[col - 1]; // Left.
- } else if (col == 0) {
- predict = upper_row[col]; // Top.
- } else {
- predict = pred_func(current_row[col - 1], upper_row + col);
- }
- predict_diff = VP8LSubPixels(current_row[col], predict);
- ++histo[0][predict_diff >> 24];
- ++histo[1][((predict_diff >> 16) & 0xff)];
- ++histo[2][((predict_diff >> 8) & 0xff)];
- ++histo[3][(predict_diff & 0xff)];
- }
- }
- cur_diff = PredictionCostSpatialHistogram(accumulated, histo);
- if (cur_diff < best_diff) {
- best_diff = cur_diff;
- best_mode = mode;
- }
- }
-
- return best_mode;
-}
-
-static void CopyTileWithPrediction(int width, int height,
- int tile_x, int tile_y, int bits, int mode,
- const uint32_t* const argb_scratch,
- uint32_t* const argb) {
- const int col_start = tile_x << bits;
- const int row_start = tile_y << bits;
- const int tile_size = 1 << bits;
- const int ymax = (tile_size <= height - row_start) ?
- tile_size : height - row_start;
- const int xmax = (tile_size <= width - col_start) ?
- tile_size : width - col_start;
- const PredictorFunc pred_func = kPredictors[mode];
- const uint32_t* current_row = argb_scratch;
-
- int y;
- for (y = 0; y < ymax; ++y) {
- int x;
- const int row = row_start + y;
- const uint32_t* const upper_row = current_row;
- current_row = upper_row + width;
- for (x = 0; x < xmax; ++x) {
- const int col = col_start + x;
- const int pix = row * width + col;
- uint32_t predict;
- if (row == 0) {
- predict = (col == 0) ? ARGB_BLACK : current_row[col - 1]; // Left.
- } else if (col == 0) {
- predict = upper_row[col]; // Top.
- } else {
- predict = pred_func(current_row[col - 1], upper_row + col);
- }
- argb[pix] = VP8LSubPixels(current_row[col], predict);
- }
- }
-}
-
-void VP8LResidualImage(int width, int height, int bits,
- uint32_t* const argb, uint32_t* const argb_scratch,
- uint32_t* const image) {
- const int max_tile_size = 1 << bits;
- const int tiles_per_row = VP8LSubSampleSize(width, bits);
- const int tiles_per_col = VP8LSubSampleSize(height, bits);
- uint32_t* const upper_row = argb_scratch;
- uint32_t* const current_tile_rows = argb_scratch + width;
- int tile_y;
- int histo[4][256];
- SbMemorySet(histo, 0, sizeof(histo));
- for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) {
- const int tile_y_offset = tile_y * max_tile_size;
- const int this_tile_height =
- (tile_y < tiles_per_col - 1) ? max_tile_size : height - tile_y_offset;
- int tile_x;
- if (tile_y > 0) {
- SbMemoryCopy(upper_row, current_tile_rows + (max_tile_size - 1) * width,
- width * sizeof(*upper_row));
- }
- SbMemoryCopy(current_tile_rows, &argb[tile_y_offset * width],
- this_tile_height * width * sizeof(*current_tile_rows));
- for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) {
- int pred;
- int y;
- const int tile_x_offset = tile_x * max_tile_size;
- int all_x_max = tile_x_offset + max_tile_size;
- if (all_x_max > width) {
- all_x_max = width;
- }
- pred = GetBestPredictorForTile(width, height, tile_x, tile_y, bits, histo,
- argb_scratch);
- image[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8);
- CopyTileWithPrediction(width, height, tile_x, tile_y, bits, pred,
- argb_scratch, argb);
- for (y = 0; y < max_tile_size; ++y) {
- int ix;
- int all_x;
- int all_y = tile_y_offset + y;
- if (all_y >= height) {
- break;
- }
- ix = all_y * width + tile_x_offset;
- for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) {
- const uint32_t a = argb[ix];
- ++histo[0][a >> 24];
- ++histo[1][((a >> 16) & 0xff)];
- ++histo[2][((a >> 8) & 0xff)];
- ++histo[3][(a & 0xff)];
- }
- }
- }
- }
-}
-
-// Inverse prediction.
-static void PredictorInverseTransform(const VP8LTransform* const transform,
- int y_start, int y_end, uint32_t* data) {
- const int width = transform->xsize_;
- if (y_start == 0) { // First Row follows the L (mode=1) mode.
- int x;
- const uint32_t pred0 = Predictor0(data[-1], NULL);
- AddPixelsEq(data, pred0);
- for (x = 1; x < width; ++x) {
- const uint32_t pred1 = Predictor1(data[x - 1], NULL);
- AddPixelsEq(data + x, pred1);
- }
- data += width;
- ++y_start;
- }
-
- {
- int y = y_start;
- const int mask = (1 << transform->bits_) - 1;
- const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_);
- const uint32_t* pred_mode_base =
- transform->data_ + (y >> transform->bits_) * tiles_per_row;
-
- while (y < y_end) {
- int x;
- const uint32_t pred2 = Predictor2(data[-1], data - width);
- const uint32_t* pred_mode_src = pred_mode_base;
- PredictorFunc pred_func;
-
- // First pixel follows the T (mode=2) mode.
- AddPixelsEq(data, pred2);
-
- // .. the rest:
- pred_func = kPredictors[((*pred_mode_src++) >> 8) & 0xf];
- for (x = 1; x < width; ++x) {
- uint32_t pred;
- if ((x & mask) == 0) { // start of tile. Read predictor function.
- pred_func = kPredictors[((*pred_mode_src++) >> 8) & 0xf];
- }
- pred = pred_func(data[x - 1], data + x - width);
- AddPixelsEq(data + x, pred);
- }
- data += width;
- ++y;
- if ((y & mask) == 0) { // Use the same mask, since tiles are squares.
- pred_mode_base += tiles_per_row;
- }
- }
- }
-}
-
-void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs) {
- int i = 0;
-#if defined(WEBP_TARGET_HAS_SSE2)
- const __m128i mask = _mm_set1_epi32(0x0000ff00);
- for (; i + 4 < num_pixs; i += 4) {
- const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]);
- const __m128i in_00g0 = _mm_and_si128(in, mask); // 00g0|00g0|...
- const __m128i in_0g00 = _mm_slli_epi32(in_00g0, 8); // 0g00|0g00|...
- const __m128i in_000g = _mm_srli_epi32(in_00g0, 8); // 000g|000g|...
- const __m128i in_0g0g = _mm_or_si128(in_0g00, in_000g);
- const __m128i out = _mm_sub_epi8(in, in_0g0g);
- _mm_storeu_si128((__m128i*)&argb_data[i], out);
- }
- // fallthrough and finish off with plain-C
-#endif
- for (; i < num_pixs; ++i) {
- const uint32_t argb = argb_data[i];
- const uint32_t green = (argb >> 8) & 0xff;
- const uint32_t new_r = (((argb >> 16) & 0xff) - green) & 0xff;
- const uint32_t new_b = ((argb & 0xff) - green) & 0xff;
- argb_data[i] = (argb & 0xff00ff00) | (new_r << 16) | new_b;
- }
-}
-
-// Add green to blue and red channels (i.e. perform the inverse transform of
-// 'subtract green').
-static void AddGreenToBlueAndRed(const VP8LTransform* const transform,
- int y_start, int y_end, uint32_t* data) {
- const int width = transform->xsize_;
- const uint32_t* const data_end = data + (y_end - y_start) * width;
-#if defined(WEBP_TARGET_HAS_SSE2)
- const __m128i mask = _mm_set1_epi32(0x0000ff00);
- for (; data + 4 < data_end; data += 4) {
- const __m128i in = _mm_loadu_si128((__m128i*)data);
- const __m128i in_00g0 = _mm_and_si128(in, mask); // 00g0|00g0|...
- const __m128i in_0g00 = _mm_slli_epi32(in_00g0, 8); // 0g00|0g00|...
- const __m128i in_000g = _mm_srli_epi32(in_00g0, 8); // 000g|000g|...
- const __m128i in_0g0g = _mm_or_si128(in_0g00, in_000g);
- const __m128i out = _mm_add_epi8(in, in_0g0g);
- _mm_storeu_si128((__m128i*)data, out);
- }
- // fallthrough and finish off with plain-C
-#endif
- while (data < data_end) {
- const uint32_t argb = *data;
- const uint32_t green = ((argb >> 8) & 0xff);
- uint32_t red_blue = (argb & 0x00ff00ffu);
- red_blue += (green << 16) | green;
- red_blue &= 0x00ff00ffu;
- *data++ = (argb & 0xff00ff00u) | red_blue;
- }
-}
-
-typedef struct {
- // Note: the members are uint8_t, so that any negative values are
- // automatically converted to "mod 256" values.
- uint8_t green_to_red_;
- uint8_t green_to_blue_;
- uint8_t red_to_blue_;
-} Multipliers;
-
-static WEBP_INLINE void MultipliersClear(Multipliers* m) {
- m->green_to_red_ = 0;
- m->green_to_blue_ = 0;
- m->red_to_blue_ = 0;
-}
-
-static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred,
- int8_t color) {
- return (uint32_t)((int)(color_pred) * color) >> 5;
-}
-
-static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code,
- Multipliers* const m) {
- m->green_to_red_ = (color_code >> 0) & 0xff;
- m->green_to_blue_ = (color_code >> 8) & 0xff;
- m->red_to_blue_ = (color_code >> 16) & 0xff;
-}
-
-static WEBP_INLINE uint32_t MultipliersToColorCode(Multipliers* const m) {
- return 0xff000000u |
- ((uint32_t)(m->red_to_blue_) << 16) |
- ((uint32_t)(m->green_to_blue_) << 8) |
- m->green_to_red_;
-}
-
-static WEBP_INLINE uint32_t TransformColor(const Multipliers* const m,
- uint32_t argb, int inverse) {
- const uint32_t green = argb >> 8;
- const uint32_t red = argb >> 16;
- uint32_t new_red = red;
- uint32_t new_blue = argb;
-
- if (inverse) {
- new_red += ColorTransformDelta(m->green_to_red_, green);
- new_red &= 0xff;
- new_blue += ColorTransformDelta(m->green_to_blue_, green);
- new_blue += ColorTransformDelta(m->red_to_blue_, new_red);
- new_blue &= 0xff;
- } else {
- new_red -= ColorTransformDelta(m->green_to_red_, green);
- new_red &= 0xff;
- new_blue -= ColorTransformDelta(m->green_to_blue_, green);
- new_blue -= ColorTransformDelta(m->red_to_blue_, red);
- new_blue &= 0xff;
- }
- return (argb & 0xff00ff00u) | (new_red << 16) | (new_blue);
-}
-
-static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
- uint32_t argb) {
- const uint32_t green = argb >> 8;
- uint32_t new_red = argb >> 16;
- new_red -= ColorTransformDelta(green_to_red, green);
- return (new_red & 0xff);
-}
-
-static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue,
- uint8_t red_to_blue,
- uint32_t argb) {
- const uint32_t green = argb >> 8;
- const uint32_t red = argb >> 16;
- uint8_t new_blue = argb;
- new_blue -= ColorTransformDelta(green_to_blue, green);
- new_blue -= ColorTransformDelta(red_to_blue, red);
- return (new_blue & 0xff);
-}
-
-static WEBP_INLINE int SkipRepeatedPixels(const uint32_t* const argb,
- int ix, int xsize) {
- const uint32_t v = argb[ix];
- if (ix >= xsize + 3) {
- if (v == argb[ix - xsize] &&
- argb[ix - 1] == argb[ix - xsize - 1] &&
- argb[ix - 2] == argb[ix - xsize - 2] &&
- argb[ix - 3] == argb[ix - xsize - 3]) {
- return 1;
- }
- return v == argb[ix - 3] && v == argb[ix - 2] && v == argb[ix - 1];
- } else if (ix >= 3) {
- return v == argb[ix - 3] && v == argb[ix - 2] && v == argb[ix - 1];
- }
- return 0;
-}
-
-static float PredictionCostCrossColor(const int accumulated[256],
- const int counts[256]) {
- // Favor low entropy, locally and globally.
- // Favor small absolute values for PredictionCostSpatial
- static const double kExpValue = 2.4;
- return CombinedShannonEntropy(counts, accumulated, 256) +
- PredictionCostSpatial(counts, 3, kExpValue);
-}
-
-static Multipliers GetBestColorTransformForTile(
- int tile_x, int tile_y, int bits,
- Multipliers prevX,
- Multipliers prevY,
- int step, int xsize, int ysize,
- int* accumulated_red_histo,
- int* accumulated_blue_histo,
- const uint32_t* const argb) {
- float best_diff = MAX_DIFF_COST;
- float cur_diff;
- const int halfstep = step / 2;
- const int max_tile_size = 1 << bits;
- const int tile_y_offset = tile_y * max_tile_size;
- const int tile_x_offset = tile_x * max_tile_size;
- int green_to_red;
- int green_to_blue;
- int red_to_blue;
- int all_x_max = tile_x_offset + max_tile_size;
- int all_y_max = tile_y_offset + max_tile_size;
- Multipliers best_tx;
- MultipliersClear(&best_tx);
- if (all_x_max > xsize) {
- all_x_max = xsize;
- }
- if (all_y_max > ysize) {
- all_y_max = ysize;
- }
-
- for (green_to_red = -64; green_to_red <= 64; green_to_red += halfstep) {
- int histo[256] = { 0 };
- int all_y;
-
- for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) {
- int ix = all_y * xsize + tile_x_offset;
- int all_x;
- for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) {
- if (SkipRepeatedPixels(argb, ix, xsize)) {
- continue;
- }
- ++histo[TransformColorRed(green_to_red, argb[ix])]; // red.
- }
- }
- cur_diff = PredictionCostCrossColor(&accumulated_red_histo[0], &histo[0]);
- if ((uint8_t)green_to_red == prevX.green_to_red_) {
- cur_diff -= 3; // favor keeping the areas locally similar
- }
- if ((uint8_t)green_to_red == prevY.green_to_red_) {
- cur_diff -= 3; // favor keeping the areas locally similar
- }
- if (green_to_red == 0) {
- cur_diff -= 3;
- }
- if (cur_diff < best_diff) {
- best_diff = cur_diff;
- best_tx.green_to_red_ = green_to_red;
- }
- }
- best_diff = MAX_DIFF_COST;
- for (green_to_blue = -32; green_to_blue <= 32; green_to_blue += step) {
- for (red_to_blue = -32; red_to_blue <= 32; red_to_blue += step) {
- int all_y;
- int histo[256] = { 0 };
- for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) {
- int all_x;
- int ix = all_y * xsize + tile_x_offset;
- for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) {
- if (SkipRepeatedPixels(argb, ix, xsize)) {
- continue;
- }
- ++histo[TransformColorBlue(green_to_blue, red_to_blue, argb[ix])];
- }
- }
- cur_diff =
- PredictionCostCrossColor(&accumulated_blue_histo[0], &histo[0]);
- if ((uint8_t)green_to_blue == prevX.green_to_blue_) {
- cur_diff -= 3; // favor keeping the areas locally similar
- }
- if ((uint8_t)green_to_blue == prevY.green_to_blue_) {
- cur_diff -= 3; // favor keeping the areas locally similar
- }
- if ((uint8_t)red_to_blue == prevX.red_to_blue_) {
- cur_diff -= 3; // favor keeping the areas locally similar
- }
- if ((uint8_t)red_to_blue == prevY.red_to_blue_) {
- cur_diff -= 3; // favor keeping the areas locally similar
- }
- if (green_to_blue == 0) {
- cur_diff -= 3;
- }
- if (red_to_blue == 0) {
- cur_diff -= 3;
- }
- if (cur_diff < best_diff) {
- best_diff = cur_diff;
- best_tx.green_to_blue_ = green_to_blue;
- best_tx.red_to_blue_ = red_to_blue;
- }
- }
- }
- return best_tx;
-}
-
-static void CopyTileWithColorTransform(int xsize, int ysize,
- int tile_x, int tile_y, int bits,
- Multipliers color_transform,
- uint32_t* const argb) {
- int y;
- int xscan = 1 << bits;
- int yscan = 1 << bits;
- tile_x <<= bits;
- tile_y <<= bits;
- if (xscan > xsize - tile_x) {
- xscan = xsize - tile_x;
- }
- if (yscan > ysize - tile_y) {
- yscan = ysize - tile_y;
- }
- yscan += tile_y;
- for (y = tile_y; y < yscan; ++y) {
- int ix = y * xsize + tile_x;
- const int end_ix = ix + xscan;
- for (; ix < end_ix; ++ix) {
- argb[ix] = TransformColor(&color_transform, argb[ix], 0);
- }
- }
-}
-
-void VP8LColorSpaceTransform(int width, int height, int bits, int step,
- uint32_t* const argb, uint32_t* image) {
- const int max_tile_size = 1 << bits;
- int tile_xsize = VP8LSubSampleSize(width, bits);
- int tile_ysize = VP8LSubSampleSize(height, bits);
- int accumulated_red_histo[256] = { 0 };
- int accumulated_blue_histo[256] = { 0 };
- int tile_y;
- int tile_x;
- Multipliers prevX;
- Multipliers prevY;
- MultipliersClear(&prevY);
- MultipliersClear(&prevX);
- for (tile_y = 0; tile_y < tile_ysize; ++tile_y) {
- for (tile_x = 0; tile_x < tile_xsize; ++tile_x) {
- Multipliers color_transform;
- int all_x_max;
- int y;
- const int tile_y_offset = tile_y * max_tile_size;
- const int tile_x_offset = tile_x * max_tile_size;
- if (tile_y != 0) {
- ColorCodeToMultipliers(image[tile_y * tile_xsize + tile_x - 1], &prevX);
- ColorCodeToMultipliers(image[(tile_y - 1) * tile_xsize + tile_x],
- &prevY);
- } else if (tile_x != 0) {
- ColorCodeToMultipliers(image[tile_y * tile_xsize + tile_x - 1], &prevX);
- }
- color_transform =
- GetBestColorTransformForTile(tile_x, tile_y, bits,
- prevX, prevY,
- step, width, height,
- &accumulated_red_histo[0],
- &accumulated_blue_histo[0],
- argb);
- image[tile_y * tile_xsize + tile_x] =
- MultipliersToColorCode(&color_transform);
- CopyTileWithColorTransform(width, height, tile_x, tile_y, bits,
- color_transform, argb);
-
- // Gather accumulated histogram data.
- all_x_max = tile_x_offset + max_tile_size;
- if (all_x_max > width) {
- all_x_max = width;
- }
- for (y = 0; y < max_tile_size; ++y) {
- int ix;
- int all_x;
- int all_y = tile_y_offset + y;
- if (all_y >= height) {
- break;
- }
- ix = all_y * width + tile_x_offset;
- for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) {
- if (ix >= 2 &&
- argb[ix] == argb[ix - 2] &&
- argb[ix] == argb[ix - 1]) {
- continue; // repeated pixels are handled by backward references
- }
- if (ix >= width + 2 &&
- argb[ix - 2] == argb[ix - width - 2] &&
- argb[ix - 1] == argb[ix - width - 1] &&
- argb[ix] == argb[ix - width]) {
- continue; // repeated pixels are handled by backward references
- }
- ++accumulated_red_histo[(argb[ix] >> 16) & 0xff];
- ++accumulated_blue_histo[argb[ix] & 0xff];
- }
- }
- }
- }
-}
-
-// Color space inverse transform.
-static void ColorSpaceInverseTransform(const VP8LTransform* const transform,
- int y_start, int y_end, uint32_t* data) {
- const int width = transform->xsize_;
- const int mask = (1 << transform->bits_) - 1;
- const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_);
- int y = y_start;
- const uint32_t* pred_row =
- transform->data_ + (y >> transform->bits_) * tiles_per_row;
-
- while (y < y_end) {
- const uint32_t* pred = pred_row;
- Multipliers m = { 0, 0, 0 };
- int x;
-
- for (x = 0; x < width; ++x) {
- if ((x & mask) == 0) ColorCodeToMultipliers(*pred++, &m);
- data[x] = TransformColor(&m, data[x], 1);
- }
- data += width;
- ++y;
- if ((y & mask) == 0) pred_row += tiles_per_row;;
- }
-}
-
-// Separate out pixels packed together using pixel-bundling.
-// We define two methods for ARGB data (uint32_t) and alpha-only data (uint8_t).
-#define COLOR_INDEX_INVERSE(FUNC_NAME, TYPE, GET_INDEX, GET_VALUE) \
-void FUNC_NAME(const VP8LTransform* const transform, \
- int y_start, int y_end, const TYPE* src, TYPE* dst) { \
- int y; \
- const int bits_per_pixel = 8 >> transform->bits_; \
- const int width = transform->xsize_; \
- const uint32_t* const color_map = transform->data_; \
- if (bits_per_pixel < 8) { \
- const int pixels_per_byte = 1 << transform->bits_; \
- const int count_mask = pixels_per_byte - 1; \
- const uint32_t bit_mask = (1 << bits_per_pixel) - 1; \
- for (y = y_start; y < y_end; ++y) { \
- uint32_t packed_pixels = 0; \
- int x; \
- for (x = 0; x < width; ++x) { \
- /* We need to load fresh 'packed_pixels' once every */ \
- /* 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte */ \
- /* is a power of 2, so can just use a mask for that, instead of */ \
- /* decrementing a counter. */ \
- if ((x & count_mask) == 0) packed_pixels = GET_INDEX(*src++); \
- *dst++ = GET_VALUE(color_map[packed_pixels & bit_mask]); \
- packed_pixels >>= bits_per_pixel; \
- } \
- } \
- } else { \
- for (y = y_start; y < y_end; ++y) { \
- int x; \
- for (x = 0; x < width; ++x) { \
- *dst++ = GET_VALUE(color_map[GET_INDEX(*src++)]); \
- } \
- } \
- } \
-}
-
-static WEBP_INLINE uint32_t GetARGBIndex(uint32_t idx) {
- return (idx >> 8) & 0xff;
-}
-
-static WEBP_INLINE uint8_t GetAlphaIndex(uint8_t idx) {
- return idx;
-}
-
-static WEBP_INLINE uint32_t GetARGBValue(uint32_t val) {
- return val;
-}
-
-static WEBP_INLINE uint8_t GetAlphaValue(uint32_t val) {
- return (val >> 8) & 0xff;
-}
-
-static COLOR_INDEX_INVERSE(ColorIndexInverseTransform, uint32_t, GetARGBIndex,
- GetARGBValue)
-COLOR_INDEX_INVERSE(VP8LColorIndexInverseTransformAlpha, uint8_t, GetAlphaIndex,
- GetAlphaValue)
-
-#undef COLOR_INDEX_INVERSE
-
-void VP8LInverseTransform(const VP8LTransform* const transform,
- int row_start, int row_end,
- const uint32_t* const in, uint32_t* const out) {
- SB_DCHECK(row_start < row_end);
- SB_DCHECK(row_end <= transform->ysize_);
- switch (transform->type_) {
- case SUBTRACT_GREEN:
- AddGreenToBlueAndRed(transform, row_start, row_end, out);
- break;
- case PREDICTOR_TRANSFORM:
- PredictorInverseTransform(transform, row_start, row_end, out);
- if (row_end != transform->ysize_) {
- // The last predicted row in this iteration will be the top-pred row
- // for the first row in next iteration.
- const int width = transform->xsize_;
- SbMemoryCopy(out - width, out + (row_end - row_start - 1) * width,
- width * sizeof(*out));
- }
- break;
- case CROSS_COLOR_TRANSFORM:
- ColorSpaceInverseTransform(transform, row_start, row_end, out);
- break;
- case COLOR_INDEXING_TRANSFORM:
- if (in == out && transform->bits_ > 0) {
- // Move packed pixels to the end of unpacked region, so that unpacking
- // can occur seamlessly.
- // Also, note that this is the only transform that applies on
- // the effective width of VP8LSubSampleSize(xsize_, bits_). All other
- // transforms work on effective width of xsize_.
- const int out_stride = (row_end - row_start) * transform->xsize_;
- const int in_stride = (row_end - row_start) *
- VP8LSubSampleSize(transform->xsize_, transform->bits_);
- uint32_t* const src = out + out_stride - in_stride;
- SbMemoryMove(src, out, in_stride * sizeof(*src));
- ColorIndexInverseTransform(transform, row_start, row_end, src, out);
- } else {
- ColorIndexInverseTransform(transform, row_start, row_end, in, out);
- }
- break;
- }
-}
-
-//------------------------------------------------------------------------------
-// Color space conversion.
-
-static int is_big_endian(void) {
- static const union {
- uint16_t w;
- uint8_t b[2];
- } tmp = { 1 };
- return (tmp.b[0] != 1);
-}
-
-static void ConvertBGRAToRGB(const uint32_t* src,
- int num_pixels, uint8_t* dst) {
- const uint32_t* const src_end = src + num_pixels;
- while (src < src_end) {
- const uint32_t argb = *src++;
- *dst++ = (argb >> 16) & 0xff;
- *dst++ = (argb >> 8) & 0xff;
- *dst++ = (argb >> 0) & 0xff;
- }
-}
-
-static void ConvertBGRAToRGBA(const uint32_t* src,
- int num_pixels, uint8_t* dst) {
- const uint32_t* const src_end = src + num_pixels;
- while (src < src_end) {
- const uint32_t argb = *src++;
- *dst++ = (argb >> 16) & 0xff;
- *dst++ = (argb >> 8) & 0xff;
- *dst++ = (argb >> 0) & 0xff;
- *dst++ = (argb >> 24) & 0xff;
- }
-}
-
-static void ConvertBGRAToRGBA4444(const uint32_t* src,
- int num_pixels, uint8_t* dst) {
- const uint32_t* const src_end = src + num_pixels;
- while (src < src_end) {
- const uint32_t argb = *src++;
- const uint8_t rg = ((argb >> 16) & 0xf0) | ((argb >> 12) & 0xf);
- const uint8_t ba = ((argb >> 0) & 0xf0) | ((argb >> 28) & 0xf);
-#ifdef WEBP_SWAP_16BIT_CSP
- *dst++ = ba;
- *dst++ = rg;
-#else
- *dst++ = rg;
- *dst++ = ba;
-#endif
- }
-}
-
-static void ConvertBGRAToRGB565(const uint32_t* src,
- int num_pixels, uint8_t* dst) {
- const uint32_t* const src_end = src + num_pixels;
- while (src < src_end) {
- const uint32_t argb = *src++;
- const uint8_t rg = ((argb >> 16) & 0xf8) | ((argb >> 13) & 0x7);
- const uint8_t gb = ((argb >> 5) & 0xe0) | ((argb >> 3) & 0x1f);
-#ifdef WEBP_SWAP_16BIT_CSP
- *dst++ = gb;
- *dst++ = rg;
-#else
- *dst++ = rg;
- *dst++ = gb;
-#endif
- }
-}
-
-static void ConvertBGRAToBGR(const uint32_t* src,
- int num_pixels, uint8_t* dst) {
- const uint32_t* const src_end = src + num_pixels;
- while (src < src_end) {
- const uint32_t argb = *src++;
- *dst++ = (argb >> 0) & 0xff;
- *dst++ = (argb >> 8) & 0xff;
- *dst++ = (argb >> 16) & 0xff;
- }
-}
-
-static void CopyOrSwap(const uint32_t* src, int num_pixels, uint8_t* dst,
- int swap_on_big_endian) {
- if (is_big_endian() == swap_on_big_endian) {
- const uint32_t* const src_end = src + num_pixels;
- while (src < src_end) {
- uint32_t argb = *src++;
-
-#if !defined(__BIG_ENDIAN__)
-#if !defined(WEBP_REFERENCE_IMPLEMENTATION)
-#if defined(__i386__) || defined(__x86_64__)
- __asm__ volatile("bswap %0" : "=r"(argb) : "0"(argb));
- *(uint32_t*)dst = argb;
-#elif defined(_MSC_VER)
- argb = _byteswap_ulong(argb);
- *(uint32_t*)dst = argb;
-#else
- dst[0] = (argb >> 24) & 0xff;
- dst[1] = (argb >> 16) & 0xff;
- dst[2] = (argb >> 8) & 0xff;
- dst[3] = (argb >> 0) & 0xff;
-#endif
-#else // WEBP_REFERENCE_IMPLEMENTATION
- dst[0] = (argb >> 24) & 0xff;
- dst[1] = (argb >> 16) & 0xff;
- dst[2] = (argb >> 8) & 0xff;
- dst[3] = (argb >> 0) & 0xff;
-#endif
-#else // __BIG_ENDIAN__
- dst[0] = (argb >> 0) & 0xff;
- dst[1] = (argb >> 8) & 0xff;
- dst[2] = (argb >> 16) & 0xff;
- dst[3] = (argb >> 24) & 0xff;
-#endif
- dst += sizeof(argb);
- }
- } else {
- SbMemoryCopy(dst, src, num_pixels * sizeof(*src));
- }
-}
-
-void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels,
- WEBP_CSP_MODE out_colorspace, uint8_t* const rgba) {
- switch (out_colorspace) {
- case MODE_RGB:
- ConvertBGRAToRGB(in_data, num_pixels, rgba);
- break;
- case MODE_RGBA:
- ConvertBGRAToRGBA(in_data, num_pixels, rgba);
- break;
- case MODE_rgbA:
- ConvertBGRAToRGBA(in_data, num_pixels, rgba);
- WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0);
- break;
- case MODE_BGR:
- ConvertBGRAToBGR(in_data, num_pixels, rgba);
- break;
- case MODE_BGRA:
- CopyOrSwap(in_data, num_pixels, rgba, 1);
- break;
- case MODE_bgrA:
- CopyOrSwap(in_data, num_pixels, rgba, 1);
- WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0);
- break;
- case MODE_ARGB:
- CopyOrSwap(in_data, num_pixels, rgba, 0);
- break;
- case MODE_Argb:
- CopyOrSwap(in_data, num_pixels, rgba, 0);
- WebPApplyAlphaMultiply(rgba, 1, num_pixels, 1, 0);
- break;
- case MODE_RGBA_4444:
- ConvertBGRAToRGBA4444(in_data, num_pixels, rgba);
- break;
- case MODE_rgbA_4444:
- ConvertBGRAToRGBA4444(in_data, num_pixels, rgba);
- WebPApplyAlphaMultiply4444(rgba, num_pixels, 1, 0);
- break;
- case MODE_RGB_565:
- ConvertBGRAToRGB565(in_data, num_pixels, rgba);
- break;
- default:
- SB_DCHECK(0); // Code flow should not reach here.
- }
-}
-
-// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel.
-void VP8LBundleColorMap(const uint8_t* const row, int width,
- int xbits, uint32_t* const dst) {
- int x;
- if (xbits > 0) {
- const int bit_depth = 1 << (3 - xbits);
- const int mask = (1 << xbits) - 1;
- uint32_t code = 0xff000000;
- for (x = 0; x < width; ++x) {
- const int xsub = x & mask;
- if (xsub == 0) {
- code = 0xff000000;
- }
- code |= row[x] << (8 + bit_depth * xsub);
- dst[x >> xbits] = code;
- }
- } else {
- for (x = 0; x < width; ++x) dst[x] = 0xff000000 | (row[x] << 8);
- }
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/lossless.h b/src/third_party/libwebp/dsp/lossless.h
deleted file mode 100644
index 7490ec8..0000000
--- a/src/third_party/libwebp/dsp/lossless.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Image transforms and color space conversion methods for lossless decoder.
-//
-// Authors: Vikas Arora (vikaas.arora@gmail.com)
-// Jyrki Alakuijala (jyrki@google.com)
-
-#ifndef WEBP_DSP_LOSSLESS_H_
-#define WEBP_DSP_LOSSLESS_H_
-
-#include "../webp/types.h"
-#include "../webp/decode.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Image transforms.
-
-struct VP8LTransform; // Defined in dec/vp8li.h.
-
-// Performs inverse transform of data given transform information, start and end
-// rows. Transform will be applied to rows [row_start, row_end[.
-// The *in and *out pointers refer to source and destination data respectively
-// corresponding to the intermediate row (row_start).
-void VP8LInverseTransform(const struct VP8LTransform* const transform,
- int row_start, int row_end,
- const uint32_t* const in, uint32_t* const out);
-
-// Similar to the static method ColorIndexInverseTransform() that is part of
-// lossless.c, but used only for alpha decoding. It takes uint8_t (rather than
-// uint32_t) arguments for 'src' and 'dst'.
-void VP8LColorIndexInverseTransformAlpha(
- const struct VP8LTransform* const transform, int y_start, int y_end,
- const uint8_t* src, uint8_t* dst);
-
-// Subtracts green from blue and red channels.
-void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs);
-
-void VP8LResidualImage(int width, int height, int bits,
- uint32_t* const argb, uint32_t* const argb_scratch,
- uint32_t* const image);
-
-void VP8LColorSpaceTransform(int width, int height, int bits, int step,
- uint32_t* const argb, uint32_t* image);
-
-//------------------------------------------------------------------------------
-// Color space conversion.
-
-// Converts from BGRA to other color spaces.
-void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels,
- WEBP_CSP_MODE out_colorspace, uint8_t* const rgba);
-
-//------------------------------------------------------------------------------
-// Misc methods.
-
-// Computes sampled size of 'size' when sampling using 'sampling bits'.
-static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size,
- uint32_t sampling_bits) {
- return (size + (1 << sampling_bits) - 1) >> sampling_bits;
-}
-
-// Faster logarithm for integers. Small values use a look-up table.
-#define LOG_LOOKUP_IDX_MAX 256
-extern const float kLog2Table[LOG_LOOKUP_IDX_MAX];
-extern const float kSLog2Table[LOG_LOOKUP_IDX_MAX];
-extern float VP8LFastLog2Slow(int v);
-extern float VP8LFastSLog2Slow(int v);
-static WEBP_INLINE float VP8LFastLog2(int v) {
- return (v < LOG_LOOKUP_IDX_MAX) ? kLog2Table[v] : VP8LFastLog2Slow(v);
-}
-// Fast calculation of v * log2(v) for integer input.
-static WEBP_INLINE float VP8LFastSLog2(int v) {
- return (v < LOG_LOOKUP_IDX_MAX) ? kSLog2Table[v] : VP8LFastSLog2Slow(v);
-}
-
-
-// In-place difference of each component with mod 256.
-static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
- const uint32_t alpha_and_green =
- 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u);
- const uint32_t red_and_blue =
- 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu);
- return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu);
-}
-
-void VP8LBundleColorMap(const uint8_t* const row, int width,
- int xbits, uint32_t* const dst);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif // WEBP_DSP_LOSSLESS_H_
diff --git a/src/third_party/libwebp/dsp/upsampling.c b/src/third_party/libwebp/dsp/upsampling.c
deleted file mode 100644
index 80ba4f8..0000000
--- a/src/third_party/libwebp/dsp/upsampling.c
+++ /dev/null
@@ -1,369 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// YUV to RGB upsampling functions.
-//
-// Author: somnath@google.com (Somnath Banerjee)
-
-#include "./dsp.h"
-#include "./yuv.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Fancy upsampler
-
-#ifdef FANCY_UPSAMPLING
-
-// Fancy upsampling functions to convert YUV to RGB
-WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST];
-
-// Given samples laid out in a square as:
-// [a b]
-// [c d]
-// we interpolate u/v as:
-// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16
-// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16
-
-// We process u and v together stashed into 32bit (16bit each).
-#define LOAD_UV(u, v) ((u) | ((v) << 16))
-
-#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
-static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
- const uint8_t* top_u, const uint8_t* top_v, \
- const uint8_t* cur_u, const uint8_t* cur_v, \
- uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
- int x; \
- const int last_pixel_pair = (len - 1) >> 1; \
- uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \
- uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \
- if (top_y) { \
- const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
- FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \
- } \
- if (bottom_y) { \
- const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
- FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \
- } \
- for (x = 1; x <= last_pixel_pair; ++x) { \
- const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \
- const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \
- /* precompute invariant values associated with first and second diagonals*/\
- const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \
- const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \
- const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \
- if (top_y) { \
- const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \
- const uint32_t uv1 = (diag_03 + t_uv) >> 1; \
- FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
- top_dst + (2 * x - 1) * XSTEP); \
- FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \
- top_dst + (2 * x - 0) * XSTEP); \
- } \
- if (bottom_y) { \
- const uint32_t uv0 = (diag_03 + l_uv) >> 1; \
- const uint32_t uv1 = (diag_12 + uv) >> 1; \
- FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
- bottom_dst + (2 * x - 1) * XSTEP); \
- FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \
- bottom_dst + (2 * x + 0) * XSTEP); \
- } \
- tl_uv = t_uv; \
- l_uv = uv; \
- } \
- if (!(len & 1)) { \
- if (top_y) { \
- const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
- FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
- top_dst + (len - 1) * XSTEP); \
- } \
- if (bottom_y) { \
- const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
- FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
- bottom_dst + (len - 1) * XSTEP); \
- } \
- } \
-}
-
-// All variants implemented.
-UPSAMPLE_FUNC(UpsampleRgbLinePair, VP8YuvToRgb, 3)
-UPSAMPLE_FUNC(UpsampleBgrLinePair, VP8YuvToBgr, 3)
-UPSAMPLE_FUNC(UpsampleRgbaLinePair, VP8YuvToRgba, 4)
-UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4)
-UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4)
-UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2)
-UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2)
-
-#undef LOAD_UV
-#undef UPSAMPLE_FUNC
-
-#endif // FANCY_UPSAMPLING
-
-//------------------------------------------------------------------------------
-// simple point-sampling
-
-#define SAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
-static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
- const uint8_t* u, const uint8_t* v, \
- uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
- int i; \
- for (i = 0; i < len - 1; i += 2) { \
- FUNC(top_y[0], u[0], v[0], top_dst); \
- FUNC(top_y[1], u[0], v[0], top_dst + XSTEP); \
- FUNC(bottom_y[0], u[0], v[0], bottom_dst); \
- FUNC(bottom_y[1], u[0], v[0], bottom_dst + XSTEP); \
- top_y += 2; \
- bottom_y += 2; \
- u++; \
- v++; \
- top_dst += 2 * XSTEP; \
- bottom_dst += 2 * XSTEP; \
- } \
- if (i == len - 1) { /* last one */ \
- FUNC(top_y[0], u[0], v[0], top_dst); \
- FUNC(bottom_y[0], u[0], v[0], bottom_dst); \
- } \
-}
-
-// All variants implemented.
-SAMPLE_FUNC(SampleRgbLinePair, VP8YuvToRgb, 3)
-SAMPLE_FUNC(SampleBgrLinePair, VP8YuvToBgr, 3)
-SAMPLE_FUNC(SampleRgbaLinePair, VP8YuvToRgba, 4)
-SAMPLE_FUNC(SampleBgraLinePair, VP8YuvToBgra, 4)
-SAMPLE_FUNC(SampleArgbLinePair, VP8YuvToArgb, 4)
-SAMPLE_FUNC(SampleRgba4444LinePair, VP8YuvToRgba4444, 2)
-SAMPLE_FUNC(SampleRgb565LinePair, VP8YuvToRgb565, 2)
-
-#undef SAMPLE_FUNC
-
-const WebPSampleLinePairFunc WebPSamplers[MODE_LAST] = {
- SampleRgbLinePair, // MODE_RGB
- SampleRgbaLinePair, // MODE_RGBA
- SampleBgrLinePair, // MODE_BGR
- SampleBgraLinePair, // MODE_BGRA
- SampleArgbLinePair, // MODE_ARGB
- SampleRgba4444LinePair, // MODE_RGBA_4444
- SampleRgb565LinePair, // MODE_RGB_565
- SampleRgbaLinePair, // MODE_rgbA
- SampleBgraLinePair, // MODE_bgrA
- SampleArgbLinePair, // MODE_Argb
- SampleRgba4444LinePair // MODE_rgbA_4444
-};
-
-//------------------------------------------------------------------------------
-
-#if !defined(FANCY_UPSAMPLING)
-#define DUAL_SAMPLE_FUNC(FUNC_NAME, FUNC) \
-static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \
- const uint8_t* top_u, const uint8_t* top_v, \
- const uint8_t* bot_u, const uint8_t* bot_v, \
- uint8_t* top_dst, uint8_t* bot_dst, int len) { \
- const int half_len = len >> 1; \
- int x; \
- if (top_dst != NULL) { \
- for (x = 0; x < half_len; ++x) { \
- FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x + 0); \
- FUNC(top_y[2 * x + 1], top_u[x], top_v[x], top_dst + 8 * x + 4); \
- } \
- if (len & 1) FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x); \
- } \
- if (bot_dst != NULL) { \
- for (x = 0; x < half_len; ++x) { \
- FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x + 0); \
- FUNC(bot_y[2 * x + 1], bot_u[x], bot_v[x], bot_dst + 8 * x + 4); \
- } \
- if (len & 1) FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x); \
- } \
-}
-
-DUAL_SAMPLE_FUNC(DualLineSamplerBGRA, VP8YuvToBgra)
-DUAL_SAMPLE_FUNC(DualLineSamplerARGB, VP8YuvToArgb)
-#undef DUAL_SAMPLE_FUNC
-
-#endif // !FANCY_UPSAMPLING
-
-WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) {
- WebPInitUpsamplers();
- VP8YUVInit();
-#ifdef FANCY_UPSAMPLING
- return WebPUpsamplers[alpha_is_last ? MODE_BGRA : MODE_ARGB];
-#else
- return (alpha_is_last ? DualLineSamplerBGRA : DualLineSamplerARGB);
-#endif
-}
-
-//------------------------------------------------------------------------------
-// YUV444 converter
-
-#define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \
-static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
- uint8_t* dst, int len) { \
- int i; \
- for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * XSTEP]); \
-}
-
-YUV444_FUNC(Yuv444ToRgb, VP8YuvToRgb, 3)
-YUV444_FUNC(Yuv444ToBgr, VP8YuvToBgr, 3)
-YUV444_FUNC(Yuv444ToRgba, VP8YuvToRgba, 4)
-YUV444_FUNC(Yuv444ToBgra, VP8YuvToBgra, 4)
-YUV444_FUNC(Yuv444ToArgb, VP8YuvToArgb, 4)
-YUV444_FUNC(Yuv444ToRgba4444, VP8YuvToRgba4444, 2)
-YUV444_FUNC(Yuv444ToRgb565, VP8YuvToRgb565, 2)
-
-#undef YUV444_FUNC
-
-const WebPYUV444Converter WebPYUV444Converters[MODE_LAST] = {
- Yuv444ToRgb, // MODE_RGB
- Yuv444ToRgba, // MODE_RGBA
- Yuv444ToBgr, // MODE_BGR
- Yuv444ToBgra, // MODE_BGRA
- Yuv444ToArgb, // MODE_ARGB
- Yuv444ToRgba4444, // MODE_RGBA_4444
- Yuv444ToRgb565, // MODE_RGB_565
- Yuv444ToRgba, // MODE_rgbA
- Yuv444ToBgra, // MODE_bgrA
- Yuv444ToArgb, // MODE_Argb
- Yuv444ToRgba4444 // MODE_rgbA_4444
-};
-
-//------------------------------------------------------------------------------
-// Premultiplied modes
-
-// non dithered-modes
-
-// (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.)
-// for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5),
-// one can use instead: (x * a * 65793 + (1 << 23)) >> 24
-#if 1 // (int)(x * a / 255.)
-#define MULTIPLIER(a) ((a) * 32897UL)
-#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
-#else // (int)(x * a / 255. + .5)
-#define MULTIPLIER(a) ((a) * 65793UL)
-#define PREMULTIPLY(x, m) (((x) * (m) + (1UL << 23)) >> 24)
-#endif
-
-static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first,
- int w, int h, int stride) {
- while (h-- > 0) {
- uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
- const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
- int i;
- for (i = 0; i < w; ++i) {
- const uint32_t a = alpha[4 * i];
- if (a != 0xff) {
- const uint32_t mult = MULTIPLIER(a);
- rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
- rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
- rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
- }
- }
- rgba += stride;
- }
-}
-#undef MULTIPLIER
-#undef PREMULTIPLY
-
-// rgbA4444
-
-#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
-
-static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
- return (x & 0xf0) | (x >> 4);
-}
-
-static WEBP_INLINE uint8_t dither_lo(uint8_t x) {
- return (x & 0x0f) | (x << 4);
-}
-
-static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
- return (x * m) >> 16;
-}
-
-static void ApplyAlphaMultiply4444(uint8_t* rgba4444,
- int w, int h, int stride) {
- while (h-- > 0) {
- int i;
- for (i = 0; i < w; ++i) {
- const uint8_t a = (rgba4444[2 * i + 1] & 0x0f);
- const uint32_t mult = MULTIPLIER(a);
- const uint8_t r = multiply(dither_hi(rgba4444[2 * i + 0]), mult);
- const uint8_t g = multiply(dither_lo(rgba4444[2 * i + 0]), mult);
- const uint8_t b = multiply(dither_hi(rgba4444[2 * i + 1]), mult);
- rgba4444[2 * i + 0] = (r & 0xf0) | ((g >> 4) & 0x0f);
- rgba4444[2 * i + 1] = (b & 0xf0) | a;
- }
- rgba4444 += stride;
- }
-}
-#undef MULTIPLIER
-
-void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int)
- = ApplyAlphaMultiply;
-void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int)
- = ApplyAlphaMultiply4444;
-
-//------------------------------------------------------------------------------
-// Main call
-
-void WebPInitUpsamplers(void) {
-#ifdef FANCY_UPSAMPLING
- WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair;
- WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair;
- WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair;
- WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair;
- WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair;
- WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair;
- WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair;
-
- // If defined, use CPUInfo() to overwrite some pointers with faster versions.
- if (VP8GetCPUInfo != NULL) {
-#if defined(WEBP_USE_SSE2)
- if (VP8GetCPUInfo(kSSE2)) {
- WebPInitUpsamplersSSE2();
- }
-#endif
-#if defined(WEBP_USE_NEON)
- if (VP8GetCPUInfo(kNEON)) {
- WebPInitUpsamplersNEON();
- }
-#endif
- }
-#endif // FANCY_UPSAMPLING
-}
-
-void WebPInitPremultiply(void) {
- WebPApplyAlphaMultiply = ApplyAlphaMultiply;
- WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply4444;
-
-#ifdef FANCY_UPSAMPLING
- WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair;
- WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair;
- WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair;
- WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair;
-
- if (VP8GetCPUInfo != NULL) {
-#if defined(WEBP_USE_SSE2)
- if (VP8GetCPUInfo(kSSE2)) {
- WebPInitPremultiplySSE2();
- }
-#endif
-#if defined(WEBP_USE_NEON)
- if (VP8GetCPUInfo(kNEON)) {
- WebPInitPremultiplyNEON();
- }
-#endif
- }
-#endif // FANCY_UPSAMPLING
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/upsampling_neon.c b/src/third_party/libwebp/dsp/upsampling_neon.c
deleted file mode 100644
index 2e6a8e5..0000000
--- a/src/third_party/libwebp/dsp/upsampling_neon.c
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// NEON version of YUV to RGB upsampling functions.
-//
-// Author: mans@mansr.com (Mans Rullgard)
-// Based on SSE code by: somnath@google.com (Somnath Banerjee)
-
-#include "./dsp.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if defined(WEBP_USE_NEON)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <string.h>
-#endif
-#include <arm_neon.h>
-#include "./yuv.h"
-
-#ifdef FANCY_UPSAMPLING
-
-// Loads 9 pixels each from rows r1 and r2 and generates 16 pixels.
-#define UPSAMPLE_16PIXELS(r1, r2, out) { \
- uint8x8_t a = vld1_u8(r1); \
- uint8x8_t b = vld1_u8(r1 + 1); \
- uint8x8_t c = vld1_u8(r2); \
- uint8x8_t d = vld1_u8(r2 + 1); \
- \
- uint16x8_t al = vshll_n_u8(a, 1); \
- uint16x8_t bl = vshll_n_u8(b, 1); \
- uint16x8_t cl = vshll_n_u8(c, 1); \
- uint16x8_t dl = vshll_n_u8(d, 1); \
- \
- uint8x8_t diag1, diag2; \
- uint16x8_t sl; \
- \
- /* a + b + c + d */ \
- sl = vaddl_u8(a, b); \
- sl = vaddw_u8(sl, c); \
- sl = vaddw_u8(sl, d); \
- \
- al = vaddq_u16(sl, al); /* 3a + b + c + d */ \
- bl = vaddq_u16(sl, bl); /* a + 3b + c + d */ \
- \
- al = vaddq_u16(al, dl); /* 3a + b + c + 3d */ \
- bl = vaddq_u16(bl, cl); /* a + 3b + 3c + d */ \
- \
- diag2 = vshrn_n_u16(al, 3); \
- diag1 = vshrn_n_u16(bl, 3); \
- \
- a = vrhadd_u8(a, diag1); \
- b = vrhadd_u8(b, diag2); \
- c = vrhadd_u8(c, diag2); \
- d = vrhadd_u8(d, diag1); \
- \
- { \
- const uint8x8x2_t a_b = {{ a, b }}; \
- const uint8x8x2_t c_d = {{ c, d }}; \
- vst2_u8(out, a_b); \
- vst2_u8(out + 32, c_d); \
- } \
-}
-
-// Turn the macro into a function for reducing code-size when non-critical
-static void Upsample16Pixels(const uint8_t *r1, const uint8_t *r2,
- uint8_t *out) {
- UPSAMPLE_16PIXELS(r1, r2, out);
-}
-
-#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \
- uint8_t r1[9], r2[9]; \
- SbMemoryCopy(r1, (tb), (num_pixels)); \
- SbMemoryCopy(r2, (bb), (num_pixels)); \
- /* replicate last byte */ \
- SbMemorySet(r1 + (num_pixels), r1[(num_pixels) - 1], 9 - (num_pixels)); \
- SbMemorySet(r2 + (num_pixels), r2[(num_pixels) - 1], 9 - (num_pixels)); \
- Upsample16Pixels(r1, r2, out); \
-}
-
-#define CY 76283
-#define CVR 89858
-#define CUG 22014
-#define CVG 45773
-#define CUB 113618
-
-static const int16_t coef[4] = { CVR / 4, CUG, CVG / 2, CUB / 4 };
-
-#define CONVERT8(FMT, XSTEP, N, src_y, src_uv, out, cur_x) { \
- int i; \
- for (i = 0; i < N; i += 8) { \
- int off = ((cur_x) + i) * XSTEP; \
- uint8x8_t y = vld1_u8(src_y + (cur_x) + i); \
- uint8x8_t u = vld1_u8((src_uv) + i); \
- uint8x8_t v = vld1_u8((src_uv) + i + 16); \
- int16x8_t yy = vreinterpretq_s16_u16(vsubl_u8(y, u16)); \
- int16x8_t uu = vreinterpretq_s16_u16(vsubl_u8(u, u128)); \
- int16x8_t vv = vreinterpretq_s16_u16(vsubl_u8(v, u128)); \
- \
- int16x8_t ud = vshlq_n_s16(uu, 1); \
- int16x8_t vd = vshlq_n_s16(vv, 1); \
- \
- int32x4_t vrl = vqdmlal_lane_s16(vshll_n_s16(vget_low_s16(vv), 1), \
- vget_low_s16(vd), cf16, 0); \
- int32x4_t vrh = vqdmlal_lane_s16(vshll_n_s16(vget_high_s16(vv), 1), \
- vget_high_s16(vd), cf16, 0); \
- int16x8_t vr = vcombine_s16(vrshrn_n_s32(vrl, 16), \
- vrshrn_n_s32(vrh, 16)); \
- \
- int32x4_t vl = vmovl_s16(vget_low_s16(vv)); \
- int32x4_t vh = vmovl_s16(vget_high_s16(vv)); \
- int32x4_t ugl = vmlal_lane_s16(vl, vget_low_s16(uu), cf16, 1); \
- int32x4_t ugh = vmlal_lane_s16(vh, vget_high_s16(uu), cf16, 1); \
- int32x4_t gcl = vqdmlal_lane_s16(ugl, vget_low_s16(vv), cf16, 2); \
- int32x4_t gch = vqdmlal_lane_s16(ugh, vget_high_s16(vv), cf16, 2); \
- int16x8_t gc = vcombine_s16(vrshrn_n_s32(gcl, 16), \
- vrshrn_n_s32(gch, 16)); \
- \
- int32x4_t ubl = vqdmlal_lane_s16(vshll_n_s16(vget_low_s16(uu), 1), \
- vget_low_s16(ud), cf16, 3); \
- int32x4_t ubh = vqdmlal_lane_s16(vshll_n_s16(vget_high_s16(uu), 1), \
- vget_high_s16(ud), cf16, 3); \
- int16x8_t ub = vcombine_s16(vrshrn_n_s32(ubl, 16), \
- vrshrn_n_s32(ubh, 16)); \
- \
- int32x4_t rl = vaddl_s16(vget_low_s16(yy), vget_low_s16(vr)); \
- int32x4_t rh = vaddl_s16(vget_high_s16(yy), vget_high_s16(vr)); \
- int32x4_t gl = vsubl_s16(vget_low_s16(yy), vget_low_s16(gc)); \
- int32x4_t gh = vsubl_s16(vget_high_s16(yy), vget_high_s16(gc)); \
- int32x4_t bl = vaddl_s16(vget_low_s16(yy), vget_low_s16(ub)); \
- int32x4_t bh = vaddl_s16(vget_high_s16(yy), vget_high_s16(ub)); \
- \
- rl = vmulq_lane_s32(rl, cf32, 0); \
- rh = vmulq_lane_s32(rh, cf32, 0); \
- gl = vmulq_lane_s32(gl, cf32, 0); \
- gh = vmulq_lane_s32(gh, cf32, 0); \
- bl = vmulq_lane_s32(bl, cf32, 0); \
- bh = vmulq_lane_s32(bh, cf32, 0); \
- \
- y = vqmovun_s16(vcombine_s16(vrshrn_n_s32(rl, 16), \
- vrshrn_n_s32(rh, 16))); \
- u = vqmovun_s16(vcombine_s16(vrshrn_n_s32(gl, 16), \
- vrshrn_n_s32(gh, 16))); \
- v = vqmovun_s16(vcombine_s16(vrshrn_n_s32(bl, 16), \
- vrshrn_n_s32(bh, 16))); \
- STR_ ## FMT(out + off, y, u, v); \
- } \
-}
-
-#define v255 vmov_n_u8(255)
-
-#define STR_Rgb(out, r, g, b) do { \
- const uint8x8x3_t r_g_b = {{ r, g, b }}; \
- vst3_u8(out, r_g_b); \
-} while (0)
-
-#define STR_Bgr(out, r, g, b) do { \
- const uint8x8x3_t b_g_r = {{ b, g, r }}; \
- vst3_u8(out, b_g_r); \
-} while (0)
-
-#define STR_Rgba(out, r, g, b) do { \
- const uint8x8x4_t r_g_b_v255 = {{ r, g, b, v255 }}; \
- vst4_u8(out, r_g_b_v255); \
-} while (0)
-
-#define STR_Bgra(out, r, g, b) do { \
- const uint8x8x4_t b_g_r_v255 = {{ b, g, r, v255 }}; \
- vst4_u8(out, b_g_r_v255); \
-} while (0)
-
-#define CONVERT1(FMT, XSTEP, N, src_y, src_uv, rgb, cur_x) { \
- int i; \
- for (i = 0; i < N; i++) { \
- int off = ((cur_x) + i) * XSTEP; \
- int y = src_y[(cur_x) + i]; \
- int u = (src_uv)[i]; \
- int v = (src_uv)[i + 16]; \
- VP8YuvTo ## FMT(y, u, v, rgb + off); \
- } \
-}
-
-#define CONVERT2RGB_8(FMT, XSTEP, top_y, bottom_y, uv, \
- top_dst, bottom_dst, cur_x, len) { \
- if (top_y) { \
- CONVERT8(FMT, XSTEP, len, top_y, uv, top_dst, cur_x) \
- } \
- if (bottom_y) { \
- CONVERT8(FMT, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x) \
- } \
-}
-
-#define CONVERT2RGB_1(FMT, XSTEP, top_y, bottom_y, uv, \
- top_dst, bottom_dst, cur_x, len) { \
- if (top_y) { \
- CONVERT1(FMT, XSTEP, len, top_y, uv, top_dst, cur_x); \
- } \
- if (bottom_y) { \
- CONVERT1(FMT, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x); \
- } \
-}
-
-#define NEON_UPSAMPLE_FUNC(FUNC_NAME, FMT, XSTEP) \
-static void FUNC_NAME(const uint8_t *top_y, const uint8_t *bottom_y, \
- const uint8_t *top_u, const uint8_t *top_v, \
- const uint8_t *cur_u, const uint8_t *cur_v, \
- uint8_t *top_dst, uint8_t *bottom_dst, int len) { \
- int block; \
- /* 16 byte aligned array to cache reconstructed u and v */ \
- uint8_t uv_buf[2 * 32 + 15]; \
- uint8_t *const r_uv = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
- const int uv_len = (len + 1) >> 1; \
- /* 9 pixels must be read-able for each block */ \
- const int num_blocks = (uv_len - 1) >> 3; \
- const int leftover = uv_len - num_blocks * 8; \
- const int last_pos = 1 + 16 * num_blocks; \
- \
- const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
- const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
- \
- const int16x4_t cf16 = vld1_s16(coef); \
- const int32x2_t cf32 = vmov_n_s32(CY); \
- const uint8x8_t u16 = vmov_n_u8(16); \
- const uint8x8_t u128 = vmov_n_u8(128); \
- \
- /* Treat the first pixel in regular way */ \
- if (top_y) { \
- const int u0 = (top_u[0] + u_diag) >> 1; \
- const int v0 = (top_v[0] + v_diag) >> 1; \
- VP8YuvTo ## FMT(top_y[0], u0, v0, top_dst); \
- } \
- if (bottom_y) { \
- const int u0 = (cur_u[0] + u_diag) >> 1; \
- const int v0 = (cur_v[0] + v_diag) >> 1; \
- VP8YuvTo ## FMT(bottom_y[0], u0, v0, bottom_dst); \
- } \
- \
- for (block = 0; block < num_blocks; ++block) { \
- UPSAMPLE_16PIXELS(top_u, cur_u, r_uv); \
- UPSAMPLE_16PIXELS(top_v, cur_v, r_uv + 16); \
- CONVERT2RGB_8(FMT, XSTEP, top_y, bottom_y, r_uv, \
- top_dst, bottom_dst, 16 * block + 1, 16); \
- top_u += 8; \
- cur_u += 8; \
- top_v += 8; \
- cur_v += 8; \
- } \
- \
- UPSAMPLE_LAST_BLOCK(top_u, cur_u, leftover, r_uv); \
- UPSAMPLE_LAST_BLOCK(top_v, cur_v, leftover, r_uv + 16); \
- CONVERT2RGB_1(FMT, XSTEP, top_y, bottom_y, r_uv, \
- top_dst, bottom_dst, last_pos, len - last_pos); \
-}
-
-// NEON variants of the fancy upsampler.
-NEON_UPSAMPLE_FUNC(UpsampleRgbLinePairNEON, Rgb, 3)
-NEON_UPSAMPLE_FUNC(UpsampleBgrLinePairNEON, Bgr, 3)
-NEON_UPSAMPLE_FUNC(UpsampleRgbaLinePairNEON, Rgba, 4)
-NEON_UPSAMPLE_FUNC(UpsampleBgraLinePairNEON, Bgra, 4)
-
-#endif // FANCY_UPSAMPLING
-
-#endif // WEBP_USE_NEON
-
-//------------------------------------------------------------------------------
-
-extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
-
-void WebPInitUpsamplersNEON(void) {
-#if defined(WEBP_USE_NEON)
- WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePairNEON;
- WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePairNEON;
- WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePairNEON;
- WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePairNEON;
-#endif // WEBP_USE_NEON
-}
-
-void WebPInitPremultiplyNEON(void) {
-#if defined(WEBP_USE_NEON)
- WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePairNEON;
- WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePairNEON;
-#endif // WEBP_USE_NEON
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/upsampling_sse2.c b/src/third_party/libwebp/dsp/upsampling_sse2.c
deleted file mode 100644
index 491160c..0000000
--- a/src/third_party/libwebp/dsp/upsampling_sse2.c
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// SSE2 version of YUV to RGB upsampling functions.
-//
-// Author: somnath@google.com (Somnath Banerjee)
-
-#include "./dsp.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if defined(WEBP_USE_SSE2)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <string.h>
-#endif
-#include <emmintrin.h>
-#include "./yuv.h"
-
-#ifdef FANCY_UPSAMPLING
-
-// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows
-// u = (9*a + 3*b + 3*c + d + 8) / 16
-// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2
-// = (a + m + 1) / 2
-// where m = (a + 3*b + 3*c + d) / 8
-// = ((a + b + c + d) / 2 + b + c) / 4
-//
-// Let's say k = (a + b + c + d) / 4.
-// We can compute k as
-// k = (s + t + 1) / 2 - ((a^d) | (b^c) | (s^t)) & 1
-// where s = (a + d + 1) / 2 and t = (b + c + 1) / 2
-//
-// Then m can be written as
-// m = (k + t + 1) / 2 - (((b^c) & (s^t)) | (k^t)) & 1
-
-// Computes out = (k + in + 1) / 2 - ((ij & (s^t)) | (k^in)) & 1
-#define GET_M(ij, in, out) do { \
- const __m128i tmp0 = _mm_avg_epu8(k, (in)); /* (k + in + 1) / 2 */ \
- const __m128i tmp1 = _mm_and_si128((ij), st); /* (ij) & (s^t) */ \
- const __m128i tmp2 = _mm_xor_si128(k, (in)); /* (k^in) */ \
- const __m128i tmp3 = _mm_or_si128(tmp1, tmp2); /* ((ij) & (s^t)) | (k^in) */\
- const __m128i tmp4 = _mm_and_si128(tmp3, one); /* & 1 -> lsb_correction */ \
- (out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \
-} while (0)
-
-// pack and store two alterning pixel rows
-#define PACK_AND_STORE(a, b, da, db, out) do { \
- const __m128i t_a = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \
- const __m128i t_b = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \
- const __m128i t_1 = _mm_unpacklo_epi8(t_a, t_b); \
- const __m128i t_2 = _mm_unpackhi_epi8(t_a, t_b); \
- _mm_store_si128(((__m128i*)(out)) + 0, t_1); \
- _mm_store_si128(((__m128i*)(out)) + 1, t_2); \
-} while (0)
-
-// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
-#define UPSAMPLE_32PIXELS(r1, r2, out) { \
- const __m128i one = _mm_set1_epi8(1); \
- const __m128i a = _mm_loadu_si128((__m128i*)&(r1)[0]); \
- const __m128i b = _mm_loadu_si128((__m128i*)&(r1)[1]); \
- const __m128i c = _mm_loadu_si128((__m128i*)&(r2)[0]); \
- const __m128i d = _mm_loadu_si128((__m128i*)&(r2)[1]); \
- \
- const __m128i s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \
- const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \
- const __m128i st = _mm_xor_si128(s, t); /* st = s^t */ \
- \
- const __m128i ad = _mm_xor_si128(a, d); /* ad = a^d */ \
- const __m128i bc = _mm_xor_si128(b, c); /* bc = b^c */ \
- \
- const __m128i t1 = _mm_or_si128(ad, bc); /* (a^d) | (b^c) */ \
- const __m128i t2 = _mm_or_si128(t1, st); /* (a^d) | (b^c) | (s^t) */ \
- const __m128i t3 = _mm_and_si128(t2, one); /* (a^d) | (b^c) | (s^t) & 1 */ \
- const __m128i t4 = _mm_avg_epu8(s, t); \
- const __m128i k = _mm_sub_epi8(t4, t3); /* k = (a + b + c + d) / 4 */ \
- __m128i diag1, diag2; \
- \
- GET_M(bc, t, diag1); /* diag1 = (a + 3b + 3c + d) / 8 */ \
- GET_M(ad, s, diag2); /* diag2 = (3a + b + c + 3d) / 8 */ \
- \
- /* pack the alternate pixels */ \
- PACK_AND_STORE(a, b, diag1, diag2, &(out)[0 * 32]); \
- PACK_AND_STORE(c, d, diag2, diag1, &(out)[2 * 32]); \
-}
-
-// Turn the macro into a function for reducing code-size when non-critical
-static void Upsample32Pixels(const uint8_t r1[], const uint8_t r2[],
- uint8_t* const out) {
- UPSAMPLE_32PIXELS(r1, r2, out);
-}
-
-#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \
- uint8_t r1[17], r2[17]; \
- SbMemoryCopy(r1, (tb), (num_pixels)); \
- SbMemoryCopy(r2, (bb), (num_pixels)); \
- /* replicate last byte */ \
- SbMemorySet(r1 + (num_pixels), r1[(num_pixels) - 1], 17 - (num_pixels)); \
- SbMemorySet(r2 + (num_pixels), r2[(num_pixels) - 1], 17 - (num_pixels)); \
- /* using the shared function instead of the macro saves ~3k code size */ \
- Upsample32Pixels(r1, r2, out); \
-}
-
-#define CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, uv, \
- top_dst, bottom_dst, cur_x, num_pixels) { \
- int n; \
- if (top_y) { \
- for (n = 0; n < (num_pixels); ++n) { \
- FUNC(top_y[(cur_x) + n], (uv)[n], (uv)[32 + n], \
- top_dst + ((cur_x) + n) * XSTEP); \
- } \
- } \
- if (bottom_y) { \
- for (n = 0; n < (num_pixels); ++n) { \
- FUNC(bottom_y[(cur_x) + n], (uv)[64 + n], (uv)[64 + 32 + n], \
- bottom_dst + ((cur_x) + n) * XSTEP); \
- } \
- } \
-}
-
-#define SSE2_UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
-static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
- const uint8_t* top_u, const uint8_t* top_v, \
- const uint8_t* cur_u, const uint8_t* cur_v, \
- uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
- int block; \
- /* 16 byte aligned array to cache reconstructed u and v */ \
- uint8_t uv_buf[4 * 32 + 15]; \
- uint8_t* const r_uv = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
- const int uv_len = (len + 1) >> 1; \
- /* 17 pixels must be read-able for each block */ \
- const int num_blocks = (uv_len - 1) >> 4; \
- const int leftover = uv_len - num_blocks * 16; \
- const int last_pos = 1 + 32 * num_blocks; \
- \
- const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
- const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
- \
- SB_DCHECK(len > 0); \
- /* Treat the first pixel in regular way */ \
- if (top_y) { \
- const int u0 = (top_u[0] + u_diag) >> 1; \
- const int v0 = (top_v[0] + v_diag) >> 1; \
- FUNC(top_y[0], u0, v0, top_dst); \
- } \
- if (bottom_y) { \
- const int u0 = (cur_u[0] + u_diag) >> 1; \
- const int v0 = (cur_v[0] + v_diag) >> 1; \
- FUNC(bottom_y[0], u0, v0, bottom_dst); \
- } \
- \
- for (block = 0; block < num_blocks; ++block) { \
- UPSAMPLE_32PIXELS(top_u, cur_u, r_uv + 0 * 32); \
- UPSAMPLE_32PIXELS(top_v, cur_v, r_uv + 1 * 32); \
- CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, r_uv, top_dst, bottom_dst, \
- 32 * block + 1, 32) \
- top_u += 16; \
- cur_u += 16; \
- top_v += 16; \
- cur_v += 16; \
- } \
- \
- UPSAMPLE_LAST_BLOCK(top_u, cur_u, leftover, r_uv + 0 * 32); \
- UPSAMPLE_LAST_BLOCK(top_v, cur_v, leftover, r_uv + 1 * 32); \
- CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, r_uv, top_dst, bottom_dst, \
- last_pos, len - last_pos); \
-}
-
-// SSE2 variants of the fancy upsampler.
-SSE2_UPSAMPLE_FUNC(UpsampleRgbLinePairSSE2, VP8YuvToRgb, 3)
-SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePairSSE2, VP8YuvToBgr, 3)
-SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePairSSE2, VP8YuvToRgba, 4)
-SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePairSSE2, VP8YuvToBgra, 4)
-
-#undef GET_M
-#undef PACK_AND_STORE
-#undef UPSAMPLE_32PIXELS
-#undef UPSAMPLE_LAST_BLOCK
-#undef CONVERT2RGB
-#undef SSE2_UPSAMPLE_FUNC
-
-#endif // FANCY_UPSAMPLING
-
-#endif // WEBP_USE_SSE2
-
-//------------------------------------------------------------------------------
-
-extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
-
-void WebPInitUpsamplersSSE2(void) {
-#if defined(WEBP_USE_SSE2)
- WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePairSSE2;
- WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePairSSE2;
- WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePairSSE2;
- WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePairSSE2;
-#endif // WEBP_USE_SSE2
-}
-
-void WebPInitPremultiplySSE2(void) {
-#if defined(WEBP_USE_SSE2)
- WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePairSSE2;
- WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePairSSE2;
-#endif // WEBP_USE_SSE2
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-
diff --git a/src/third_party/libwebp/dsp/yuv.c b/src/third_party/libwebp/dsp/yuv.c
deleted file mode 100644
index 1a59f74..0000000
--- a/src/third_party/libwebp/dsp/yuv.c
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// YUV->RGB conversion function
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "./yuv.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#ifdef WEBP_YUV_USE_TABLE
-
-int16_t VP8kVToR[256], VP8kUToB[256];
-int32_t VP8kVToG[256], VP8kUToG[256];
-uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN];
-uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN];
-
-static int done = 0;
-
-static WEBP_INLINE uint8_t clip(int v, int max_value) {
- return v < 0 ? 0 : v > max_value ? max_value : v;
-}
-
-void VP8YUVInit(void) {
- int i;
- if (done) {
- return;
- }
-#ifndef USE_YUVj
- for (i = 0; i < 256; ++i) {
- VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX;
- VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF;
- VP8kVToG[i] = -45773 * (i - 128);
- VP8kUToB[i] = (113618 * (i - 128) + YUV_HALF) >> YUV_FIX;
- }
- for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) {
- const int k = ((i - 16) * 76283 + YUV_HALF) >> YUV_FIX;
- VP8kClip[i - YUV_RANGE_MIN] = clip(k, 255);
- VP8kClip4Bits[i - YUV_RANGE_MIN] = clip((k + 8) >> 4, 15);
- }
-#else
- for (i = 0; i < 256; ++i) {
- VP8kVToR[i] = (91881 * (i - 128) + YUV_HALF) >> YUV_FIX;
- VP8kUToG[i] = -22554 * (i - 128) + YUV_HALF;
- VP8kVToG[i] = -46802 * (i - 128);
- VP8kUToB[i] = (116130 * (i - 128) + YUV_HALF) >> YUV_FIX;
- }
- for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) {
- const int k = i;
- VP8kClip[i - YUV_RANGE_MIN] = clip(k, 255);
- VP8kClip4Bits[i - YUV_RANGE_MIN] = clip((k + 8) >> 4, 15);
- }
-#endif
-
- done = 1;
-}
-
-#else
-
-void VP8YUVInit(void) {}
-
-#endif // WEBP_YUV_USE_TABLE
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/dsp/yuv.h b/src/third_party/libwebp/dsp/yuv.h
deleted file mode 100644
index 3844d8c..0000000
--- a/src/third_party/libwebp/dsp/yuv.h
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// inline YUV<->RGB conversion function
-//
-// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
-// More information at: http://en.wikipedia.org/wiki/YCbCr
-// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
-// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
-// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
-// We use 16bit fixed point operations for RGB->YUV conversion.
-//
-// For the Y'CbCr to RGB conversion, the BT.601 specification reads:
-// R = 1.164 * (Y-16) + 1.596 * (V-128)
-// G = 1.164 * (Y-16) - 0.813 * (V-128) - 0.391 * (U-128)
-// B = 1.164 * (Y-16) + 2.018 * (U-128)
-// where Y is in the [16,235] range, and U/V in the [16,240] range.
-// In the table-lookup version (WEBP_YUV_USE_TABLE), the common factor
-// "1.164 * (Y-16)" can be handled as an offset in the VP8kClip[] table.
-// So in this case the formulae should be read as:
-// R = 1.164 * [Y + 1.371 * (V-128) ] - 18.624
-// G = 1.164 * [Y - 0.698 * (V-128) - 0.336 * (U-128)] - 18.624
-// B = 1.164 * [Y + 1.733 * (U-128)] - 18.624
-// once factorized. Here too, 16bit fixed precision is used.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_DSP_YUV_H_
-#define WEBP_DSP_YUV_H_
-
-#include "../dec/decode_vp8.h"
-
-// Define the following to use the LUT-based code:
-#define WEBP_YUV_USE_TABLE
-
-#if defined(WEBP_EXPERIMENTAL_FEATURES)
-// Do NOT activate this feature for real compression. This is only experimental!
-// This flag is for comparison purpose against JPEG's "YUVj" natural colorspace.
-// This colorspace is close to Rec.601's Y'CbCr model with the notable
-// difference of allowing larger range for luma/chroma.
-// See http://en.wikipedia.org/wiki/YCbCr#JPEG_conversion paragraph, and its
-// difference with http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
-// #define USE_YUVj
-#endif
-
-//------------------------------------------------------------------------------
-// YUV -> RGB conversion
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-enum { YUV_FIX = 16, // fixed-point precision
- YUV_HALF = 1 << (YUV_FIX - 1),
- YUV_MASK = (256 << YUV_FIX) - 1,
- YUV_RANGE_MIN = -227, // min value of r/g/b output
- YUV_RANGE_MAX = 256 + 226 // max value of r/g/b output
-};
-
-#ifdef WEBP_YUV_USE_TABLE
-
-extern int16_t VP8kVToR[256], VP8kUToB[256];
-extern int32_t VP8kVToG[256], VP8kUToG[256];
-extern uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN];
-extern uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN];
-
-static WEBP_INLINE void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const rgb) {
- const int r_off = VP8kVToR[v];
- const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
- const int b_off = VP8kUToB[u];
- rgb[0] = VP8kClip[y + r_off - YUV_RANGE_MIN];
- rgb[1] = VP8kClip[y + g_off - YUV_RANGE_MIN];
- rgb[2] = VP8kClip[y + b_off - YUV_RANGE_MIN];
-}
-
-static WEBP_INLINE void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const bgr) {
- const int r_off = VP8kVToR[v];
- const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
- const int b_off = VP8kUToB[u];
- bgr[0] = VP8kClip[y + b_off - YUV_RANGE_MIN];
- bgr[1] = VP8kClip[y + g_off - YUV_RANGE_MIN];
- bgr[2] = VP8kClip[y + r_off - YUV_RANGE_MIN];
-}
-
-static WEBP_INLINE void VP8YuvToRgb565(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const rgb) {
- const int r_off = VP8kVToR[v];
- const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
- const int b_off = VP8kUToB[u];
- const uint8_t rg = ((VP8kClip[y + r_off - YUV_RANGE_MIN] & 0xf8) |
- (VP8kClip[y + g_off - YUV_RANGE_MIN] >> 5));
- const uint8_t gb = (((VP8kClip[y + g_off - YUV_RANGE_MIN] << 3) & 0xe0) |
- (VP8kClip[y + b_off - YUV_RANGE_MIN] >> 3));
-#ifdef WEBP_SWAP_16BIT_CSP
- rgb[0] = gb;
- rgb[1] = rg;
-#else
- rgb[0] = rg;
- rgb[1] = gb;
-#endif
-}
-
-static WEBP_INLINE void VP8YuvToRgba4444(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const argb) {
- const int r_off = VP8kVToR[v];
- const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
- const int b_off = VP8kUToB[u];
- const uint8_t rg = ((VP8kClip4Bits[y + r_off - YUV_RANGE_MIN] << 4) |
- VP8kClip4Bits[y + g_off - YUV_RANGE_MIN]);
- const uint8_t ba = (VP8kClip4Bits[y + b_off - YUV_RANGE_MIN] << 4) | 0x0f;
-#ifdef WEBP_SWAP_16BIT_CSP
- argb[0] = ba;
- argb[1] = rg;
-#else
- argb[0] = rg;
- argb[1] = ba;
-#endif
-}
-
-#else // Table-free version (slower on x86)
-
-// These constants are 16b fixed-point version of ITU-R BT.601 constants
-#define kYScale 76309 // 1.164 = 255 / 219
-#define kVToR 104597 // 1.596 = 255 / 112 * 0.701
-#define kUToG 25674 // 0.391 = 255 / 112 * 0.886 * 0.114 / 0.587
-#define kVToG 53278 // 0.813 = 255 / 112 * 0.701 * 0.299 / 0.587
-#define kUToB 132201 // 2.018 = 255 / 112 * 0.886
-#define kRCst (-kYScale * 16 - kVToR * 128 + YUV_HALF)
-#define kGCst (-kYScale * 16 + kUToG * 128 + kVToG * 128 + YUV_HALF)
-#define kBCst (-kYScale * 16 - kUToB * 128 + YUV_HALF)
-
-static WEBP_INLINE uint8_t VP8Clip8(int v) {
- return ((v & ~YUV_MASK) == 0) ? (uint8_t)(v >> YUV_FIX)
- : (v < 0) ? 0u : 255u;
-}
-
-static WEBP_INLINE uint8_t VP8ClipN(int v, int N) { // clip to N bits
- return ((v & ~YUV_MASK) == 0) ? (uint8_t)(v >> (YUV_FIX + (8 - N)))
- : (v < 0) ? 0u : (255u >> (8 - N));
-}
-
-static WEBP_INLINE int VP8YUVToR(int y, int v) {
- return kYScale * y + kVToR * v + kRCst;
-}
-
-static WEBP_INLINE int VP8YUVToG(int y, int u, int v) {
- return kYScale * y - kUToG * u - kVToG * v + kGCst;
-}
-
-static WEBP_INLINE int VP8YUVToB(int y, int u) {
- return kYScale * y + kUToB * u + kBCst;
-}
-
-static WEBP_INLINE void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const rgb) {
- rgb[0] = VP8Clip8(VP8YUVToR(y, v));
- rgb[1] = VP8Clip8(VP8YUVToG(y, u, v));
- rgb[2] = VP8Clip8(VP8YUVToB(y, u));
-}
-
-static WEBP_INLINE void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const bgr) {
- bgr[0] = VP8Clip8(VP8YUVToB(y, u));
- bgr[1] = VP8Clip8(VP8YUVToG(y, u, v));
- bgr[2] = VP8Clip8(VP8YUVToR(y, v));
-}
-
-static WEBP_INLINE void VP8YuvToRgb565(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const rgb) {
- const int r = VP8Clip8(VP8YUVToR(y, u));
- const int g = VP8ClipN(VP8YUVToG(y, u, v), 6);
- const int b = VP8ClipN(VP8YUVToB(y, v), 5);
- const uint8_t rg = (r & 0xf8) | (g >> 3);
- const uint8_t gb = (g << 5) | b;
-#ifdef WEBP_SWAP_16BIT_CSP
- rgb[0] = gb;
- rgb[1] = rg;
-#else
- rgb[0] = rg;
- rgb[1] = gb;
-#endif
-}
-
-static WEBP_INLINE void VP8YuvToRgba4444(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const argb) {
- const int r = VP8Clip8(VP8YUVToR(y, u));
- const int g = VP8ClipN(VP8YUVToG(y, u, v), 4);
- const int b = VP8Clip8(VP8YUVToB(y, v));
- const uint8_t rg = (r & 0xf0) | g;
- const uint8_t ba = b | 0x0f; // overwrite the lower 4 bits
-#ifdef WEBP_SWAP_16BIT_CSP
- argb[0] = ba;
- argb[1] = rg;
-#else
- argb[0] = rg;
- argb[1] = ba;
-#endif
-}
-
-#endif // WEBP_YUV_USE_TABLE
-
-static WEBP_INLINE void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const argb) {
- argb[0] = 0xff;
- VP8YuvToRgb(y, u, v, argb + 1);
-}
-
-static WEBP_INLINE void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const bgra) {
- VP8YuvToBgr(y, u, v, bgra);
- bgra[3] = 0xff;
-}
-
-static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
- uint8_t* const rgba) {
- VP8YuvToRgb(y, u, v, rgba);
- rgba[3] = 0xff;
-}
-
-// Must be called before everything, to initialize the tables.
-void VP8YUVInit(void);
-
-//------------------------------------------------------------------------------
-// RGB -> YUV conversion
-
-static WEBP_INLINE int VP8ClipUV(int v) {
- v = (v + (257 << (YUV_FIX + 2 - 1))) >> (YUV_FIX + 2);
- return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255;
-}
-
-#ifndef USE_YUVj
-
-static WEBP_INLINE int VP8RGBToY(int r, int g, int b) {
- const int kRound = (1 << (YUV_FIX - 1)) + (16 << YUV_FIX);
- const int luma = 16839 * r + 33059 * g + 6420 * b;
- return (luma + kRound) >> YUV_FIX; // no need to clip
-}
-
-static WEBP_INLINE int VP8RGBToU(int r, int g, int b) {
- const int u = -9719 * r - 19081 * g + 28800 * b;
- return VP8ClipUV(u);
-}
-
-static WEBP_INLINE int VP8RGBToV(int r, int g, int b) {
- const int v = +28800 * r - 24116 * g - 4684 * b;
- return VP8ClipUV(v);
-}
-
-#else
-
-// This JPEG-YUV colorspace, only for comparison!
-// These are also 16-bit precision coefficients from Rec.601, but with full
-// [0..255] output range.
-static WEBP_INLINE int VP8RGBToY(int r, int g, int b) {
- const int kRound = (1 << (YUV_FIX - 1));
- const int luma = 19595 * r + 38470 * g + 7471 * b;
- return (luma + kRound) >> YUV_FIX; // no need to clip
-}
-
-static WEBP_INLINE int VP8RGBToU(int r, int g, int b) {
- const int u = -11058 * r - 21710 * g + 32768 * b;
- return VP8ClipUV(u);
-}
-
-static WEBP_INLINE int VP8RGBToV(int r, int g, int b) {
- const int v = 32768 * r - 27439 * g - 5329 * b;
- return VP8ClipUV(v);
-}
-
-#endif // USE_YUVj
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_DSP_YUV_H_ */
diff --git a/src/third_party/libwebp/enc/alpha.c b/src/third_party/libwebp/enc/alpha.c
deleted file mode 100644
index 9b6c7e8..0000000
--- a/src/third_party/libwebp/enc/alpha.c
+++ /dev/null
@@ -1,413 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Alpha-plane compression.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#endif
-
-#include "./vp8enci.h"
-#include "../utils/filters.h"
-#include "../utils/quant_levels.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// -----------------------------------------------------------------------------
-// Encodes the given alpha data via specified compression method 'method'.
-// The pre-processing (quantization) is performed if 'quality' is less than 100.
-// For such cases, the encoding is lossy. The valid range is [0, 100] for
-// 'quality' and [0, 1] for 'method':
-// 'method = 0' - No compression;
-// 'method = 1' - Use lossless coder on the alpha plane only
-// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
-// vertical & gradient filters. The prediction mode 4 will try all the
-// prediction modes 0 to 3 and pick the best one.
-// 'effort_level': specifies how much effort must be spent to try and reduce
-// the compressed output size. In range 0 (quick) to 6 (slow).
-//
-// 'output' corresponds to the buffer containing compressed alpha data.
-// This buffer is allocated by this method and caller should call
-// SbMemoryDeallocate(*output) when done.
-// 'output_size' corresponds to size of this compressed alpha buffer.
-//
-// Returns 1 on successfully encoding the alpha and
-// 0 if either:
-// invalid quality or method, or
-// memory allocation for the compressed data fails.
-
-#include "../enc/vp8li.h"
-
-static int EncodeLossless(const uint8_t* const data, int width, int height,
- int effort_level, // in [0..6] range
- VP8BitWriter* const bw,
- WebPAuxStats* const stats) {
- int ok = 0;
- WebPConfig config;
- WebPPicture picture;
- VP8LBitWriter tmp_bw;
-
- WebPPictureInit(&picture);
- picture.width = width;
- picture.height = height;
- picture.use_argb = 1;
- picture.stats = stats;
- if (!WebPPictureAlloc(&picture)) return 0;
-
- // Transfer the alpha values to the green channel.
- {
- int i, j;
- uint32_t* dst = picture.argb;
- const uint8_t* src = data;
- for (j = 0; j < picture.height; ++j) {
- for (i = 0; i < picture.width; ++i) {
- dst[i] = (src[i] << 8) | 0xff000000u;
- }
- src += width;
- dst += picture.argb_stride;
- }
- }
-
- WebPConfigInit(&config);
- config.lossless = 1;
- config.method = effort_level; // impact is very small
- // Set a moderate default quality setting for alpha.
- config.quality = 10.f * effort_level;
- SB_DCHECK(config.quality >= 0 && config.quality <= 100.f);
-
- ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
- ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
- WebPPictureFree(&picture);
- if (ok) {
- const uint8_t* const buffer = VP8LBitWriterFinish(&tmp_bw);
- const size_t buffer_size = VP8LBitWriterNumBytes(&tmp_bw);
- VP8BitWriterAppend(bw, buffer, buffer_size);
- }
- VP8LBitWriterDestroy(&tmp_bw);
- return ok && !bw->error_;
-}
-
-// -----------------------------------------------------------------------------
-
-static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
- int method, int filter, int reduce_levels,
- int effort_level, // in [0..6] range
- uint8_t* const tmp_alpha,
- VP8BitWriter* const bw,
- WebPAuxStats* const stats) {
- int ok = 0;
- const uint8_t* alpha_src;
- WebPFilterFunc filter_func;
- uint8_t header;
- size_t expected_size;
- const size_t data_size = width * height;
-
- SB_DCHECK((uint64_t)data_size == (uint64_t)width * height); // as per spec
- SB_DCHECK(filter >= 0 && filter < WEBP_FILTER_LAST);
- SB_DCHECK(method >= ALPHA_NO_COMPRESSION);
- SB_DCHECK(method <= ALPHA_LOSSLESS_COMPRESSION);
- SB_DCHECK(sizeof(header) == ALPHA_HEADER_LEN);
- // TODO(skal): have a common function and #define's to validate alpha params.
-
- expected_size =
- (method == ALPHA_NO_COMPRESSION) ? (ALPHA_HEADER_LEN + data_size)
- : (data_size >> 5);
- header = method | (filter << 2);
- if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
-
- VP8BitWriterInit(bw, expected_size);
- VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN);
-
- filter_func = WebPFilters[filter];
- if (filter_func != NULL) {
- filter_func(data, width, height, width, tmp_alpha);
- alpha_src = tmp_alpha;
- } else {
- alpha_src = data;
- }
-
- if (method == ALPHA_NO_COMPRESSION) {
- ok = VP8BitWriterAppend(bw, alpha_src, width * height);
- ok = ok && !bw->error_;
- } else {
- ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats);
- VP8BitWriterFinish(bw);
- }
- return ok;
-}
-
-// -----------------------------------------------------------------------------
-
-// TODO(skal): move to dsp/ ?
-static void CopyPlane(const uint8_t* src, int src_stride,
- uint8_t* dst, int dst_stride, int width, int height) {
- while (height-- > 0) {
- SbMemoryCopy(dst, src, width);
- src += src_stride;
- dst += dst_stride;
- }
-}
-
-static int GetNumColors(const uint8_t* data, int width, int height,
- int stride) {
- int j;
- int colors = 0;
- uint8_t color[256] = { 0 };
-
- for (j = 0; j < height; ++j) {
- int i;
- const uint8_t* const p = data + j * stride;
- for (i = 0; i < width; ++i) {
- color[p[i]] = 1;
- }
- }
- for (j = 0; j < 256; ++j) {
- if (color[j] > 0) ++colors;
- }
- return colors;
-}
-
-static int EncodeAlpha(VP8Encoder* const enc,
- int quality, int method, int filter,
- int effort_level,
- uint8_t** const output, size_t* const output_size) {
- const WebPPicture* const pic = enc->pic_;
- const int width = pic->width;
- const int height = pic->height;
-
- uint8_t* quant_alpha = NULL;
- const size_t data_size = width * height;
- uint64_t sse = 0;
- int ok = 1;
- const int reduce_levels = (quality < 100);
-
- // quick sanity checks
- SB_DCHECK((uint64_t)data_size == (uint64_t)width * height); // as per spec
- SB_DCHECK(enc != NULL && pic != NULL && pic->a != NULL);
- SB_DCHECK(output != NULL && output_size != NULL);
- SB_DCHECK(width > 0 && height > 0);
- SB_DCHECK(pic->a_stride >= width);
- SB_DCHECK(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
-
- if (quality < 0 || quality > 100) {
- return 0;
- }
-
- if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
- return 0;
- }
-
- quant_alpha = (uint8_t*)SbMemoryAllocate(data_size);
- if (quant_alpha == NULL) {
- return 0;
- }
-
- // Extract alpha data (width x height) from raw_data (stride x height).
- CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
-
- if (reduce_levels) { // No Quantization required for 'quality = 100'.
- // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
- // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
- // and Quality:]70, 100] -> Levels:]16, 256].
- const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
- : (16 + (quality - 70) * 8);
- ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
- }
-
- if (ok) {
- VP8BitWriter bw;
- int test_filter;
- uint8_t* filtered_alpha = NULL;
- int try_filter_none = (effort_level > 3);
-
- if (filter == WEBP_FILTER_FAST) { // Quick estimate of the best candidate.
- const int kMinColorsForFilterNone = 16;
- const int kMaxColorsForFilterNone = 192;
- const int num_colors = GetNumColors(quant_alpha, width, height, width);
- // For low number of colors, NONE yeilds better compression.
- filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE :
- EstimateBestFilter(quant_alpha, width, height, width);
- // For large number of colors, try FILTER_NONE in addition to the best
- // filter as well.
- if (num_colors > kMaxColorsForFilterNone) {
- try_filter_none = 1;
- }
- }
-
- // Test for WEBP_FILTER_NONE for higher effort levels.
- if (try_filter_none || filter == WEBP_FILTER_NONE) {
- ok = EncodeAlphaInternal(quant_alpha, width, height,
- method, WEBP_FILTER_NONE, reduce_levels,
- effort_level, NULL, &bw, pic->stats);
-
- if (!ok) {
- VP8BitWriterWipeOut(&bw);
- goto End;
- }
- }
- // Stop?
- if (filter == WEBP_FILTER_NONE) {
- goto Ok;
- }
-
- filtered_alpha = (uint8_t*)SbMemoryAllocate(data_size);
- ok = (filtered_alpha != NULL);
- if (!ok) {
- goto End;
- }
-
- // Try the other mode(s).
- {
- WebPAuxStats best_stats;
- size_t best_score = try_filter_none ?
- VP8BitWriterSize(&bw) : (size_t)~0U;
- int wipe_tmp_bw = try_filter_none;
-
- SbMemorySet(&best_stats, 0, sizeof(best_stats)); // prevent spurious warning
- if (pic->stats != NULL) best_stats = *pic->stats;
- for (test_filter =
- try_filter_none ? WEBP_FILTER_HORIZONTAL : WEBP_FILTER_NONE;
- ok && (test_filter <= WEBP_FILTER_GRADIENT);
- ++test_filter) {
- VP8BitWriter tmp_bw;
- if (filter != WEBP_FILTER_BEST && test_filter != filter) {
- continue;
- }
- ok = EncodeAlphaInternal(quant_alpha, width, height,
- method, test_filter, reduce_levels,
- effort_level, filtered_alpha, &tmp_bw,
- pic->stats);
- if (ok) {
- const size_t score = VP8BitWriterSize(&tmp_bw);
- if (score < best_score) {
- // swap bitwriter objects.
- VP8BitWriter tmp = tmp_bw;
- tmp_bw = bw;
- bw = tmp;
- best_score = score;
- if (pic->stats != NULL) best_stats = *pic->stats;
- }
- } else {
- VP8BitWriterWipeOut(&bw);
- }
- if (wipe_tmp_bw) {
- VP8BitWriterWipeOut(&tmp_bw);
- }
- wipe_tmp_bw = 1; // For next filter trial for WEBP_FILTER_BEST.
- }
- if (pic->stats != NULL) *pic->stats = best_stats;
- }
- Ok:
- if (ok) {
- *output_size = VP8BitWriterSize(&bw);
- *output = VP8BitWriterBuf(&bw);
- if (pic->stats != NULL) { // need stats?
- pic->stats->coded_size += (int)(*output_size);
- enc->sse_[3] = sse;
- }
- }
- SbMemoryDeallocate(filtered_alpha);
- }
- End:
- SbMemoryDeallocate(quant_alpha);
- return ok;
-}
-
-
-//------------------------------------------------------------------------------
-// Main calls
-
-static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
- const WebPConfig* config = enc->config_;
- uint8_t* alpha_data = NULL;
- size_t alpha_size = 0;
- const int effort_level = config->method; // maps to [0..6]
- const WEBP_FILTER_TYPE filter =
- (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
- (config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
- WEBP_FILTER_BEST;
- if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
- filter, effort_level, &alpha_data, &alpha_size)) {
- return 0;
- }
- if (alpha_size != (uint32_t)alpha_size) { // Sanity check.
- SbMemoryDeallocate(alpha_data);
- return 0;
- }
- enc->alpha_data_size_ = (uint32_t)alpha_size;
- enc->alpha_data_ = alpha_data;
- (void)dummy;
- return 1;
-}
-
-void VP8EncInitAlpha(VP8Encoder* const enc) {
- enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
- enc->alpha_data_ = NULL;
- enc->alpha_data_size_ = 0;
- if (enc->thread_level_ > 0) {
- WebPWorker* const worker = &enc->alpha_worker_;
- WebPWorkerInit(worker);
- worker->data1 = enc;
- worker->data2 = NULL;
- worker->hook = (WebPWorkerHook)CompressAlphaJob;
- }
-}
-
-int VP8EncStartAlpha(VP8Encoder* const enc) {
- if (enc->has_alpha_) {
- if (enc->thread_level_ > 0) {
- WebPWorker* const worker = &enc->alpha_worker_;
- if (!WebPWorkerReset(worker)) { // Makes sure worker is good to go.
- return 0;
- }
- WebPWorkerLaunch(worker);
- return 1;
- } else {
- return CompressAlphaJob(enc, NULL); // just do the job right away
- }
- }
- return 1;
-}
-
-int VP8EncFinishAlpha(VP8Encoder* const enc) {
- if (enc->has_alpha_) {
- if (enc->thread_level_ > 0) {
- WebPWorker* const worker = &enc->alpha_worker_;
- if (!WebPWorkerSync(worker)) return 0; // error
- }
- }
- return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
-}
-
-int VP8EncDeleteAlpha(VP8Encoder* const enc) {
- int ok = 1;
- if (enc->thread_level_ > 0) {
- WebPWorker* const worker = &enc->alpha_worker_;
- ok = WebPWorkerSync(worker); // finish anything left in flight
- WebPWorkerEnd(worker); // still need to end the worker, even if !ok
- }
- SbMemoryDeallocate(enc->alpha_data_);
- enc->alpha_data_ = NULL;
- enc->alpha_data_size_ = 0;
- enc->has_alpha_ = 0;
- return ok;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/analysis.c b/src/third_party/libwebp/enc/analysis.c
deleted file mode 100644
index f435807..0000000
--- a/src/third_party/libwebp/enc/analysis.c
+++ /dev/null
@@ -1,426 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Macroblock analysis
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#endif
-
-#include "./vp8enci.h"
-#include "./cost.h"
-#include "../utils/utils.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define MAX_ITERS_K_MEANS 6
-
-//------------------------------------------------------------------------------
-// Smooth the segment map by replacing isolated block by the majority of its
-// neighbours.
-
-static void SmoothSegmentMap(VP8Encoder* const enc) {
- int n, x, y;
- const int w = enc->mb_w_;
- const int h = enc->mb_h_;
- const int majority_cnt_3_x_3_grid = 5;
- uint8_t* const tmp = (uint8_t*)WebPSafeMalloc((uint64_t)w * h, sizeof(*tmp));
- SB_DCHECK((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec
-
- if (tmp == NULL) return;
- for (y = 1; y < h - 1; ++y) {
- for (x = 1; x < w - 1; ++x) {
- int cnt[NUM_MB_SEGMENTS] = { 0 };
- const VP8MBInfo* const mb = &enc->mb_info_[x + w * y];
- int majority_seg = mb->segment_;
- // Check the 8 neighbouring segment values.
- cnt[mb[-w - 1].segment_]++; // top-left
- cnt[mb[-w + 0].segment_]++; // top
- cnt[mb[-w + 1].segment_]++; // top-right
- cnt[mb[ - 1].segment_]++; // left
- cnt[mb[ + 1].segment_]++; // right
- cnt[mb[ w - 1].segment_]++; // bottom-left
- cnt[mb[ w + 0].segment_]++; // bottom
- cnt[mb[ w + 1].segment_]++; // bottom-right
- for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
- if (cnt[n] >= majority_cnt_3_x_3_grid) {
- majority_seg = n;
- }
- }
- tmp[x + y * w] = majority_seg;
- }
- }
- for (y = 1; y < h - 1; ++y) {
- for (x = 1; x < w - 1; ++x) {
- VP8MBInfo* const mb = &enc->mb_info_[x + w * y];
- mb->segment_ = tmp[x + y * w];
- }
- }
- SbMemoryDeallocate(tmp);
-}
-
-//------------------------------------------------------------------------------
-// set segment susceptibility alpha_ / beta_
-
-static WEBP_INLINE int clip(int v, int m, int M) {
- return (v < m) ? m : (v > M) ? M : v;
-}
-
-static void SetSegmentAlphas(VP8Encoder* const enc,
- const int centers[NUM_MB_SEGMENTS],
- int mid) {
- const int nb = enc->segment_hdr_.num_segments_;
- int min = centers[0], max = centers[0];
- int n;
-
- if (nb > 1) {
- for (n = 0; n < nb; ++n) {
- if (min > centers[n]) min = centers[n];
- if (max < centers[n]) max = centers[n];
- }
- }
- if (max == min) max = min + 1;
- SB_DCHECK(mid <= max && mid >= min);
- for (n = 0; n < nb; ++n) {
- const int alpha = 255 * (centers[n] - mid) / (max - min);
- const int beta = 255 * (centers[n] - min) / (max - min);
- enc->dqm_[n].alpha_ = clip(alpha, -127, 127);
- enc->dqm_[n].beta_ = clip(beta, 0, 255);
- }
-}
-
-//------------------------------------------------------------------------------
-// Compute susceptibility based on DCT-coeff histograms:
-// the higher, the "easier" the macroblock is to compress.
-
-#define MAX_ALPHA 255 // 8b of precision for susceptibilities.
-#define ALPHA_SCALE (2 * MAX_ALPHA) // scaling factor for alpha.
-#define DEFAULT_ALPHA (-1)
-#define IS_BETTER_ALPHA(alpha, best_alpha) ((alpha) > (best_alpha))
-
-static int FinalAlphaValue(int alpha) {
- alpha = MAX_ALPHA - alpha;
- return clip(alpha, 0, MAX_ALPHA);
-}
-
-static int GetAlpha(const VP8Histogram* const histo) {
- int max_value = 0, last_non_zero = 1;
- int k;
- int alpha;
- for (k = 0; k <= MAX_COEFF_THRESH; ++k) {
- const int value = histo->distribution[k];
- if (value > 0) {
- if (value > max_value) max_value = value;
- last_non_zero = k;
- }
- }
- // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer
- // values which happen to be mostly noise. This leaves the maximum precision
- // for handling the useful small values which contribute most.
- alpha = (max_value > 1) ? ALPHA_SCALE * last_non_zero / max_value : 0;
- return alpha;
-}
-
-static void MergeHistograms(const VP8Histogram* const in,
- VP8Histogram* const out) {
- int i;
- for (i = 0; i <= MAX_COEFF_THRESH; ++i) {
- out->distribution[i] += in->distribution[i];
- }
-}
-
-//------------------------------------------------------------------------------
-// Simplified k-Means, to assign Nb segments based on alpha-histogram
-
-static void AssignSegments(VP8Encoder* const enc,
- const int alphas[MAX_ALPHA + 1]) {
- const int nb = enc->segment_hdr_.num_segments_;
- int centers[NUM_MB_SEGMENTS];
- int weighted_average = 0;
- int map[MAX_ALPHA + 1];
- int a, n, k;
- int min_a = 0, max_a = MAX_ALPHA, range_a;
- // 'int' type is ok for histo, and won't overflow
- int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS];
-
- // bracket the input
- for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {}
- min_a = n;
- for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {}
- max_a = n;
- range_a = max_a - min_a;
-
- // Spread initial centers evenly
- for (n = 1, k = 0; n < 2 * nb; n += 2) {
- centers[k++] = min_a + (n * range_a) / (2 * nb);
- }
-
- for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough
- int total_weight;
- int displaced;
- // Reset stats
- for (n = 0; n < nb; ++n) {
- accum[n] = 0;
- dist_accum[n] = 0;
- }
- // Assign nearest center for each 'a'
- n = 0; // track the nearest center for current 'a'
- for (a = min_a; a <= max_a; ++a) {
- if (alphas[a]) {
- while (n < nb - 1 && abs(a - centers[n + 1]) < abs(a - centers[n])) {
- n++;
- }
- map[a] = n;
- // accumulate contribution into best centroid
- dist_accum[n] += a * alphas[a];
- accum[n] += alphas[a];
- }
- }
- // All point are classified. Move the centroids to the
- // center of their respective cloud.
- displaced = 0;
- weighted_average = 0;
- total_weight = 0;
- for (n = 0; n < nb; ++n) {
- if (accum[n]) {
- const int new_center = (dist_accum[n] + accum[n] / 2) / accum[n];
- displaced += abs(centers[n] - new_center);
- centers[n] = new_center;
- weighted_average += new_center * accum[n];
- total_weight += accum[n];
- }
- }
- weighted_average = (weighted_average + total_weight / 2) / total_weight;
- if (displaced < 5) break; // no need to keep on looping...
- }
-
- // Map each original value to the closest centroid
- for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
- VP8MBInfo* const mb = &enc->mb_info_[n];
- const int alpha = mb->alpha_;
- mb->segment_ = map[alpha];
- mb->alpha_ = centers[map[alpha]]; // for the record.
- }
-
- if (nb > 1) {
- const int smooth = (enc->config_->preprocessing & 1);
- if (smooth) SmoothSegmentMap(enc);
- }
-
- SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas.
-}
-
-//------------------------------------------------------------------------------
-// Macroblock analysis: collect histogram for each mode, deduce the maximal
-// susceptibility and set best modes for this macroblock.
-// Segment assignment is done later.
-
-// Number of modes to inspect for alpha_ evaluation. For high-quality settings
-// (method >= FAST_ANALYSIS_METHOD) we don't need to test all the possible modes
-// during the analysis phase.
-#define FAST_ANALYSIS_METHOD 4 // method above which we do partial analysis
-#define MAX_INTRA16_MODE 2
-#define MAX_INTRA4_MODE 2
-#define MAX_UV_MODE 2
-
-static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) {
- const int max_mode =
- (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_INTRA16_MODE
- : NUM_PRED_MODES;
- int mode;
- int best_alpha = DEFAULT_ALPHA;
- int best_mode = 0;
-
- VP8MakeLuma16Preds(it);
- for (mode = 0; mode < max_mode; ++mode) {
- VP8Histogram histo = { { 0 } };
- int alpha;
-
- VP8CollectHistogram(it->yuv_in_ + Y_OFF,
- it->yuv_p_ + VP8I16ModeOffsets[mode],
- 0, 16, &histo);
- alpha = GetAlpha(&histo);
- if (IS_BETTER_ALPHA(alpha, best_alpha)) {
- best_alpha = alpha;
- best_mode = mode;
- }
- }
- VP8SetIntra16Mode(it, best_mode);
- return best_alpha;
-}
-
-static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
- int best_alpha) {
- uint8_t modes[16];
- const int max_mode =
- (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_INTRA4_MODE
- : NUM_BMODES;
- int i4_alpha;
- VP8Histogram total_histo = { { 0 } };
- int cur_histo = 0;
-
- VP8IteratorStartI4(it);
- do {
- int mode;
- int best_mode_alpha = DEFAULT_ALPHA;
- VP8Histogram histos[2];
- const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
-
- VP8MakeIntra4Preds(it);
- for (mode = 0; mode < max_mode; ++mode) {
- int alpha;
-
- SbMemorySet(&histos[cur_histo], 0, sizeof(histos[cur_histo]));
- VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode],
- 0, 1, &histos[cur_histo]);
- alpha = GetAlpha(&histos[cur_histo]);
- if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) {
- best_mode_alpha = alpha;
- modes[it->i4_] = mode;
- cur_histo ^= 1; // keep track of best histo so far.
- }
- }
- // accumulate best histogram
- MergeHistograms(&histos[cur_histo ^ 1], &total_histo);
- // Note: we reuse the original samples for predictors
- } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF));
-
- i4_alpha = GetAlpha(&total_histo);
- if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) {
- VP8SetIntra4Mode(it, modes);
- best_alpha = i4_alpha;
- }
- return best_alpha;
-}
-
-static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
- int best_alpha = DEFAULT_ALPHA;
- int best_mode = 0;
- const int max_mode =
- (it->enc_->method_ >= FAST_ANALYSIS_METHOD) ? MAX_UV_MODE
- : NUM_PRED_MODES;
- int mode;
- VP8MakeChroma8Preds(it);
- for (mode = 0; mode < max_mode; ++mode) {
- VP8Histogram histo = { { 0 } };
- int alpha;
- VP8CollectHistogram(it->yuv_in_ + U_OFF,
- it->yuv_p_ + VP8UVModeOffsets[mode],
- 16, 16 + 4 + 4, &histo);
- alpha = GetAlpha(&histo);
- if (IS_BETTER_ALPHA(alpha, best_alpha)) {
- best_alpha = alpha;
- best_mode = mode;
- }
- }
- VP8SetIntraUVMode(it, best_mode);
- return best_alpha;
-}
-
-static void MBAnalyze(VP8EncIterator* const it,
- int alphas[MAX_ALPHA + 1],
- int* const alpha, int* const uv_alpha) {
- const VP8Encoder* const enc = it->enc_;
- int best_alpha, best_uv_alpha;
-
- VP8SetIntra16Mode(it, 0); // default: Intra16, DC_PRED
- VP8SetSkip(it, 0); // not skipped
- VP8SetSegment(it, 0); // default segment, spec-wise.
-
- best_alpha = MBAnalyzeBestIntra16Mode(it);
- if (enc->method_ >= 5) {
- // We go and make a fast decision for intra4/intra16.
- // It's usually not a good and definitive pick, but helps seeding the stats
- // about level bit-cost.
- // TODO(skal): improve criterion.
- best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha);
- }
- best_uv_alpha = MBAnalyzeBestUVMode(it);
-
- // Final susceptibility mix
- best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2;
- best_alpha = FinalAlphaValue(best_alpha);
- alphas[best_alpha]++;
- it->mb_->alpha_ = best_alpha; // for later remapping.
-
- // Accumulate for later complexity analysis.
- *alpha += best_alpha; // mixed susceptibility (not just luma)
- *uv_alpha += best_uv_alpha;
-}
-
-static void DefaultMBInfo(VP8MBInfo* const mb) {
- mb->type_ = 1; // I16x16
- mb->uv_mode_ = 0;
- mb->skip_ = 0; // not skipped
- mb->segment_ = 0; // default segment
- mb->alpha_ = 0;
-}
-
-//------------------------------------------------------------------------------
-// Main analysis loop:
-// Collect all susceptibilities for each macroblock and record their
-// distribution in alphas[]. Segments is assigned a-posteriori, based on
-// this histogram.
-// We also pick an intra16 prediction mode, which shouldn't be considered
-// final except for fast-encode settings. We can also pick some intra4 modes
-// and decide intra4/intra16, but that's usually almost always a bad choice at
-// this stage.
-
-static void ResetAllMBInfo(VP8Encoder* const enc) {
- int n;
- for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
- DefaultMBInfo(&enc->mb_info_[n]);
- }
- // Default susceptibilities.
- enc->dqm_[0].alpha_ = 0;
- enc->dqm_[0].beta_ = 0;
- // Note: we can't compute this alpha_ / uv_alpha_.
- WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
-}
-
-int VP8EncAnalyze(VP8Encoder* const enc) {
- int ok = 1;
- const int do_segments =
- enc->config_->emulate_jpeg_size || // We need the complexity evaluation.
- (enc->segment_hdr_.num_segments_ > 1) ||
- (enc->method_ == 0); // for method 0, we need preds_[] to be filled.
- enc->alpha_ = 0;
- enc->uv_alpha_ = 0;
- if (do_segments) {
- int alphas[MAX_ALPHA + 1] = { 0 };
- VP8EncIterator it;
-
- VP8IteratorInit(enc, &it);
- do {
- VP8IteratorImport(&it);
- MBAnalyze(&it, alphas, &enc->alpha_, &enc->uv_alpha_);
- ok = VP8IteratorProgress(&it, 20);
- // Let's pretend we have perfect lossless reconstruction.
- } while (ok && VP8IteratorNext(&it, it.yuv_in_));
- enc->alpha_ /= enc->mb_w_ * enc->mb_h_;
- enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
- if (ok) AssignSegments(enc, alphas);
- } else { // Use only one default segment.
- ResetAllMBInfo(enc);
- }
- return ok;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/backward_references.c b/src/third_party/libwebp/enc/backward_references.c
deleted file mode 100644
index 3004f9d..0000000
--- a/src/third_party/libwebp/enc/backward_references.c
+++ /dev/null
@@ -1,899 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Author: Jyrki Alakuijala (jyrki@google.com)
-//
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <math.h>
-#include <stdio.h>
-#endif
-
-#include "./backward_references.h"
-#include "./histogram.h"
-#include "../dsp/lossless.h"
-#include "../utils/color_cache.h"
-#include "../utils/utils.h"
-
-#define VALUES_IN_BYTE 256
-
-#define HASH_BITS 18
-#define HASH_SIZE (1 << HASH_BITS)
-#define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL)
-
-// 1M window (4M bytes) minus 120 special codes for short distances.
-#define WINDOW_SIZE ((1 << 20) - 120)
-
-// Bounds for the match length.
-#define MIN_LENGTH 2
-#define MAX_LENGTH 4096
-
-typedef struct {
- // Stores the most recently added position with the given hash value.
- int32_t hash_to_first_index_[HASH_SIZE];
- // chain_[pos] stores the previous position with the same hash value
- // for every pixel in the image.
- int32_t* chain_;
-} HashChain;
-
-// -----------------------------------------------------------------------------
-
-static const uint8_t plane_to_code_lut[128] = {
- 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,
- 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,
- 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,
- 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,
- 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,
- 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,
- 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,
- 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117
-};
-
-static int DistanceToPlaneCode(int xsize, int dist) {
- const int yoffset = dist / xsize;
- const int xoffset = dist - yoffset * xsize;
- if (xoffset <= 8 && yoffset < 8) {
- return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
- } else if (xoffset > xsize - 8 && yoffset < 7) {
- return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
- }
- return dist + 120;
-}
-
-static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
- const uint32_t* const array2,
- const int max_limit) {
- int match_len = 0;
- while (match_len < max_limit && array1[match_len] == array2[match_len]) {
- ++match_len;
- }
- return match_len;
-}
-
-// -----------------------------------------------------------------------------
-// VP8LBackwardRefs
-
-void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) {
- if (refs != NULL) {
- refs->refs = NULL;
- refs->size = 0;
- refs->max_size = 0;
- }
-}
-
-void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {
- if (refs != NULL) {
- SbMemoryDeallocate(refs->refs);
- VP8LInitBackwardRefs(refs);
- }
-}
-
-int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size) {
- SB_DCHECK(refs != NULL);
- refs->size = 0;
- refs->max_size = 0;
- refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size,
- sizeof(*refs->refs));
- if (refs->refs == NULL) return 0;
- refs->max_size = max_size;
- return 1;
-}
-
-// -----------------------------------------------------------------------------
-// Hash chains
-
-static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) {
- uint64_t key = ((uint64_t)(argb[1]) << 32) | argb[0];
- key = (key * HASH_MULTIPLIER) >> (64 - HASH_BITS);
- return key;
-}
-
-static int HashChainInit(HashChain* const p, int size) {
- int i;
- p->chain_ = (int*)WebPSafeMalloc((uint64_t)size, sizeof(*p->chain_));
- if (p->chain_ == NULL) {
- return 0;
- }
- for (i = 0; i < size; ++i) {
- p->chain_[i] = -1;
- }
- for (i = 0; i < HASH_SIZE; ++i) {
- p->hash_to_first_index_[i] = -1;
- }
- return 1;
-}
-
-static void HashChainDelete(HashChain* const p) {
- if (p != NULL) {
- SbMemoryDeallocate(p->chain_);
- SbMemoryDeallocate(p);
- }
-}
-
-// Insertion of two pixels at a time.
-static void HashChainInsert(HashChain* const p,
- const uint32_t* const argb, int pos) {
- const uint64_t hash_code = GetPixPairHash64(argb);
- p->chain_[pos] = p->hash_to_first_index_[hash_code];
- p->hash_to_first_index_[hash_code] = pos;
-}
-
-static void GetParamsForHashChainFindCopy(int quality, int xsize,
- int cache_bits, int* window_size,
- int* iter_pos, int* iter_limit) {
- const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4);
- const int iter_neg = -iter_mult * (quality >> 1);
- // Limit the backward-ref window size for lower qualities.
- const int max_window_size = (quality > 50) ? WINDOW_SIZE
- : (quality > 25) ? (xsize << 8)
- : (xsize << 4);
- SB_DCHECK(xsize > 0);
- *window_size = (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE
- : max_window_size;
- *iter_pos = 8 + (quality >> 3);
- // For lower entropy images, the rigourous search loop in HashChainFindCopy
- // can be relaxed.
- *iter_limit = (cache_bits > 0) ? iter_neg : iter_neg / 2;
-}
-
-static int HashChainFindCopy(const HashChain* const p,
- int base_position, int xsize_signed,
- const uint32_t* const argb, int maxlen,
- int window_size, int iter_pos, int iter_limit,
- int* const distance_ptr,
- int* const length_ptr) {
- const uint32_t* const argb_start = argb + base_position;
- uint64_t best_val = 0;
- uint32_t best_length = 1;
- uint32_t best_distance = 0;
- const uint32_t xsize = (uint32_t)xsize_signed;
- const int min_pos =
- (base_position > window_size) ? base_position - window_size : 0;
- int pos;
- SB_DCHECK(xsize > 0);
- for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)];
- pos >= min_pos;
- pos = p->chain_[pos]) {
- uint64_t val;
- uint32_t curr_length;
- uint32_t distance;
- if (iter_pos < 0) {
- if (iter_pos < iter_limit || best_val >= 0xff0000) {
- break;
- }
- }
- --iter_pos;
- if (argb[pos + best_length - 1] != argb_start[best_length - 1]) {
- continue;
- }
- curr_length = FindMatchLength(argb + pos, argb_start, maxlen);
- if (curr_length < best_length) {
- continue;
- }
- distance = (uint32_t)(base_position - pos);
- val = curr_length << 16;
- // Favoring 2d locality here gives savings for certain images.
- if (distance < 9 * xsize) {
- const uint32_t y = distance / xsize;
- uint32_t x = distance % xsize;
- if (x > (xsize >> 1)) {
- x = xsize - x;
- }
- if (x <= 7) {
- val += 9 * 9 + 9 * 9;
- val -= y * y + x * x;
- }
- }
- if (best_val < val) {
- best_val = val;
- best_length = curr_length;
- best_distance = distance;
- if (curr_length >= MAX_LENGTH) {
- break;
- }
- if ((best_distance == 1 || distance == xsize) &&
- best_length >= 128) {
- break;
- }
- }
- }
- *distance_ptr = (int)best_distance;
- *length_ptr = best_length;
- return (best_length >= MIN_LENGTH);
-}
-
-static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) {
- int size = refs->size;
- while (length >= MAX_LENGTH) {
- refs->refs[size++] = PixOrCopyCreateCopy(1, MAX_LENGTH);
- length -= MAX_LENGTH;
- }
- if (length > 0) {
- refs->refs[size++] = PixOrCopyCreateCopy(1, length);
- }
- refs->size = size;
-}
-
-static void BackwardReferencesRle(int xsize, int ysize,
- const uint32_t* const argb,
- VP8LBackwardRefs* const refs) {
- const int pix_count = xsize * ysize;
- int match_len = 0;
- int i;
- refs->size = 0;
- PushBackCopy(refs, match_len); // i=0 case
- refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[0]);
- for (i = 1; i < pix_count; ++i) {
- if (argb[i] == argb[i - 1]) {
- ++match_len;
- } else {
- PushBackCopy(refs, match_len);
- match_len = 0;
- refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[i]);
- }
- }
- PushBackCopy(refs, match_len);
-}
-
-static int BackwardReferencesHashChain(int xsize, int ysize,
- const uint32_t* const argb,
- int cache_bits, int quality,
- VP8LBackwardRefs* const refs) {
- int i;
- int ok = 0;
- int cc_init = 0;
- const int use_color_cache = (cache_bits > 0);
- const int pix_count = xsize * ysize;
- HashChain* const hash_chain = (HashChain*)SbMemoryAllocate(sizeof(*hash_chain));
- VP8LColorCache hashers;
- int window_size = WINDOW_SIZE;
- int iter_pos = 1;
- int iter_limit = -1;
-
- if (hash_chain == NULL) return 0;
- if (use_color_cache) {
- cc_init = VP8LColorCacheInit(&hashers, cache_bits);
- if (!cc_init) goto Error;
- }
-
- if (!HashChainInit(hash_chain, pix_count)) goto Error;
-
- refs->size = 0;
- GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
- &window_size, &iter_pos, &iter_limit);
- for (i = 0; i < pix_count; ) {
- // Alternative#1: Code the pixels starting at 'i' using backward reference.
- int offset = 0;
- int len = 0;
- if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1].
- int maxlen = pix_count - i;
- if (maxlen > MAX_LENGTH) {
- maxlen = MAX_LENGTH;
- }
- HashChainFindCopy(hash_chain, i, xsize, argb, maxlen,
- window_size, iter_pos, iter_limit,
- &offset, &len);
- }
- if (len >= MIN_LENGTH) {
- // Alternative#2: Insert the pixel at 'i' as literal, and code the
- // pixels starting at 'i + 1' using backward reference.
- int offset2 = 0;
- int len2 = 0;
- int k;
- HashChainInsert(hash_chain, &argb[i], i);
- if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2].
- int maxlen = pix_count - (i + 1);
- if (maxlen > MAX_LENGTH) {
- maxlen = MAX_LENGTH;
- }
- HashChainFindCopy(hash_chain, i + 1, xsize, argb, maxlen,
- window_size, iter_pos, iter_limit,
- &offset2, &len2);
- if (len2 > len + 1) {
- const uint32_t pixel = argb[i];
- // Alternative#2 is a better match. So push pixel at 'i' as literal.
- if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
- const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
- refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
- } else {
- refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
- }
- ++refs->size;
- if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
- i++; // Backward reference to be done for next pixel.
- len = len2;
- offset = offset2;
- }
- }
- if (len >= MAX_LENGTH) {
- len = MAX_LENGTH - 1;
- }
- refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len);
- if (use_color_cache) {
- for (k = 0; k < len; ++k) {
- VP8LColorCacheInsert(&hashers, argb[i + k]);
- }
- }
- // Add to the hash_chain (but cannot add the last pixel).
- {
- const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
- for (k = 1; k < last; ++k) {
- HashChainInsert(hash_chain, &argb[i + k], i + k);
- }
- }
- i += len;
- } else {
- const uint32_t pixel = argb[i];
- if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) {
- // push pixel as a PixOrCopyCreateCacheIdx pixel
- const int ix = VP8LColorCacheGetIndex(&hashers, pixel);
- refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix);
- } else {
- refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel);
- }
- ++refs->size;
- if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel);
- if (i + 1 < pix_count) {
- HashChainInsert(hash_chain, &argb[i], i);
- }
- ++i;
- }
- }
- ok = 1;
-Error:
- if (cc_init) VP8LColorCacheClear(&hashers);
- HashChainDelete(hash_chain);
- return ok;
-}
-
-// -----------------------------------------------------------------------------
-
-typedef struct {
- double alpha_[VALUES_IN_BYTE];
- double red_[VALUES_IN_BYTE];
- double literal_[PIX_OR_COPY_CODES_MAX];
- double blue_[VALUES_IN_BYTE];
- double distance_[NUM_DISTANCE_CODES];
-} CostModel;
-
-static int BackwardReferencesTraceBackwards(
- int xsize, int ysize, int recursive_cost_model,
- const uint32_t* const argb, int quality, int cache_bits,
- VP8LBackwardRefs* const refs);
-
-static void ConvertPopulationCountTableToBitEstimates(
- int num_symbols, const int population_counts[], double output[]) {
- int sum = 0;
- int nonzeros = 0;
- int i;
- for (i = 0; i < num_symbols; ++i) {
- sum += population_counts[i];
- if (population_counts[i] > 0) {
- ++nonzeros;
- }
- }
- if (nonzeros <= 1) {
- SbMemorySet(output, 0, num_symbols * sizeof(*output));
- } else {
- const double logsum = VP8LFastLog2(sum);
- for (i = 0; i < num_symbols; ++i) {
- output[i] = logsum - VP8LFastLog2(population_counts[i]);
- }
- }
-}
-
-static int CostModelBuild(CostModel* const m, int xsize, int ysize,
- int recursion_level, const uint32_t* const argb,
- int quality, int cache_bits) {
- int ok = 0;
- VP8LHistogram histo;
- VP8LBackwardRefs refs;
-
- if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error;
-
- if (recursion_level > 0) {
- if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1,
- argb, quality, cache_bits, &refs)) {
- goto Error;
- }
- } else {
- if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality,
- &refs)) {
- goto Error;
- }
- }
- VP8LHistogramCreate(&histo, &refs, cache_bits);
- ConvertPopulationCountTableToBitEstimates(
- VP8LHistogramNumCodes(&histo), histo.literal_, m->literal_);
- ConvertPopulationCountTableToBitEstimates(
- VALUES_IN_BYTE, histo.red_, m->red_);
- ConvertPopulationCountTableToBitEstimates(
- VALUES_IN_BYTE, histo.blue_, m->blue_);
- ConvertPopulationCountTableToBitEstimates(
- VALUES_IN_BYTE, histo.alpha_, m->alpha_);
- ConvertPopulationCountTableToBitEstimates(
- NUM_DISTANCE_CODES, histo.distance_, m->distance_);
- ok = 1;
-
- Error:
- VP8LClearBackwardRefs(&refs);
- return ok;
-}
-
-static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
- return m->alpha_[v >> 24] +
- m->red_[(v >> 16) & 0xff] +
- m->literal_[(v >> 8) & 0xff] +
- m->blue_[v & 0xff];
-}
-
-static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
- const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
- return m->literal_[literal_idx];
-}
-
-static WEBP_INLINE double GetLengthCost(const CostModel* const m,
- uint32_t length) {
- int code, extra_bits_count, extra_bits_value;
- PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value);
- return m->literal_[VALUES_IN_BYTE + code] + extra_bits_count;
-}
-
-static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
- uint32_t distance) {
- int code, extra_bits_count, extra_bits_value;
- PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value);
- return m->distance_[code] + extra_bits_count;
-}
-
-static int BackwardReferencesHashChainDistanceOnly(
- int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb,
- int quality, int cache_bits, uint32_t* const dist_array) {
- int i;
- int ok = 0;
- int cc_init = 0;
- const int pix_count = xsize * ysize;
- const int use_color_cache = (cache_bits > 0);
- float* const cost =
- (float*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost));
- CostModel* cost_model = (CostModel*)SbMemoryAllocate(sizeof(*cost_model));
- HashChain* hash_chain = (HashChain*)SbMemoryAllocate(sizeof(*hash_chain));
- VP8LColorCache hashers;
- const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68;
- const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82;
- const int min_distance_code = 2; // TODO(vikasa): tune as function of quality
- int window_size = WINDOW_SIZE;
- int iter_pos = 1;
- int iter_limit = -1;
-
- if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error;
-
- if (!HashChainInit(hash_chain, pix_count)) goto Error;
-
- if (use_color_cache) {
- cc_init = VP8LColorCacheInit(&hashers, cache_bits);
- if (!cc_init) goto Error;
- }
-
- if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb,
- quality, cache_bits)) {
- goto Error;
- }
-
- for (i = 0; i < pix_count; ++i) cost[i] = 1e38f;
-
- // We loop one pixel at a time, but store all currently best points to
- // non-processed locations from this point.
- dist_array[0] = 0;
- GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
- &window_size, &iter_pos, &iter_limit);
- for (i = 0; i < pix_count; ++i) {
- double prev_cost = 0.0;
- int shortmax;
- if (i > 0) {
- prev_cost = cost[i - 1];
- }
- for (shortmax = 0; shortmax < 2; ++shortmax) {
- int offset = 0;
- int len = 0;
- if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1].
- int maxlen = shortmax ? 2 : MAX_LENGTH;
- if (maxlen > pix_count - i) {
- maxlen = pix_count - i;
- }
- HashChainFindCopy(hash_chain, i, xsize, argb, maxlen,
- window_size, iter_pos, iter_limit,
- &offset, &len);
- }
- if (len >= MIN_LENGTH) {
- const int code = DistanceToPlaneCode(xsize, offset);
- const double distance_cost =
- prev_cost + GetDistanceCost(cost_model, code);
- int k;
- for (k = 1; k < len; ++k) {
- const double cost_val = distance_cost + GetLengthCost(cost_model, k);
- if (cost[i + k] > cost_val) {
- cost[i + k] = (float)cost_val;
- dist_array[i + k] = k + 1;
- }
- }
- // This if is for speedup only. It roughly doubles the speed, and
- // makes compression worse by .1 %.
- if (len >= 128 && code <= min_distance_code) {
- // Long copy for short distances, let's skip the middle
- // lookups for better copies.
- // 1) insert the hashes.
- if (use_color_cache) {
- for (k = 0; k < len; ++k) {
- VP8LColorCacheInsert(&hashers, argb[i + k]);
- }
- }
- // 2) Add to the hash_chain (but cannot add the last pixel)
- {
- const int last = (len + i < pix_count - 1) ? len + i
- : pix_count - 1;
- for (k = i; k < last; ++k) {
- HashChainInsert(hash_chain, &argb[k], k);
- }
- }
- // 3) jump.
- i += len - 1; // for loop does ++i, thus -1 here.
- goto next_symbol;
- }
- }
- }
- if (i < pix_count - 1) {
- HashChainInsert(hash_chain, &argb[i], i);
- }
- {
- // inserting a literal pixel
- double cost_val = prev_cost;
- if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
- const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]);
- cost_val += GetCacheCost(cost_model, ix) * mul0;
- } else {
- cost_val += GetLiteralCost(cost_model, argb[i]) * mul1;
- }
- if (cost[i] > cost_val) {
- cost[i] = (float)cost_val;
- dist_array[i] = 1; // only one is inserted.
- }
- if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
- }
- next_symbol: ;
- }
- // Last pixel still to do, it can only be a single step if not reached
- // through cheaper means already.
- ok = 1;
-Error:
- if (cc_init) VP8LColorCacheClear(&hashers);
- HashChainDelete(hash_chain);
- SbMemoryDeallocate(cost_model);
- SbMemoryDeallocate(cost);
- return ok;
-}
-
-// We pack the path at the end of *dist_array and return
-// a pointer to this part of the array. Example:
-// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]
-static void TraceBackwards(uint32_t* const dist_array,
- int dist_array_size,
- uint32_t** const chosen_path,
- int* const chosen_path_size) {
- uint32_t* path = dist_array + dist_array_size;
- uint32_t* cur = dist_array + dist_array_size - 1;
- while (cur >= dist_array) {
- const int k = *cur;
- --path;
- *path = k;
- cur -= k;
- }
- *chosen_path = path;
- *chosen_path_size = (int)(dist_array + dist_array_size - path);
-}
-
-static int BackwardReferencesHashChainFollowChosenPath(
- int xsize, int ysize, const uint32_t* const argb,
- int quality, int cache_bits,
- const uint32_t* const chosen_path, int chosen_path_size,
- VP8LBackwardRefs* const refs) {
- const int pix_count = xsize * ysize;
- const int use_color_cache = (cache_bits > 0);
- int size = 0;
- int i = 0;
- int k;
- int ix;
- int ok = 0;
- int cc_init = 0;
- int window_size = WINDOW_SIZE;
- int iter_pos = 1;
- int iter_limit = -1;
- HashChain* hash_chain = (HashChain*)SbMemoryAllocate(sizeof(*hash_chain));
- VP8LColorCache hashers;
-
- if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) {
- goto Error;
- }
- if (use_color_cache) {
- cc_init = VP8LColorCacheInit(&hashers, cache_bits);
- if (!cc_init) goto Error;
- }
-
- refs->size = 0;
- GetParamsForHashChainFindCopy(quality, xsize, cache_bits,
- &window_size, &iter_pos, &iter_limit);
- for (ix = 0; ix < chosen_path_size; ++ix, ++size) {
- int offset = 0;
- int len = 0;
- int maxlen = chosen_path[ix];
- if (maxlen != 1) {
- HashChainFindCopy(hash_chain, i, xsize, argb, maxlen,
- window_size, iter_pos, iter_limit,
- &offset, &len);
- SB_DCHECK(len == maxlen);
- refs->refs[size] = PixOrCopyCreateCopy(offset, len);
- if (use_color_cache) {
- for (k = 0; k < len; ++k) {
- VP8LColorCacheInsert(&hashers, argb[i + k]);
- }
- }
- {
- const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i;
- for (k = 0; k < last; ++k) {
- HashChainInsert(hash_chain, &argb[i + k], i + k);
- }
- }
- i += len;
- } else {
- if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) {
- // push pixel as a color cache index
- const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]);
- refs->refs[size] = PixOrCopyCreateCacheIdx(idx);
- } else {
- refs->refs[size] = PixOrCopyCreateLiteral(argb[i]);
- }
- if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
- if (i + 1 < pix_count) {
- HashChainInsert(hash_chain, &argb[i], i);
- }
- ++i;
- }
- }
- SB_DCHECK(size <= refs->max_size);
- refs->size = size;
- ok = 1;
-Error:
- if (cc_init) VP8LColorCacheClear(&hashers);
- HashChainDelete(hash_chain);
- return ok;
-}
-
-// Returns 1 on success.
-static int BackwardReferencesTraceBackwards(int xsize, int ysize,
- int recursive_cost_model,
- const uint32_t* const argb,
- int quality, int cache_bits,
- VP8LBackwardRefs* const refs) {
- int ok = 0;
- const int dist_array_size = xsize * ysize;
- uint32_t* chosen_path = NULL;
- int chosen_path_size = 0;
- uint32_t* dist_array =
- (uint32_t*)WebPSafeMalloc((uint64_t)dist_array_size, sizeof(*dist_array));
-
- if (dist_array == NULL) goto Error;
-
- if (!BackwardReferencesHashChainDistanceOnly(
- xsize, ysize, recursive_cost_model, argb, quality, cache_bits,
- dist_array)) {
- goto Error;
- }
- TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
- if (!BackwardReferencesHashChainFollowChosenPath(
- xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size,
- refs)) {
- goto Error;
- }
- ok = 1;
- Error:
- SbMemoryDeallocate(dist_array);
- return ok;
-}
-
-static void BackwardReferences2DLocality(int xsize,
- VP8LBackwardRefs* const refs) {
- int i;
- for (i = 0; i < refs->size; ++i) {
- if (PixOrCopyIsCopy(&refs->refs[i])) {
- const int dist = refs->refs[i].argb_or_distance;
- const int transformed_dist = DistanceToPlaneCode(xsize, dist);
- refs->refs[i].argb_or_distance = transformed_dist;
- }
- }
-}
-
-int VP8LGetBackwardReferences(int width, int height,
- const uint32_t* const argb,
- int quality, int cache_bits, int use_2d_locality,
- VP8LBackwardRefs* const best) {
- int ok = 0;
- int lz77_is_useful;
- VP8LBackwardRefs refs_rle, refs_lz77;
- const int num_pix = width * height;
-
- VP8LBackwardRefsAlloc(&refs_rle, num_pix);
- VP8LBackwardRefsAlloc(&refs_lz77, num_pix);
- VP8LInitBackwardRefs(best);
- if (refs_rle.refs == NULL || refs_lz77.refs == NULL) {
- Error1:
- VP8LClearBackwardRefs(&refs_rle);
- VP8LClearBackwardRefs(&refs_lz77);
- goto End;
- }
-
- if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality,
- &refs_lz77)) {
- goto End;
- }
- // Backward Reference using RLE only.
- BackwardReferencesRle(width, height, argb, &refs_rle);
-
- {
- double bit_cost_lz77, bit_cost_rle;
- VP8LHistogram* const histo = (VP8LHistogram*)SbMemoryAllocate(sizeof(*histo));
- if (histo == NULL) goto Error1;
- // Evaluate lz77 coding
- VP8LHistogramCreate(histo, &refs_lz77, cache_bits);
- bit_cost_lz77 = VP8LHistogramEstimateBits(histo);
- // Evaluate RLE coding
- VP8LHistogramCreate(histo, &refs_rle, cache_bits);
- bit_cost_rle = VP8LHistogramEstimateBits(histo);
- // Decide if LZ77 is useful.
- lz77_is_useful = (bit_cost_lz77 < bit_cost_rle);
- SbMemoryDeallocate(histo);
- }
-
- // Choose appropriate backward reference.
- if (lz77_is_useful) {
- // TraceBackwards is costly. Don't execute it at lower quality (q <= 10).
- const int try_lz77_trace_backwards = (quality > 10);
- *best = refs_lz77; // default guess: lz77 is better
- VP8LClearBackwardRefs(&refs_rle);
- if (try_lz77_trace_backwards) {
- // Set recursion level for large images using a color cache.
- const int recursion_level =
- (num_pix < 320 * 200) && (cache_bits > 0) ? 1 : 0;
- VP8LBackwardRefs refs_trace;
- if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) {
- goto End;
- }
- if (BackwardReferencesTraceBackwards(width, height, recursion_level, argb,
- quality, cache_bits, &refs_trace)) {
- VP8LClearBackwardRefs(&refs_lz77);
- *best = refs_trace;
- }
- }
- } else {
- VP8LClearBackwardRefs(&refs_lz77);
- *best = refs_rle;
- }
-
- if (use_2d_locality) BackwardReferences2DLocality(width, best);
-
- ok = 1;
-
- End:
- if (!ok) {
- VP8LClearBackwardRefs(best);
- }
- return ok;
-}
-
-// Returns 1 on success.
-static int ComputeCacheHistogram(const uint32_t* const argb,
- int xsize, int ysize,
- const VP8LBackwardRefs* const refs,
- int cache_bits,
- VP8LHistogram* const histo) {
- int pixel_index = 0;
- int i;
- uint32_t k;
- VP8LColorCache hashers;
- const int use_color_cache = (cache_bits > 0);
- int cc_init = 0;
-
- if (use_color_cache) {
- cc_init = VP8LColorCacheInit(&hashers, cache_bits);
- if (!cc_init) return 0;
- }
-
- for (i = 0; i < refs->size; ++i) {
- const PixOrCopy* const v = &refs->refs[i];
- if (PixOrCopyIsLiteral(v)) {
- if (use_color_cache &&
- VP8LColorCacheContains(&hashers, argb[pixel_index])) {
- // push pixel as a cache index
- const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
- const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
- VP8LHistogramAddSinglePixOrCopy(histo, &token);
- } else {
- VP8LHistogramAddSinglePixOrCopy(histo, v);
- }
- } else {
- VP8LHistogramAddSinglePixOrCopy(histo, v);
- }
- if (use_color_cache) {
- for (k = 0; k < PixOrCopyLength(v); ++k) {
- VP8LColorCacheInsert(&hashers, argb[pixel_index + k]);
- }
- }
- pixel_index += PixOrCopyLength(v);
- }
- SB_DCHECK(pixel_index == xsize * ysize);
- (void)xsize; // xsize is not used in non-debug compilations otherwise.
- (void)ysize; // ysize is not used in non-debug compilations otherwise.
- if (cc_init) VP8LColorCacheClear(&hashers);
- return 1;
-}
-
-// Returns how many bits are to be used for a color cache.
-int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
- int xsize, int ysize,
- int* const best_cache_bits) {
- int ok = 0;
- int cache_bits;
- double lowest_entropy = 1e99;
- VP8LBackwardRefs refs;
- static const double kSmallPenaltyForLargeCache = 4.0;
- static const int quality = 30;
- if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) ||
- !BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) {
- goto Error;
- }
- for (cache_bits = 0; cache_bits <= MAX_COLOR_CACHE_BITS; ++cache_bits) {
- double cur_entropy;
- VP8LHistogram histo;
- VP8LHistogramInit(&histo, cache_bits);
- ComputeCacheHistogram(argb, xsize, ysize, &refs, cache_bits, &histo);
- cur_entropy = VP8LHistogramEstimateBits(&histo) +
- kSmallPenaltyForLargeCache * cache_bits;
- if (cache_bits == 0 || cur_entropy < lowest_entropy) {
- *best_cache_bits = cache_bits;
- lowest_entropy = cur_entropy;
- }
- }
- ok = 1;
- Error:
- VP8LClearBackwardRefs(&refs);
- return ok;
-}
diff --git a/src/third_party/libwebp/enc/backward_references.h b/src/third_party/libwebp/enc/backward_references.h
deleted file mode 100644
index e13441c..0000000
--- a/src/third_party/libwebp/enc/backward_references.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Author: Jyrki Alakuijala (jyrki@google.com)
-//
-
-#ifndef WEBP_ENC_BACKWARD_REFERENCES_H_
-#define WEBP_ENC_BACKWARD_REFERENCES_H_
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#endif
-#include "../webp/types.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// The spec allows 11, we use 9 bits to reduce memory consumption in encoding.
-// Having 9 instead of 11 only removes about 0.25 % of compression density.
-#define MAX_COLOR_CACHE_BITS 9
-
-// Max ever number of codes we'll use:
-#define PIX_OR_COPY_CODES_MAX \
- (NUM_LITERAL_CODES + NUM_LENGTH_CODES + (1 << MAX_COLOR_CACHE_BITS))
-
-// -----------------------------------------------------------------------------
-// PrefixEncode()
-
-// use GNU builtins where available.
-#if defined(__GNUC__) && \
- ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4)
-static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
- SB_DCHECK(n != 0);
- return 31 ^ __builtin_clz(n);
-}
-#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
-#include <intrin.h>
-#pragma intrinsic(_BitScanReverse)
-
-static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
- unsigned long first_set_bit;
- SB_DCHECK(n != 0);
- _BitScanReverse(&first_set_bit, n);
- return first_set_bit;
-}
-#else
-// Returns (int)floor(log2(n)). n must be > 0.
-static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
- int log = 0;
- uint32_t value = n;
- int i;
-
- SB_DCHECK(n != 0);
- for (i = 4; i >= 0; --i) {
- const int shift = (1 << i);
- const uint32_t x = value >> shift;
- if (x != 0) {
- value = x;
- log += shift;
- }
- }
- return log;
-}
-#endif
-
-static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) {
- const int log_floor = BitsLog2Floor(n);
- if (n == (n & ~(n - 1))) // zero or a power of two.
- return log_floor;
- else
- return log_floor + 1;
-}
-
-// Splitting of distance and length codes into prefixes and
-// extra bits. The prefixes are encoded with an entropy code
-// while the extra bits are stored just as normal bits.
-static WEBP_INLINE void PrefixEncode(int distance, int* const code,
- int* const extra_bits_count,
- int* const extra_bits_value) {
- if (distance > 2) { // Collect the two most significant bits.
- const int highest_bit = BitsLog2Floor(--distance);
- const int second_highest_bit = (distance >> (highest_bit - 1)) & 1;
- *extra_bits_count = highest_bit - 1;
- *extra_bits_value = distance & ((1 << *extra_bits_count) - 1);
- *code = 2 * highest_bit + second_highest_bit;
- } else {
- *extra_bits_count = 0;
- *extra_bits_value = 0;
- *code = (distance == 2) ? 1 : 0;
- }
-}
-
-// -----------------------------------------------------------------------------
-// PixOrCopy
-
-enum Mode {
- kLiteral,
- kCacheIdx,
- kCopy,
- kNone
-};
-
-typedef struct {
- // mode as uint8_t to make the memory layout to be exactly 8 bytes.
- uint8_t mode;
- uint16_t len;
- uint32_t argb_or_distance;
-} PixOrCopy;
-
-static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance,
- uint16_t len) {
- PixOrCopy retval;
- retval.mode = kCopy;
- retval.argb_or_distance = distance;
- retval.len = len;
- return retval;
-}
-
-static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) {
- PixOrCopy retval;
- SB_DCHECK(idx >= 0);
- SB_DCHECK(idx < (1 << MAX_COLOR_CACHE_BITS));
- retval.mode = kCacheIdx;
- retval.argb_or_distance = idx;
- retval.len = 1;
- return retval;
-}
-
-static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) {
- PixOrCopy retval;
- retval.mode = kLiteral;
- retval.argb_or_distance = argb;
- retval.len = 1;
- return retval;
-}
-
-static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) {
- return (p->mode == kLiteral);
-}
-
-static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) {
- return (p->mode == kCacheIdx);
-}
-
-static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) {
- return (p->mode == kCopy);
-}
-
-static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p,
- int component) {
- SB_DCHECK(p->mode == kLiteral);
- return (p->argb_or_distance >> (component * 8)) & 0xff;
-}
-
-static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) {
- return p->len;
-}
-
-static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy* const p) {
- SB_DCHECK(p->mode == kLiteral);
- return p->argb_or_distance;
-}
-
-static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) {
- SB_DCHECK(p->mode == kCacheIdx);
- SB_DCHECK(p->argb_or_distance < (1U << MAX_COLOR_CACHE_BITS));
- return p->argb_or_distance;
-}
-
-static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) {
- SB_DCHECK(p->mode == kCopy);
- return p->argb_or_distance;
-}
-
-// -----------------------------------------------------------------------------
-// VP8LBackwardRefs
-
-typedef struct {
- PixOrCopy* refs;
- int size; // currently used
- int max_size; // maximum capacity
-} VP8LBackwardRefs;
-
-// Initialize the object. Must be called first. 'refs' can be NULL.
-void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs);
-
-// Release memory and re-initialize the object. 'refs' can be NULL.
-void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
-
-// Allocate 'max_size' references. Returns false in case of memory error.
-int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size);
-
-// -----------------------------------------------------------------------------
-// Main entry points
-
-// Evaluates best possible backward references for specified quality.
-// Further optimize for 2D locality if use_2d_locality flag is set.
-int VP8LGetBackwardReferences(int width, int height,
- const uint32_t* const argb,
- int quality, int cache_bits, int use_2d_locality,
- VP8LBackwardRefs* const best);
-
-// Produce an estimate for a good color cache size for the image.
-int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
- int xsize, int ysize,
- int* const best_cache_bits);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-}
-#endif
-
-#endif // WEBP_ENC_BACKWARD_REFERENCES_H_
diff --git a/src/third_party/libwebp/enc/config.c b/src/third_party/libwebp/enc/config.c
deleted file mode 100644
index acf96b0..0000000
--- a/src/third_party/libwebp/enc/config.c
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Coding tools configuration
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "../webp/encode.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// WebPConfig
-//------------------------------------------------------------------------------
-
-int WebPConfigInitInternal(WebPConfig* config,
- WebPPreset preset, float quality, int version) {
- if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
- return 0; // caller/system version mismatch!
- }
- if (config == NULL) return 0;
-
- config->quality = quality;
- config->target_size = 0;
- config->target_PSNR = 0.;
- config->method = 4;
- config->sns_strength = 50;
- config->filter_strength = 60; // rather high filtering, helps w/ gradients.
- config->filter_sharpness = 0;
- config->filter_type = 1; // default: strong (so U/V is filtered too)
- config->partitions = 0;
- config->segments = 4;
- config->pass = 1;
- config->show_compressed = 0;
- config->preprocessing = 0;
- config->autofilter = 0;
- config->partition_limit = 0;
- config->alpha_compression = 1;
- config->alpha_filtering = 1;
- config->alpha_quality = 100;
- config->lossless = 0;
- config->image_hint = WEBP_HINT_DEFAULT;
- config->emulate_jpeg_size = 0;
- config->thread_level = 0;
- config->low_memory = 0;
-
- // TODO(skal): tune.
- switch (preset) {
- case WEBP_PRESET_PICTURE:
- config->sns_strength = 80;
- config->filter_sharpness = 4;
- config->filter_strength = 35;
- break;
- case WEBP_PRESET_PHOTO:
- config->sns_strength = 80;
- config->filter_sharpness = 3;
- config->filter_strength = 30;
- break;
- case WEBP_PRESET_DRAWING:
- config->sns_strength = 25;
- config->filter_sharpness = 6;
- config->filter_strength = 10;
- break;
- case WEBP_PRESET_ICON:
- config->sns_strength = 0;
- config->filter_strength = 0; // disable filtering to retain sharpness
- break;
- case WEBP_PRESET_TEXT:
- config->sns_strength = 0;
- config->filter_strength = 0; // disable filtering to retain sharpness
- config->segments = 2;
- break;
- case WEBP_PRESET_DEFAULT:
- default:
- break;
- }
- return WebPValidateConfig(config);
-}
-
-int WebPValidateConfig(const WebPConfig* config) {
- if (config == NULL) return 0;
- if (config->quality < 0 || config->quality > 100)
- return 0;
- if (config->target_size < 0)
- return 0;
- if (config->target_PSNR < 0)
- return 0;
- if (config->method < 0 || config->method > 6)
- return 0;
- if (config->segments < 1 || config->segments > 4)
- return 0;
- if (config->sns_strength < 0 || config->sns_strength > 100)
- return 0;
- if (config->filter_strength < 0 || config->filter_strength > 100)
- return 0;
- if (config->filter_sharpness < 0 || config->filter_sharpness > 7)
- return 0;
- if (config->filter_type < 0 || config->filter_type > 1)
- return 0;
- if (config->autofilter < 0 || config->autofilter > 1)
- return 0;
- if (config->pass < 1 || config->pass > 10)
- return 0;
- if (config->show_compressed < 0 || config->show_compressed > 1)
- return 0;
- if (config->preprocessing < 0 || config->preprocessing > 1)
- return 0;
- if (config->partitions < 0 || config->partitions > 3)
- return 0;
- if (config->partition_limit < 0 || config->partition_limit > 100)
- return 0;
- if (config->alpha_compression < 0)
- return 0;
- if (config->alpha_filtering < 0)
- return 0;
- if (config->alpha_quality < 0 || config->alpha_quality > 100)
- return 0;
- if (config->lossless < 0 || config->lossless > 1)
- return 0;
- if (config->image_hint >= WEBP_HINT_LAST)
- return 0;
- if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1)
- return 0;
- if (config->thread_level < 0 || config->thread_level > 1)
- return 0;
- if (config->low_memory < 0 || config->low_memory > 1)
- return 0;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/cost.c b/src/third_party/libwebp/enc/cost.c
deleted file mode 100644
index d4916d7..0000000
--- a/src/third_party/libwebp/enc/cost.c
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Cost tables for level and modes
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "./cost.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Boolean-cost cost table
-
-const uint16_t VP8EntropyCost[256] = {
- 1792, 1792, 1792, 1536, 1536, 1408, 1366, 1280, 1280, 1216,
- 1178, 1152, 1110, 1076, 1061, 1024, 1024, 992, 968, 951,
- 939, 911, 896, 878, 871, 854, 838, 820, 811, 794,
- 786, 768, 768, 752, 740, 732, 720, 709, 704, 690,
- 683, 672, 666, 655, 647, 640, 631, 622, 615, 607,
- 598, 592, 586, 576, 572, 564, 559, 555, 547, 541,
- 534, 528, 522, 512, 512, 504, 500, 494, 488, 483,
- 477, 473, 467, 461, 458, 452, 448, 443, 438, 434,
- 427, 424, 419, 415, 410, 406, 403, 399, 394, 390,
- 384, 384, 377, 374, 370, 366, 362, 359, 355, 351,
- 347, 342, 342, 336, 333, 330, 326, 323, 320, 316,
- 312, 308, 305, 302, 299, 296, 293, 288, 287, 283,
- 280, 277, 274, 272, 268, 266, 262, 256, 256, 256,
- 251, 248, 245, 242, 240, 237, 234, 232, 228, 226,
- 223, 221, 218, 216, 214, 211, 208, 205, 203, 201,
- 198, 196, 192, 191, 188, 187, 183, 181, 179, 176,
- 175, 171, 171, 168, 165, 163, 160, 159, 156, 154,
- 152, 150, 148, 146, 144, 142, 139, 138, 135, 133,
- 131, 128, 128, 125, 123, 121, 119, 117, 115, 113,
- 111, 110, 107, 105, 103, 102, 100, 98, 96, 94,
- 92, 91, 89, 86, 86, 83, 82, 80, 77, 76,
- 74, 73, 71, 69, 67, 66, 64, 63, 61, 59,
- 57, 55, 54, 52, 51, 49, 47, 46, 44, 43,
- 41, 40, 38, 36, 35, 33, 32, 30, 29, 27,
- 25, 24, 22, 21, 19, 18, 16, 15, 13, 12,
- 10, 9, 7, 6, 4, 3
-};
-
-//------------------------------------------------------------------------------
-// Level cost tables
-
-// For each given level, the following table gives the pattern of contexts to
-// use for coding it (in [][0]) as well as the bit value to use for each
-// context (in [][1]).
-const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = {
- {0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005},
- {0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023},
- {0x033, 0x023}, {0x033, 0x023}, {0x033, 0x023}, {0x0d3, 0x013},
- {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013},
- {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x093},
- {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
- {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
- {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
- {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
- {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x153}
-};
-
-// fixed costs for coding levels, deduce from the coding tree.
-// This is only the part that doesn't depend on the probability state.
-const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1] = {
- 0, 256, 256, 256, 256, 432, 618, 630,
- 731, 640, 640, 828, 901, 948, 1021, 1101,
- 1174, 1221, 1294, 1042, 1085, 1115, 1158, 1202,
- 1245, 1275, 1318, 1337, 1380, 1410, 1453, 1497,
- 1540, 1570, 1613, 1280, 1295, 1317, 1332, 1358,
- 1373, 1395, 1410, 1454, 1469, 1491, 1506, 1532,
- 1547, 1569, 1584, 1601, 1616, 1638, 1653, 1679,
- 1694, 1716, 1731, 1775, 1790, 1812, 1827, 1853,
- 1868, 1890, 1905, 1727, 1733, 1742, 1748, 1759,
- 1765, 1774, 1780, 1800, 1806, 1815, 1821, 1832,
- 1838, 1847, 1853, 1878, 1884, 1893, 1899, 1910,
- 1916, 1925, 1931, 1951, 1957, 1966, 1972, 1983,
- 1989, 1998, 2004, 2027, 2033, 2042, 2048, 2059,
- 2065, 2074, 2080, 2100, 2106, 2115, 2121, 2132,
- 2138, 2147, 2153, 2178, 2184, 2193, 2199, 2210,
- 2216, 2225, 2231, 2251, 2257, 2266, 2272, 2283,
- 2289, 2298, 2304, 2168, 2174, 2183, 2189, 2200,
- 2206, 2215, 2221, 2241, 2247, 2256, 2262, 2273,
- 2279, 2288, 2294, 2319, 2325, 2334, 2340, 2351,
- 2357, 2366, 2372, 2392, 2398, 2407, 2413, 2424,
- 2430, 2439, 2445, 2468, 2474, 2483, 2489, 2500,
- 2506, 2515, 2521, 2541, 2547, 2556, 2562, 2573,
- 2579, 2588, 2594, 2619, 2625, 2634, 2640, 2651,
- 2657, 2666, 2672, 2692, 2698, 2707, 2713, 2724,
- 2730, 2739, 2745, 2540, 2546, 2555, 2561, 2572,
- 2578, 2587, 2593, 2613, 2619, 2628, 2634, 2645,
- 2651, 2660, 2666, 2691, 2697, 2706, 2712, 2723,
- 2729, 2738, 2744, 2764, 2770, 2779, 2785, 2796,
- 2802, 2811, 2817, 2840, 2846, 2855, 2861, 2872,
- 2878, 2887, 2893, 2913, 2919, 2928, 2934, 2945,
- 2951, 2960, 2966, 2991, 2997, 3006, 3012, 3023,
- 3029, 3038, 3044, 3064, 3070, 3079, 3085, 3096,
- 3102, 3111, 3117, 2981, 2987, 2996, 3002, 3013,
- 3019, 3028, 3034, 3054, 3060, 3069, 3075, 3086,
- 3092, 3101, 3107, 3132, 3138, 3147, 3153, 3164,
- 3170, 3179, 3185, 3205, 3211, 3220, 3226, 3237,
- 3243, 3252, 3258, 3281, 3287, 3296, 3302, 3313,
- 3319, 3328, 3334, 3354, 3360, 3369, 3375, 3386,
- 3392, 3401, 3407, 3432, 3438, 3447, 3453, 3464,
- 3470, 3479, 3485, 3505, 3511, 3520, 3526, 3537,
- 3543, 3552, 3558, 2816, 2822, 2831, 2837, 2848,
- 2854, 2863, 2869, 2889, 2895, 2904, 2910, 2921,
- 2927, 2936, 2942, 2967, 2973, 2982, 2988, 2999,
- 3005, 3014, 3020, 3040, 3046, 3055, 3061, 3072,
- 3078, 3087, 3093, 3116, 3122, 3131, 3137, 3148,
- 3154, 3163, 3169, 3189, 3195, 3204, 3210, 3221,
- 3227, 3236, 3242, 3267, 3273, 3282, 3288, 3299,
- 3305, 3314, 3320, 3340, 3346, 3355, 3361, 3372,
- 3378, 3387, 3393, 3257, 3263, 3272, 3278, 3289,
- 3295, 3304, 3310, 3330, 3336, 3345, 3351, 3362,
- 3368, 3377, 3383, 3408, 3414, 3423, 3429, 3440,
- 3446, 3455, 3461, 3481, 3487, 3496, 3502, 3513,
- 3519, 3528, 3534, 3557, 3563, 3572, 3578, 3589,
- 3595, 3604, 3610, 3630, 3636, 3645, 3651, 3662,
- 3668, 3677, 3683, 3708, 3714, 3723, 3729, 3740,
- 3746, 3755, 3761, 3781, 3787, 3796, 3802, 3813,
- 3819, 3828, 3834, 3629, 3635, 3644, 3650, 3661,
- 3667, 3676, 3682, 3702, 3708, 3717, 3723, 3734,
- 3740, 3749, 3755, 3780, 3786, 3795, 3801, 3812,
- 3818, 3827, 3833, 3853, 3859, 3868, 3874, 3885,
- 3891, 3900, 3906, 3929, 3935, 3944, 3950, 3961,
- 3967, 3976, 3982, 4002, 4008, 4017, 4023, 4034,
- 4040, 4049, 4055, 4080, 4086, 4095, 4101, 4112,
- 4118, 4127, 4133, 4153, 4159, 4168, 4174, 4185,
- 4191, 4200, 4206, 4070, 4076, 4085, 4091, 4102,
- 4108, 4117, 4123, 4143, 4149, 4158, 4164, 4175,
- 4181, 4190, 4196, 4221, 4227, 4236, 4242, 4253,
- 4259, 4268, 4274, 4294, 4300, 4309, 4315, 4326,
- 4332, 4341, 4347, 4370, 4376, 4385, 4391, 4402,
- 4408, 4417, 4423, 4443, 4449, 4458, 4464, 4475,
- 4481, 4490, 4496, 4521, 4527, 4536, 4542, 4553,
- 4559, 4568, 4574, 4594, 4600, 4609, 4615, 4626,
- 4632, 4641, 4647, 3515, 3521, 3530, 3536, 3547,
- 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
- 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
- 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
- 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
- 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
- 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
- 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
- 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
- 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
- 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
- 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
- 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
- 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
- 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
- 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
- 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
- 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
- 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
- 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
- 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
- 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
- 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
- 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
- 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
- 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
- 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
- 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
- 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
- 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
- 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
- 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
- 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
- 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
- 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
- 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
- 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
- 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
- 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
- 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
- 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
- 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
- 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
- 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
- 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
- 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
- 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
- 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
- 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
- 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
- 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
- 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
- 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
- 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
- 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
- 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
- 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
- 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
- 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
- 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
- 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
- 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
- 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
- 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
- 6420, 6429, 6435, 3515, 3521, 3530, 3536, 3547,
- 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
- 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
- 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
- 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
- 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
- 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
- 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
- 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
- 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
- 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
- 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
- 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
- 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
- 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
- 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
- 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
- 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
- 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
- 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
- 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
- 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
- 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
- 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
- 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
- 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
- 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
- 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
- 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
- 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
- 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
- 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
- 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
- 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
- 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
- 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
- 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
- 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
- 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
- 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
- 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
- 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
- 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
- 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
- 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
- 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
- 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
- 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
- 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
- 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
- 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
- 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
- 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
- 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
- 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
- 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
- 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
- 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
- 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
- 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
- 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
- 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
- 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
- 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
- 6420, 6429, 6435, 5303, 5309, 5318, 5324, 5335,
- 5341, 5350, 5356, 5376, 5382, 5391, 5397, 5408,
- 5414, 5423, 5429, 5454, 5460, 5469, 5475, 5486,
- 5492, 5501, 5507, 5527, 5533, 5542, 5548, 5559,
- 5565, 5574, 5580, 5603, 5609, 5618, 5624, 5635,
- 5641, 5650, 5656, 5676, 5682, 5691, 5697, 5708,
- 5714, 5723, 5729, 5754, 5760, 5769, 5775, 5786,
- 5792, 5801, 5807, 5827, 5833, 5842, 5848, 5859,
- 5865, 5874, 5880, 5744, 5750, 5759, 5765, 5776,
- 5782, 5791, 5797, 5817, 5823, 5832, 5838, 5849,
- 5855, 5864, 5870, 5895, 5901, 5910, 5916, 5927,
- 5933, 5942, 5948, 5968, 5974, 5983, 5989, 6000,
- 6006, 6015, 6021, 6044, 6050, 6059, 6065, 6076,
- 6082, 6091, 6097, 6117, 6123, 6132, 6138, 6149,
- 6155, 6164, 6170, 6195, 6201, 6210, 6216, 6227,
- 6233, 6242, 6248, 6268, 6274, 6283, 6289, 6300,
- 6306, 6315, 6321, 6116, 6122, 6131, 6137, 6148,
- 6154, 6163, 6169, 6189, 6195, 6204, 6210, 6221,
- 6227, 6236, 6242, 6267, 6273, 6282, 6288, 6299,
- 6305, 6314, 6320, 6340, 6346, 6355, 6361, 6372,
- 6378, 6387, 6393, 6416, 6422, 6431, 6437, 6448,
- 6454, 6463, 6469, 6489, 6495, 6504, 6510, 6521,
- 6527, 6536, 6542, 6567, 6573, 6582, 6588, 6599,
- 6605, 6614, 6620, 6640, 6646, 6655, 6661, 6672,
- 6678, 6687, 6693, 6557, 6563, 6572, 6578, 6589,
- 6595, 6604, 6610, 6630, 6636, 6645, 6651, 6662,
- 6668, 6677, 6683, 6708, 6714, 6723, 6729, 6740,
- 6746, 6755, 6761, 6781, 6787, 6796, 6802, 6813,
- 6819, 6828, 6834, 6857, 6863, 6872, 6878, 6889,
- 6895, 6904, 6910, 6930, 6936, 6945, 6951, 6962,
- 6968, 6977, 6983, 7008, 7014, 7023, 7029, 7040,
- 7046, 7055, 7061, 7081, 7087, 7096, 7102, 7113,
- 7119, 7128, 7134, 6392, 6398, 6407, 6413, 6424,
- 6430, 6439, 6445, 6465, 6471, 6480, 6486, 6497,
- 6503, 6512, 6518, 6543, 6549, 6558, 6564, 6575,
- 6581, 6590, 6596, 6616, 6622, 6631, 6637, 6648,
- 6654, 6663, 6669, 6692, 6698, 6707, 6713, 6724,
- 6730, 6739, 6745, 6765, 6771, 6780, 6786, 6797,
- 6803, 6812, 6818, 6843, 6849, 6858, 6864, 6875,
- 6881, 6890, 6896, 6916, 6922, 6931, 6937, 6948,
- 6954, 6963, 6969, 6833, 6839, 6848, 6854, 6865,
- 6871, 6880, 6886, 6906, 6912, 6921, 6927, 6938,
- 6944, 6953, 6959, 6984, 6990, 6999, 7005, 7016,
- 7022, 7031, 7037, 7057, 7063, 7072, 7078, 7089,
- 7095, 7104, 7110, 7133, 7139, 7148, 7154, 7165,
- 7171, 7180, 7186, 7206, 7212, 7221, 7227, 7238,
- 7244, 7253, 7259, 7284, 7290, 7299, 7305, 7316,
- 7322, 7331, 7337, 7357, 7363, 7372, 7378, 7389,
- 7395, 7404, 7410, 7205, 7211, 7220, 7226, 7237,
- 7243, 7252, 7258, 7278, 7284, 7293, 7299, 7310,
- 7316, 7325, 7331, 7356, 7362, 7371, 7377, 7388,
- 7394, 7403, 7409, 7429, 7435, 7444, 7450, 7461,
- 7467, 7476, 7482, 7505, 7511, 7520, 7526, 7537,
- 7543, 7552, 7558, 7578, 7584, 7593, 7599, 7610,
- 7616, 7625, 7631, 7656, 7662, 7671, 7677, 7688,
- 7694, 7703, 7709, 7729, 7735, 7744, 7750, 7761
-};
-
-static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) {
- int pattern = VP8LevelCodes[level - 1][0];
- int bits = VP8LevelCodes[level - 1][1];
- int cost = 0;
- int i;
- for (i = 2; pattern; ++i) {
- if (pattern & 1) {
- cost += VP8BitCost(bits & 1, probas[i]);
- }
- bits >>= 1;
- pattern >>= 1;
- }
- return cost;
-}
-
-//------------------------------------------------------------------------------
-// Pre-calc level costs once for all
-
-void VP8CalculateLevelCosts(VP8Proba* const proba) {
- int ctype, band, ctx;
-
- if (!proba->dirty_) return; // nothing to do.
-
- for (ctype = 0; ctype < NUM_TYPES; ++ctype) {
- for (band = 0; band < NUM_BANDS; ++band) {
- for (ctx = 0; ctx < NUM_CTX; ++ctx) {
- const uint8_t* const p = proba->coeffs_[ctype][band][ctx];
- uint16_t* const table = proba->level_cost_[ctype][band][ctx];
- const int cost_base = VP8BitCost(1, p[1]);
- int v;
- table[0] = VP8BitCost(0, p[1]);
- for (v = 1; v <= MAX_VARIABLE_LEVEL; ++v) {
- table[v] = cost_base + VariableLevelCost(v, p);
- }
- // Starting at level 67 and up, the variable part of the cost is
- // actually constant.
- }
- }
- }
- proba->dirty_ = 0;
-}
-
-//------------------------------------------------------------------------------
-// Mode cost tables.
-
-// These are the fixed probabilities (in the coding trees) turned into bit-cost
-// by calling VP8BitCost().
-const uint16_t VP8FixedCostsUV[4] = { 302, 984, 439, 642 };
-// note: these values include the fixed VP8BitCost(1, 145) mode selection cost.
-const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 };
-const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = {
- { { 251, 1362, 1934, 2085, 2314, 2230, 1839, 1988, 2437, 2348 },
- { 403, 680, 1507, 1519, 2060, 2005, 1992, 1914, 1924, 1733 },
- { 353, 1121, 973, 1895, 2060, 1787, 1671, 1516, 2012, 1868 },
- { 770, 852, 1581, 632, 1393, 1780, 1823, 1936, 1074, 1218 },
- { 510, 1270, 1467, 1319, 847, 1279, 1792, 2094, 1080, 1353 },
- { 488, 1322, 918, 1573, 1300, 883, 1814, 1752, 1756, 1502 },
- { 425, 992, 1820, 1514, 1843, 2440, 937, 1771, 1924, 1129 },
- { 363, 1248, 1257, 1970, 2194, 2385, 1569, 953, 1951, 1601 },
- { 723, 1257, 1631, 964, 963, 1508, 1697, 1824, 671, 1418 },
- { 635, 1038, 1573, 930, 1673, 1413, 1410, 1687, 1410, 749 } },
- { { 451, 613, 1345, 1702, 1870, 1716, 1728, 1766, 2190, 2310 },
- { 678, 453, 1171, 1443, 1925, 1831, 2045, 1781, 1887, 1602 },
- { 711, 666, 674, 1718, 1910, 1493, 1775, 1193, 2325, 2325 },
- { 883, 854, 1583, 542, 1800, 1878, 1664, 2149, 1207, 1087 },
- { 669, 994, 1248, 1122, 949, 1179, 1376, 1729, 1070, 1244 },
- { 715, 1026, 715, 1350, 1430, 930, 1717, 1296, 1479, 1479 },
- { 544, 841, 1656, 1450, 2094, 3883, 1010, 1759, 2076, 809 },
- { 610, 855, 957, 1553, 2067, 1561, 1704, 824, 2066, 1226 },
- { 833, 960, 1416, 819, 1277, 1619, 1501, 1617, 757, 1182 },
- { 711, 964, 1252, 879, 1441, 1828, 1508, 1636, 1594, 734 } },
- { { 605, 764, 734, 1713, 1747, 1192, 1819, 1353, 1877, 2392 },
- { 866, 641, 586, 1622, 2072, 1431, 1888, 1346, 2189, 1764 },
- { 901, 851, 456, 2165, 2281, 1405, 1739, 1193, 2183, 2443 },
- { 770, 1045, 952, 1078, 1342, 1191, 1436, 1063, 1303, 995 },
- { 901, 1086, 727, 1170, 884, 1105, 1267, 1401, 1739, 1337 },
- { 951, 1162, 595, 1488, 1388, 703, 1790, 1366, 2057, 1724 },
- { 534, 986, 1273, 1987, 3273, 1485, 1024, 1399, 1583, 866 },
- { 699, 1182, 695, 1978, 1726, 1986, 1326, 714, 1750, 1672 },
- { 951, 1217, 1209, 920, 1062, 1441, 1548, 999, 952, 932 },
- { 733, 1284, 784, 1256, 1557, 1098, 1257, 1357, 1414, 908 } },
- { { 316, 1075, 1653, 1220, 2145, 2051, 1730, 2131, 1884, 1790 },
- { 745, 516, 1404, 894, 1599, 2375, 2013, 2105, 1475, 1381 },
- { 516, 729, 1088, 1319, 1637, 3426, 1636, 1275, 1531, 1453 },
- { 894, 943, 2138, 468, 1704, 2259, 2069, 1763, 1266, 1158 },
- { 605, 1025, 1235, 871, 1170, 1767, 1493, 1500, 1104, 1258 },
- { 739, 826, 1207, 1151, 1412, 846, 1305, 2726, 1014, 1569 },
- { 558, 825, 1820, 1398, 3344, 1556, 1218, 1550, 1228, 878 },
- { 429, 951, 1089, 1816, 3861, 3861, 1556, 969, 1568, 1828 },
- { 883, 961, 1752, 769, 1468, 1810, 2081, 2346, 613, 1298 },
- { 803, 895, 1372, 641, 1303, 1708, 1686, 1700, 1306, 1033 } },
- { { 439, 1267, 1270, 1579, 963, 1193, 1723, 1729, 1198, 1993 },
- { 705, 725, 1029, 1153, 1176, 1103, 1821, 1567, 1259, 1574 },
- { 723, 859, 802, 1253, 972, 1202, 1407, 1665, 1520, 1674 },
- { 894, 960, 1254, 887, 1052, 1607, 1344, 1349, 865, 1150 },
- { 833, 1312, 1337, 1205, 572, 1288, 1414, 1529, 1088, 1430 },
- { 842, 1279, 1068, 1861, 862, 688, 1861, 1630, 1039, 1381 },
- { 766, 938, 1279, 1546, 3338, 1550, 1031, 1542, 1288, 640 },
- { 715, 1090, 835, 1609, 1100, 1100, 1603, 1019, 1102, 1617 },
- { 894, 1813, 1500, 1188, 789, 1194, 1491, 1919, 617, 1333 },
- { 610, 1076, 1644, 1281, 1283, 975, 1179, 1688, 1434, 889 } },
- { { 544, 971, 1146, 1849, 1221, 740, 1857, 1621, 1683, 2430 },
- { 723, 705, 961, 1371, 1426, 821, 2081, 2079, 1839, 1380 },
- { 783, 857, 703, 2145, 1419, 814, 1791, 1310, 1609, 2206 },
- { 997, 1000, 1153, 792, 1229, 1162, 1810, 1418, 942, 979 },
- { 901, 1226, 883, 1289, 793, 715, 1904, 1649, 1319, 3108 },
- { 979, 1478, 782, 2216, 1454, 455, 3092, 1591, 1997, 1664 },
- { 663, 1110, 1504, 1114, 1522, 3311, 676, 1522, 1530, 1024 },
- { 605, 1138, 1153, 1314, 1569, 1315, 1157, 804, 1574, 1320 },
- { 770, 1216, 1218, 1227, 869, 1384, 1232, 1375, 834, 1239 },
- { 775, 1007, 843, 1216, 1225, 1074, 2527, 1479, 1149, 975 } },
- { { 477, 817, 1309, 1439, 1708, 1454, 1159, 1241, 1945, 1672 },
- { 577, 796, 1112, 1271, 1618, 1458, 1087, 1345, 1831, 1265 },
- { 663, 776, 753, 1940, 1690, 1690, 1227, 1097, 3149, 1361 },
- { 766, 1299, 1744, 1161, 1565, 1106, 1045, 1230, 1232, 707 },
- { 915, 1026, 1404, 1182, 1184, 851, 1428, 2425, 1043, 789 },
- { 883, 1456, 790, 1082, 1086, 985, 1083, 1484, 1238, 1160 },
- { 507, 1345, 2261, 1995, 1847, 3636, 653, 1761, 2287, 933 },
- { 553, 1193, 1470, 2057, 2059, 2059, 833, 779, 2058, 1263 },
- { 766, 1275, 1515, 1039, 957, 1554, 1286, 1540, 1289, 705 },
- { 499, 1378, 1496, 1385, 1850, 1850, 1044, 2465, 1515, 720 } },
- { { 553, 930, 978, 2077, 1968, 1481, 1457, 761, 1957, 2362 },
- { 694, 864, 905, 1720, 1670, 1621, 1429, 718, 2125, 1477 },
- { 699, 968, 658, 3190, 2024, 1479, 1865, 750, 2060, 2320 },
- { 733, 1308, 1296, 1062, 1576, 1322, 1062, 1112, 1172, 816 },
- { 920, 927, 1052, 939, 947, 1156, 1152, 1073, 3056, 1268 },
- { 723, 1534, 711, 1547, 1294, 892, 1553, 928, 1815, 1561 },
- { 663, 1366, 1583, 2111, 1712, 3501, 522, 1155, 2130, 1133 },
- { 614, 1731, 1188, 2343, 1944, 3733, 1287, 487, 3546, 1758 },
- { 770, 1585, 1312, 826, 884, 2673, 1185, 1006, 1195, 1195 },
- { 758, 1333, 1273, 1023, 1621, 1162, 1351, 833, 1479, 862 } },
- { { 376, 1193, 1446, 1149, 1545, 1577, 1870, 1789, 1175, 1823 },
- { 803, 633, 1136, 1058, 1350, 1323, 1598, 2247, 1072, 1252 },
- { 614, 1048, 943, 981, 1152, 1869, 1461, 1020, 1618, 1618 },
- { 1107, 1085, 1282, 592, 1779, 1933, 1648, 2403, 691, 1246 },
- { 851, 1309, 1223, 1243, 895, 1593, 1792, 2317, 627, 1076 },
- { 770, 1216, 1030, 1125, 921, 981, 1629, 1131, 1049, 1646 },
- { 626, 1469, 1456, 1081, 1489, 3278, 981, 1232, 1498, 733 },
- { 617, 1201, 812, 1220, 1476, 1476, 1478, 970, 1228, 1488 },
- { 1179, 1393, 1540, 999, 1243, 1503, 1916, 1925, 414, 1614 },
- { 943, 1088, 1490, 682, 1112, 1372, 1756, 1505, 966, 966 } },
- { { 322, 1142, 1589, 1396, 2144, 1859, 1359, 1925, 2084, 1518 },
- { 617, 625, 1241, 1234, 2121, 1615, 1524, 1858, 1720, 1004 },
- { 553, 851, 786, 1299, 1452, 1560, 1372, 1561, 1967, 1713 },
- { 770, 977, 1396, 568, 1893, 1639, 1540, 2108, 1430, 1013 },
- { 684, 1120, 1375, 982, 930, 2719, 1638, 1643, 933, 993 },
- { 553, 1103, 996, 1356, 1361, 1005, 1507, 1761, 1184, 1268 },
- { 419, 1247, 1537, 1554, 1817, 3606, 1026, 1666, 1829, 923 },
- { 439, 1139, 1101, 1257, 3710, 1922, 1205, 1040, 1931, 1529 },
- { 979, 935, 1269, 847, 1202, 1286, 1530, 1535, 827, 1036 },
- { 516, 1378, 1569, 1110, 1798, 1798, 1198, 2199, 1543, 712 } },
-};
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/cost.h b/src/third_party/libwebp/enc/cost.h
deleted file mode 100644
index 7d7c2c7..0000000
--- a/src/third_party/libwebp/enc/cost.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Cost tables for level and modes.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_ENC_COST_H_
-#define WEBP_ENC_COST_H_
-
-#include "./vp8enci.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// approximate cost per level:
-extern const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1];
-extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p)
-
-// Cost of coding one event with probability 'proba'.
-static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) {
- return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba];
-}
-
-// Level cost calculations
-extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2];
-void VP8CalculateLevelCosts(VP8Proba* const proba);
-static WEBP_INLINE int VP8LevelCost(const uint16_t* const table, int level) {
- return VP8LevelFixedCosts[level]
- + table[(level > MAX_VARIABLE_LEVEL) ? MAX_VARIABLE_LEVEL : level];
-}
-
-// Mode costs
-extern const uint16_t VP8FixedCostsUV[4];
-extern const uint16_t VP8FixedCostsI16[4];
-extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES];
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_ENC_COST_H_ */
diff --git a/src/third_party/libwebp/enc/filter.c b/src/third_party/libwebp/enc/filter.c
deleted file mode 100644
index 237727c..0000000
--- a/src/third_party/libwebp/enc/filter.c
+++ /dev/null
@@ -1,415 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Selecting filter level
-//
-// Author: somnath@google.com (Somnath Banerjee)
-
-#include "./vp8enci.h"
-
-#if definend(STARBOARD)
-#include "starboard/memory.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// NOTE: clip1, tables and InitTables are repeated entries of dsp.c
-static uint8_t abs0[255 + 255 + 1]; // abs(i)
-static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1
-static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127]
-static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15]
-static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
-
-static int tables_ok = 0;
-
-static void InitTables(void) {
- if (!tables_ok) {
- int i;
- for (i = -255; i <= 255; ++i) {
- abs0[255 + i] = (i < 0) ? -i : i;
- abs1[255 + i] = abs0[255 + i] >> 1;
- }
- for (i = -1020; i <= 1020; ++i) {
- sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
- }
- for (i = -112; i <= 112; ++i) {
- sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
- }
- for (i = -255; i <= 255 + 255; ++i) {
- clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
- }
- tables_ok = 1;
- }
-}
-
-//------------------------------------------------------------------------------
-// Edge filtering functions
-
-// 4 pixels in, 2 pixels out
-static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
- const int a1 = sclip2[112 + ((a + 4) >> 3)];
- const int a2 = sclip2[112 + ((a + 3) >> 3)];
- p[-step] = clip1[255 + p0 + a2];
- p[ 0] = clip1[255 + q0 - a1];
-}
-
-// 4 pixels in, 4 pixels out
-static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- const int a = 3 * (q0 - p0);
- const int a1 = sclip2[112 + ((a + 4) >> 3)];
- const int a2 = sclip2[112 + ((a + 3) >> 3)];
- const int a3 = (a1 + 1) >> 1;
- p[-2*step] = clip1[255 + p1 + a3];
- p[- step] = clip1[255 + p0 + a2];
- p[ 0] = clip1[255 + q0 - a1];
- p[ step] = clip1[255 + q1 - a3];
-}
-
-// high edge-variance
-static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
-}
-
-static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
- const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
- return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
-}
-
-static WEBP_INLINE int needs_filter2(const uint8_t* p,
- int step, int t, int it) {
- const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
- const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
- if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
- return 0;
- return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
- abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
- abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
-}
-
-//------------------------------------------------------------------------------
-// Simple In-loop filtering (Paragraph 15.2)
-
-static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
- int i;
- for (i = 0; i < 16; ++i) {
- if (needs_filter(p + i, stride, thresh)) {
- do_filter2(p + i, stride);
- }
- }
-}
-
-static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
- int i;
- for (i = 0; i < 16; ++i) {
- if (needs_filter(p + i * stride, 1, thresh)) {
- do_filter2(p + i * stride, 1);
- }
- }
-}
-
-static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4 * stride;
- SimpleVFilter16(p, stride, thresh);
- }
-}
-
-static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4;
- SimpleHFilter16(p, stride, thresh);
- }
-}
-
-//------------------------------------------------------------------------------
-// Complex In-loop filtering (Paragraph 15.3)
-
-static WEBP_INLINE void FilterLoop24(uint8_t* p,
- int hstride, int vstride, int size,
- int thresh, int ithresh, int hev_thresh) {
- while (size-- > 0) {
- if (needs_filter2(p, hstride, thresh, ithresh)) {
- if (hev(p, hstride, hev_thresh)) {
- do_filter2(p, hstride);
- } else {
- do_filter4(p, hstride);
- }
- }
- p += vstride;
- }
-}
-
-// on three inner edges
-static void VFilter16i(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4 * stride;
- FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh);
- }
-}
-
-static void HFilter16i(uint8_t* p, int stride,
- int thresh, int ithresh, int hev_thresh) {
- int k;
- for (k = 3; k > 0; --k) {
- p += 4;
- FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh);
- }
-}
-
-static void VFilter8i(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
- FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
-}
-
-static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
- int thresh, int ithresh, int hev_thresh) {
- FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
- FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
-}
-
-//------------------------------------------------------------------------------
-
-void (*VP8EncVFilter16i)(uint8_t*, int, int, int, int) = VFilter16i;
-void (*VP8EncHFilter16i)(uint8_t*, int, int, int, int) = HFilter16i;
-void (*VP8EncVFilter8i)(uint8_t*, uint8_t*, int, int, int, int) = VFilter8i;
-void (*VP8EncHFilter8i)(uint8_t*, uint8_t*, int, int, int, int) = HFilter8i;
-
-void (*VP8EncSimpleVFilter16i)(uint8_t*, int, int) = SimpleVFilter16i;
-void (*VP8EncSimpleHFilter16i)(uint8_t*, int, int) = SimpleHFilter16i;
-
-//------------------------------------------------------------------------------
-// Paragraph 15.4: compute the inner-edge filtering strength
-
-static int GetILevel(int sharpness, int level) {
- if (sharpness > 0) {
- if (sharpness > 4) {
- level >>= 2;
- } else {
- level >>= 1;
- }
- if (level > 9 - sharpness) {
- level = 9 - sharpness;
- }
- }
- if (level < 1) level = 1;
- return level;
-}
-
-static void DoFilter(const VP8EncIterator* const it, int level) {
- const VP8Encoder* const enc = it->enc_;
- const int ilevel = GetILevel(enc->config_->filter_sharpness, level);
- const int limit = 2 * level + ilevel;
-
- uint8_t* const y_dst = it->yuv_out2_ + Y_OFF;
- uint8_t* const u_dst = it->yuv_out2_ + U_OFF;
- uint8_t* const v_dst = it->yuv_out2_ + V_OFF;
-
- // copy current block to yuv_out2_
- SbMemoryCopy(y_dst, it->yuv_out_, YUV_SIZE * sizeof(uint8_t));
-
- if (enc->filter_hdr_.simple_ == 1) { // simple
- VP8EncSimpleHFilter16i(y_dst, BPS, limit);
- VP8EncSimpleVFilter16i(y_dst, BPS, limit);
- } else { // complex
- const int hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
- VP8EncHFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
- VP8EncHFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
- VP8EncVFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
- VP8EncVFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
- }
-}
-
-//------------------------------------------------------------------------------
-// SSIM metric
-
-enum { KERNEL = 3 };
-static const double kMinValue = 1.e-10; // minimal threshold
-
-void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst) {
- dst->w += src->w;
- dst->xm += src->xm;
- dst->ym += src->ym;
- dst->xxm += src->xxm;
- dst->xym += src->xym;
- dst->yym += src->yym;
-}
-
-static void VP8SSIMAccumulate(const uint8_t* src1, int stride1,
- const uint8_t* src2, int stride2,
- int xo, int yo, int W, int H,
- DistoStats* const stats) {
- const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL;
- const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL;
- const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL;
- const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL;
- int x, y;
- src1 += ymin * stride1;
- src2 += ymin * stride2;
- for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
- for (x = xmin; x <= xmax; ++x) {
- const int s1 = src1[x];
- const int s2 = src2[x];
- stats->w += 1;
- stats->xm += s1;
- stats->ym += s2;
- stats->xxm += s1 * s1;
- stats->xym += s1 * s2;
- stats->yym += s2 * s2;
- }
- }
-}
-
-double VP8SSIMGet(const DistoStats* const stats) {
- const double xmxm = stats->xm * stats->xm;
- const double ymym = stats->ym * stats->ym;
- const double xmym = stats->xm * stats->ym;
- const double w2 = stats->w * stats->w;
- double sxx = stats->xxm * stats->w - xmxm;
- double syy = stats->yym * stats->w - ymym;
- double sxy = stats->xym * stats->w - xmym;
- double C1, C2;
- double fnum;
- double fden;
- // small errors are possible, due to rounding. Clamp to zero.
- if (sxx < 0.) sxx = 0.;
- if (syy < 0.) syy = 0.;
- C1 = 6.5025 * w2;
- C2 = 58.5225 * w2;
- fnum = (2 * xmym + C1) * (2 * sxy + C2);
- fden = (xmxm + ymym + C1) * (sxx + syy + C2);
- return (fden != 0.) ? fnum / fden : kMinValue;
-}
-
-double VP8SSIMGetSquaredError(const DistoStats* const s) {
- if (s->w > 0.) {
- const double iw2 = 1. / (s->w * s->w);
- const double sxx = s->xxm * s->w - s->xm * s->xm;
- const double syy = s->yym * s->w - s->ym * s->ym;
- const double sxy = s->xym * s->w - s->xm * s->ym;
- const double SSE = iw2 * (sxx + syy - 2. * sxy);
- if (SSE > kMinValue) return SSE;
- }
- return kMinValue;
-}
-
-void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
- const uint8_t* src2, int stride2,
- int W, int H, DistoStats* const stats) {
- int x, y;
- for (y = 0; y < H; ++y) {
- for (x = 0; x < W; ++x) {
- VP8SSIMAccumulate(src1, stride1, src2, stride2, x, y, W, H, stats);
- }
- }
-}
-
-static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
- int x, y;
- DistoStats s = { .0, .0, .0, .0, .0, .0 };
-
- // compute SSIM in a 10 x 10 window
- for (x = 3; x < 13; x++) {
- for (y = 3; y < 13; y++) {
- VP8SSIMAccumulate(yuv1 + Y_OFF, BPS, yuv2 + Y_OFF, BPS, x, y, 16, 16, &s);
- }
- }
- for (x = 1; x < 7; x++) {
- for (y = 1; y < 7; y++) {
- VP8SSIMAccumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s);
- VP8SSIMAccumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s);
- }
- }
- return VP8SSIMGet(&s);
-}
-
-//------------------------------------------------------------------------------
-// Exposed APIs: Encoder should call the following 3 functions to adjust
-// loop filter strength
-
-void VP8InitFilter(VP8EncIterator* const it) {
- int s, i;
- if (!it->lf_stats_) return;
-
- InitTables();
- for (s = 0; s < NUM_MB_SEGMENTS; s++) {
- for (i = 0; i < MAX_LF_LEVELS; i++) {
- (*it->lf_stats_)[s][i] = 0;
- }
- }
-}
-
-void VP8StoreFilterStats(VP8EncIterator* const it) {
- int d;
- const int s = it->mb_->segment_;
- const int level0 = it->enc_->dqm_[s].fstrength_; // TODO: ref_lf_delta[]
-
- // explore +/-quant range of values around level0
- const int delta_min = -it->enc_->dqm_[s].quant_;
- const int delta_max = it->enc_->dqm_[s].quant_;
- const int step_size = (delta_max - delta_min >= 4) ? 4 : 1;
-
- if (!it->lf_stats_) return;
-
- // NOTE: Currently we are applying filter only across the sublock edges
- // There are two reasons for that.
- // 1. Applying filter on macro block edges will change the pixels in
- // the left and top macro blocks. That will be hard to restore
- // 2. Macro Blocks on the bottom and right are not yet compressed. So we
- // cannot apply filter on the right and bottom macro block edges.
- if (it->mb_->type_ == 1 && it->mb_->skip_) return;
-
- // Always try filter level zero
- (*it->lf_stats_)[s][0] += GetMBSSIM(it->yuv_in_, it->yuv_out_);
-
- for (d = delta_min; d <= delta_max; d += step_size) {
- const int level = level0 + d;
- if (level <= 0 || level >= MAX_LF_LEVELS) {
- continue;
- }
- DoFilter(it, level);
- (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_);
- }
-}
-
-void VP8AdjustFilterStrength(VP8EncIterator* const it) {
- int s;
- VP8Encoder* const enc = it->enc_;
-
- if (!it->lf_stats_) {
- return;
- }
- for (s = 0; s < NUM_MB_SEGMENTS; s++) {
- int i, best_level = 0;
- // Improvement over filter level 0 should be at least 1e-5 (relatively)
- double best_v = 1.00001 * (*it->lf_stats_)[s][0];
- for (i = 1; i < MAX_LF_LEVELS; i++) {
- const double v = (*it->lf_stats_)[s][i];
- if (v > best_v) {
- best_v = v;
- best_level = i;
- }
- }
- enc->dqm_[s].fstrength_ = best_level;
- }
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/frame.c b/src/third_party/libwebp/enc/frame.c
deleted file mode 100644
index 3cbd14f..0000000
--- a/src/third_party/libwebp/enc/frame.c
+++ /dev/null
@@ -1,984 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// frame coding and analysis
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h."
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#endif
-
-#include "./vp8enci.h"
-#include "./cost.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define SEGMENT_VISU 0
-#define DEBUG_SEARCH 0 // useful to track search convergence
-
-// On-the-fly info about the current set of residuals. Handy to avoid
-// passing zillions of params.
-typedef struct {
- int first;
- int last;
- const int16_t* coeffs;
-
- int coeff_type;
- ProbaArray* prob;
- StatsArray* stats;
- CostArray* cost;
-} VP8Residual;
-
-//------------------------------------------------------------------------------
-// Tables for level coding
-
-const uint8_t VP8EncBands[16 + 1] = {
- 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
- 0 // sentinel
-};
-
-const uint8_t VP8Cat3[] = { 173, 148, 140 };
-const uint8_t VP8Cat4[] = { 176, 155, 140, 135 };
-const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 };
-const uint8_t VP8Cat6[] =
- { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
-
-//------------------------------------------------------------------------------
-// Reset the statistics about: number of skips, token proba, level cost,...
-
-static void ResetStats(VP8Encoder* const enc) {
- VP8Proba* const proba = &enc->proba_;
- VP8CalculateLevelCosts(proba);
- proba->nb_skip_ = 0;
-}
-
-//------------------------------------------------------------------------------
-// Skip decision probability
-
-#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK.
-
-static int CalcSkipProba(uint64_t nb, uint64_t total) {
- return (int)(total ? (total - nb) * 255 / total : 255);
-}
-
-// Returns the bit-cost for coding the skip probability.
-static int FinalizeSkipProba(VP8Encoder* const enc) {
- VP8Proba* const proba = &enc->proba_;
- const int nb_mbs = enc->mb_w_ * enc->mb_h_;
- const int nb_events = proba->nb_skip_;
- int size;
- proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs);
- proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD);
- size = 256; // 'use_skip_proba' bit
- if (proba->use_skip_proba_) {
- size += nb_events * VP8BitCost(1, proba->skip_proba_)
- + (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_);
- size += 8 * 256; // cost of signaling the skip_proba_ itself.
- }
- return size;
-}
-
-//------------------------------------------------------------------------------
-// Recording of token probabilities.
-
-static void ResetTokenStats(VP8Encoder* const enc) {
- VP8Proba* const proba = &enc->proba_;
- SbMemorySet(proba->stats_, 0, sizeof(proba->stats_));
-}
-
-// Record proba context used
-static int Record(int bit, proba_t* const stats) {
- proba_t p = *stats;
- if (p >= 0xffff0000u) { // an overflow is inbound.
- p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
- }
- // record bit count (lower 16 bits) and increment total count (upper 16 bits).
- p += 0x00010000u + bit;
- *stats = p;
- return bit;
-}
-
-// We keep the table free variant around for reference, in case.
-#define USE_LEVEL_CODE_TABLE
-
-// Simulate block coding, but only record statistics.
-// Note: no need to record the fixed probas.
-static int RecordCoeffs(int ctx, const VP8Residual* const res) {
- int n = res->first;
- // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1
- proba_t* s = res->stats[n][ctx];
- if (res->last < 0) {
- Record(0, s + 0);
- return 0;
- }
- while (n <= res->last) {
- int v;
- Record(1, s + 0); // order of record doesn't matter
- while ((v = res->coeffs[n++]) == 0) {
- Record(0, s + 1);
- s = res->stats[VP8EncBands[n]][0];
- }
- Record(1, s + 1);
- if (!Record(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1
- s = res->stats[VP8EncBands[n]][1];
- } else {
- v = abs(v);
-#if !defined(USE_LEVEL_CODE_TABLE)
- if (!Record(v > 4, s + 3)) {
- if (Record(v != 2, s + 4))
- Record(v == 4, s + 5);
- } else if (!Record(v > 10, s + 6)) {
- Record(v > 6, s + 7);
- } else if (!Record((v >= 3 + (8 << 2)), s + 8)) {
- Record((v >= 3 + (8 << 1)), s + 9);
- } else {
- Record((v >= 3 + (8 << 3)), s + 10);
- }
-#else
- if (v > MAX_VARIABLE_LEVEL)
- v = MAX_VARIABLE_LEVEL;
-
- {
- const int bits = VP8LevelCodes[v - 1][1];
- int pattern = VP8LevelCodes[v - 1][0];
- int i;
- for (i = 0; (pattern >>= 1) != 0; ++i) {
- const int mask = 2 << i;
- if (pattern & 1) Record(!!(bits & mask), s + 3 + i);
- }
- }
-#endif
- s = res->stats[VP8EncBands[n]][2];
- }
- }
- if (n < 16) Record(0, s + 0);
- return 1;
-}
-
-// Collect statistics and deduce probabilities for next coding pass.
-// Return the total bit-cost for coding the probability updates.
-static int CalcTokenProba(int nb, int total) {
- SB_DCHECK(nb <= total);
- return nb ? (255 - nb * 255 / total) : 255;
-}
-
-// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.
-static int BranchCost(int nb, int total, int proba) {
- return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);
-}
-
-static int FinalizeTokenProbas(VP8Proba* const proba) {
- int has_changed = 0;
- int size = 0;
- int t, b, c, p;
- for (t = 0; t < NUM_TYPES; ++t) {
- for (b = 0; b < NUM_BANDS; ++b) {
- for (c = 0; c < NUM_CTX; ++c) {
- for (p = 0; p < NUM_PROBAS; ++p) {
- const proba_t stats = proba->stats_[t][b][c][p];
- const int nb = (stats >> 0) & 0xffff;
- const int total = (stats >> 16) & 0xffff;
- const int update_proba = VP8CoeffsUpdateProba[t][b][c][p];
- const int old_p = VP8CoeffsProba0[t][b][c][p];
- const int new_p = CalcTokenProba(nb, total);
- const int old_cost = BranchCost(nb, total, old_p)
- + VP8BitCost(0, update_proba);
- const int new_cost = BranchCost(nb, total, new_p)
- + VP8BitCost(1, update_proba)
- + 8 * 256;
- const int use_new_p = (old_cost > new_cost);
- size += VP8BitCost(use_new_p, update_proba);
- if (use_new_p) { // only use proba that seem meaningful enough.
- proba->coeffs_[t][b][c][p] = new_p;
- has_changed |= (new_p != old_p);
- size += 8 * 256;
- } else {
- proba->coeffs_[t][b][c][p] = old_p;
- }
- }
- }
- }
- }
- proba->dirty_ = has_changed;
- return size;
-}
-
-//------------------------------------------------------------------------------
-// Finalize Segment probability based on the coding tree
-
-static int GetProba(int a, int b) {
- const int total = a + b;
- return (total == 0) ? 255 // that's the default probability.
- : (255 * a + total / 2) / total; // rounded proba
-}
-
-static void SetSegmentProbas(VP8Encoder* const enc) {
- int p[NUM_MB_SEGMENTS] = { 0 };
- int n;
-
- for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
- const VP8MBInfo* const mb = &enc->mb_info_[n];
- p[mb->segment_]++;
- }
- if (enc->pic_->stats != NULL) {
- for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
- enc->pic_->stats->segment_size[n] = p[n];
- }
- }
- if (enc->segment_hdr_.num_segments_ > 1) {
- uint8_t* const probas = enc->proba_.segments_;
- probas[0] = GetProba(p[0] + p[1], p[2] + p[3]);
- probas[1] = GetProba(p[0], p[1]);
- probas[2] = GetProba(p[2], p[3]);
-
- enc->segment_hdr_.update_map_ =
- (probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255);
- enc->segment_hdr_.size_ =
- p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) +
- p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) +
- p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) +
- p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2]));
- } else {
- enc->segment_hdr_.update_map_ = 0;
- enc->segment_hdr_.size_ = 0;
- }
-}
-
-//------------------------------------------------------------------------------
-// helper functions for residuals struct VP8Residual.
-
-static void InitResidual(int first, int coeff_type,
- VP8Encoder* const enc, VP8Residual* const res) {
- res->coeff_type = coeff_type;
- res->prob = enc->proba_.coeffs_[coeff_type];
- res->stats = enc->proba_.stats_[coeff_type];
- res->cost = enc->proba_.level_cost_[coeff_type];
- res->first = first;
-}
-
-static void SetResidualCoeffs(const int16_t* const coeffs,
- VP8Residual* const res) {
- int n;
- res->last = -1;
- for (n = 15; n >= res->first; --n) {
- if (coeffs[n]) {
- res->last = n;
- break;
- }
- }
- res->coeffs = coeffs;
-}
-
-//------------------------------------------------------------------------------
-// Mode costs
-
-static int GetResidualCost(int ctx0, const VP8Residual* const res) {
- int n = res->first;
- // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
- int p0 = res->prob[n][ctx0][0];
- const uint16_t* t = res->cost[n][ctx0];
- int cost;
-
- if (res->last < 0) {
- return VP8BitCost(0, p0);
- }
- cost = 0;
- while (n < res->last) {
- int v = res->coeffs[n];
- const int b = VP8EncBands[n + 1];
- ++n;
- if (v == 0) {
- // short-case for VP8LevelCost(t, 0) (note: VP8LevelFixedCosts[0] == 0):
- cost += t[0];
- t = res->cost[b][0];
- continue;
- }
- v = abs(v);
- cost += VP8BitCost(1, p0);
- cost += VP8LevelCost(t, v);
- {
- const int ctx = (v == 1) ? 1 : 2;
- p0 = res->prob[b][ctx][0];
- t = res->cost[b][ctx];
- }
- }
- // Last coefficient is always non-zero
- {
- const int v = abs(res->coeffs[n]);
- SB_DCHECK(v != 0);
- cost += VP8BitCost(1, p0);
- cost += VP8LevelCost(t, v);
- if (n < 15) {
- const int b = VP8EncBands[n + 1];
- const int ctx = (v == 1) ? 1 : 2;
- const int last_p0 = res->prob[b][ctx][0];
- cost += VP8BitCost(0, last_p0);
- }
- }
- return cost;
-}
-
-int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) {
- const int x = (it->i4_ & 3), y = (it->i4_ >> 2);
- VP8Residual res;
- VP8Encoder* const enc = it->enc_;
- int R = 0;
- int ctx;
-
- InitResidual(0, 3, enc, &res);
- ctx = it->top_nz_[x] + it->left_nz_[y];
- SetResidualCoeffs(levels, &res);
- R += GetResidualCost(ctx, &res);
- return R;
-}
-
-int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) {
- VP8Residual res;
- VP8Encoder* const enc = it->enc_;
- int x, y;
- int R = 0;
-
- VP8IteratorNzToBytes(it); // re-import the non-zero context
-
- // DC
- InitResidual(0, 1, enc, &res);
- SetResidualCoeffs(rd->y_dc_levels, &res);
- R += GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res);
-
- // AC
- InitResidual(1, 0, enc, &res);
- for (y = 0; y < 4; ++y) {
- for (x = 0; x < 4; ++x) {
- const int ctx = it->top_nz_[x] + it->left_nz_[y];
- SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
- R += GetResidualCost(ctx, &res);
- it->top_nz_[x] = it->left_nz_[y] = (res.last >= 0);
- }
- }
- return R;
-}
-
-int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) {
- VP8Residual res;
- VP8Encoder* const enc = it->enc_;
- int ch, x, y;
- int R = 0;
-
- VP8IteratorNzToBytes(it); // re-import the non-zero context
-
- InitResidual(0, 2, enc, &res);
- for (ch = 0; ch <= 2; ch += 2) {
- for (y = 0; y < 2; ++y) {
- for (x = 0; x < 2; ++x) {
- const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
- SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
- R += GetResidualCost(ctx, &res);
- it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = (res.last >= 0);
- }
- }
- }
- return R;
-}
-
-//------------------------------------------------------------------------------
-// Coefficient coding
-
-static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) {
- int n = res->first;
- // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
- const uint8_t* p = res->prob[n][ctx];
- if (!VP8PutBit(bw, res->last >= 0, p[0])) {
- return 0;
- }
-
- while (n < 16) {
- const int c = res->coeffs[n++];
- const int sign = c < 0;
- int v = sign ? -c : c;
- if (!VP8PutBit(bw, v != 0, p[1])) {
- p = res->prob[VP8EncBands[n]][0];
- continue;
- }
- if (!VP8PutBit(bw, v > 1, p[2])) {
- p = res->prob[VP8EncBands[n]][1];
- } else {
- if (!VP8PutBit(bw, v > 4, p[3])) {
- if (VP8PutBit(bw, v != 2, p[4]))
- VP8PutBit(bw, v == 4, p[5]);
- } else if (!VP8PutBit(bw, v > 10, p[6])) {
- if (!VP8PutBit(bw, v > 6, p[7])) {
- VP8PutBit(bw, v == 6, 159);
- } else {
- VP8PutBit(bw, v >= 9, 165);
- VP8PutBit(bw, !(v & 1), 145);
- }
- } else {
- int mask;
- const uint8_t* tab;
- if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)
- VP8PutBit(bw, 0, p[8]);
- VP8PutBit(bw, 0, p[9]);
- v -= 3 + (8 << 0);
- mask = 1 << 2;
- tab = VP8Cat3;
- } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b)
- VP8PutBit(bw, 0, p[8]);
- VP8PutBit(bw, 1, p[9]);
- v -= 3 + (8 << 1);
- mask = 1 << 3;
- tab = VP8Cat4;
- } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b)
- VP8PutBit(bw, 1, p[8]);
- VP8PutBit(bw, 0, p[10]);
- v -= 3 + (8 << 2);
- mask = 1 << 4;
- tab = VP8Cat5;
- } else { // VP8Cat6 (11b)
- VP8PutBit(bw, 1, p[8]);
- VP8PutBit(bw, 1, p[10]);
- v -= 3 + (8 << 3);
- mask = 1 << 10;
- tab = VP8Cat6;
- }
- while (mask) {
- VP8PutBit(bw, !!(v & mask), *tab++);
- mask >>= 1;
- }
- }
- p = res->prob[VP8EncBands[n]][2];
- }
- VP8PutBitUniform(bw, sign);
- if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) {
- return 1; // EOB
- }
- }
- return 1;
-}
-
-static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it,
- const VP8ModeScore* const rd) {
- int x, y, ch;
- VP8Residual res;
- uint64_t pos1, pos2, pos3;
- const int i16 = (it->mb_->type_ == 1);
- const int segment = it->mb_->segment_;
- VP8Encoder* const enc = it->enc_;
-
- VP8IteratorNzToBytes(it);
-
- pos1 = VP8BitWriterPos(bw);
- if (i16) {
- InitResidual(0, 1, enc, &res);
- SetResidualCoeffs(rd->y_dc_levels, &res);
- it->top_nz_[8] = it->left_nz_[8] =
- PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res);
- InitResidual(1, 0, enc, &res);
- } else {
- InitResidual(0, 3, enc, &res);
- }
-
- // luma-AC
- for (y = 0; y < 4; ++y) {
- for (x = 0; x < 4; ++x) {
- const int ctx = it->top_nz_[x] + it->left_nz_[y];
- SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
- it->top_nz_[x] = it->left_nz_[y] = PutCoeffs(bw, ctx, &res);
- }
- }
- pos2 = VP8BitWriterPos(bw);
-
- // U/V
- InitResidual(0, 2, enc, &res);
- for (ch = 0; ch <= 2; ch += 2) {
- for (y = 0; y < 2; ++y) {
- for (x = 0; x < 2; ++x) {
- const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
- SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
- it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
- PutCoeffs(bw, ctx, &res);
- }
- }
- }
- pos3 = VP8BitWriterPos(bw);
- it->luma_bits_ = pos2 - pos1;
- it->uv_bits_ = pos3 - pos2;
- it->bit_count_[segment][i16] += it->luma_bits_;
- it->bit_count_[segment][2] += it->uv_bits_;
- VP8IteratorBytesToNz(it);
-}
-
-// Same as CodeResiduals, but doesn't actually write anything.
-// Instead, it just records the event distribution.
-static void RecordResiduals(VP8EncIterator* const it,
- const VP8ModeScore* const rd) {
- int x, y, ch;
- VP8Residual res;
- VP8Encoder* const enc = it->enc_;
-
- VP8IteratorNzToBytes(it);
-
- if (it->mb_->type_ == 1) { // i16x16
- InitResidual(0, 1, enc, &res);
- SetResidualCoeffs(rd->y_dc_levels, &res);
- it->top_nz_[8] = it->left_nz_[8] =
- RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res);
- InitResidual(1, 0, enc, &res);
- } else {
- InitResidual(0, 3, enc, &res);
- }
-
- // luma-AC
- for (y = 0; y < 4; ++y) {
- for (x = 0; x < 4; ++x) {
- const int ctx = it->top_nz_[x] + it->left_nz_[y];
- SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
- it->top_nz_[x] = it->left_nz_[y] = RecordCoeffs(ctx, &res);
- }
- }
-
- // U/V
- InitResidual(0, 2, enc, &res);
- for (ch = 0; ch <= 2; ch += 2) {
- for (y = 0; y < 2; ++y) {
- for (x = 0; x < 2; ++x) {
- const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
- SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
- it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
- RecordCoeffs(ctx, &res);
- }
- }
- }
-
- VP8IteratorBytesToNz(it);
-}
-
-//------------------------------------------------------------------------------
-// Token buffer
-
-#if !defined(DISABLE_TOKEN_BUFFER)
-
-static void RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,
- VP8TBuffer* const tokens) {
- int x, y, ch;
- VP8Residual res;
- VP8Encoder* const enc = it->enc_;
-
- VP8IteratorNzToBytes(it);
- if (it->mb_->type_ == 1) { // i16x16
- const int ctx = it->top_nz_[8] + it->left_nz_[8];
- InitResidual(0, 1, enc, &res);
- SetResidualCoeffs(rd->y_dc_levels, &res);
- it->top_nz_[8] = it->left_nz_[8] =
- VP8RecordCoeffTokens(ctx, 1,
- res.first, res.last, res.coeffs, tokens);
- RecordCoeffs(ctx, &res);
- InitResidual(1, 0, enc, &res);
- } else {
- InitResidual(0, 3, enc, &res);
- }
-
- // luma-AC
- for (y = 0; y < 4; ++y) {
- for (x = 0; x < 4; ++x) {
- const int ctx = it->top_nz_[x] + it->left_nz_[y];
- SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
- it->top_nz_[x] = it->left_nz_[y] =
- VP8RecordCoeffTokens(ctx, res.coeff_type,
- res.first, res.last, res.coeffs, tokens);
- RecordCoeffs(ctx, &res);
- }
- }
-
- // U/V
- InitResidual(0, 2, enc, &res);
- for (ch = 0; ch <= 2; ch += 2) {
- for (y = 0; y < 2; ++y) {
- for (x = 0; x < 2; ++x) {
- const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
- SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
- it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
- VP8RecordCoeffTokens(ctx, 2,
- res.first, res.last, res.coeffs, tokens);
- RecordCoeffs(ctx, &res);
- }
- }
- }
- VP8IteratorBytesToNz(it);
-}
-
-#endif // !DISABLE_TOKEN_BUFFER
-
-//------------------------------------------------------------------------------
-// ExtraInfo map / Debug function
-
-#if SEGMENT_VISU
-static void SetBlock(uint8_t* p, int value, int size) {
- int y;
- for (y = 0; y < size; ++y) {
- SbMemorySet(p, value, size);
- p += BPS;
- }
-}
-#endif
-
-static void ResetSSE(VP8Encoder* const enc) {
- enc->sse_[0] = 0;
- enc->sse_[1] = 0;
- enc->sse_[2] = 0;
- // Note: enc->sse_[3] is managed by alpha.c
- enc->sse_count_ = 0;
-}
-
-static void StoreSSE(const VP8EncIterator* const it) {
- VP8Encoder* const enc = it->enc_;
- const uint8_t* const in = it->yuv_in_;
- const uint8_t* const out = it->yuv_out_;
- // Note: not totally accurate at boundary. And doesn't include in-loop filter.
- enc->sse_[0] += VP8SSE16x16(in + Y_OFF, out + Y_OFF);
- enc->sse_[1] += VP8SSE8x8(in + U_OFF, out + U_OFF);
- enc->sse_[2] += VP8SSE8x8(in + V_OFF, out + V_OFF);
- enc->sse_count_ += 16 * 16;
-}
-
-static void StoreSideInfo(const VP8EncIterator* const it) {
- VP8Encoder* const enc = it->enc_;
- const VP8MBInfo* const mb = it->mb_;
- WebPPicture* const pic = enc->pic_;
-
- if (pic->stats != NULL) {
- StoreSSE(it);
- enc->block_count_[0] += (mb->type_ == 0);
- enc->block_count_[1] += (mb->type_ == 1);
- enc->block_count_[2] += (mb->skip_ != 0);
- }
-
- if (pic->extra_info != NULL) {
- uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_];
- switch (pic->extra_info_type) {
- case 1: *info = mb->type_; break;
- case 2: *info = mb->segment_; break;
- case 3: *info = enc->dqm_[mb->segment_].quant_; break;
- case 4: *info = (mb->type_ == 1) ? it->preds_[0] : 0xff; break;
- case 5: *info = mb->uv_mode_; break;
- case 6: {
- const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3);
- *info = (b > 255) ? 255 : b; break;
- }
- case 7: *info = mb->alpha_; break;
- default: *info = 0; break;
- };
- }
-#if SEGMENT_VISU // visualize segments and prediction modes
- SetBlock(it->yuv_out_ + Y_OFF, mb->segment_ * 64, 16);
- SetBlock(it->yuv_out_ + U_OFF, it->preds_[0] * 64, 8);
- SetBlock(it->yuv_out_ + V_OFF, mb->uv_mode_ * 64, 8);
-#endif
-}
-
-//------------------------------------------------------------------------------
-// StatLoop(): only collect statistics (number of skips, token usage, ...).
-// This is used for deciding optimal probabilities. It also modifies the
-// quantizer value if some target (size, PNSR) was specified.
-
-#define kHeaderSizeEstimate (15 + 20 + 10) // TODO: fix better
-
-static void SetLoopParams(VP8Encoder* const enc, float q) {
- // Make sure the quality parameter is inside valid bounds
- if (q < 0.) {
- q = 0;
- } else if (q > 100.) {
- q = 100;
- }
-
- VP8SetSegmentParams(enc, q); // setup segment quantizations and filters
- SetSegmentProbas(enc); // compute segment probabilities
-
- ResetStats(enc);
- ResetTokenStats(enc);
-
- ResetSSE(enc);
-}
-
-static int OneStatPass(VP8Encoder* const enc, float q, VP8RDLevel rd_opt,
- int nb_mbs, float* const PSNR, int percent_delta) {
- VP8EncIterator it;
- uint64_t size = 0;
- uint64_t distortion = 0;
- const uint64_t pixel_count = nb_mbs * 384;
-
- SetLoopParams(enc, q);
-
- VP8IteratorInit(enc, &it);
- do {
- VP8ModeScore info;
- VP8IteratorImport(&it);
- if (VP8Decimate(&it, &info, rd_opt)) {
- // Just record the number of skips and act like skip_proba is not used.
- enc->proba_.nb_skip_++;
- }
- RecordResiduals(&it, &info);
- size += info.R;
- distortion += info.D;
- if (percent_delta && !VP8IteratorProgress(&it, percent_delta))
- return 0;
- } while (VP8IteratorNext(&it, it.yuv_out_) && --nb_mbs > 0);
- size += FinalizeSkipProba(enc);
- size += FinalizeTokenProbas(&enc->proba_);
- size += enc->segment_hdr_.size_;
- size = ((size + 1024) >> 11) + kHeaderSizeEstimate;
-
- if (PSNR) {
- *PSNR = (float)(10.* log10(255. * 255. * pixel_count / distortion));
- }
- return (int)size;
-}
-
-// successive refinement increments.
-static const int dqs[] = { 20, 15, 10, 8, 6, 4, 2, 1, 0 };
-
-static int StatLoop(VP8Encoder* const enc) {
- const int method = enc->method_;
- const int do_search = enc->do_search_;
- const int fast_probe = ((method == 0 || method == 3) && !do_search);
- float q = enc->config_->quality;
- const int max_passes = enc->config_->pass;
- const int task_percent = 20;
- const int percent_per_pass = (task_percent + max_passes / 2) / max_passes;
- const int final_percent = enc->percent_ + task_percent;
- int pass;
- int nb_mbs;
-
- // Fast mode: quick analysis pass over few mbs. Better than nothing.
- nb_mbs = enc->mb_w_ * enc->mb_h_;
- if (fast_probe) {
- if (method == 3) { // we need more stats for method 3 to be reliable.
- nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;
- } else {
- nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50;
- }
- }
-
- // No target size: just do several pass without changing 'q'
- if (!do_search) {
- for (pass = 0; pass < max_passes; ++pass) {
- const VP8RDLevel rd_opt = (method >= 3) ? RD_OPT_BASIC : RD_OPT_NONE;
- if (!OneStatPass(enc, q, rd_opt, nb_mbs, NULL, percent_per_pass)) {
- return 0;
- }
- }
- } else {
- // binary search for a size close to target
- for (pass = 0; pass < max_passes && (dqs[pass] > 0); ++pass) {
- float PSNR;
- int criterion;
- const int size = OneStatPass(enc, q, RD_OPT_BASIC, nb_mbs, &PSNR,
- percent_per_pass);
-#if DEBUG_SEARCH
- printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q);
-#endif
- if (size == 0) return 0;
- if (enc->config_->target_PSNR > 0) {
- criterion = (PSNR < enc->config_->target_PSNR);
- } else {
- criterion = (size < enc->config_->target_size);
- }
- // dichotomize
- if (criterion) {
- q += dqs[pass];
- } else {
- q -= dqs[pass];
- }
- }
- }
- VP8CalculateLevelCosts(&enc->proba_); // finalize costs
- return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);
-}
-
-//------------------------------------------------------------------------------
-// Main loops
-//
-
-static const int kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 };
-
-static int PreLoopInitialize(VP8Encoder* const enc) {
- int p;
- int ok = 1;
- const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4];
- const int bytes_per_parts =
- enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_;
- // Initialize the bit-writers
- for (p = 0; ok && p < enc->num_parts_; ++p) {
- ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts);
- }
- if (!ok) VP8EncFreeBitWriters(enc); // malloc error occurred
- return ok;
-}
-
-static int PostLoopFinalize(VP8EncIterator* const it, int ok) {
- VP8Encoder* const enc = it->enc_;
- if (ok) { // Finalize the partitions, check for extra errors.
- int p;
- for (p = 0; p < enc->num_parts_; ++p) {
- VP8BitWriterFinish(enc->parts_ + p);
- ok &= !enc->parts_[p].error_;
- }
- }
-
- if (ok) { // All good. Finish up.
- if (enc->pic_->stats) { // finalize byte counters...
- int i, s;
- for (i = 0; i <= 2; ++i) {
- for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
- enc->residual_bytes_[i][s] = (int)((it->bit_count_[s][i] + 7) >> 3);
- }
- }
- }
- VP8AdjustFilterStrength(it); // ...and store filter stats.
- } else {
- // Something bad happened -> need to do some memory cleanup.
- VP8EncFreeBitWriters(enc);
- }
- return ok;
-}
-
-//------------------------------------------------------------------------------
-// VP8EncLoop(): does the final bitstream coding.
-
-static void ResetAfterSkip(VP8EncIterator* const it) {
- if (it->mb_->type_ == 1) {
- *it->nz_ = 0; // reset all predictors
- it->left_nz_[8] = 0;
- } else {
- *it->nz_ &= (1 << 24); // preserve the dc_nz bit
- }
-}
-
-int VP8EncLoop(VP8Encoder* const enc) {
- VP8EncIterator it;
- int ok = PreLoopInitialize(enc);
- if (!ok) return 0;
-
- StatLoop(enc); // stats-collection loop
-
- VP8IteratorInit(enc, &it);
- VP8InitFilter(&it);
- do {
- VP8ModeScore info;
- const int dont_use_skip = !enc->proba_.use_skip_proba_;
- const VP8RDLevel rd_opt = enc->rd_opt_level_;
-
- VP8IteratorImport(&it);
- // Warning! order is important: first call VP8Decimate() and
- // *then* decide how to code the skip decision if there's one.
- if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {
- CodeResiduals(it.bw_, &it, &info);
- } else { // reset predictors after a skip
- ResetAfterSkip(&it);
- }
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (enc->use_layer_) {
- VP8EncCodeLayerBlock(&it);
- }
-#endif
- StoreSideInfo(&it);
- VP8StoreFilterStats(&it);
- VP8IteratorExport(&it);
- ok = VP8IteratorProgress(&it, 20);
- } while (ok && VP8IteratorNext(&it, it.yuv_out_));
-
- return PostLoopFinalize(&it, ok);
-}
-
-//------------------------------------------------------------------------------
-// Single pass using Token Buffer.
-
-#if !defined(DISABLE_TOKEN_BUFFER)
-
-#define MIN_COUNT 96 // minimum number of macroblocks before updating stats
-
-int VP8EncTokenLoop(VP8Encoder* const enc) {
- int ok;
- // Roughly refresh the proba height times per pass
- int max_count = (enc->mb_w_ * enc->mb_h_) >> 3;
- int cnt;
- VP8EncIterator it;
- VP8Proba* const proba = &enc->proba_;
- const VP8RDLevel rd_opt = enc->rd_opt_level_;
-
- if (max_count < MIN_COUNT) max_count = MIN_COUNT;
- cnt = max_count;
-
- SB_DCHECK(enc->num_parts_ == 1);
- SB_DCHECK(enc->use_tokens_);
- SB_DCHECK(proba->use_skip_proba_ == 0);
- SB_DCHECK(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful
- SB_DCHECK(!enc->do_search_); // TODO(skal): handle pass and dichotomy
-
- SetLoopParams(enc, enc->config_->quality);
-
- ok = PreLoopInitialize(enc);
- if (!ok) return 0;
-
- VP8IteratorInit(enc, &it);
- VP8InitFilter(&it);
- do {
- VP8ModeScore info;
- VP8IteratorImport(&it);
- if (--cnt < 0) {
- FinalizeTokenProbas(proba);
- VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt
- cnt = max_count;
- }
- VP8Decimate(&it, &info, rd_opt);
- RecordTokens(&it, &info, &enc->tokens_);
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (enc->use_layer_) {
- VP8EncCodeLayerBlock(&it);
- }
-#endif
- StoreSideInfo(&it);
- VP8StoreFilterStats(&it);
- VP8IteratorExport(&it);
- ok = VP8IteratorProgress(&it, 20);
- } while (ok && VP8IteratorNext(&it, it.yuv_out_));
-
- ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
-
- if (ok) {
- FinalizeTokenProbas(proba);
- ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0,
- (const uint8_t*)proba->coeffs_, 1);
- }
-
- return PostLoopFinalize(&it, ok);
-}
-
-#else
-
-int VP8EncTokenLoop(VP8Encoder* const enc) {
- (void)enc;
- return 0; // we shouldn't be here.
-}
-
-#endif // DISABLE_TOKEN_BUFFER
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/histogram.c b/src/third_party/libwebp/enc/histogram.c
deleted file mode 100644
index 474c377..0000000
--- a/src/third_party/libwebp/enc/histogram.c
+++ /dev/null
@@ -1,519 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Author: Jyrki Alakuijala (jyrki@google.com)
-//
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <math.h>
-#include <stdio.h>
-#endif
-
-#include "./backward_references.h"
-#include "./histogram.h"
-#include "../dsp/lossless.h"
-#include "../utils/utils.h"
-
-static void HistogramClear(VP8LHistogram* const p) {
- SbMemorySet(p->literal_, 0, sizeof(p->literal_));
- SbMemorySet(p->red_, 0, sizeof(p->red_));
- SbMemorySet(p->blue_, 0, sizeof(p->blue_));
- SbMemorySet(p->alpha_, 0, sizeof(p->alpha_));
- SbMemorySet(p->distance_, 0, sizeof(p->distance_));
- p->bit_cost_ = 0;
-}
-
-void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
- VP8LHistogram* const histo) {
- int i;
- for (i = 0; i < refs->size; ++i) {
- VP8LHistogramAddSinglePixOrCopy(histo, &refs->refs[i]);
- }
-}
-
-void VP8LHistogramCreate(VP8LHistogram* const p,
- const VP8LBackwardRefs* const refs,
- int palette_code_bits) {
- if (palette_code_bits >= 0) {
- p->palette_code_bits_ = palette_code_bits;
- }
- HistogramClear(p);
- VP8LHistogramStoreRefs(refs, p);
-}
-
-void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) {
- p->palette_code_bits_ = palette_code_bits;
- HistogramClear(p);
-}
-
-VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
- int i;
- VP8LHistogramSet* set;
- VP8LHistogram* bulk;
- const uint64_t total_size = sizeof(*set)
- + (uint64_t)size * sizeof(*set->histograms)
- + (uint64_t)size * sizeof(**set->histograms);
- uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
- if (memory == NULL) return NULL;
-
- set = (VP8LHistogramSet*)memory;
- memory += sizeof(*set);
- set->histograms = (VP8LHistogram**)memory;
- memory += size * sizeof(*set->histograms);
- bulk = (VP8LHistogram*)memory;
- set->max_size = size;
- set->size = size;
- for (i = 0; i < size; ++i) {
- set->histograms[i] = bulk + i;
- VP8LHistogramInit(set->histograms[i], cache_bits);
- }
- return set;
-}
-
-// -----------------------------------------------------------------------------
-
-void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
- const PixOrCopy* const v) {
- if (PixOrCopyIsLiteral(v)) {
- ++histo->alpha_[PixOrCopyLiteral(v, 3)];
- ++histo->red_[PixOrCopyLiteral(v, 2)];
- ++histo->literal_[PixOrCopyLiteral(v, 1)];
- ++histo->blue_[PixOrCopyLiteral(v, 0)];
- } else if (PixOrCopyIsCacheIdx(v)) {
- int literal_ix = 256 + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
- ++histo->literal_[literal_ix];
- } else {
- int code, extra_bits_count, extra_bits_value;
- PrefixEncode(PixOrCopyLength(v),
- &code, &extra_bits_count, &extra_bits_value);
- ++histo->literal_[256 + code];
- PrefixEncode(PixOrCopyDistance(v),
- &code, &extra_bits_count, &extra_bits_value);
- ++histo->distance_[code];
- }
-}
-
-static double BitsEntropy(const int* const array, int n) {
- double retval = 0.;
- int sum = 0;
- int nonzeros = 0;
- int max_val = 0;
- int i;
- double mix;
- for (i = 0; i < n; ++i) {
- if (array[i] != 0) {
- sum += array[i];
- ++nonzeros;
- retval -= VP8LFastSLog2(array[i]);
- if (max_val < array[i]) {
- max_val = array[i];
- }
- }
- }
- retval += VP8LFastSLog2(sum);
-
- if (nonzeros < 5) {
- if (nonzeros <= 1) {
- return 0;
- }
- // Two symbols, they will be 0 and 1 in a Huffman code.
- // Let's mix in a bit of entropy to favor good clustering when
- // distributions of these are combined.
- if (nonzeros == 2) {
- return 0.99 * sum + 0.01 * retval;
- }
- // No matter what the entropy says, we cannot be better than min_limit
- // with Huffman coding. I am mixing a bit of entropy into the
- // min_limit since it produces much better (~0.5 %) compression results
- // perhaps because of better entropy clustering.
- if (nonzeros == 3) {
- mix = 0.95;
- } else {
- mix = 0.7; // nonzeros == 4.
- }
- } else {
- mix = 0.627;
- }
-
- {
- double min_limit = 2 * sum - max_val;
- min_limit = mix * min_limit + (1.0 - mix) * retval;
- return (retval < min_limit) ? min_limit : retval;
- }
-}
-
-// Returns the cost encode the rle-encoded entropy code.
-// The constants in this function are experimental.
-static double HuffmanCost(const int* const population, int length) {
- // Small bias because Huffman code length is typically not stored in
- // full length.
- static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3;
- static const double kSmallBias = 9.1;
- double retval = kHuffmanCodeOfHuffmanCodeSize - kSmallBias;
- int streak = 0;
- int i = 0;
- for (; i < length - 1; ++i) {
- ++streak;
- if (population[i] == population[i + 1]) {
- continue;
- }
- last_streak_hack:
- // population[i] points now to the symbol in the streak of same values.
- if (streak > 3) {
- if (population[i] == 0) {
- retval += 1.5625 + 0.234375 * streak;
- } else {
- retval += 2.578125 + 0.703125 * streak;
- }
- } else {
- if (population[i] == 0) {
- retval += 1.796875 * streak;
- } else {
- retval += 3.28125 * streak;
- }
- }
- streak = 0;
- }
- if (i == length - 1) {
- ++streak;
- goto last_streak_hack;
- }
- return retval;
-}
-
-static double PopulationCost(const int* const population, int length) {
- return BitsEntropy(population, length) + HuffmanCost(population, length);
-}
-
-static double ExtraCost(const int* const population, int length) {
- int i;
- double cost = 0.;
- for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2];
- return cost;
-}
-
-// Estimates the Entropy + Huffman + other block overhead size cost.
-double VP8LHistogramEstimateBits(const VP8LHistogram* const p) {
- return PopulationCost(p->literal_, VP8LHistogramNumCodes(p))
- + PopulationCost(p->red_, 256)
- + PopulationCost(p->blue_, 256)
- + PopulationCost(p->alpha_, 256)
- + PopulationCost(p->distance_, NUM_DISTANCE_CODES)
- + ExtraCost(p->literal_ + 256, NUM_LENGTH_CODES)
- + ExtraCost(p->distance_, NUM_DISTANCE_CODES);
-}
-
-double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
- return BitsEntropy(p->literal_, VP8LHistogramNumCodes(p))
- + BitsEntropy(p->red_, 256)
- + BitsEntropy(p->blue_, 256)
- + BitsEntropy(p->alpha_, 256)
- + BitsEntropy(p->distance_, NUM_DISTANCE_CODES)
- + ExtraCost(p->literal_ + 256, NUM_LENGTH_CODES)
- + ExtraCost(p->distance_, NUM_DISTANCE_CODES);
-}
-
-// -----------------------------------------------------------------------------
-// Various histogram combine/cost-eval functions
-
-// Adds 'in' histogram to 'out'
-static void HistogramAdd(const VP8LHistogram* const in,
- VP8LHistogram* const out) {
- int i;
- for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
- out->literal_[i] += in->literal_[i];
- }
- for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
- out->distance_[i] += in->distance_[i];
- }
- for (i = 0; i < 256; ++i) {
- out->red_[i] += in->red_[i];
- out->blue_[i] += in->blue_[i];
- out->alpha_[i] += in->alpha_[i];
- }
-}
-
-// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing
-// to the threshold value 'cost_threshold'. The score returned is
-// Score = C(a+b) - C(a) - C(b), where C(a) + C(b) is known and fixed.
-// Since the previous score passed is 'cost_threshold', we only need to compare
-// the partial cost against 'cost_threshold + C(a) + C(b)' to possibly bail-out
-// early.
-static double HistogramAddEval(const VP8LHistogram* const a,
- const VP8LHistogram* const b,
- VP8LHistogram* const out,
- double cost_threshold) {
- double cost = 0;
- const double sum_cost = a->bit_cost_ + b->bit_cost_;
- int i;
-
- cost_threshold += sum_cost;
-
- // palette_code_bits_ is part of the cost evaluation for literal_.
- // TODO(skal): remove/simplify this palette_code_bits_?
- out->palette_code_bits_ =
- (a->palette_code_bits_ > b->palette_code_bits_) ? a->palette_code_bits_ :
- b->palette_code_bits_;
- for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
- out->literal_[i] = a->literal_[i] + b->literal_[i];
- }
- cost += PopulationCost(out->literal_, VP8LHistogramNumCodes(out));
- cost += ExtraCost(out->literal_ + 256, NUM_LENGTH_CODES);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < 256; ++i) out->red_[i] = a->red_[i] + b->red_[i];
- cost += PopulationCost(out->red_, 256);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < 256; ++i) out->blue_[i] = a->blue_[i] + b->blue_[i];
- cost += PopulationCost(out->blue_, 256);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
- out->distance_[i] = a->distance_[i] + b->distance_[i];
- }
- cost += PopulationCost(out->distance_, NUM_DISTANCE_CODES);
- cost += ExtraCost(out->distance_, NUM_DISTANCE_CODES);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < 256; ++i) out->alpha_[i] = a->alpha_[i] + b->alpha_[i];
- cost += PopulationCost(out->alpha_, 256);
-
- out->bit_cost_ = cost;
- return cost - sum_cost;
-}
-
-// Same as HistogramAddEval(), except that the resulting histogram
-// is not stored. Only the cost C(a+b) - C(a) is evaluated. We omit
-// the term C(b) which is constant over all the evaluations.
-static double HistogramAddThresh(const VP8LHistogram* const a,
- const VP8LHistogram* const b,
- double cost_threshold) {
- int tmp[PIX_OR_COPY_CODES_MAX]; // <= max storage we'll need
- int i;
- double cost = -a->bit_cost_;
-
- for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
- tmp[i] = a->literal_[i] + b->literal_[i];
- }
- // note that the tests are ordered so that the usually largest
- // cost shares come first.
- cost += PopulationCost(tmp, VP8LHistogramNumCodes(a));
- cost += ExtraCost(tmp + 256, NUM_LENGTH_CODES);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < 256; ++i) tmp[i] = a->red_[i] + b->red_[i];
- cost += PopulationCost(tmp, 256);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < 256; ++i) tmp[i] = a->blue_[i] + b->blue_[i];
- cost += PopulationCost(tmp, 256);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
- tmp[i] = a->distance_[i] + b->distance_[i];
- }
- cost += PopulationCost(tmp, NUM_DISTANCE_CODES);
- cost += ExtraCost(tmp, NUM_DISTANCE_CODES);
- if (cost > cost_threshold) return cost;
-
- for (i = 0; i < 256; ++i) tmp[i] = a->alpha_[i] + b->alpha_[i];
- cost += PopulationCost(tmp, 256);
-
- return cost;
-}
-
-// -----------------------------------------------------------------------------
-
-static void HistogramBuildImage(int xsize, int histo_bits,
- const VP8LBackwardRefs* const backward_refs,
- VP8LHistogramSet* const image) {
- int i;
- int x = 0, y = 0;
- const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits);
- VP8LHistogram** const histograms = image->histograms;
- SB_DCHECK(histo_bits > 0);
- for (i = 0; i < backward_refs->size; ++i) {
- const PixOrCopy* const v = &backward_refs->refs[i];
- const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits);
- VP8LHistogramAddSinglePixOrCopy(histograms[ix], v);
- x += PixOrCopyLength(v);
- while (x >= xsize) {
- x -= xsize;
- ++y;
- }
- }
-}
-
-static uint32_t MyRand(uint32_t *seed) {
- *seed *= 16807U;
- if (*seed == 0) {
- *seed = 1;
- }
- return *seed;
-}
-
-static int HistogramCombine(const VP8LHistogramSet* const in,
- VP8LHistogramSet* const out, int iter_mult,
- int num_pairs, int num_tries_no_success) {
- int ok = 0;
- int i, iter;
- uint32_t seed = 0;
- int tries_with_no_success = 0;
- int out_size = in->size;
- const int outer_iters = in->size * iter_mult;
- const int min_cluster_size = 2;
- VP8LHistogram* const histos = (VP8LHistogram*)SbMemoryAllocate(2 * sizeof(*histos));
- VP8LHistogram* cur_combo = histos + 0; // trial merged histogram
- VP8LHistogram* best_combo = histos + 1; // best merged histogram so far
- if (histos == NULL) goto End;
-
- // Copy histograms from in[] to out[].
- SB_DCHECK(in->size <= out->size);
- for (i = 0; i < in->size; ++i) {
- in->histograms[i]->bit_cost_ = VP8LHistogramEstimateBits(in->histograms[i]);
- *out->histograms[i] = *in->histograms[i];
- }
-
- // Collapse similar histograms in 'out'.
- for (iter = 0; iter < outer_iters && out_size >= min_cluster_size; ++iter) {
- double best_cost_diff = 0.;
- int best_idx1 = -1, best_idx2 = 1;
- int j;
- const int num_tries = (num_pairs < out_size) ? num_pairs : out_size;
- seed += iter;
- for (j = 0; j < num_tries; ++j) {
- double curr_cost_diff;
- // Choose two histograms at random and try to combine them.
- const uint32_t idx1 = MyRand(&seed) % out_size;
- const uint32_t tmp = (j & 7) + 1;
- const uint32_t diff = (tmp < 3) ? tmp : MyRand(&seed) % (out_size - 1);
- const uint32_t idx2 = (idx1 + diff + 1) % out_size;
- if (idx1 == idx2) {
- continue;
- }
- // Calculate cost reduction on combining.
- curr_cost_diff = HistogramAddEval(out->histograms[idx1],
- out->histograms[idx2],
- cur_combo, best_cost_diff);
- if (curr_cost_diff < best_cost_diff) { // found a better pair?
- { // swap cur/best combo histograms
- VP8LHistogram* const tmp_histo = cur_combo;
- cur_combo = best_combo;
- best_combo = tmp_histo;
- }
- best_cost_diff = curr_cost_diff;
- best_idx1 = idx1;
- best_idx2 = idx2;
- }
- }
-
- if (best_idx1 >= 0) {
- *out->histograms[best_idx1] = *best_combo;
- // swap best_idx2 slot with last one (which is now unused)
- --out_size;
- if (best_idx2 != out_size) {
- out->histograms[best_idx2] = out->histograms[out_size];
- out->histograms[out_size] = NULL; // just for sanity check.
- }
- tries_with_no_success = 0;
- }
- if (++tries_with_no_success >= num_tries_no_success) {
- break;
- }
- }
- out->size = out_size;
- ok = 1;
-
- End:
- SbMemoryDeallocate(histos);
- return ok;
-}
-
-// -----------------------------------------------------------------------------
-// Histogram refinement
-
-// What is the bit cost of moving square_histogram from cur_symbol to candidate.
-static double HistogramDistance(const VP8LHistogram* const square_histogram,
- const VP8LHistogram* const candidate,
- double cost_threshold) {
- return HistogramAddThresh(candidate, square_histogram, cost_threshold);
-}
-
-// Find the best 'out' histogram for each of the 'in' histograms.
-// Note: we assume that out[]->bit_cost_ is already up-to-date.
-static void HistogramRemap(const VP8LHistogramSet* const in,
- const VP8LHistogramSet* const out,
- uint16_t* const symbols) {
- int i;
- for (i = 0; i < in->size; ++i) {
- int best_out = 0;
- double best_bits =
- HistogramDistance(in->histograms[i], out->histograms[0], 1.e38);
- int k;
- for (k = 1; k < out->size; ++k) {
- const double cur_bits =
- HistogramDistance(in->histograms[i], out->histograms[k], best_bits);
- if (cur_bits < best_bits) {
- best_bits = cur_bits;
- best_out = k;
- }
- }
- symbols[i] = best_out;
- }
-
- // Recompute each out based on raw and symbols.
- for (i = 0; i < out->size; ++i) {
- HistogramClear(out->histograms[i]);
- }
- for (i = 0; i < in->size; ++i) {
- HistogramAdd(in->histograms[i], out->histograms[symbols[i]]);
- }
-}
-
-int VP8LGetHistoImageSymbols(int xsize, int ysize,
- const VP8LBackwardRefs* const refs,
- int quality, int histo_bits, int cache_bits,
- VP8LHistogramSet* const image_in,
- uint16_t* const histogram_symbols) {
- int ok = 0;
- const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
- const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
- const int histo_image_raw_size = histo_xsize * histo_ysize;
-
- // Heuristic params for HistogramCombine().
- const int num_tries_no_success = 8 + (quality >> 1);
- const int iter_mult = (quality < 27) ? 1 : 1 + ((quality - 27) >> 4);
- const int num_pairs = (quality < 25) ? 10 : (5 * quality) >> 3;
-
- VP8LHistogramSet* const image_out =
- VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits);
- if (image_out == NULL) return 0;
-
- // Build histogram image.
- HistogramBuildImage(xsize, histo_bits, refs, image_out);
- // Collapse similar histograms.
- if (!HistogramCombine(image_out, image_in, iter_mult, num_pairs,
- num_tries_no_success)) {
- goto Error;
- }
- // Find the optimal map from original histograms to the final ones.
- HistogramRemap(image_out, image_in, histogram_symbols);
- ok = 1;
-
-Error:
- SbMemoryDeallocate(image_out);
- return ok;
-}
diff --git a/src/third_party/libwebp/enc/histogram.h b/src/third_party/libwebp/enc/histogram.h
deleted file mode 100644
index 70831a4..0000000
--- a/src/third_party/libwebp/enc/histogram.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Author: Jyrki Alakuijala (jyrki@google.com)
-//
-// Models the histograms of literal and distance codes.
-
-#ifndef WEBP_ENC_HISTOGRAM_H_
-#define WEBP_ENC_HISTOGRAM_H_
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#endif
-
-#include "./backward_references.h"
-#include "../webp/format_constants.h"
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// A simple container for histograms of data.
-typedef struct {
- // literal_ contains green literal, palette-code and
- // copy-length-prefix histogram
- int literal_[PIX_OR_COPY_CODES_MAX];
- int red_[256];
- int blue_[256];
- int alpha_[256];
- // Backward reference prefix-code histogram.
- int distance_[NUM_DISTANCE_CODES];
- int palette_code_bits_;
- double bit_cost_; // cached value of VP8LHistogramEstimateBits(this)
-} VP8LHistogram;
-
-// Collection of histograms with fixed capacity, allocated as one
-// big memory chunk. Can be destroyed by simply calling 'SbMemoryDeallocate()'.
-typedef struct {
- int size; // number of slots currently in use
- int max_size; // maximum capacity
- VP8LHistogram** histograms;
-} VP8LHistogramSet;
-
-// Create the histogram.
-//
-// The input data is the PixOrCopy data, which models the literals, stop
-// codes and backward references (both distances and lengths). Also: if
-// palette_code_bits is >= 0, initialize the histogram with this value.
-void VP8LHistogramCreate(VP8LHistogram* const p,
- const VP8LBackwardRefs* const refs,
- int palette_code_bits);
-
-// Set the palette_code_bits and reset the stats.
-void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
-
-// Collect all the references into a histogram (without reset)
-void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
- VP8LHistogram* const histo);
-
-// Allocate an array of pointer to histograms, allocated and initialized
-// using 'cache_bits'. Return NULL in case of memory error.
-VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits);
-
-// Accumulate a token 'v' into a histogram.
-void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
- const PixOrCopy* const v);
-
-// Estimate how many bits the combined entropy of literals and distance
-// approximately maps to.
-double VP8LHistogramEstimateBits(const VP8LHistogram* const p);
-
-// This function estimates the cost in bits excluding the bits needed to
-// represent the entropy code itself.
-double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p);
-
-static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) {
- return 256 + NUM_LENGTH_CODES +
- ((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0);
-}
-
-// Builds the histogram image.
-int VP8LGetHistoImageSymbols(int xsize, int ysize,
- const VP8LBackwardRefs* const refs,
- int quality, int histogram_bits, int cache_bits,
- VP8LHistogramSet* const image_in,
- uint16_t* const histogram_symbols);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-}
-#endif
-
-#endif // WEBP_ENC_HISTOGRAM_H_
diff --git a/src/third_party/libwebp/enc/iterator.c b/src/third_party/libwebp/enc/iterator.c
deleted file mode 100644
index 2cd1bc0..0000000
--- a/src/third_party/libwebp/enc/iterator.c
+++ /dev/null
@@ -1,428 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// VP8Iterator: block iterator
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include <string.h>
-
-#include "./vp8enci.h"
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// VP8Iterator
-//------------------------------------------------------------------------------
-
-static void InitLeft(VP8EncIterator* const it) {
- const VP8Encoder* const enc = it->enc_;
- enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] =
- (it->y_ > 0) ? 129 : 127;
- SbMemorySet(enc->y_left_, 129, 16);
- SbMemorySet(enc->u_left_, 129, 8);
- SbMemorySet(enc->v_left_, 129, 8);
- it->left_nz_[8] = 0;
-}
-
-static void InitTop(VP8EncIterator* const it) {
- const VP8Encoder* const enc = it->enc_;
- const size_t top_size = enc->mb_w_ * 16;
- SbMemorySet(enc->y_top_, 127, 2 * top_size);
- SbMemorySet(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_));
-}
-
-void VP8IteratorReset(VP8EncIterator* const it) {
- VP8Encoder* const enc = it->enc_;
- it->x_ = 0;
- it->y_ = 0;
- it->y_offset_ = 0;
- it->uv_offset_ = 0;
- it->mb_ = enc->mb_info_;
- it->preds_ = enc->preds_;
- it->nz_ = enc->nz_;
- it->bw_ = &enc->parts_[0];
- it->done_ = enc->mb_w_* enc->mb_h_;
- InitTop(it);
- InitLeft(it);
- SbMemorySet(it->bit_count_, 0, sizeof(it->bit_count_));
- it->do_trellis_ = 0;
-}
-
-void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
- it->enc_ = enc;
- it->y_stride_ = enc->pic_->y_stride;
- it->uv_stride_ = enc->pic_->uv_stride;
- // TODO(later): for multithreading, these should be owned by 'it'.
- it->yuv_in_ = enc->yuv_in_;
- it->yuv_out_ = enc->yuv_out_;
- it->yuv_out2_ = enc->yuv_out2_;
- it->yuv_p_ = enc->yuv_p_;
- it->lf_stats_ = enc->lf_stats_;
- it->percent0_ = enc->percent_;
- VP8IteratorReset(it);
-}
-
-int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
- VP8Encoder* const enc = it->enc_;
- if (delta && enc->pic_->progress_hook) {
- const int percent = (enc->mb_h_ <= 1)
- ? it->percent0_
- : it->percent0_ + delta * it->y_ / (enc->mb_h_ - 1);
- return WebPReportProgress(enc->pic_, percent, &enc->percent_);
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Import the source samples into the cache. Takes care of replicating
-// boundary pixels if necessary.
-
-static void ImportBlock(const uint8_t* src, int src_stride,
- uint8_t* dst, int w, int h, int size) {
- int i;
- for (i = 0; i < h; ++i) {
- SbMemoryCopy(dst, src, w);
- if (w < size) {
- SbMemorySet(dst + w, dst[w - 1], size - w);
- }
- dst += BPS;
- src += src_stride;
- }
- for (i = h; i < size; ++i) {
- SbMemoryCopy(dst, dst - BPS, size);
- dst += BPS;
- }
-}
-
-void VP8IteratorImport(const VP8EncIterator* const it) {
- const VP8Encoder* const enc = it->enc_;
- const int x = it->x_, y = it->y_;
- const WebPPicture* const pic = enc->pic_;
- const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16;
- const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8;
- const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8;
- uint8_t* const ydst = it->yuv_in_ + Y_OFF;
- uint8_t* const udst = it->yuv_in_ + U_OFF;
- uint8_t* const vdst = it->yuv_in_ + V_OFF;
- int w = (pic->width - x * 16);
- int h = (pic->height - y * 16);
-
- if (w > 16) w = 16;
- if (h > 16) h = 16;
-
- // Luma plane
- ImportBlock(ysrc, pic->y_stride, ydst, w, h, 16);
-
- { // U/V planes
- const int uv_w = (w + 1) >> 1;
- const int uv_h = (h + 1) >> 1;
- ImportBlock(usrc, pic->uv_stride, udst, uv_w, uv_h, 8);
- ImportBlock(vsrc, pic->uv_stride, vdst, uv_w, uv_h, 8);
- }
-}
-
-//------------------------------------------------------------------------------
-// Copy back the compressed samples into user space if requested.
-
-static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride,
- int w, int h) {
- while (h-- > 0) {
- SbMemoryCopy(dst, src, w);
- dst += dst_stride;
- src += BPS;
- }
-}
-
-void VP8IteratorExport(const VP8EncIterator* const it) {
- const VP8Encoder* const enc = it->enc_;
- if (enc->config_->show_compressed) {
- const int x = it->x_, y = it->y_;
- const uint8_t* const ysrc = it->yuv_out_ + Y_OFF;
- const uint8_t* const usrc = it->yuv_out_ + U_OFF;
- const uint8_t* const vsrc = it->yuv_out_ + V_OFF;
- const WebPPicture* const pic = enc->pic_;
- uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16;
- uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8;
- uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8;
- int w = (pic->width - x * 16);
- int h = (pic->height - y * 16);
-
- if (w > 16) w = 16;
- if (h > 16) h = 16;
-
- // Luma plane
- ExportBlock(ysrc, ydst, pic->y_stride, w, h);
-
- { // U/V planes
- const int uv_w = (w + 1) >> 1;
- const int uv_h = (h + 1) >> 1;
- ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h);
- ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// Non-zero contexts setup/teardown
-
-// Nz bits:
-// 0 1 2 3 Y
-// 4 5 6 7
-// 8 9 10 11
-// 12 13 14 15
-// 16 17 U
-// 18 19
-// 20 21 V
-// 22 23
-// 24 DC-intra16
-
-// Convert packed context to byte array
-#define BIT(nz, n) (!!((nz) & (1 << (n))))
-
-void VP8IteratorNzToBytes(VP8EncIterator* const it) {
- const int tnz = it->nz_[0], lnz = it->nz_[-1];
- int* const top_nz = it->top_nz_;
- int* const left_nz = it->left_nz_;
-
- // Top-Y
- top_nz[0] = BIT(tnz, 12);
- top_nz[1] = BIT(tnz, 13);
- top_nz[2] = BIT(tnz, 14);
- top_nz[3] = BIT(tnz, 15);
- // Top-U
- top_nz[4] = BIT(tnz, 18);
- top_nz[5] = BIT(tnz, 19);
- // Top-V
- top_nz[6] = BIT(tnz, 22);
- top_nz[7] = BIT(tnz, 23);
- // DC
- top_nz[8] = BIT(tnz, 24);
-
- // left-Y
- left_nz[0] = BIT(lnz, 3);
- left_nz[1] = BIT(lnz, 7);
- left_nz[2] = BIT(lnz, 11);
- left_nz[3] = BIT(lnz, 15);
- // left-U
- left_nz[4] = BIT(lnz, 17);
- left_nz[5] = BIT(lnz, 19);
- // left-V
- left_nz[6] = BIT(lnz, 21);
- left_nz[7] = BIT(lnz, 23);
- // left-DC is special, iterated separately
-}
-
-void VP8IteratorBytesToNz(VP8EncIterator* const it) {
- uint32_t nz = 0;
- const int* const top_nz = it->top_nz_;
- const int* const left_nz = it->left_nz_;
- // top
- nz |= (top_nz[0] << 12) | (top_nz[1] << 13);
- nz |= (top_nz[2] << 14) | (top_nz[3] << 15);
- nz |= (top_nz[4] << 18) | (top_nz[5] << 19);
- nz |= (top_nz[6] << 22) | (top_nz[7] << 23);
- nz |= (top_nz[8] << 24); // we propagate the _top_ bit, esp. for intra4
- // left
- nz |= (left_nz[0] << 3) | (left_nz[1] << 7);
- nz |= (left_nz[2] << 11);
- nz |= (left_nz[4] << 17) | (left_nz[6] << 21);
-
- *it->nz_ = nz;
-}
-
-#undef BIT
-
-//------------------------------------------------------------------------------
-// Advance to the next position, doing the bookeeping.
-
-int VP8IteratorNext(VP8EncIterator* const it,
- const uint8_t* const block_to_save) {
- VP8Encoder* const enc = it->enc_;
- if (block_to_save) {
- const int x = it->x_, y = it->y_;
- const uint8_t* const ysrc = block_to_save + Y_OFF;
- const uint8_t* const usrc = block_to_save + U_OFF;
- if (x < enc->mb_w_ - 1) { // left
- int i;
- for (i = 0; i < 16; ++i) {
- enc->y_left_[i] = ysrc[15 + i * BPS];
- }
- for (i = 0; i < 8; ++i) {
- enc->u_left_[i] = usrc[7 + i * BPS];
- enc->v_left_[i] = usrc[15 + i * BPS];
- }
- // top-left (before 'top'!)
- enc->y_left_[-1] = enc->y_top_[x * 16 + 15];
- enc->u_left_[-1] = enc->uv_top_[x * 16 + 0 + 7];
- enc->v_left_[-1] = enc->uv_top_[x * 16 + 8 + 7];
- }
- if (y < enc->mb_h_ - 1) { // top
- SbMemoryCopy(enc->y_top_ + x * 16, ysrc + 15 * BPS, 16);
- SbMemoryCopy(enc->uv_top_ + x * 16, usrc + 7 * BPS, 8 + 8);
- }
- }
-
- it->mb_++;
- it->preds_ += 4;
- it->nz_++;
- it->x_++;
- if (it->x_ == enc->mb_w_) {
- it->x_ = 0;
- it->y_++;
- it->bw_ = &enc->parts_[it->y_ & (enc->num_parts_ - 1)];
- it->preds_ = enc->preds_ + it->y_ * 4 * enc->preds_w_;
- it->nz_ = enc->nz_;
- InitLeft(it);
- }
- return (0 < --it->done_);
-}
-
-//------------------------------------------------------------------------------
-// Helper function to set mode properties
-
-void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) {
- uint8_t* preds = it->preds_;
- int y;
- for (y = 0; y < 4; ++y) {
- SbMemorySet(preds, mode, 4);
- preds += it->enc_->preds_w_;
- }
- it->mb_->type_ = 1;
-}
-
-void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) {
- uint8_t* preds = it->preds_;
- int y;
- for (y = 4; y > 0; --y) {
- SbMemoryCopy(preds, modes, 4 * sizeof(*modes));
- preds += it->enc_->preds_w_;
- modes += 4;
- }
- it->mb_->type_ = 0;
-}
-
-void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) {
- it->mb_->uv_mode_ = mode;
-}
-
-void VP8SetSkip(const VP8EncIterator* const it, int skip) {
- it->mb_->skip_ = skip;
-}
-
-void VP8SetSegment(const VP8EncIterator* const it, int segment) {
- it->mb_->segment_ = segment;
-}
-
-//------------------------------------------------------------------------------
-// Intra4x4 sub-blocks iteration
-//
-// We store and update the boundary samples into an array of 37 pixels. They
-// are updated as we iterate and reconstructs each intra4x4 blocks in turn.
-// The position of the samples has the following snake pattern:
-//
-// 16|17 18 19 20|21 22 23 24|25 26 27 28|29 30 31 32|33 34 35 36 <- Top-right
-// --+-----------+-----------+-----------+-----------+
-// 15| 19| 23| 27| 31|
-// 14| 18| 22| 26| 30|
-// 13| 17| 21| 25| 29|
-// 12|13 14 15 16|17 18 19 20|21 22 23 24|25 26 27 28|
-// --+-----------+-----------+-----------+-----------+
-// 11| 15| 19| 23| 27|
-// 10| 14| 18| 22| 26|
-// 9| 13| 17| 21| 25|
-// 8| 9 10 11 12|13 14 15 16|17 18 19 20|21 22 23 24|
-// --+-----------+-----------+-----------+-----------+
-// 7| 11| 15| 19| 23|
-// 6| 10| 14| 18| 22|
-// 5| 9| 13| 17| 21|
-// 4| 5 6 7 8| 9 10 11 12|13 14 15 16|17 18 19 20|
-// --+-----------+-----------+-----------+-----------+
-// 3| 7| 11| 15| 19|
-// 2| 6| 10| 14| 18|
-// 1| 5| 9| 13| 17|
-// 0| 1 2 3 4| 5 6 7 8| 9 10 11 12|13 14 15 16|
-// --+-----------+-----------+-----------+-----------+
-
-// Array to record the position of the top sample to pass to the prediction
-// functions in dsp.c.
-static const uint8_t VP8TopLeftI4[16] = {
- 17, 21, 25, 29,
- 13, 17, 21, 25,
- 9, 13, 17, 21,
- 5, 9, 13, 17
-};
-
-void VP8IteratorStartI4(VP8EncIterator* const it) {
- const VP8Encoder* const enc = it->enc_;
- int i;
-
- it->i4_ = 0; // first 4x4 sub-block
- it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0];
-
- // Import the boundary samples
- for (i = 0; i < 17; ++i) { // left
- it->i4_boundary_[i] = enc->y_left_[15 - i];
- }
- for (i = 0; i < 16; ++i) { // top
- it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i];
- }
- // top-right samples have a special case on the far right of the picture
- if (it->x_ < enc->mb_w_ - 1) {
- for (i = 16; i < 16 + 4; ++i) {
- it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i];
- }
- } else { // else, replicate the last valid pixel four times
- for (i = 16; i < 16 + 4; ++i) {
- it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15];
- }
- }
- VP8IteratorNzToBytes(it); // import the non-zero context
-}
-
-int VP8IteratorRotateI4(VP8EncIterator* const it,
- const uint8_t* const yuv_out) {
- const uint8_t* const blk = yuv_out + VP8Scan[it->i4_];
- uint8_t* const top = it->i4_top_;
- int i;
-
- // Update the cache with 7 fresh samples
- for (i = 0; i <= 3; ++i) {
- top[-4 + i] = blk[i + 3 * BPS]; // store future top samples
- }
- if ((it->i4_ & 3) != 3) { // if not on the right sub-blocks #3, #7, #11, #15
- for (i = 0; i <= 2; ++i) { // store future left samples
- top[i] = blk[3 + (2 - i) * BPS];
- }
- } else { // else replicate top-right samples, as says the specs.
- for (i = 0; i <= 3; ++i) {
- top[i] = top[i + 4];
- }
- }
- // move pointers to next sub-block
- ++it->i4_;
- if (it->i4_ == 16) { // we're done
- return 0;
- }
-
- it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_];
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/layer.c b/src/third_party/libwebp/enc/layer.c
deleted file mode 100644
index c6194c8..0000000
--- a/src/third_party/libwebp/enc/layer.c
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Enhancement layer (for YUV444/422)
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#else
-#include <stdlib.h>
-#endif
-
-#include "./vp8enci.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-
-void VP8EncInitLayer(VP8Encoder* const enc) {
- enc->use_layer_ = (enc->pic_->u0 != NULL);
- enc->layer_data_size_ = 0;
- enc->layer_data_ = NULL;
- if (enc->use_layer_) {
- VP8BitWriterInit(&enc->layer_bw_, enc->mb_w_ * enc->mb_h_ * 3);
- }
-}
-
-void VP8EncCodeLayerBlock(VP8EncIterator* it) {
- (void)it; // remove a warning
-}
-
-int VP8EncFinishLayer(VP8Encoder* const enc) {
- if (enc->use_layer_) {
- enc->layer_data_ = VP8BitWriterFinish(&enc->layer_bw_);
- enc->layer_data_size_ = VP8BitWriterSize(&enc->layer_bw_);
- }
- return 1;
-}
-
-void VP8EncDeleteLayer(VP8Encoder* enc) {
- SbMemoryDeallocate(enc->layer_data_);
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/picture.c b/src/third_party/libwebp/enc/picture.c
deleted file mode 100644
index f94bf7c..0000000
--- a/src/third_party/libwebp/enc/picture.c
+++ /dev/null
@@ -1,1120 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// WebPPicture utils: colorspace conversion, crop, ...
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#include <math.h>
-#endif
-
-#include "./vp8enci.h"
-#include "../utils/rescaler.h"
-#include "../utils/utils.h"
-#include "../dsp/dsp.h"
-#include "../dsp/yuv.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define HALVE(x) (((x) + 1) >> 1)
-#define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP))
-
-static const union {
- uint32_t argb;
- uint8_t bytes[4];
-} test_endian = { 0xff000000u };
-#define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff)
-
-//------------------------------------------------------------------------------
-// WebPPicture
-//------------------------------------------------------------------------------
-
-int WebPPictureAlloc(WebPPicture* picture) {
- if (picture != NULL) {
- const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
- const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
- const int width = picture->width;
- const int height = picture->height;
-
- if (!picture->use_argb) {
- const int y_stride = width;
- const int uv_width = HALVE(width);
- const int uv_height = HALVE(height);
- const int uv_stride = uv_width;
- int uv0_stride = 0;
- int a_width, a_stride;
- uint64_t y_size, uv_size, uv0_size, a_size, total_size;
- uint8_t* mem;
-
- // U/V
- switch (uv_csp) {
- case WEBP_YUV420:
- break;
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- case WEBP_YUV400: // for now, we'll just reset the U/V samples
- break;
- case WEBP_YUV422:
- uv0_stride = uv_width;
- break;
- case WEBP_YUV444:
- uv0_stride = width;
- break;
-#endif
- default:
- return 0;
- }
- uv0_size = height * uv0_stride;
-
- // alpha
- a_width = has_alpha ? width : 0;
- a_stride = a_width;
- y_size = (uint64_t)y_stride * height;
- uv_size = (uint64_t)uv_stride * uv_height;
- a_size = (uint64_t)a_stride * height;
-
- total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size;
-
- // Security and validation checks
- if (width <= 0 || height <= 0 || // luma/alpha param error
- uv_width < 0 || uv_height < 0) { // u/v param error
- return 0;
- }
- // Clear previous buffer and allocate a new one.
- WebPPictureFree(picture); // erase previous buffer
- mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem));
- if (mem == NULL) return 0;
-
- // From now on, we're in the clear, we can no longer fail...
- picture->memory_ = (void*)mem;
- picture->y_stride = y_stride;
- picture->uv_stride = uv_stride;
- picture->a_stride = a_stride;
- picture->uv0_stride = uv0_stride;
- // TODO(skal): we could align the y/u/v planes and adjust stride.
- picture->y = mem;
- mem += y_size;
-
- picture->u = mem;
- mem += uv_size;
- picture->v = mem;
- mem += uv_size;
-
- if (a_size) {
- picture->a = mem;
- mem += a_size;
- }
- if (uv0_size) {
- picture->u0 = mem;
- mem += uv0_size;
- picture->v0 = mem;
- mem += uv0_size;
- }
- } else {
- void* memory;
- const uint64_t argb_size = (uint64_t)width * height;
- if (width <= 0 || height <= 0) {
- return 0;
- }
- // Clear previous buffer and allocate a new one.
- WebPPictureFree(picture); // erase previous buffer
- memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb));
- if (memory == NULL) return 0;
-
- // TODO(skal): align plane to cache line?
- picture->memory_argb_ = memory;
- picture->argb = (uint32_t*)memory;
- picture->argb_stride = width;
- }
- }
- return 1;
-}
-
-// Remove reference to the ARGB buffer (doesn't free anything).
-static void PictureResetARGB(WebPPicture* const picture) {
- picture->memory_argb_ = NULL;
- picture->argb = NULL;
- picture->argb_stride = 0;
-}
-
-// Remove reference to the YUVA buffer (doesn't free anything).
-static void PictureResetYUVA(WebPPicture* const picture) {
- picture->memory_ = NULL;
- picture->y = picture->u = picture->v = picture->a = NULL;
- picture->u0 = picture->v0 = NULL;
- picture->y_stride = picture->uv_stride = 0;
- picture->a_stride = 0;
- picture->uv0_stride = 0;
-}
-
-// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
-// into 'dst'. Mark 'dst' as not owning any memory.
-static void WebPPictureGrabSpecs(const WebPPicture* const src,
- WebPPicture* const dst) {
- SB_DCHECK(src != NULL && dst != NULL);
- *dst = *src;
- PictureResetYUVA(dst);
- PictureResetARGB(dst);
-}
-
-// Allocate a new argb buffer, discarding any existing one and preserving
-// the other YUV(A) buffer.
-static int PictureAllocARGB(WebPPicture* const picture) {
- WebPPicture tmp;
- SbMemoryDeallocate(picture->memory_argb_);
- PictureResetARGB(picture);
- picture->use_argb = 1;
- WebPPictureGrabSpecs(picture, &tmp);
- if (!WebPPictureAlloc(&tmp)) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
- }
- picture->memory_argb_ = tmp.memory_argb_;
- picture->argb = tmp.argb;
- picture->argb_stride = tmp.argb_stride;
- return 1;
-}
-
-// Release memory owned by 'picture' (both YUV and ARGB buffers).
-void WebPPictureFree(WebPPicture* picture) {
- if (picture != NULL) {
- SbMemoryDeallocate(picture->memory_);
- SbMemoryDeallocate(picture->memory_argb_);
- PictureResetYUVA(picture);
- PictureResetARGB(picture);
- }
-}
-
-//------------------------------------------------------------------------------
-// Picture copying
-
-// Not worth moving to dsp/enc.c (only used here).
-static void CopyPlane(const uint8_t* src, int src_stride,
- uint8_t* dst, int dst_stride, int width, int height) {
- while (height-- > 0) {
- SbMemoryCopy(dst, src, width);
- src += src_stride;
- dst += dst_stride;
- }
-}
-
-// Adjust top-left corner to chroma sample position.
-static void SnapTopLeftPosition(const WebPPicture* const pic,
- int* const left, int* const top) {
- if (!pic->use_argb) {
- const int is_yuv422 = IS_YUV_CSP(pic->colorspace, WEBP_YUV422);
- if (IS_YUV_CSP(pic->colorspace, WEBP_YUV420) || is_yuv422) {
- *left &= ~1;
- if (!is_yuv422) *top &= ~1;
- }
- }
-}
-
-// Adjust top-left corner and verify that the sub-rectangle is valid.
-static int AdjustAndCheckRectangle(const WebPPicture* const pic,
- int* const left, int* const top,
- int width, int height) {
- SnapTopLeftPosition(pic, left, top);
- if ((*left) < 0 || (*top) < 0) return 0;
- if (width <= 0 || height <= 0) return 0;
- if ((*left) + width > pic->width) return 0;
- if ((*top) + height > pic->height) return 0;
- return 1;
-}
-
-int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
- if (src == NULL || dst == NULL) return 0;
- if (src == dst) return 1;
-
- WebPPictureGrabSpecs(src, dst);
- if (!WebPPictureAlloc(dst)) return 0;
-
- if (!src->use_argb) {
- CopyPlane(src->y, src->y_stride,
- dst->y, dst->y_stride, dst->width, dst->height);
- CopyPlane(src->u, src->uv_stride,
- dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
- CopyPlane(src->v, src->uv_stride,
- dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height));
- if (dst->a != NULL) {
- CopyPlane(src->a, src->a_stride,
- dst->a, dst->a_stride, dst->width, dst->height);
- }
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (dst->u0 != NULL) {
- int uv0_width = src->width;
- if (IS_YUV_CSP(dst->colorspace, WEBP_YUV422)) {
- uv0_width = HALVE(uv0_width);
- }
- CopyPlane(src->u0, src->uv0_stride,
- dst->u0, dst->uv0_stride, uv0_width, dst->height);
- CopyPlane(src->v0, src->uv0_stride,
- dst->v0, dst->uv0_stride, uv0_width, dst->height);
- }
-#endif
- } else {
- CopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
- (uint8_t*)dst->argb, 4 * dst->argb_stride,
- 4 * dst->width, dst->height);
- }
- return 1;
-}
-
-int WebPPictureIsView(const WebPPicture* picture) {
- if (picture == NULL) return 0;
- if (picture->use_argb) {
- return (picture->memory_argb_ == NULL);
- }
- return (picture->memory_ == NULL);
-}
-
-int WebPPictureView(const WebPPicture* src,
- int left, int top, int width, int height,
- WebPPicture* dst) {
- if (src == NULL || dst == NULL) return 0;
-
- // verify rectangle position.
- if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
-
- if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'.
- WebPPictureGrabSpecs(src, dst);
- }
- dst->width = width;
- dst->height = height;
- if (!src->use_argb) {
- dst->y = src->y + top * src->y_stride + left;
- dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
- dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
- dst->y_stride = src->y_stride;
- dst->uv_stride = src->uv_stride;
- if (src->a != NULL) {
- dst->a = src->a + top * src->a_stride + left;
- dst->a_stride = src->a_stride;
- }
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (src->u0 != NULL) {
- const int left_pos =
- IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left;
- dst->u0 = src->u0 + top * src->uv0_stride + left_pos;
- dst->v0 = src->v0 + top * src->uv0_stride + left_pos;
- dst->uv0_stride = src->uv0_stride;
- }
-#endif
- } else {
- dst->argb = src->argb + top * src->argb_stride + left;
- dst->argb_stride = src->argb_stride;
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Picture cropping
-
-int WebPPictureCrop(WebPPicture* pic,
- int left, int top, int width, int height) {
- WebPPicture tmp;
-
- if (pic == NULL) return 0;
- if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
-
- WebPPictureGrabSpecs(pic, &tmp);
- tmp.width = width;
- tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
-
- if (!pic->use_argb) {
- const int y_offset = top * pic->y_stride + left;
- const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
- CopyPlane(pic->y + y_offset, pic->y_stride,
- tmp.y, tmp.y_stride, width, height);
- CopyPlane(pic->u + uv_offset, pic->uv_stride,
- tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
- CopyPlane(pic->v + uv_offset, pic->uv_stride,
- tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
-
- if (tmp.a != NULL) {
- const int a_offset = top * pic->a_stride + left;
- CopyPlane(pic->a + a_offset, pic->a_stride,
- tmp.a, tmp.a_stride, width, height);
- }
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (tmp.u0 != NULL) {
- int w = width;
- int left_pos = left;
- if (IS_YUV_CSP(tmp.colorspace, WEBP_YUV422)) {
- w = HALVE(w);
- left_pos = HALVE(left_pos);
- }
- CopyPlane(pic->u0 + top * pic->uv0_stride + left_pos, pic->uv0_stride,
- tmp.u0, tmp.uv0_stride, w, height);
- CopyPlane(pic->v0 + top * pic->uv0_stride + left_pos, pic->uv0_stride,
- tmp.v0, tmp.uv0_stride, w, height);
- }
-#endif
- } else {
- const uint8_t* const src =
- (const uint8_t*)(pic->argb + top * pic->argb_stride + left);
- CopyPlane(src, pic->argb_stride * 4,
- (uint8_t*)tmp.argb, tmp.argb_stride * 4,
- width * 4, height);
- }
- WebPPictureFree(pic);
- *pic = tmp;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Simple picture rescaler
-
-static void RescalePlane(const uint8_t* src,
- int src_width, int src_height, int src_stride,
- uint8_t* dst,
- int dst_width, int dst_height, int dst_stride,
- int32_t* const work,
- int num_channels) {
- WebPRescaler rescaler;
- int y = 0;
- WebPRescalerInit(&rescaler, src_width, src_height,
- dst, dst_width, dst_height, dst_stride,
- num_channels,
- src_width, dst_width,
- src_height, dst_height,
- work);
- SbMemorySet(work, 0, 2 * dst_width * num_channels * sizeof(*work));
- while (y < src_height) {
- y += WebPRescalerImport(&rescaler, src_height - y,
- src + y * src_stride, src_stride);
- WebPRescalerExport(&rescaler);
- }
-}
-
-int WebPPictureRescale(WebPPicture* pic, int width, int height) {
- WebPPicture tmp;
- int prev_width, prev_height;
- int32_t* work;
-
- if (pic == NULL) return 0;
- prev_width = pic->width;
- prev_height = pic->height;
- // if width is unspecified, scale original proportionally to height ratio.
- if (width == 0) {
- width = (prev_width * height + prev_height / 2) / prev_height;
- }
- // if height is unspecified, scale original proportionally to width ratio.
- if (height == 0) {
- height = (prev_height * width + prev_width / 2) / prev_width;
- }
- // Check if the overall dimensions still make sense.
- if (width <= 0 || height <= 0) return 0;
-
- WebPPictureGrabSpecs(pic, &tmp);
- tmp.width = width;
- tmp.height = height;
- if (!WebPPictureAlloc(&tmp)) return 0;
-
- if (!pic->use_argb) {
- work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
- if (work == NULL) {
- WebPPictureFree(&tmp);
- return 0;
- }
-
- RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
- tmp.y, width, height, tmp.y_stride, work, 1);
- RescalePlane(pic->u,
- HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
- tmp.u,
- HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
- RescalePlane(pic->v,
- HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
- tmp.v,
- HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
-
- if (tmp.a != NULL) {
- RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
- tmp.a, width, height, tmp.a_stride, work, 1);
- }
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (tmp.u0 != NULL) {
- const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1;
- RescalePlane(
- pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
- tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1);
- RescalePlane(
- pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride,
- tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1);
- }
-#endif
- } else {
- work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
- if (work == NULL) {
- WebPPictureFree(&tmp);
- return 0;
- }
-
- RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
- pic->argb_stride * 4,
- (uint8_t*)tmp.argb, width, height,
- tmp.argb_stride * 4,
- work, 4);
- }
- WebPPictureFree(pic);
- SbMemoryDeallocate(work);
- *pic = tmp;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// WebPMemoryWriter: Write-to-memory
-
-void WebPMemoryWriterInit(WebPMemoryWriter* writer) {
- writer->mem = NULL;
- writer->size = 0;
- writer->max_size = 0;
-}
-
-int WebPMemoryWrite(const uint8_t* data, size_t data_size,
- const WebPPicture* picture) {
- WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
- uint64_t next_size;
- if (w == NULL) {
- return 1;
- }
- next_size = (uint64_t)w->size + data_size;
- if (next_size > w->max_size) {
- uint8_t* new_mem;
- uint64_t next_max_size = 2ULL * w->max_size;
- if (next_max_size < next_size) next_max_size = next_size;
- if (next_max_size < 8192ULL) next_max_size = 8192ULL;
- new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1);
- if (new_mem == NULL) {
- return 0;
- }
- if (w->size > 0) {
- SbMemoryCopy(new_mem, w->mem, w->size);
- }
- SbMemoryDeallocate(w->mem);
- w->mem = new_mem;
- // down-cast is ok, thanks to WebPSafeMalloc
- w->max_size = (size_t)next_max_size;
- }
- if (data_size > 0) {
- SbMemoryCopy(w->mem + w->size, data, data_size);
- w->size += data_size;
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Detection of non-trivial transparency
-
-// Returns true if alpha[] has non-0xff values.
-static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
- int x_step, int y_step) {
- if (alpha == NULL) return 0;
- while (height-- > 0) {
- int x;
- for (x = 0; x < width * x_step; x += x_step) {
- if (alpha[x] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time.
- }
- alpha += y_step;
- }
- return 0;
-}
-
-// Checking for the presence of non-opaque alpha.
-int WebPPictureHasTransparency(const WebPPicture* picture) {
- if (picture == NULL) return 0;
- if (!picture->use_argb) {
- return CheckNonOpaque(picture->a, picture->width, picture->height,
- 1, picture->a_stride);
- } else {
- int x, y;
- const uint32_t* argb = picture->argb;
- if (argb == NULL) return 0;
- for (y = 0; y < picture->height; ++y) {
- for (x = 0; x < picture->width; ++x) {
- if (argb[x] < 0xff000000u) return 1; // test any alpha values != 0xff
- }
- argb += picture->argb_stride;
- }
- }
- return 0;
-}
-
-//------------------------------------------------------------------------------
-// RGB -> YUV conversion
-
-// TODO: we can do better than simply 2x2 averaging on U/V samples.
-#define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \
- (ptr)[rgb_stride] + (ptr)[rgb_stride + step])
-#define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step])
-#define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride])
-#define SUM1(ptr) (4 * (ptr)[0])
-#define RGB_TO_UV(x, y, SUM) { \
- const int src = (2 * (step * (x) + (y) * rgb_stride)); \
- const int dst = (x) + (y) * picture->uv_stride; \
- const int r = SUM(r_ptr + src); \
- const int g = SUM(g_ptr + src); \
- const int b = SUM(b_ptr + src); \
- picture->u[dst] = VP8RGBToU(r, g, b); \
- picture->v[dst] = VP8RGBToV(r, g, b); \
-}
-
-#define RGB_TO_UV0(x_in, x_out, y, SUM) { \
- const int src = (step * (x_in) + (y) * rgb_stride); \
- const int dst = (x_out) + (y) * picture->uv0_stride; \
- const int r = SUM(r_ptr + src); \
- const int g = SUM(g_ptr + src); \
- const int b = SUM(b_ptr + src); \
- picture->u0[dst] = VP8RGBToU(r, g, b); \
- picture->v0[dst] = VP8RGBToV(r, g, b); \
-}
-
-static void MakeGray(WebPPicture* const picture) {
- int y;
- const int uv_width = HALVE(picture->width);
- const int uv_height = HALVE(picture->height);
- for (y = 0; y < uv_height; ++y) {
- SbMemorySet(picture->u + y * picture->uv_stride, 128, uv_width);
- SbMemorySet(picture->v + y * picture->uv_stride, 128, uv_width);
- }
-}
-
-static int ImportYUVAFromRGBA(const uint8_t* const r_ptr,
- const uint8_t* const g_ptr,
- const uint8_t* const b_ptr,
- const uint8_t* const a_ptr,
- int step, // bytes per pixel
- int rgb_stride, // bytes per scanline
- WebPPicture* const picture) {
- const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK;
- int x, y;
- const int width = picture->width;
- const int height = picture->height;
- const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride);
-
- picture->colorspace = uv_csp;
- picture->use_argb = 0;
- if (has_alpha) {
- picture->colorspace |= WEBP_CSP_ALPHA_BIT;
- }
- if (!WebPPictureAlloc(picture)) return 0;
-
- // Import luma plane
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- const int offset = step * x + y * rgb_stride;
- picture->y[x + y * picture->y_stride] =
- VP8RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset]);
- }
- }
-
- // Downsample U/V plane
- if (uv_csp != WEBP_YUV400) {
- for (y = 0; y < (height >> 1); ++y) {
- for (x = 0; x < (width >> 1); ++x) {
- RGB_TO_UV(x, y, SUM4);
- }
- if (width & 1) {
- RGB_TO_UV(x, y, SUM2V);
- }
- }
- if (height & 1) {
- for (x = 0; x < (width >> 1); ++x) {
- RGB_TO_UV(x, y, SUM2H);
- }
- if (width & 1) {
- RGB_TO_UV(x, y, SUM1);
- }
- }
-
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- // Store original U/V samples too
- if (uv_csp == WEBP_YUV422) {
- for (y = 0; y < height; ++y) {
- for (x = 0; x < (width >> 1); ++x) {
- RGB_TO_UV0(2 * x, x, y, SUM2H);
- }
- if (width & 1) {
- RGB_TO_UV0(2 * x, x, y, SUM1);
- }
- }
- } else if (uv_csp == WEBP_YUV444) {
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- RGB_TO_UV0(x, x, y, SUM1);
- }
- }
- }
-#endif
- } else {
- MakeGray(picture);
- }
-
- if (has_alpha) {
- SB_DCHECK(step >= 4);
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- picture->a[x + y * picture->a_stride] =
- a_ptr[step * x + y * rgb_stride];
- }
- }
- }
- return 1;
-}
-
-static int Import(WebPPicture* const picture,
- const uint8_t* const rgb, int rgb_stride,
- int step, int swap_rb, int import_alpha) {
- const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0);
- const uint8_t* const g_ptr = rgb + 1;
- const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2);
- const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL;
- const int width = picture->width;
- const int height = picture->height;
-
- if (!picture->use_argb) {
- return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
- picture);
- }
- if (import_alpha) {
- picture->colorspace |= WEBP_CSP_ALPHA_BIT;
- } else {
- picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
- }
- if (!WebPPictureAlloc(picture)) return 0;
-
- if (!import_alpha) {
- int x, y;
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- const int offset = step * x + y * rgb_stride;
- const uint32_t argb =
- 0xff000000u |
- (r_ptr[offset] << 16) |
- (g_ptr[offset] << 8) |
- (b_ptr[offset]);
- picture->argb[x + y * picture->argb_stride] = argb;
- }
- }
- } else {
- int x, y;
- SB_DCHECK(step >= 4);
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- const int offset = step * x + y * rgb_stride;
- const uint32_t argb = ((uint32_t)a_ptr[offset] << 24) |
- (r_ptr[offset] << 16) |
- (g_ptr[offset] << 8) |
- (b_ptr[offset]);
- picture->argb[x + y * picture->argb_stride] = argb;
- }
- }
- }
- return 1;
-}
-#undef SUM4
-#undef SUM2V
-#undef SUM2H
-#undef SUM1
-#undef RGB_TO_UV
-
-int WebPPictureImportRGB(WebPPicture* picture,
- const uint8_t* rgb, int rgb_stride) {
- return Import(picture, rgb, rgb_stride, 3, 0, 0);
-}
-
-int WebPPictureImportBGR(WebPPicture* picture,
- const uint8_t* rgb, int rgb_stride) {
- return Import(picture, rgb, rgb_stride, 3, 1, 0);
-}
-
-int WebPPictureImportRGBA(WebPPicture* picture,
- const uint8_t* rgba, int rgba_stride) {
- return Import(picture, rgba, rgba_stride, 4, 0, 1);
-}
-
-int WebPPictureImportBGRA(WebPPicture* picture,
- const uint8_t* rgba, int rgba_stride) {
- return Import(picture, rgba, rgba_stride, 4, 1, 1);
-}
-
-int WebPPictureImportRGBX(WebPPicture* picture,
- const uint8_t* rgba, int rgba_stride) {
- return Import(picture, rgba, rgba_stride, 4, 0, 0);
-}
-
-int WebPPictureImportBGRX(WebPPicture* picture,
- const uint8_t* rgba, int rgba_stride) {
- return Import(picture, rgba, rgba_stride, 4, 1, 0);
-}
-
-//------------------------------------------------------------------------------
-// Automatic YUV <-> ARGB conversions.
-
-int WebPPictureYUVAToARGB(WebPPicture* picture) {
- if (picture == NULL) return 0;
- if (picture->memory_ == NULL || picture->y == NULL ||
- picture->u == NULL || picture->v == NULL) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
- }
- if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
- }
- if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
- }
- // Allocate a new argb buffer (discarding the previous one).
- if (!PictureAllocARGB(picture)) return 0;
-
- // Convert
- {
- int y;
- const int width = picture->width;
- const int height = picture->height;
- const int argb_stride = 4 * picture->argb_stride;
- uint8_t* dst = (uint8_t*)picture->argb;
- const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
- WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST);
-
- // First row, with replicated top samples.
- upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, width);
- cur_y += picture->y_stride;
- dst += argb_stride;
- // Center rows.
- for (y = 1; y + 1 < height; y += 2) {
- const uint8_t* const top_u = cur_u;
- const uint8_t* const top_v = cur_v;
- cur_u += picture->uv_stride;
- cur_v += picture->uv_stride;
- upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v,
- dst, dst + argb_stride, width);
- cur_y += 2 * picture->y_stride;
- dst += 2 * argb_stride;
- }
- // Last row (if needed), with replicated bottom samples.
- if (height > 1 && !(height & 1)) {
- upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
- }
- // Insert alpha values if needed, in replacement for the default 0xff ones.
- if (picture->colorspace & WEBP_CSP_ALPHA_BIT) {
- for (y = 0; y < height; ++y) {
- uint32_t* const argb_dst = picture->argb + y * picture->argb_stride;
- const uint8_t* const src = picture->a + y * picture->a_stride;
- int x;
- for (x = 0; x < width; ++x) {
- argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24);
- }
- }
- }
- }
- return 1;
-}
-
-int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
- if (picture == NULL) return 0;
- if (picture->argb == NULL) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
- } else {
- const uint8_t* const argb = (const uint8_t*)picture->argb;
- const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1;
- const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2;
- const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3;
- const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0;
- // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA()
- // would be calling WebPPictureFree(picture) otherwise.
- WebPPicture tmp = *picture;
- PictureResetARGB(&tmp); // reset ARGB buffer so that it's not SbMemoryDeallocate()'d.
- tmp.use_argb = 0;
- tmp.colorspace = colorspace & WEBP_CSP_UV_MASK;
- if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, &tmp)) {
- return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
- }
- // Copy back the YUV specs into 'picture'.
- tmp.argb = picture->argb;
- tmp.argb_stride = picture->argb_stride;
- tmp.memory_argb_ = picture->memory_argb_;
- *picture = tmp;
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Helper: clean up fully transparent area to help compressibility.
-
-#define SIZE 8
-#define SIZE2 (SIZE / 2)
-static int is_transparent_area(const uint8_t* ptr, int stride, int size) {
- int y, x;
- for (y = 0; y < size; ++y) {
- for (x = 0; x < size; ++x) {
- if (ptr[x]) {
- return 0;
- }
- }
- ptr += stride;
- }
- return 1;
-}
-
-static WEBP_INLINE void flatten(uint8_t* ptr, int v, int stride, int size) {
- int y;
- for (y = 0; y < size; ++y) {
- SbMemorySet(ptr, v, size);
- ptr += stride;
- }
-}
-
-void WebPCleanupTransparentArea(WebPPicture* pic) {
- int x, y, w, h;
- const uint8_t* a_ptr;
- int values[3] = { 0 };
-
- if (pic == NULL) return;
-
- a_ptr = pic->a;
- if (a_ptr == NULL) return; // nothing to do
-
- w = pic->width / SIZE;
- h = pic->height / SIZE;
- for (y = 0; y < h; ++y) {
- int need_reset = 1;
- for (x = 0; x < w; ++x) {
- const int off_a = (y * pic->a_stride + x) * SIZE;
- const int off_y = (y * pic->y_stride + x) * SIZE;
- const int off_uv = (y * pic->uv_stride + x) * SIZE2;
- if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) {
- if (need_reset) {
- values[0] = pic->y[off_y];
- values[1] = pic->u[off_uv];
- values[2] = pic->v[off_uv];
- need_reset = 0;
- }
- flatten(pic->y + off_y, values[0], pic->y_stride, SIZE);
- flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2);
- flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2);
- } else {
- need_reset = 1;
- }
- }
- // ignore the left-overs on right/bottom
- }
-}
-
-#undef SIZE
-#undef SIZE2
-
-//------------------------------------------------------------------------------
-// local-min distortion
-//
-// For every pixel in the *reference* picture, we search for the local best
-// match in the compressed image. This is not a symmetrical measure.
-
-// search radius. Shouldn't be too large.
-#define RADIUS 2
-
-static float AccumulateLSIM(const uint8_t* src, int src_stride,
- const uint8_t* ref, int ref_stride,
- int w, int h) {
- int x, y;
- double total_sse = 0.;
- for (y = 0; y < h; ++y) {
- const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS;
- const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1;
- for (x = 0; x < w; ++x) {
- const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS;
- const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1;
- double best_sse = 255. * 255.;
- const double value = (double)ref[y * ref_stride + x];
- int i, j;
- for (j = y_0; j < y_1; ++j) {
- const uint8_t* s = src + j * src_stride;
- for (i = x_0; i < x_1; ++i) {
- const double sse = (double)(s[i] - value) * (s[i] - value);
- if (sse < best_sse) best_sse = sse;
- }
- }
- total_sse += best_sse;
- }
- }
- return (float)total_sse;
-}
-#undef RADIUS
-
-//------------------------------------------------------------------------------
-// Distortion
-
-// Max value returned in case of exact similarity.
-static const double kMinDistortion_dB = 99.;
-static float GetPSNR(const double v) {
- return (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.))
- : kMinDistortion_dB);
-}
-
-int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
- int type, float result[5]) {
- DistoStats stats[5];
- int has_alpha;
- int uv_w, uv_h;
-
- if (src == NULL || ref == NULL ||
- src->width != ref->width || src->height != ref->height ||
- src->y == NULL || ref->y == NULL ||
- src->u == NULL || ref->u == NULL ||
- src->v == NULL || ref->v == NULL ||
- result == NULL) {
- return 0;
- }
- // TODO(skal): provide distortion for ARGB too.
- if (src->use_argb == 1 || src->use_argb != ref->use_argb) {
- return 0;
- }
-
- has_alpha = !!(src->colorspace & WEBP_CSP_ALPHA_BIT);
- if (has_alpha != !!(ref->colorspace & WEBP_CSP_ALPHA_BIT) ||
- (has_alpha && (src->a == NULL || ref->a == NULL))) {
- return 0;
- }
-
- SbMemorySet(stats, 0, sizeof(stats));
-
- uv_w = HALVE(src->width);
- uv_h = HALVE(src->height);
- if (type >= 2) {
- float sse[4];
- sse[0] = AccumulateLSIM(src->y, src->y_stride,
- ref->y, ref->y_stride, src->width, src->height);
- sse[1] = AccumulateLSIM(src->u, src->uv_stride,
- ref->u, ref->uv_stride, uv_w, uv_h);
- sse[2] = AccumulateLSIM(src->v, src->uv_stride,
- ref->v, ref->uv_stride, uv_w, uv_h);
- sse[3] = has_alpha ? AccumulateLSIM(src->a, src->a_stride,
- ref->a, ref->a_stride,
- src->width, src->height)
- : 0.f;
- result[0] = GetPSNR(sse[0] / (src->width * src->height));
- result[1] = GetPSNR(sse[1] / (uv_w * uv_h));
- result[2] = GetPSNR(sse[2] / (uv_w * uv_h));
- result[3] = GetPSNR(sse[3] / (src->width * src->height));
- {
- double total_sse = sse[0] + sse[1] + sse[2];
- int total_pixels = src->width * src->height + 2 * uv_w * uv_h;
- if (has_alpha) {
- total_pixels += src->width * src->height;
- total_sse += sse[3];
- }
- result[4] = GetPSNR(total_sse / total_pixels);
- }
- } else {
- int c;
- VP8SSIMAccumulatePlane(src->y, src->y_stride,
- ref->y, ref->y_stride,
- src->width, src->height, &stats[0]);
- VP8SSIMAccumulatePlane(src->u, src->uv_stride,
- ref->u, ref->uv_stride,
- uv_w, uv_h, &stats[1]);
- VP8SSIMAccumulatePlane(src->v, src->uv_stride,
- ref->v, ref->uv_stride,
- uv_w, uv_h, &stats[2]);
- if (has_alpha) {
- VP8SSIMAccumulatePlane(src->a, src->a_stride,
- ref->a, ref->a_stride,
- src->width, src->height, &stats[3]);
- }
- for (c = 0; c <= 4; ++c) {
- if (type == 1) {
- const double v = VP8SSIMGet(&stats[c]);
- result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v)
- : kMinDistortion_dB);
- } else {
- const double v = VP8SSIMGetSquaredError(&stats[c]);
- result[c] = GetPSNR(v);
- }
- // Accumulate forward
- if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]);
- }
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// Simplest high-level calls:
-
-typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
-
-static size_t Encode(const uint8_t* rgba, int width, int height, int stride,
- Importer import, float quality_factor, int lossless,
- uint8_t** output) {
- WebPPicture pic;
- WebPConfig config;
- WebPMemoryWriter wrt;
- int ok;
-
- if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) ||
- !WebPPictureInit(&pic)) {
- return 0; // shouldn't happen, except if system installation is broken
- }
-
- config.lossless = !!lossless;
- pic.use_argb = !!lossless;
- pic.width = width;
- pic.height = height;
- pic.writer = WebPMemoryWrite;
- pic.custom_ptr = &wrt;
- WebPMemoryWriterInit(&wrt);
-
- ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic);
- WebPPictureFree(&pic);
- if (!ok) {
- SbMemoryDeallocate(wrt.mem);
- *output = NULL;
- return 0;
- }
- *output = wrt.mem;
- return wrt.size;
-}
-
-#define ENCODE_FUNC(NAME, IMPORTER) \
-size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
- uint8_t** out) { \
- return Encode(in, w, h, bps, IMPORTER, q, 0, out); \
-}
-
-ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB);
-ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR);
-ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA);
-ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA);
-
-#undef ENCODE_FUNC
-
-#define LOSSLESS_DEFAULT_QUALITY 70.
-#define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \
-size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \
- return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \
-}
-
-LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB);
-LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR);
-LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA);
-LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA);
-
-#undef LOSSLESS_ENCODE_FUNC
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/quant.c b/src/third_party/libwebp/enc/quant.c
deleted file mode 100644
index f6ab0b6..0000000
--- a/src/third_party/libwebp/enc/quant.c
+++ /dev/null
@@ -1,1057 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Quantization
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <math.h>
-#endif
-
-#include "./vp8enci.h"
-#include "./cost.h"
-
-#define DO_TRELLIS_I4 1
-#define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate.
-#define DO_TRELLIS_UV 0 // disable trellis for UV. Risky. Not worth.
-#define USE_TDISTO 1
-
-#define MID_ALPHA 64 // neutral value for susceptibility
-#define MIN_ALPHA 30 // lowest usable value for susceptibility
-#define MAX_ALPHA 100 // higher meaninful value for susceptibility
-
-#define SNS_TO_DQ 0.9 // Scaling constant between the sns value and the QP
- // power-law modulation. Must be strictly less than 1.
-
-#define I4_PENALTY 4000 // Rate-penalty for quick i4/i16 decision
-
-#define MULT_8B(a, b) (((a) * (b) + 128) >> 8)
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-
-static WEBP_INLINE int clip(int v, int m, int M) {
- return v < m ? m : v > M ? M : v;
-}
-
-static const uint8_t kZigzag[16] = {
- 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
-};
-
-static const uint8_t kDcTable[128] = {
- 4, 5, 6, 7, 8, 9, 10, 10,
- 11, 12, 13, 14, 15, 16, 17, 17,
- 18, 19, 20, 20, 21, 21, 22, 22,
- 23, 23, 24, 25, 25, 26, 27, 28,
- 29, 30, 31, 32, 33, 34, 35, 36,
- 37, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 46, 47, 48, 49, 50,
- 51, 52, 53, 54, 55, 56, 57, 58,
- 59, 60, 61, 62, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 72, 73, 74,
- 75, 76, 76, 77, 78, 79, 80, 81,
- 82, 83, 84, 85, 86, 87, 88, 89,
- 91, 93, 95, 96, 98, 100, 101, 102,
- 104, 106, 108, 110, 112, 114, 116, 118,
- 122, 124, 126, 128, 130, 132, 134, 136,
- 138, 140, 143, 145, 148, 151, 154, 157
-};
-
-static const uint16_t kAcTable[128] = {
- 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 27,
- 28, 29, 30, 31, 32, 33, 34, 35,
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, 57, 58, 60,
- 62, 64, 66, 68, 70, 72, 74, 76,
- 78, 80, 82, 84, 86, 88, 90, 92,
- 94, 96, 98, 100, 102, 104, 106, 108,
- 110, 112, 114, 116, 119, 122, 125, 128,
- 131, 134, 137, 140, 143, 146, 149, 152,
- 155, 158, 161, 164, 167, 170, 173, 177,
- 181, 185, 189, 193, 197, 201, 205, 209,
- 213, 217, 221, 225, 229, 234, 239, 245,
- 249, 254, 259, 264, 269, 274, 279, 284
-};
-
-static const uint16_t kAcTable2[128] = {
- 8, 8, 9, 10, 12, 13, 15, 17,
- 18, 20, 21, 23, 24, 26, 27, 29,
- 31, 32, 34, 35, 37, 38, 40, 41,
- 43, 44, 46, 48, 49, 51, 52, 54,
- 55, 57, 58, 60, 62, 63, 65, 66,
- 68, 69, 71, 72, 74, 75, 77, 79,
- 80, 82, 83, 85, 86, 88, 89, 93,
- 96, 99, 102, 105, 108, 111, 114, 117,
- 120, 124, 127, 130, 133, 136, 139, 142,
- 145, 148, 151, 155, 158, 161, 164, 167,
- 170, 173, 176, 179, 184, 189, 193, 198,
- 203, 207, 212, 217, 221, 226, 230, 235,
- 240, 244, 249, 254, 258, 263, 268, 274,
- 280, 286, 292, 299, 305, 311, 317, 323,
- 330, 336, 342, 348, 354, 362, 370, 379,
- 385, 393, 401, 409, 416, 424, 432, 440
-};
-
-static const uint16_t kCoeffThresh[16] = {
- 0, 10, 20, 30,
- 10, 20, 30, 30,
- 20, 30, 30, 30,
- 30, 30, 30, 30
-};
-
-// TODO(skal): tune more. Coeff thresholding?
-static const uint8_t kBiasMatrices[3][16] = { // [3] = [luma-ac,luma-dc,chroma]
- { 96, 96, 96, 96,
- 96, 96, 96, 96,
- 96, 96, 96, 96,
- 96, 96, 96, 96 },
- { 96, 96, 96, 96,
- 96, 96, 96, 96,
- 96, 96, 96, 96,
- 96, 96, 96, 96 },
- { 96, 96, 96, 96,
- 96, 96, 96, 96,
- 96, 96, 96, 96,
- 96, 96, 96, 96 }
-};
-
-// Sharpening by (slightly) raising the hi-frequency coeffs (only for trellis).
-// Hack-ish but helpful for mid-bitrate range. Use with care.
-static const uint8_t kFreqSharpening[16] = {
- 0, 30, 60, 90,
- 30, 60, 90, 90,
- 60, 90, 90, 90,
- 90, 90, 90, 90
-};
-
-//------------------------------------------------------------------------------
-// Initialize quantization parameters in VP8Matrix
-
-// Returns the average quantizer
-static int ExpandMatrix(VP8Matrix* const m, int type) {
- int i;
- int sum = 0;
- for (i = 2; i < 16; ++i) {
- m->q_[i] = m->q_[1];
- }
- for (i = 0; i < 16; ++i) {
- const int j = kZigzag[i];
- const int bias = kBiasMatrices[type][j];
- m->iq_[j] = (1 << QFIX) / m->q_[j];
- m->bias_[j] = BIAS(bias);
- // TODO(skal): tune kCoeffThresh[]
- m->zthresh_[j] = ((256 /*+ kCoeffThresh[j]*/ - bias) * m->q_[j] + 127) >> 8;
- m->sharpen_[j] = (kFreqSharpening[j] * m->q_[j]) >> 11;
- sum += m->q_[j];
- }
- return (sum + 8) >> 4;
-}
-
-static void SetupMatrices(VP8Encoder* enc) {
- int i;
- const int tlambda_scale =
- (enc->method_ >= 4) ? enc->config_->sns_strength
- : 0;
- const int num_segments = enc->segment_hdr_.num_segments_;
- for (i = 0; i < num_segments; ++i) {
- VP8SegmentInfo* const m = &enc->dqm_[i];
- const int q = m->quant_;
- int q4, q16, quv;
- m->y1_.q_[0] = kDcTable[clip(q + enc->dq_y1_dc_, 0, 127)];
- m->y1_.q_[1] = kAcTable[clip(q, 0, 127)];
-
- m->y2_.q_[0] = kDcTable[ clip(q + enc->dq_y2_dc_, 0, 127)] * 2;
- m->y2_.q_[1] = kAcTable2[clip(q + enc->dq_y2_ac_, 0, 127)];
-
- m->uv_.q_[0] = kDcTable[clip(q + enc->dq_uv_dc_, 0, 117)];
- m->uv_.q_[1] = kAcTable[clip(q + enc->dq_uv_ac_, 0, 127)];
-
- q4 = ExpandMatrix(&m->y1_, 0);
- q16 = ExpandMatrix(&m->y2_, 1);
- quv = ExpandMatrix(&m->uv_, 2);
-
- // TODO: Switch to kLambda*[] tables?
- {
- m->lambda_i4_ = (3 * q4 * q4) >> 7;
- m->lambda_i16_ = (3 * q16 * q16);
- m->lambda_uv_ = (3 * quv * quv) >> 6;
- m->lambda_mode_ = (1 * q4 * q4) >> 7;
- m->lambda_trellis_i4_ = (7 * q4 * q4) >> 3;
- m->lambda_trellis_i16_ = (q16 * q16) >> 2;
- m->lambda_trellis_uv_ = (quv *quv) << 1;
- m->tlambda_ = (tlambda_scale * q4) >> 5;
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// Initialize filtering parameters
-
-// Very small filter-strength values have close to no visual effect. So we can
-// save a little decoding-CPU by turning filtering off for these.
-#define FSTRENGTH_CUTOFF 3
-
-static void SetupFilterStrength(VP8Encoder* const enc) {
- int i;
- const int level0 = enc->config_->filter_strength;
- for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
- // Segments with lower quantizer will be less filtered. TODO: tune (wrt SNS)
- const int level = level0 * 256 * enc->dqm_[i].quant_ / 128;
- const int f = level / (256 + enc->dqm_[i].beta_);
- enc->dqm_[i].fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f;
- }
- // We record the initial strength (mainly for the case of 1-segment only).
- enc->filter_hdr_.level_ = enc->dqm_[0].fstrength_;
- enc->filter_hdr_.simple_ = (enc->config_->filter_type == 0);
- enc->filter_hdr_.sharpness_ = enc->config_->filter_sharpness;
-}
-
-//------------------------------------------------------------------------------
-
-// Note: if you change the values below, remember that the max range
-// allowed by the syntax for DQ_UV is [-16,16].
-#define MAX_DQ_UV (6)
-#define MIN_DQ_UV (-4)
-
-// We want to emulate jpeg-like behaviour where the expected "good" quality
-// is around q=75. Internally, our "good" middle is around c=50. So we
-// map accordingly using linear piece-wise function
-static double QualityToCompression(double c) {
- const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.;
- // The file size roughly scales as pow(quantizer, 3.). Actually, the
- // exponent is somewhere between 2.8 and 3.2, but we're mostly interested
- // in the mid-quant range. So we scale the compressibility inversely to
- // this power-law: quant ~= compression ^ 1/3. This law holds well for
- // low quant. Finer modelling for high-quant would make use of kAcTable[]
- // more explicitly.
- const double v = pow(linear_c, 1 / 3.);
- return v;
-}
-
-static double QualityToJPEGCompression(double c, double alpha) {
- // We map the complexity 'alpha' and quality setting 'c' to a compression
- // exponent empirically matched to the compression curve of libjpeg6b.
- // On average, the WebP output size will be roughly similar to that of a
- // JPEG file compressed with same quality factor.
- const double amin = 0.30;
- const double amax = 0.85;
- const double exp_min = 0.4;
- const double exp_max = 0.9;
- const double slope = (exp_min - exp_max) / (amax - amin);
- // Linearly interpolate 'expn' from exp_min to exp_max
- // in the [amin, amax] range.
- const double expn = (alpha > amax) ? exp_min
- : (alpha < amin) ? exp_max
- : exp_max + slope * (alpha - amin);
- const double v = pow(c, expn);
- return v;
-}
-
-static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1,
- const VP8SegmentInfo* const S2) {
- return (S1->quant_ == S2->quant_) && (S1->fstrength_ == S2->fstrength_);
-}
-
-static void SimplifySegments(VP8Encoder* const enc) {
- int map[NUM_MB_SEGMENTS] = { 0, 1, 2, 3 };
- const int num_segments = enc->segment_hdr_.num_segments_;
- int num_final_segments = 1;
- int s1, s2;
- for (s1 = 1; s1 < num_segments; ++s1) { // find similar segments
- const VP8SegmentInfo* const S1 = &enc->dqm_[s1];
- int found = 0;
- // check if we already have similar segment
- for (s2 = 0; s2 < num_final_segments; ++s2) {
- const VP8SegmentInfo* const S2 = &enc->dqm_[s2];
- if (SegmentsAreEquivalent(S1, S2)) {
- found = 1;
- break;
- }
- }
- map[s1] = s2;
- if (!found) {
- if (num_final_segments != s1) {
- enc->dqm_[num_final_segments] = enc->dqm_[s1];
- }
- ++num_final_segments;
- }
- }
- if (num_final_segments < num_segments) { // Remap
- int i = enc->mb_w_ * enc->mb_h_;
- while (i-- > 0) enc->mb_info_[i].segment_ = map[enc->mb_info_[i].segment_];
- enc->segment_hdr_.num_segments_ = num_final_segments;
- // Replicate the trailing segment infos (it's mostly cosmetics)
- for (i = num_final_segments; i < num_segments; ++i) {
- enc->dqm_[i] = enc->dqm_[num_final_segments - 1];
- }
- }
-}
-
-void VP8SetSegmentParams(VP8Encoder* const enc, float quality) {
- int i;
- int dq_uv_ac, dq_uv_dc;
- const int num_segments = enc->segment_hdr_.num_segments_;
- const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.;
- const double Q = quality / 100.;
- const double c_base = enc->config_->emulate_jpeg_size ?
- QualityToJPEGCompression(Q, enc->alpha_ / 255.) :
- QualityToCompression(Q);
- for (i = 0; i < num_segments; ++i) {
- // We modulate the base coefficient to accommodate for the quantization
- // susceptibility and allow denser segments to be quantized more.
- const double expn = 1. - amp * enc->dqm_[i].alpha_;
- const double c = pow(c_base, expn);
- const int q = (int)(127. * (1. - c));
- SB_DCHECK(expn > 0.);
- enc->dqm_[i].quant_ = clip(q, 0, 127);
- }
-
- // purely indicative in the bitstream (except for the 1-segment case)
- enc->base_quant_ = enc->dqm_[0].quant_;
-
- // fill-in values for the unused segments (required by the syntax)
- for (i = num_segments; i < NUM_MB_SEGMENTS; ++i) {
- enc->dqm_[i].quant_ = enc->base_quant_;
- }
-
- // uv_alpha_ is normally spread around ~60. The useful range is
- // typically ~30 (quite bad) to ~100 (ok to decimate UV more).
- // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv.
- dq_uv_ac = (enc->uv_alpha_ - MID_ALPHA) * (MAX_DQ_UV - MIN_DQ_UV)
- / (MAX_ALPHA - MIN_ALPHA);
- // we rescale by the user-defined strength of adaptation
- dq_uv_ac = dq_uv_ac * enc->config_->sns_strength / 100;
- // and make it safe.
- dq_uv_ac = clip(dq_uv_ac, MIN_DQ_UV, MAX_DQ_UV);
- // We also boost the dc-uv-quant a little, based on sns-strength, since
- // U/V channels are quite more reactive to high quants (flat DC-blocks
- // tend to appear, and are displeasant).
- dq_uv_dc = -4 * enc->config_->sns_strength / 100;
- dq_uv_dc = clip(dq_uv_dc, -15, 15); // 4bit-signed max allowed
-
- enc->dq_y1_dc_ = 0; // TODO(skal): dq-lum
- enc->dq_y2_dc_ = 0;
- enc->dq_y2_ac_ = 0;
- enc->dq_uv_dc_ = dq_uv_dc;
- enc->dq_uv_ac_ = dq_uv_ac;
-
- SetupFilterStrength(enc); // initialize segments' filtering, eventually
-
- if (num_segments > 1) SimplifySegments(enc);
-
- SetupMatrices(enc); // finalize quantization matrices
-}
-
-//------------------------------------------------------------------------------
-// Form the predictions in cache
-
-// Must be ordered using {DC_PRED, TM_PRED, V_PRED, H_PRED} as index
-const int VP8I16ModeOffsets[4] = { I16DC16, I16TM16, I16VE16, I16HE16 };
-const int VP8UVModeOffsets[4] = { C8DC8, C8TM8, C8VE8, C8HE8 };
-
-// Must be indexed using {B_DC_PRED -> B_HU_PRED} as index
-const int VP8I4ModeOffsets[NUM_BMODES] = {
- I4DC4, I4TM4, I4VE4, I4HE4, I4RD4, I4VR4, I4LD4, I4VL4, I4HD4, I4HU4
-};
-
-void VP8MakeLuma16Preds(const VP8EncIterator* const it) {
- const VP8Encoder* const enc = it->enc_;
- const uint8_t* const left = it->x_ ? enc->y_left_ : NULL;
- const uint8_t* const top = it->y_ ? enc->y_top_ + it->x_ * 16 : NULL;
- VP8EncPredLuma16(it->yuv_p_, left, top);
-}
-
-void VP8MakeChroma8Preds(const VP8EncIterator* const it) {
- const VP8Encoder* const enc = it->enc_;
- const uint8_t* const left = it->x_ ? enc->u_left_ : NULL;
- const uint8_t* const top = it->y_ ? enc->uv_top_ + it->x_ * 16 : NULL;
- VP8EncPredChroma8(it->yuv_p_, left, top);
-}
-
-void VP8MakeIntra4Preds(const VP8EncIterator* const it) {
- VP8EncPredLuma4(it->yuv_p_, it->i4_top_);
-}
-
-//------------------------------------------------------------------------------
-// Quantize
-
-// Layout:
-// +----+
-// |YYYY| 0
-// |YYYY| 4
-// |YYYY| 8
-// |YYYY| 12
-// +----+
-// |UUVV| 16
-// |UUVV| 20
-// +----+
-
-const int VP8Scan[16 + 4 + 4] = {
- // Luma
- 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
- 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
- 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
- 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS,
-
- 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U
- 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V
-};
-
-//------------------------------------------------------------------------------
-// Distortion measurement
-
-static const uint16_t kWeightY[16] = {
- 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2
-};
-
-static const uint16_t kWeightTrellis[16] = {
-#if USE_TDISTO == 0
- 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
-#else
- 30, 27, 19, 11,
- 27, 24, 17, 10,
- 19, 17, 12, 8,
- 11, 10, 8, 6
-#endif
-};
-
-// Init/Copy the common fields in score.
-static void InitScore(VP8ModeScore* const rd) {
- rd->D = 0;
- rd->SD = 0;
- rd->R = 0;
- rd->nz = 0;
- rd->score = MAX_COST;
-}
-
-static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
- dst->D = src->D;
- dst->SD = src->SD;
- dst->R = src->R;
- dst->nz = src->nz; // note that nz is not accumulated, but just copied.
- dst->score = src->score;
-}
-
-static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
- dst->D += src->D;
- dst->SD += src->SD;
- dst->R += src->R;
- dst->nz |= src->nz; // here, new nz bits are accumulated.
- dst->score += src->score;
-}
-
-//------------------------------------------------------------------------------
-// Performs trellis-optimized quantization.
-
-// Trellis
-
-typedef struct {
- int prev; // best previous
- int level; // level
- int sign; // sign of coeff_i
- score_t cost; // bit cost
- score_t error; // distortion = sum of (|coeff_i| - level_i * Q_i)^2
- int ctx; // context (only depends on 'level'. Could be spared.)
-} Node;
-
-// If a coefficient was quantized to a value Q (using a neutral bias),
-// we test all alternate possibilities between [Q-MIN_DELTA, Q+MAX_DELTA]
-// We don't test negative values though.
-#define MIN_DELTA 0 // how much lower level to try
-#define MAX_DELTA 1 // how much higher
-#define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA)
-#define NODE(n, l) (nodes[(n) + 1][(l) + MIN_DELTA])
-
-static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) {
- // TODO: incorporate the "* 256" in the tables?
- rd->score = rd->R * lambda + 256 * (rd->D + rd->SD);
-}
-
-static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate,
- score_t distortion) {
- return rate * lambda + 256 * distortion;
-}
-
-static int TrellisQuantizeBlock(const VP8EncIterator* const it,
- int16_t in[16], int16_t out[16],
- int ctx0, int coeff_type,
- const VP8Matrix* const mtx,
- int lambda) {
- ProbaArray* const last_costs = it->enc_->proba_.coeffs_[coeff_type];
- CostArray* const costs = it->enc_->proba_.level_cost_[coeff_type];
- const int first = (coeff_type == 0) ? 1 : 0;
- Node nodes[17][NUM_NODES];
- int best_path[3] = {-1, -1, -1}; // store best-last/best-level/best-previous
- score_t best_score;
- int best_node;
- int last = first - 1;
- int n, m, p, nz;
-
- {
- score_t cost;
- score_t max_error;
- const int thresh = mtx->q_[1] * mtx->q_[1] / 4;
- const int last_proba = last_costs[VP8EncBands[first]][ctx0][0];
-
- // compute maximal distortion.
- max_error = 0;
- for (n = first; n < 16; ++n) {
- const int j = kZigzag[n];
- const int err = in[j] * in[j];
- max_error += kWeightTrellis[j] * err;
- if (err > thresh) last = n;
- }
- // we don't need to go inspect up to n = 16 coeffs. We can just go up
- // to last + 1 (inclusive) without losing much.
- if (last < 15) ++last;
-
- // compute 'skip' score. This is the max score one can do.
- cost = VP8BitCost(0, last_proba);
- best_score = RDScoreTrellis(lambda, cost, max_error);
-
- // initialize source node.
- n = first - 1;
- for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
- NODE(n, m).cost = 0;
- NODE(n, m).error = max_error;
- NODE(n, m).ctx = ctx0;
- }
- }
-
- // traverse trellis.
- for (n = first; n <= last; ++n) {
- const int j = kZigzag[n];
- const int Q = mtx->q_[j];
- const int iQ = mtx->iq_[j];
- const int B = BIAS(0x00); // neutral bias
- // note: it's important to take sign of the _original_ coeff,
- // so we don't have to consider level < 0 afterward.
- const int sign = (in[j] < 0);
- int coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
- int level0;
- if (coeff0 > 2047) coeff0 = 2047;
-
- level0 = QUANTDIV(coeff0, iQ, B);
- // test all alternate level values around level0.
- for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
- Node* const cur = &NODE(n, m);
- int delta_error, new_error;
- score_t cur_score = MAX_COST;
- int level = level0 + m;
- int last_proba;
-
- cur->sign = sign;
- cur->level = level;
- cur->ctx = (level == 0) ? 0 : (level == 1) ? 1 : 2;
- if (level >= 2048 || level < 0) { // node is dead?
- cur->cost = MAX_COST;
- continue;
- }
- last_proba = last_costs[VP8EncBands[n + 1]][cur->ctx][0];
-
- // Compute delta_error = how much coding this level will
- // subtract as distortion to max_error
- new_error = coeff0 - level * Q;
- delta_error =
- kWeightTrellis[j] * (coeff0 * coeff0 - new_error * new_error);
-
- // Inspect all possible non-dead predecessors. Retain only the best one.
- for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) {
- const Node* const prev = &NODE(n - 1, p);
- const int prev_ctx = prev->ctx;
- const uint16_t* const tcost = costs[VP8EncBands[n]][prev_ctx];
- const score_t total_error = prev->error - delta_error;
- score_t cost, base_cost, score;
-
- if (prev->cost >= MAX_COST) { // dead node?
- continue;
- }
-
- // Base cost of both terminal/non-terminal
- base_cost = prev->cost + VP8LevelCost(tcost, level);
-
- // Examine node assuming it's a non-terminal one.
- cost = base_cost;
- if (level && n < 15) {
- cost += VP8BitCost(1, last_proba);
- }
- score = RDScoreTrellis(lambda, cost, total_error);
- if (score < cur_score) {
- cur_score = score;
- cur->cost = cost;
- cur->error = total_error;
- cur->prev = p;
- }
-
- // Now, record best terminal node (and thus best entry in the graph).
- if (level) {
- cost = base_cost;
- if (n < 15) cost += VP8BitCost(0, last_proba);
- score = RDScoreTrellis(lambda, cost, total_error);
- if (score < best_score) {
- best_score = score;
- best_path[0] = n; // best eob position
- best_path[1] = m; // best level
- best_path[2] = p; // best predecessor
- }
- }
- }
- }
- }
-
- // Fresh start
- SbMemorySet(in + first, 0, (16 - first) * sizeof(*in));
- SbMemorySet(out + first, 0, (16 - first) * sizeof(*out));
- if (best_path[0] == -1) {
- return 0; // skip!
- }
-
- // Unwind the best path.
- // Note: best-prev on terminal node is not necessarily equal to the
- // best_prev for non-terminal. So we patch best_path[2] in.
- n = best_path[0];
- best_node = best_path[1];
- NODE(n, best_node).prev = best_path[2]; // force best-prev for terminal
- nz = 0;
-
- for (; n >= first; --n) {
- const Node* const node = &NODE(n, best_node);
- const int j = kZigzag[n];
- out[n] = node->sign ? -node->level : node->level;
- nz |= (node->level != 0);
- in[j] = out[n] * mtx->q_[j];
- best_node = node->prev;
- }
- return nz;
-}
-
-#undef NODE
-
-//------------------------------------------------------------------------------
-// Performs: difference, transform, quantize, back-transform, add
-// all at once. Output is the reconstructed block in *yuv_out, and the
-// quantized levels in *levels.
-
-static int ReconstructIntra16(VP8EncIterator* const it,
- VP8ModeScore* const rd,
- uint8_t* const yuv_out,
- int mode) {
- const VP8Encoder* const enc = it->enc_;
- const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
- const uint8_t* const src = it->yuv_in_ + Y_OFF;
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
- int nz = 0;
- int n;
- int16_t tmp[16][16], dc_tmp[16];
-
- for (n = 0; n < 16; ++n) {
- VP8FTransform(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]);
- }
- VP8FTransformWHT(tmp[0], dc_tmp);
- nz |= VP8EncQuantizeBlock(dc_tmp, rd->y_dc_levels, 0, &dqm->y2_) << 24;
-
- if (DO_TRELLIS_I16 && it->do_trellis_) {
- int x, y;
- VP8IteratorNzToBytes(it);
- for (y = 0, n = 0; y < 4; ++y) {
- for (x = 0; x < 4; ++x, ++n) {
- const int ctx = it->top_nz_[x] + it->left_nz_[y];
- const int non_zero =
- TrellisQuantizeBlock(it, tmp[n], rd->y_ac_levels[n], ctx, 0,
- &dqm->y1_, dqm->lambda_trellis_i16_);
- it->top_nz_[x] = it->left_nz_[y] = non_zero;
- nz |= non_zero << n;
- }
- }
- } else {
- for (n = 0; n < 16; ++n) {
- nz |= VP8EncQuantizeBlock(tmp[n], rd->y_ac_levels[n], 1, &dqm->y1_) << n;
- }
- }
-
- // Transform back
- VP8ITransformWHT(dc_tmp, tmp[0]);
- for (n = 0; n < 16; n += 2) {
- VP8ITransform(ref + VP8Scan[n], tmp[n], yuv_out + VP8Scan[n], 1);
- }
-
- return nz;
-}
-
-static int ReconstructIntra4(VP8EncIterator* const it,
- int16_t levels[16],
- const uint8_t* const src,
- uint8_t* const yuv_out,
- int mode) {
- const VP8Encoder* const enc = it->enc_;
- const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode];
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
- int nz = 0;
- int16_t tmp[16];
-
- VP8FTransform(src, ref, tmp);
- if (DO_TRELLIS_I4 && it->do_trellis_) {
- const int x = it->i4_ & 3, y = it->i4_ >> 2;
- const int ctx = it->top_nz_[x] + it->left_nz_[y];
- nz = TrellisQuantizeBlock(it, tmp, levels, ctx, 3, &dqm->y1_,
- dqm->lambda_trellis_i4_);
- } else {
- nz = VP8EncQuantizeBlock(tmp, levels, 0, &dqm->y1_);
- }
- VP8ITransform(ref, tmp, yuv_out, 0);
- return nz;
-}
-
-static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
- uint8_t* const yuv_out, int mode) {
- const VP8Encoder* const enc = it->enc_;
- const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode];
- const uint8_t* const src = it->yuv_in_ + U_OFF;
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
- int nz = 0;
- int n;
- int16_t tmp[8][16];
-
- for (n = 0; n < 8; ++n) {
- VP8FTransform(src + VP8Scan[16 + n], ref + VP8Scan[16 + n], tmp[n]);
- }
- if (DO_TRELLIS_UV && it->do_trellis_) {
- int ch, x, y;
- for (ch = 0, n = 0; ch <= 2; ch += 2) {
- for (y = 0; y < 2; ++y) {
- for (x = 0; x < 2; ++x, ++n) {
- const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
- const int non_zero =
- TrellisQuantizeBlock(it, tmp[n], rd->uv_levels[n], ctx, 2,
- &dqm->uv_, dqm->lambda_trellis_uv_);
- it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = non_zero;
- nz |= non_zero << n;
- }
- }
- }
- } else {
- for (n = 0; n < 8; ++n) {
- nz |= VP8EncQuantizeBlock(tmp[n], rd->uv_levels[n], 0, &dqm->uv_) << n;
- }
- }
-
- for (n = 0; n < 8; n += 2) {
- VP8ITransform(ref + VP8Scan[16 + n], tmp[n], yuv_out + VP8Scan[16 + n], 1);
- }
- return (nz << 16);
-}
-
-//------------------------------------------------------------------------------
-// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost.
-// Pick the mode is lower RD-cost = Rate + lamba * Distortion.
-
-static void SwapPtr(uint8_t** a, uint8_t** b) {
- uint8_t* const tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-static void SwapOut(VP8EncIterator* const it) {
- SwapPtr(&it->yuv_out_, &it->yuv_out2_);
-}
-
-static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) {
- const VP8Encoder* const enc = it->enc_;
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
- const int lambda = dqm->lambda_i16_;
- const int tlambda = dqm->tlambda_;
- const uint8_t* const src = it->yuv_in_ + Y_OFF;
- VP8ModeScore rd16;
- int mode;
-
- rd->mode_i16 = -1;
- for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
- uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF; // scratch buffer
- int nz;
-
- // Reconstruct
- nz = ReconstructIntra16(it, &rd16, tmp_dst, mode);
-
- // Measure RD-score
- rd16.D = VP8SSE16x16(src, tmp_dst);
- rd16.SD = tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY))
- : 0;
- rd16.R = VP8GetCostLuma16(it, &rd16);
- rd16.R += VP8FixedCostsI16[mode];
-
- // Since we always examine Intra16 first, we can overwrite *rd directly.
- SetRDScore(lambda, &rd16);
- if (mode == 0 || rd16.score < rd->score) {
- CopyScore(rd, &rd16);
- rd->mode_i16 = mode;
- rd->nz = nz;
- SbMemoryCopy(rd->y_ac_levels, rd16.y_ac_levels, sizeof(rd16.y_ac_levels));
- SbMemoryCopy(rd->y_dc_levels, rd16.y_dc_levels, sizeof(rd16.y_dc_levels));
- SwapOut(it);
- }
- }
- SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision.
- VP8SetIntra16Mode(it, rd->mode_i16);
-}
-
-//------------------------------------------------------------------------------
-
-// return the cost array corresponding to the surrounding prediction modes.
-static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
- const uint8_t modes[16]) {
- const int preds_w = it->enc_->preds_w_;
- const int x = (it->i4_ & 3), y = it->i4_ >> 2;
- const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1];
- const int top = (y == 0) ? it->preds_[-preds_w + x] : modes[it->i4_ - 4];
- return VP8FixedCostsI4[top][left];
-}
-
-static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
- const VP8Encoder* const enc = it->enc_;
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
- const int lambda = dqm->lambda_i4_;
- const int tlambda = dqm->tlambda_;
- const uint8_t* const src0 = it->yuv_in_ + Y_OFF;
- uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF;
- int total_header_bits = 0;
- VP8ModeScore rd_best;
-
- if (enc->max_i4_header_bits_ == 0) {
- return 0;
- }
-
- InitScore(&rd_best);
- rd_best.score = 211; // '211' is the value of VP8BitCost(0, 145)
- VP8IteratorStartI4(it);
- do {
- VP8ModeScore rd_i4;
- int mode;
- int best_mode = -1;
- const uint8_t* const src = src0 + VP8Scan[it->i4_];
- const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4);
- uint8_t* best_block = best_blocks + VP8Scan[it->i4_];
- uint8_t* tmp_dst = it->yuv_p_ + I4TMP; // scratch buffer.
-
- InitScore(&rd_i4);
- VP8MakeIntra4Preds(it);
- for (mode = 0; mode < NUM_BMODES; ++mode) {
- VP8ModeScore rd_tmp;
- int16_t tmp_levels[16];
-
- // Reconstruct
- rd_tmp.nz =
- ReconstructIntra4(it, tmp_levels, src, tmp_dst, mode) << it->i4_;
-
- // Compute RD-score
- rd_tmp.D = VP8SSE4x4(src, tmp_dst);
- rd_tmp.SD =
- tlambda ? MULT_8B(tlambda, VP8TDisto4x4(src, tmp_dst, kWeightY))
- : 0;
- rd_tmp.R = VP8GetCostLuma4(it, tmp_levels);
- rd_tmp.R += mode_costs[mode];
-
- SetRDScore(lambda, &rd_tmp);
- if (best_mode < 0 || rd_tmp.score < rd_i4.score) {
- CopyScore(&rd_i4, &rd_tmp);
- best_mode = mode;
- SwapPtr(&tmp_dst, &best_block);
- SbMemoryCopy(rd_best.y_ac_levels[it->i4_], tmp_levels, sizeof(tmp_levels));
- }
- }
- SetRDScore(dqm->lambda_mode_, &rd_i4);
- AddScore(&rd_best, &rd_i4);
- total_header_bits += mode_costs[best_mode];
- if (rd_best.score >= rd->score ||
- total_header_bits > enc->max_i4_header_bits_) {
- return 0;
- }
- // Copy selected samples if not in the right place already.
- if (best_block != best_blocks + VP8Scan[it->i4_])
- VP8Copy4x4(best_block, best_blocks + VP8Scan[it->i4_]);
- rd->modes_i4[it->i4_] = best_mode;
- it->top_nz_[it->i4_ & 3] = it->left_nz_[it->i4_ >> 2] = (rd_i4.nz ? 1 : 0);
- } while (VP8IteratorRotateI4(it, best_blocks));
-
- // finalize state
- CopyScore(rd, &rd_best);
- VP8SetIntra4Mode(it, rd->modes_i4);
- SwapOut(it);
- SbMemoryCopy(rd->y_ac_levels, rd_best.y_ac_levels, sizeof(rd->y_ac_levels));
- return 1; // select intra4x4 over intra16x16
-}
-
-//------------------------------------------------------------------------------
-
-static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
- const VP8Encoder* const enc = it->enc_;
- const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
- const int lambda = dqm->lambda_uv_;
- const uint8_t* const src = it->yuv_in_ + U_OFF;
- uint8_t* const tmp_dst = it->yuv_out2_ + U_OFF; // scratch buffer
- uint8_t* const dst0 = it->yuv_out_ + U_OFF;
- VP8ModeScore rd_best;
- int mode;
-
- rd->mode_uv = -1;
- InitScore(&rd_best);
- for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
- VP8ModeScore rd_uv;
-
- // Reconstruct
- rd_uv.nz = ReconstructUV(it, &rd_uv, tmp_dst, mode);
-
- // Compute RD-score
- rd_uv.D = VP8SSE16x8(src, tmp_dst);
- rd_uv.SD = 0; // TODO: should we call TDisto? it tends to flatten areas.
- rd_uv.R = VP8GetCostUV(it, &rd_uv);
- rd_uv.R += VP8FixedCostsUV[mode];
-
- SetRDScore(lambda, &rd_uv);
- if (mode == 0 || rd_uv.score < rd_best.score) {
- CopyScore(&rd_best, &rd_uv);
- rd->mode_uv = mode;
- SbMemoryCopy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels));
- SbMemoryCopy(dst0, tmp_dst, UV_SIZE); // TODO: SwapUVOut() ?
- }
- }
- VP8SetIntraUVMode(it, rd->mode_uv);
- AddScore(rd, &rd_best);
-}
-
-//------------------------------------------------------------------------------
-// Final reconstruction and quantization.
-
-static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) {
- const VP8Encoder* const enc = it->enc_;
- const int is_i16 = (it->mb_->type_ == 1);
- int nz = 0;
-
- if (is_i16) {
- nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF, it->preds_[0]);
- } else {
- VP8IteratorStartI4(it);
- do {
- const int mode =
- it->preds_[(it->i4_ & 3) + (it->i4_ >> 2) * enc->preds_w_];
- const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
- uint8_t* const dst = it->yuv_out_ + Y_OFF + VP8Scan[it->i4_];
- VP8MakeIntra4Preds(it);
- nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_],
- src, dst, mode) << it->i4_;
- } while (VP8IteratorRotateI4(it, it->yuv_out_ + Y_OFF));
- }
-
- nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF, it->mb_->uv_mode_);
- rd->nz = nz;
-}
-
-// Refine intra16/intra4 sub-modes based on distortion only (not rate).
-static void DistoRefine(VP8EncIterator* const it, int try_both_i4_i16) {
- const int is_i16 = (it->mb_->type_ == 1);
- score_t best_score = MAX_COST;
-
- if (try_both_i4_i16 || is_i16) {
- int mode;
- int best_mode = -1;
- for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
- const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
- const uint8_t* const src = it->yuv_in_ + Y_OFF;
- const score_t score = VP8SSE16x16(src, ref);
- if (score < best_score) {
- best_mode = mode;
- best_score = score;
- }
- }
- VP8SetIntra16Mode(it, best_mode);
- }
- if (try_both_i4_i16 || !is_i16) {
- uint8_t modes_i4[16];
- // We don't evaluate the rate here, but just account for it through a
- // constant penalty (i4 mode usually needs more bits compared to i16).
- score_t score_i4 = (score_t)I4_PENALTY;
-
- VP8IteratorStartI4(it);
- do {
- int mode;
- int best_sub_mode = -1;
- score_t best_sub_score = MAX_COST;
- const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
-
- // TODO(skal): we don't really need the prediction pixels here,
- // but just the distortion against 'src'.
- VP8MakeIntra4Preds(it);
- for (mode = 0; mode < NUM_BMODES; ++mode) {
- const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode];
- const score_t score = VP8SSE4x4(src, ref);
- if (score < best_sub_score) {
- best_sub_mode = mode;
- best_sub_score = score;
- }
- }
- modes_i4[it->i4_] = best_sub_mode;
- score_i4 += best_sub_score;
- if (score_i4 >= best_score) break;
- } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF));
- if (score_i4 < best_score) {
- VP8SetIntra4Mode(it, modes_i4);
- }
- }
-}
-
-//------------------------------------------------------------------------------
-// Entry point
-
-int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
- VP8RDLevel rd_opt) {
- int is_skipped;
- const int method = it->enc_->method_;
-
- InitScore(rd);
-
- // We can perform predictions for Luma16x16 and Chroma8x8 already.
- // Luma4x4 predictions needs to be done as-we-go.
- VP8MakeLuma16Preds(it);
- VP8MakeChroma8Preds(it);
-
- if (rd_opt > RD_OPT_NONE) {
- it->do_trellis_ = (rd_opt >= RD_OPT_TRELLIS_ALL);
- PickBestIntra16(it, rd);
- if (method >= 2) {
- PickBestIntra4(it, rd);
- }
- PickBestUV(it, rd);
- if (rd_opt == RD_OPT_TRELLIS) { // finish off with trellis-optim now
- it->do_trellis_ = 1;
- SimpleQuantize(it, rd);
- }
- } else {
- // For method == 2, pick the best intra4/intra16 based on SSE (~tad slower).
- // For method <= 1, we refine intra4 or intra16 (but don't re-examine mode).
- DistoRefine(it, (method >= 2));
- SimpleQuantize(it, rd);
- }
- is_skipped = (rd->nz == 0);
- VP8SetSkip(it, is_skipped);
- return is_skipped;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/syntax.c b/src/third_party/libwebp/enc/syntax.c
deleted file mode 100644
index a4e94e5..0000000
--- a/src/third_party/libwebp/enc/syntax.c
+++ /dev/null
@@ -1,434 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Header syntax writing
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#else
-#include <assert.h>
-#endif
-
-#include "../utils/utils.h"
-#include "../webp/format_constants.h" // RIFF constants
-#include "../webp/mux_types.h" // ALPHA_FLAG
-#include "./vp8enci.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Helper functions
-
-static int IsVP8XNeeded(const VP8Encoder* const enc) {
- return !!enc->has_alpha_; // Currently the only case when VP8X is needed.
- // This could change in the future.
-}
-
-static int PutPaddingByte(const WebPPicture* const pic) {
- const uint8_t pad_byte[1] = { 0 };
- return !!pic->writer(pad_byte, 1, pic);
-}
-
-//------------------------------------------------------------------------------
-// Writers for header's various pieces (in order of appearance)
-
-static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc,
- size_t riff_size) {
- const WebPPicture* const pic = enc->pic_;
- uint8_t riff[RIFF_HEADER_SIZE] = {
- 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'
- };
- SB_DCHECK(riff_size == (uint32_t)riff_size);
- PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
- if (!pic->writer(riff, sizeof(riff), pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
- return VP8_ENC_OK;
-}
-
-static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) {
- const WebPPicture* const pic = enc->pic_;
- uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = {
- 'V', 'P', '8', 'X'
- };
- uint32_t flags = 0;
-
- SB_DCHECK(IsVP8XNeeded(enc));
- SB_DCHECK(pic->width >= 1 && pic->height >= 1);
- SB_DCHECK(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE);
-
- if (enc->has_alpha_) {
- flags |= ALPHA_FLAG;
- }
-
- PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE);
- PutLE32(vp8x + CHUNK_HEADER_SIZE, flags);
- PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1);
- PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1);
- if (!pic->writer(vp8x, sizeof(vp8x), pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
- return VP8_ENC_OK;
-}
-
-static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) {
- const WebPPicture* const pic = enc->pic_;
- uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = {
- 'A', 'L', 'P', 'H'
- };
-
- SB_DCHECK(enc->has_alpha_);
-
- // Alpha chunk header.
- PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_);
- if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
-
- // Alpha chunk data.
- if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
-
- // Padding.
- if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
- return VP8_ENC_OK;
-}
-
-static WebPEncodingError PutVP8Header(const WebPPicture* const pic,
- size_t vp8_size) {
- uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = {
- 'V', 'P', '8', ' '
- };
- SB_DCHECK(vp8_size == (uint32_t)vp8_size);
- PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size);
- if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
- return VP8_ENC_OK;
-}
-
-static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic,
- int profile, size_t size0) {
- uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE];
- uint32_t bits;
-
- if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit
- return VP8_ENC_ERROR_PARTITION0_OVERFLOW;
- }
-
- // Paragraph 9.1.
- bits = 0 // keyframe (1b)
- | (profile << 1) // profile (3b)
- | (1 << 4) // visible (1b)
- | ((uint32_t)size0 << 5); // partition length (19b)
- vp8_frm_hdr[0] = (bits >> 0) & 0xff;
- vp8_frm_hdr[1] = (bits >> 8) & 0xff;
- vp8_frm_hdr[2] = (bits >> 16) & 0xff;
- // signature
- vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff;
- vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff;
- vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff;
- // dimensions
- vp8_frm_hdr[6] = pic->width & 0xff;
- vp8_frm_hdr[7] = pic->width >> 8;
- vp8_frm_hdr[8] = pic->height & 0xff;
- vp8_frm_hdr[9] = pic->height >> 8;
-
- if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
- return VP8_ENC_OK;
-}
-
-// WebP Headers.
-static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0,
- size_t vp8_size, size_t riff_size) {
- WebPPicture* const pic = enc->pic_;
- WebPEncodingError err = VP8_ENC_OK;
-
- // RIFF header.
- err = PutRIFFHeader(enc, riff_size);
- if (err != VP8_ENC_OK) goto Error;
-
- // VP8X.
- if (IsVP8XNeeded(enc)) {
- err = PutVP8XHeader(enc);
- if (err != VP8_ENC_OK) goto Error;
- }
-
- // Alpha.
- if (enc->has_alpha_) {
- err = PutAlphaChunk(enc);
- if (err != VP8_ENC_OK) goto Error;
- }
-
- // VP8 header.
- err = PutVP8Header(pic, vp8_size);
- if (err != VP8_ENC_OK) goto Error;
-
- // VP8 frame header.
- err = PutVP8FrameHeader(pic, enc->profile_, size0);
- if (err != VP8_ENC_OK) goto Error;
-
- // All OK.
- return 1;
-
- // Error.
- Error:
- return WebPEncodingSetError(pic, err);
-}
-
-// Segmentation header
-static void PutSegmentHeader(VP8BitWriter* const bw,
- const VP8Encoder* const enc) {
- const VP8SegmentHeader* const hdr = &enc->segment_hdr_;
- const VP8Proba* const proba = &enc->proba_;
- if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) {
- // We always 'update' the quant and filter strength values
- const int update_data = 1;
- int s;
- VP8PutBitUniform(bw, hdr->update_map_);
- if (VP8PutBitUniform(bw, update_data)) {
- // we always use absolute values, not relative ones
- VP8PutBitUniform(bw, 1); // (segment_feature_mode = 1. Paragraph 9.3.)
- for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
- VP8PutSignedValue(bw, enc->dqm_[s].quant_, 7);
- }
- for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
- VP8PutSignedValue(bw, enc->dqm_[s].fstrength_, 6);
- }
- }
- if (hdr->update_map_) {
- for (s = 0; s < 3; ++s) {
- if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) {
- VP8PutValue(bw, proba->segments_[s], 8);
- }
- }
- }
- }
-}
-
-// Filtering parameters header
-static void PutFilterHeader(VP8BitWriter* const bw,
- const VP8FilterHeader* const hdr) {
- const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0);
- VP8PutBitUniform(bw, hdr->simple_);
- VP8PutValue(bw, hdr->level_, 6);
- VP8PutValue(bw, hdr->sharpness_, 3);
- if (VP8PutBitUniform(bw, use_lf_delta)) {
- // '0' is the default value for i4x4_lf_delta_ at frame #0.
- const int need_update = (hdr->i4x4_lf_delta_ != 0);
- if (VP8PutBitUniform(bw, need_update)) {
- // we don't use ref_lf_delta => emit four 0 bits
- VP8PutValue(bw, 0, 4);
- // we use mode_lf_delta for i4x4
- VP8PutSignedValue(bw, hdr->i4x4_lf_delta_, 6);
- VP8PutValue(bw, 0, 3); // all others unused
- }
- }
-}
-
-// Nominal quantization parameters
-static void PutQuant(VP8BitWriter* const bw,
- const VP8Encoder* const enc) {
- VP8PutValue(bw, enc->base_quant_, 7);
- VP8PutSignedValue(bw, enc->dq_y1_dc_, 4);
- VP8PutSignedValue(bw, enc->dq_y2_dc_, 4);
- VP8PutSignedValue(bw, enc->dq_y2_ac_, 4);
- VP8PutSignedValue(bw, enc->dq_uv_dc_, 4);
- VP8PutSignedValue(bw, enc->dq_uv_ac_, 4);
-}
-
-// Partition sizes
-static int EmitPartitionsSize(const VP8Encoder* const enc,
- WebPPicture* const pic) {
- uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)];
- int p;
- for (p = 0; p < enc->num_parts_ - 1; ++p) {
- const size_t part_size = VP8BitWriterSize(enc->parts_ + p);
- if (part_size >= VP8_MAX_PARTITION_SIZE) {
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW);
- }
- buf[3 * p + 0] = (part_size >> 0) & 0xff;
- buf[3 * p + 1] = (part_size >> 8) & 0xff;
- buf[3 * p + 2] = (part_size >> 16) & 0xff;
- }
- return p ? pic->writer(buf, 3 * p, pic) : 1;
-}
-
-//------------------------------------------------------------------------------
-
-#ifdef WEBP_EXPERIMENTAL_FEATURES
-
-#define KTRAILER_SIZE 8
-
-static int WriteExtensions(VP8Encoder* const enc) {
- uint8_t buffer[KTRAILER_SIZE];
- VP8BitWriter* const bw = &enc->bw_;
- WebPPicture* const pic = enc->pic_;
-
- // Layer (bytes 0..3)
- PutLE24(buffer + 0, enc->layer_data_size_);
- buffer[3] = enc->pic_->colorspace & WEBP_CSP_UV_MASK;
- if (enc->layer_data_size_ > 0) {
- SB_DCHECK(enc->use_layer_);
- // append layer data to last partition
- if (!VP8BitWriterAppend(&enc->parts_[enc->num_parts_ - 1],
- enc->layer_data_, enc->layer_data_size_)) {
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
- }
- }
-
- buffer[KTRAILER_SIZE - 1] = 0x01; // marker
- if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) {
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
- }
- return 1;
-}
-
-#endif /* WEBP_EXPERIMENTAL_FEATURES */
-
-//------------------------------------------------------------------------------
-
-static size_t GeneratePartition0(VP8Encoder* const enc) {
- VP8BitWriter* const bw = &enc->bw_;
- const int mb_size = enc->mb_w_ * enc->mb_h_;
- uint64_t pos1, pos2, pos3;
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- const int need_extensions = enc->use_layer_;
-#endif
-
- pos1 = VP8BitWriterPos(bw);
- VP8BitWriterInit(bw, mb_size * 7 / 8); // ~7 bits per macroblock
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- VP8PutBitUniform(bw, need_extensions); // extensions
-#else
- VP8PutBitUniform(bw, 0); // colorspace
-#endif
- VP8PutBitUniform(bw, 0); // clamp type
-
- PutSegmentHeader(bw, enc);
- PutFilterHeader(bw, &enc->filter_hdr_);
- VP8PutValue(bw, enc->num_parts_ == 8 ? 3 :
- enc->num_parts_ == 4 ? 2 :
- enc->num_parts_ == 2 ? 1 : 0, 2);
- PutQuant(bw, enc);
- VP8PutBitUniform(bw, 0); // no proba update
- VP8WriteProbas(bw, &enc->proba_);
- pos2 = VP8BitWriterPos(bw);
- VP8CodeIntraModes(enc);
- VP8BitWriterFinish(bw);
-
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- if (need_extensions && !WriteExtensions(enc)) {
- return 0;
- }
-#endif
-
- pos3 = VP8BitWriterPos(bw);
-
- if (enc->pic_->stats) {
- enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3);
- enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3);
- enc->pic_->stats->alpha_data_size = (int)enc->alpha_data_size_;
- enc->pic_->stats->layer_data_size = (int)enc->layer_data_size_;
- }
- return !bw->error_;
-}
-
-void VP8EncFreeBitWriters(VP8Encoder* const enc) {
- int p;
- VP8BitWriterWipeOut(&enc->bw_);
- for (p = 0; p < enc->num_parts_; ++p) {
- VP8BitWriterWipeOut(enc->parts_ + p);
- }
-}
-
-int VP8EncWrite(VP8Encoder* const enc) {
- WebPPicture* const pic = enc->pic_;
- VP8BitWriter* const bw = &enc->bw_;
- const int task_percent = 19;
- const int percent_per_part = task_percent / enc->num_parts_;
- const int final_percent = enc->percent_ + task_percent;
- int ok = 0;
- size_t vp8_size, pad, riff_size;
- int p;
-
- // Partition #0 with header and partition sizes
- ok = !!GeneratePartition0(enc);
-
- // Compute VP8 size
- vp8_size = VP8_FRAME_HEADER_SIZE +
- VP8BitWriterSize(bw) +
- 3 * (enc->num_parts_ - 1);
- for (p = 0; p < enc->num_parts_; ++p) {
- vp8_size += VP8BitWriterSize(enc->parts_ + p);
- }
- pad = vp8_size & 1;
- vp8_size += pad;
-
- // Compute RIFF size
- // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
- riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size;
- if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data.
- riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
- }
- if (enc->has_alpha_) { // Add size for: ALPH header + data.
- const uint32_t padded_alpha_size = enc->alpha_data_size_ +
- (enc->alpha_data_size_ & 1);
- riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
- }
- // Sanity check.
- if (riff_size > 0xfffffffeU) {
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
- }
-
- // Emit headers and partition #0
- {
- const uint8_t* const part0 = VP8BitWriterBuf(bw);
- const size_t size0 = VP8BitWriterSize(bw);
- ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size)
- && pic->writer(part0, size0, pic)
- && EmitPartitionsSize(enc, pic);
- VP8BitWriterWipeOut(bw); // will free the internal buffer.
- }
-
- // Token partitions
- for (p = 0; p < enc->num_parts_; ++p) {
- const uint8_t* const buf = VP8BitWriterBuf(enc->parts_ + p);
- const size_t size = VP8BitWriterSize(enc->parts_ + p);
- if (size)
- ok = ok && pic->writer(buf, size, pic);
- VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer.
- ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part,
- &enc->percent_);
- }
-
- // Padding byte
- if (ok && pad) {
- ok = PutPaddingByte(pic);
- }
-
- enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
- ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_);
- return ok;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/token.c b/src/third_party/libwebp/enc/token.c
deleted file mode 100644
index 1280eb5..0000000
--- a/src/third_party/libwebp/enc/token.c
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Paginated token buffer
-//
-// A 'token' is a bit value associated with a probability, either fixed
-// or a later-to-be-determined after statistics have been collected.
-// For dynamic probability, we just record the slot id (idx) for the probability
-// value in the final probability array (uint8_t* probas in VP8EmitTokens).
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#endif
-
-#include "./vp8enci.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if !defined(DISABLE_TOKEN_BUFFER)
-
-// we use pages to reduce the number of SbMemoryCopy()
-#define MAX_NUM_TOKEN 8192 // max number of token per page
-#define FIXED_PROBA_BIT (1u << 14)
-
-struct VP8Tokens {
- uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit
- // bit #14: constant proba or idx
- // bits 0..13: slot or constant proba
- VP8Tokens* next_;
-};
-
-//------------------------------------------------------------------------------
-
-void VP8TBufferInit(VP8TBuffer* const b) {
- b->tokens_ = NULL;
- b->pages_ = NULL;
- b->last_page_ = &b->pages_;
- b->left_ = 0;
- b->error_ = 0;
-}
-
-void VP8TBufferClear(VP8TBuffer* const b) {
- if (b != NULL) {
- const VP8Tokens* p = b->pages_;
- while (p != NULL) {
- const VP8Tokens* const next = p->next_;
- SbMemoryDeallocate((void*)p);
- p = next;
- }
- VP8TBufferInit(b);
- }
-}
-
-static int TBufferNewPage(VP8TBuffer* const b) {
- VP8Tokens* const page = b->error_ ? NULL : (VP8Tokens*)SbMemoryAllocate(sizeof(*page));
- if (page == NULL) {
- b->error_ = 1;
- return 0;
- }
- *b->last_page_ = page;
- b->last_page_ = &page->next_;
- b->left_ = MAX_NUM_TOKEN;
- b->tokens_ = page->tokens_;
- page->next_ = NULL;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-#define TOKEN_ID(t, b, ctx, p) \
- ((p) + NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t))))
-
-static WEBP_INLINE int AddToken(VP8TBuffer* const b,
- int bit, uint32_t proba_idx) {
- SB_DCHECK(proba_idx < FIXED_PROBA_BIT);
- SB_DCHECK(bit == 0 || bit == 1);
- if (b->left_ > 0 || TBufferNewPage(b)) {
- const int slot = --b->left_;
- b->tokens_[slot] = (bit << 15) | proba_idx;
- }
- return bit;
-}
-
-static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b,
- int bit, int proba) {
- SB_DCHECK(proba < 256);
- SB_DCHECK(bit == 0 || bit == 1);
- if (b->left_ > 0 || TBufferNewPage(b)) {
- const int slot = --b->left_;
- b->tokens_[slot] = (bit << 15) | FIXED_PROBA_BIT | proba;
- }
-}
-
-int VP8RecordCoeffTokens(int ctx, int coeff_type, int first, int last,
- const int16_t* const coeffs,
- VP8TBuffer* const tokens) {
- int n = first;
- uint32_t base_id = TOKEN_ID(coeff_type, n, ctx, 0);
- if (!AddToken(tokens, last >= 0, base_id + 0)) {
- return 0;
- }
-
- while (n < 16) {
- const int c = coeffs[n++];
- const int sign = c < 0;
- int v = sign ? -c : c;
- if (!AddToken(tokens, v != 0, base_id + 1)) {
- ctx = 0;
- base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0);
- continue;
- }
- if (!AddToken(tokens, v > 1, base_id + 2)) {
- ctx = 1;
- } else {
- if (!AddToken(tokens, v > 4, base_id + 3)) {
- if (AddToken(tokens, v != 2, base_id + 4))
- AddToken(tokens, v == 4, base_id + 5);
- } else if (!AddToken(tokens, v > 10, base_id + 6)) {
- if (!AddToken(tokens, v > 6, base_id + 7)) {
- AddConstantToken(tokens, v == 6, 159);
- } else {
- AddConstantToken(tokens, v >= 9, 165);
- AddConstantToken(tokens, !(v & 1), 145);
- }
- } else {
- int mask;
- const uint8_t* tab;
- if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)
- AddToken(tokens, 0, base_id + 8);
- AddToken(tokens, 0, base_id + 9);
- v -= 3 + (8 << 0);
- mask = 1 << 2;
- tab = VP8Cat3;
- } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b)
- AddToken(tokens, 0, base_id + 8);
- AddToken(tokens, 1, base_id + 9);
- v -= 3 + (8 << 1);
- mask = 1 << 3;
- tab = VP8Cat4;
- } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b)
- AddToken(tokens, 1, base_id + 8);
- AddToken(tokens, 0, base_id + 10);
- v -= 3 + (8 << 2);
- mask = 1 << 4;
- tab = VP8Cat5;
- } else { // VP8Cat6 (11b)
- AddToken(tokens, 1, base_id + 8);
- AddToken(tokens, 1, base_id + 10);
- v -= 3 + (8 << 3);
- mask = 1 << 10;
- tab = VP8Cat6;
- }
- while (mask) {
- AddConstantToken(tokens, !!(v & mask), *tab++);
- mask >>= 1;
- }
- }
- ctx = 2;
- }
- AddConstantToken(tokens, sign, 128);
- base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0);
- if (n == 16 || !AddToken(tokens, n <= last, base_id + 0)) {
- return 1; // EOB
- }
- }
- return 1;
-}
-
-#undef TOKEN_ID
-
-//------------------------------------------------------------------------------
-// This function works, but isn't currently used. Saved for later.
-
-#if 0
-
-static void Record(int bit, proba_t* const stats) {
- proba_t p = *stats;
- if (p >= 0xffff0000u) { // an overflow is inbound.
- p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
- }
- // record bit count (lower 16 bits) and increment total count (upper 16 bits).
- p += 0x00010000u + bit;
- *stats = p;
-}
-
-void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats) {
- const VP8Tokens* p = b->pages_;
- while (p != NULL) {
- const int N = (p->next_ == NULL) ? b->left_ : 0;
- int n = MAX_NUM_TOKEN;
- while (n-- > N) {
- const uint16_t token = p->tokens_[n];
- if (!(token & FIXED_PROBA_BIT)) {
- Record((token >> 15) & 1, stats + (token & 0x3fffu));
- }
- }
- p = p->next_;
- }
-}
-
-#endif // 0
-
-//------------------------------------------------------------------------------
-// Final coding pass, with known probabilities
-
-int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
- const uint8_t* const probas, int final_pass) {
- const VP8Tokens* p = b->pages_;
- (void)final_pass;
- if (b->error_) return 0;
- while (p != NULL) {
- const VP8Tokens* const next = p->next_;
- const int N = (next == NULL) ? b->left_ : 0;
- int n = MAX_NUM_TOKEN;
- while (n-- > N) {
- const uint16_t token = p->tokens_[n];
- const int bit = (token >> 15) & 1;
- if (token & FIXED_PROBA_BIT) {
- VP8PutBit(bw, bit, token & 0xffu); // constant proba
- } else {
- VP8PutBit(bw, bit, probas[token & 0x3fffu]);
- }
- }
- if (final_pass) SbMemoryDeallocate((void*)p);
- p = next;
- }
- if (final_pass) b->pages_ = NULL;
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-#else // DISABLE_TOKEN_BUFFER
-
-void VP8TBufferInit(VP8TBuffer* const b) {
- (void)b;
-}
-void VP8TBufferClear(VP8TBuffer* const b) {
- (void)b;
-}
-
-#endif // !DISABLE_TOKEN_BUFFER
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/tree.c b/src/third_party/libwebp/enc/tree.c
deleted file mode 100644
index 31df71f..0000000
--- a/src/third_party/libwebp/enc/tree.c
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Token probabilities
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "./vp8enci.h"
-
-#if defined(STARBOARD)
-#include "starboard/memory.h"
-#endif
-
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Default probabilities
-
-// Paragraph 13.5
-const uint8_t
- VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
- // genereated using vp8_default_coef_probs() in entropy.c:129
- { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
- { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
- { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
- },
- { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
- { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
- { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
- },
- { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
- { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
- { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
- },
- { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
- { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
- { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
- },
- { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
- { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
- { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
- },
- { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
- { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
- { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
- },
- { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
- }
- },
- { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
- { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
- { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
- },
- { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
- { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
- { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
- },
- { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
- { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
- { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
- },
- { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
- { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
- { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
- },
- { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
- { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
- { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
- },
- { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
- { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
- { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
- },
- { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
- { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
- { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
- },
- { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
- { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
- }
- },
- { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
- { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
- { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
- },
- { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
- { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
- { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
- },
- { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
- { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
- { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
- },
- { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
- { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
- },
- { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
- { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
- { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- },
- { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
- }
- },
- { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
- { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
- { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
- },
- { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
- { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
- { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
- },
- { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
- { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
- { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
- },
- { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
- { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
- { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
- },
- { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
- { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
- { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
- },
- { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
- { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
- { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
- },
- { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
- { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
- { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
- },
- { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
- { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
- }
- }
-};
-
-void VP8DefaultProbas(VP8Encoder* const enc) {
- VP8Proba* const probas = &enc->proba_;
- probas->use_skip_proba_ = 0;
- SbMemorySet(probas->segments_, 255u, sizeof(probas->segments_));
- SbMemoryCopy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0));
- // Note: we could hard-code the level_costs_ corresponding to VP8CoeffsProba0,
- // but that's ~11k of static data. Better call VP8CalculateLevelCosts() later.
- probas->dirty_ = 1;
-}
-
-// Paragraph 11.5. 900bytes.
-static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
- { { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
- { 152, 179, 64, 126, 170, 118, 46, 70, 95 },
- { 175, 69, 143, 80, 85, 82, 72, 155, 103 },
- { 56, 58, 10, 171, 218, 189, 17, 13, 152 },
- { 114, 26, 17, 163, 44, 195, 21, 10, 173 },
- { 121, 24, 80, 195, 26, 62, 44, 64, 85 },
- { 144, 71, 10, 38, 171, 213, 144, 34, 26 },
- { 170, 46, 55, 19, 136, 160, 33, 206, 71 },
- { 63, 20, 8, 114, 114, 208, 12, 9, 226 },
- { 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
- { { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
- { 72, 187, 100, 130, 157, 111, 32, 75, 80 },
- { 66, 102, 167, 99, 74, 62, 40, 234, 128 },
- { 41, 53, 9, 178, 241, 141, 26, 8, 107 },
- { 74, 43, 26, 146, 73, 166, 49, 23, 157 },
- { 65, 38, 105, 160, 51, 52, 31, 115, 128 },
- { 104, 79, 12, 27, 217, 255, 87, 17, 7 },
- { 87, 68, 71, 44, 114, 51, 15, 186, 23 },
- { 47, 41, 14, 110, 182, 183, 21, 17, 194 },
- { 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
- { { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
- { 43, 97, 183, 117, 85, 38, 35, 179, 61 },
- { 39, 53, 200, 87, 26, 21, 43, 232, 171 },
- { 56, 34, 51, 104, 114, 102, 29, 93, 77 },
- { 39, 28, 85, 171, 58, 165, 90, 98, 64 },
- { 34, 22, 116, 206, 23, 34, 43, 166, 73 },
- { 107, 54, 32, 26, 51, 1, 81, 43, 31 },
- { 68, 25, 106, 22, 64, 171, 36, 225, 114 },
- { 34, 19, 21, 102, 132, 188, 16, 76, 124 },
- { 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
- { { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
- { 60, 148, 31, 172, 219, 228, 21, 18, 111 },
- { 112, 113, 77, 85, 179, 255, 38, 120, 114 },
- { 40, 42, 1, 196, 245, 209, 10, 25, 109 },
- { 88, 43, 29, 140, 166, 213, 37, 43, 154 },
- { 61, 63, 30, 155, 67, 45, 68, 1, 209 },
- { 100, 80, 8, 43, 154, 1, 51, 26, 71 },
- { 142, 78, 78, 16, 255, 128, 34, 197, 171 },
- { 41, 40, 5, 102, 211, 183, 4, 1, 221 },
- { 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
- { { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
- { 67, 87, 58, 169, 82, 115, 26, 59, 179 },
- { 63, 59, 90, 180, 59, 166, 93, 73, 154 },
- { 40, 40, 21, 116, 143, 209, 34, 39, 175 },
- { 47, 15, 16, 183, 34, 223, 49, 45, 183 },
- { 46, 17, 33, 183, 6, 98, 15, 32, 183 },
- { 57, 46, 22, 24, 128, 1, 54, 17, 37 },
- { 65, 32, 73, 115, 28, 128, 23, 128, 205 },
- { 40, 3, 9, 115, 51, 192, 18, 6, 223 },
- { 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
- { { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
- { 64, 90, 70, 205, 40, 41, 23, 26, 57 },
- { 54, 57, 112, 184, 5, 41, 38, 166, 213 },
- { 30, 34, 26, 133, 152, 116, 10, 32, 134 },
- { 39, 19, 53, 221, 26, 114, 32, 73, 255 },
- { 31, 9, 65, 234, 2, 15, 1, 118, 73 },
- { 75, 32, 12, 51, 192, 255, 160, 43, 51 },
- { 88, 31, 35, 67, 102, 85, 55, 186, 85 },
- { 56, 21, 23, 111, 59, 205, 45, 37, 192 },
- { 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
- { { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
- { 95, 84, 53, 89, 128, 100, 113, 101, 45 },
- { 75, 79, 123, 47, 51, 128, 81, 171, 1 },
- { 57, 17, 5, 71, 102, 57, 53, 41, 49 },
- { 38, 33, 13, 121, 57, 73, 26, 1, 85 },
- { 41, 10, 67, 138, 77, 110, 90, 47, 114 },
- { 115, 21, 2, 10, 102, 255, 166, 23, 6 },
- { 101, 29, 16, 10, 85, 128, 101, 196, 26 },
- { 57, 18, 10, 102, 102, 213, 34, 20, 43 },
- { 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
- { { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
- { 69, 60, 71, 38, 73, 119, 28, 222, 37 },
- { 68, 45, 128, 34, 1, 47, 11, 245, 171 },
- { 62, 17, 19, 70, 146, 85, 55, 62, 70 },
- { 37, 43, 37, 154, 100, 163, 85, 160, 1 },
- { 63, 9, 92, 136, 28, 64, 32, 201, 85 },
- { 75, 15, 9, 9, 64, 255, 184, 119, 16 },
- { 86, 6, 28, 5, 64, 255, 25, 248, 1 },
- { 56, 8, 17, 132, 137, 255, 55, 116, 128 },
- { 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
- { { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
- { 51, 103, 44, 131, 131, 123, 31, 6, 158 },
- { 86, 40, 64, 135, 148, 224, 45, 183, 128 },
- { 22, 26, 17, 131, 240, 154, 14, 1, 209 },
- { 45, 16, 21, 91, 64, 222, 7, 1, 197 },
- { 56, 21, 39, 155, 60, 138, 23, 102, 213 },
- { 83, 12, 13, 54, 192, 255, 68, 47, 28 },
- { 85, 26, 85, 85, 128, 128, 32, 146, 171 },
- { 18, 11, 7, 63, 144, 171, 4, 4, 246 },
- { 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
- { { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
- { 85, 126, 47, 87, 176, 51, 41, 20, 32 },
- { 101, 75, 128, 139, 118, 146, 116, 128, 85 },
- { 56, 41, 15, 176, 236, 85, 37, 9, 62 },
- { 71, 30, 17, 119, 118, 255, 17, 18, 138 },
- { 101, 38, 60, 138, 55, 70, 43, 26, 142 },
- { 146, 36, 19, 30, 171, 255, 97, 27, 20 },
- { 138, 45, 61, 62, 219, 1, 81, 188, 64 },
- { 32, 41, 20, 117, 151, 142, 20, 21, 163 },
- { 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
-};
-
-static int PutI4Mode(VP8BitWriter* const bw, int mode,
- const uint8_t* const prob) {
- if (VP8PutBit(bw, mode != B_DC_PRED, prob[0])) {
- if (VP8PutBit(bw, mode != B_TM_PRED, prob[1])) {
- if (VP8PutBit(bw, mode != B_VE_PRED, prob[2])) {
- if (!VP8PutBit(bw, mode >= B_LD_PRED, prob[3])) {
- if (VP8PutBit(bw, mode != B_HE_PRED, prob[4])) {
- VP8PutBit(bw, mode != B_RD_PRED, prob[5]);
- }
- } else {
- if (VP8PutBit(bw, mode != B_LD_PRED, prob[6])) {
- if (VP8PutBit(bw, mode != B_VL_PRED, prob[7])) {
- VP8PutBit(bw, mode != B_HD_PRED, prob[8]);
- }
- }
- }
- }
- }
- }
- return mode;
-}
-
-static void PutI16Mode(VP8BitWriter* const bw, int mode) {
- if (VP8PutBit(bw, (mode == TM_PRED || mode == H_PRED), 156)) {
- VP8PutBit(bw, mode == TM_PRED, 128); // TM or HE
- } else {
- VP8PutBit(bw, mode == V_PRED, 163); // VE or DC
- }
-}
-
-static void PutUVMode(VP8BitWriter* const bw, int uv_mode) {
- if (VP8PutBit(bw, uv_mode != DC_PRED, 142)) {
- if (VP8PutBit(bw, uv_mode != V_PRED, 114)) {
- VP8PutBit(bw, uv_mode != H_PRED, 183); // else: TM_PRED
- }
- }
-}
-
-static void PutSegment(VP8BitWriter* const bw, int s, const uint8_t* p) {
- if (VP8PutBit(bw, s >= 2, p[0])) p += 1;
- VP8PutBit(bw, s & 1, p[1]);
-}
-
-void VP8CodeIntraModes(VP8Encoder* const enc) {
- VP8BitWriter* const bw = &enc->bw_;
- VP8EncIterator it;
- VP8IteratorInit(enc, &it);
- do {
- const VP8MBInfo* mb = it.mb_;
- const uint8_t* preds = it.preds_;
- if (enc->segment_hdr_.update_map_) {
- PutSegment(bw, mb->segment_, enc->proba_.segments_);
- }
- if (enc->proba_.use_skip_proba_) {
- VP8PutBit(bw, mb->skip_, enc->proba_.skip_proba_);
- }
- if (VP8PutBit(bw, (mb->type_ != 0), 145)) { // i16x16
- PutI16Mode(bw, preds[0]);
- } else {
- const int preds_w = enc->preds_w_;
- const uint8_t* top_pred = preds - preds_w;
- int x, y;
- for (y = 0; y < 4; ++y) {
- int left = preds[-1];
- for (x = 0; x < 4; ++x) {
- const uint8_t* const probas = kBModesProba[top_pred[x]][left];
- left = PutI4Mode(bw, preds[x], probas);
- }
- top_pred = preds;
- preds += preds_w;
- }
- }
- PutUVMode(bw, mb->uv_mode_);
- } while (VP8IteratorNext(&it, 0));
-}
-
-//------------------------------------------------------------------------------
-// Paragraph 13
-
-const uint8_t
- VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
- { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
- { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- },
- { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
- { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
- },
- { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- },
- { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
- },
- { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- },
- { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
- { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- },
- { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
- { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
- }
- }
-};
-
-void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas) {
- int t, b, c, p;
- for (t = 0; t < NUM_TYPES; ++t) {
- for (b = 0; b < NUM_BANDS; ++b) {
- for (c = 0; c < NUM_CTX; ++c) {
- for (p = 0; p < NUM_PROBAS; ++p) {
- const uint8_t p0 = probas->coeffs_[t][b][c][p];
- const int update = (p0 != VP8CoeffsProba0[t][b][c][p]);
- if (VP8PutBit(bw, update, VP8CoeffsUpdateProba[t][b][c][p])) {
- VP8PutValue(bw, p0, 8);
- }
- }
- }
- }
- }
- if (VP8PutBitUniform(bw, probas->use_skip_proba_)) {
- VP8PutValue(bw, probas->skip_proba_, 8);
- }
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/vp8enci.h b/src/third_party/libwebp/enc/vp8enci.h
deleted file mode 100644
index fe37140..0000000
--- a/src/third_party/libwebp/enc/vp8enci.h
+++ /dev/null
@@ -1,555 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// WebP encoder: internal header.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_ENC_VP8ENCI_H_
-#define WEBP_ENC_VP8ENCI_H_
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <string.h> // for memcpy()
-#endif
-#include "../webp/encode.h"
-#include "../dsp/dsp.h"
-#include "../utils/bit_writer.h"
-#include "../utils/thread.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Various defines and enums
-
-// version numbers
-#define ENC_MAJ_VERSION 0
-#define ENC_MIN_VERSION 3
-#define ENC_REV_VERSION 1
-
-// intra prediction modes
-enum { B_DC_PRED = 0, // 4x4 modes
- B_TM_PRED = 1,
- B_VE_PRED = 2,
- B_HE_PRED = 3,
- B_RD_PRED = 4,
- B_VR_PRED = 5,
- B_LD_PRED = 6,
- B_VL_PRED = 7,
- B_HD_PRED = 8,
- B_HU_PRED = 9,
- NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
-
- // Luma16 or UV modes
- DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
- H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
- NUM_PRED_MODES = 4
- };
-
-enum { NUM_MB_SEGMENTS = 4,
- MAX_NUM_PARTITIONS = 8,
- NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
- NUM_BANDS = 8,
- NUM_CTX = 3,
- NUM_PROBAS = 11,
- MAX_LF_LEVELS = 64, // Maximum loop filter level
- MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
- MAX_LEVEL = 2047 // max level (note: max codable is 2047 + 67)
- };
-
-typedef enum { // Rate-distortion optimization levels
- RD_OPT_NONE = 0, // no rd-opt
- RD_OPT_BASIC = 1, // basic scoring (no trellis)
- RD_OPT_TRELLIS = 2, // perform trellis-quant on the final decision only
- RD_OPT_TRELLIS_ALL = 3 // trellis-quant for every scoring (much slower)
-} VP8RDLevel;
-
-// YUV-cache parameters. Cache is 16-pixels wide.
-// The original or reconstructed samples can be accessed using VP8Scan[]
-// The predicted blocks can be accessed using offsets to yuv_p_ and
-// the arrays VP8*ModeOffsets[];
-// +----+ YUV Samples area. See VP8Scan[] for accessing the blocks.
-// Y_OFF |YYYY| <- original samples (enc->yuv_in_)
-// |YYYY|
-// |YYYY|
-// |YYYY|
-// U_OFF |UUVV| V_OFF (=U_OFF + 8)
-// |UUVV|
-// +----+
-// Y_OFF |YYYY| <- compressed/decoded samples ('yuv_out_')
-// |YYYY| There are two buffers like this ('yuv_out_'/'yuv_out2_')
-// |YYYY|
-// |YYYY|
-// U_OFF |UUVV| V_OFF
-// |UUVV|
-// x2 (for yuv_out2_)
-// +----+ Prediction area ('yuv_p_', size = PRED_SIZE)
-// I16DC16 |YYYY| Intra16 predictions (16x16 block each)
-// |YYYY|
-// |YYYY|
-// |YYYY|
-// I16TM16 |YYYY|
-// |YYYY|
-// |YYYY|
-// |YYYY|
-// I16VE16 |YYYY|
-// |YYYY|
-// |YYYY|
-// |YYYY|
-// I16HE16 |YYYY|
-// |YYYY|
-// |YYYY|
-// |YYYY|
-// +----+ Chroma U/V predictions (16x8 block each)
-// C8DC8 |UUVV|
-// |UUVV|
-// C8TM8 |UUVV|
-// |UUVV|
-// C8VE8 |UUVV|
-// |UUVV|
-// C8HE8 |UUVV|
-// |UUVV|
-// +----+ Intra 4x4 predictions (4x4 block each)
-// |YYYY| I4DC4 I4TM4 I4VE4 I4HE4
-// |YYYY| I4RD4 I4VR4 I4LD4 I4VL4
-// |YY..| I4HD4 I4HU4 I4TMP
-// +----+
-#define BPS 16 // this is the common stride
-#define Y_SIZE (BPS * 16)
-#define UV_SIZE (BPS * 8)
-#define YUV_SIZE (Y_SIZE + UV_SIZE)
-#define PRED_SIZE (6 * 16 * BPS + 12 * BPS)
-#define Y_OFF (0)
-#define U_OFF (Y_SIZE)
-#define V_OFF (U_OFF + 8)
-#define ALIGN_CST 15
-#define DO_ALIGN(PTR) ((uintptr_t)((PTR) + ALIGN_CST) & ~ALIGN_CST)
-
-extern const int VP8Scan[16 + 4 + 4]; // in quant.c
-extern const int VP8UVModeOffsets[4]; // in analyze.c
-extern const int VP8I16ModeOffsets[4];
-extern const int VP8I4ModeOffsets[NUM_BMODES];
-
-// Layout of prediction blocks
-// intra 16x16
-#define I16DC16 (0 * 16 * BPS)
-#define I16TM16 (1 * 16 * BPS)
-#define I16VE16 (2 * 16 * BPS)
-#define I16HE16 (3 * 16 * BPS)
-// chroma 8x8, two U/V blocks side by side (hence: 16x8 each)
-#define C8DC8 (4 * 16 * BPS)
-#define C8TM8 (4 * 16 * BPS + 8 * BPS)
-#define C8VE8 (5 * 16 * BPS)
-#define C8HE8 (5 * 16 * BPS + 8 * BPS)
-// intra 4x4
-#define I4DC4 (6 * 16 * BPS + 0)
-#define I4TM4 (6 * 16 * BPS + 4)
-#define I4VE4 (6 * 16 * BPS + 8)
-#define I4HE4 (6 * 16 * BPS + 12)
-#define I4RD4 (6 * 16 * BPS + 4 * BPS + 0)
-#define I4VR4 (6 * 16 * BPS + 4 * BPS + 4)
-#define I4LD4 (6 * 16 * BPS + 4 * BPS + 8)
-#define I4VL4 (6 * 16 * BPS + 4 * BPS + 12)
-#define I4HD4 (6 * 16 * BPS + 8 * BPS + 0)
-#define I4HU4 (6 * 16 * BPS + 8 * BPS + 4)
-#define I4TMP (6 * 16 * BPS + 8 * BPS + 8)
-
-typedef int64_t score_t; // type used for scores, rate, distortion
-#define MAX_COST ((score_t)0x7fffffffffffffLL)
-
-#define QFIX 17
-#define BIAS(b) ((b) << (QFIX - 8))
-// Fun fact: this is the _only_ line where we're actually being lossy and
-// discarding bits.
-static WEBP_INLINE int QUANTDIV(int n, int iQ, int B) {
- return (n * iQ + B) >> QFIX;
-}
-
-// size of histogram used by CollectHistogram.
-#define MAX_COEFF_THRESH 31
-typedef struct VP8Histogram VP8Histogram;
-struct VP8Histogram {
- // TODO(skal): we only need to store the max_value and last_non_zero actually.
- int distribution[MAX_COEFF_THRESH + 1];
-};
-
-// Uncomment the following to remove token-buffer code:
-// #define DISABLE_TOKEN_BUFFER
-
-//------------------------------------------------------------------------------
-// Headers
-
-typedef uint32_t proba_t; // 16b + 16b
-typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS];
-typedef proba_t StatsArray[NUM_CTX][NUM_PROBAS];
-typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1];
-typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats
-
-typedef struct VP8Encoder VP8Encoder;
-
-// segment features
-typedef struct {
- int num_segments_; // Actual number of segments. 1 segment only = unused.
- int update_map_; // whether to update the segment map or not.
- // must be 0 if there's only 1 segment.
- int size_; // bit-cost for transmitting the segment map
-} VP8SegmentHeader;
-
-// Struct collecting all frame-persistent probabilities.
-typedef struct {
- uint8_t segments_[3]; // probabilities for segment tree
- uint8_t skip_proba_; // final probability of being skipped.
- ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 924 bytes
- StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes
- CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 11.4k
- int dirty_; // if true, need to call VP8CalculateLevelCosts()
- int use_skip_proba_; // Note: we always use skip_proba for now.
- int nb_skip_; // number of skipped blocks
-} VP8Proba;
-
-// Filter parameters. Not actually used in the code (we don't perform
-// the in-loop filtering), but filled from user's config
-typedef struct {
- int simple_; // filtering type: 0=complex, 1=simple
- int level_; // base filter level [0..63]
- int sharpness_; // [0..7]
- int i4x4_lf_delta_; // delta filter level for i4x4 relative to i16x16
-} VP8FilterHeader;
-
-//------------------------------------------------------------------------------
-// Informations about the macroblocks.
-
-typedef struct {
- // block type
- unsigned int type_:2; // 0=i4x4, 1=i16x16
- unsigned int uv_mode_:2;
- unsigned int skip_:1;
- unsigned int segment_:2;
- uint8_t alpha_; // quantization-susceptibility
-} VP8MBInfo;
-
-typedef struct VP8Matrix {
- uint16_t q_[16]; // quantizer steps
- uint16_t iq_[16]; // reciprocals, fixed point.
- uint16_t bias_[16]; // rounding bias
- uint16_t zthresh_[16]; // value under which a coefficient is zeroed
- uint16_t sharpen_[16]; // frequency boosters for slight sharpening
-} VP8Matrix;
-
-typedef struct {
- VP8Matrix y1_, y2_, uv_; // quantization matrices
- int alpha_; // quant-susceptibility, range [-127,127]. Zero is neutral.
- // Lower values indicate a lower risk of blurriness.
- int beta_; // filter-susceptibility, range [0,255].
- int quant_; // final segment quantizer.
- int fstrength_; // final in-loop filtering strength
- // reactivities
- int lambda_i16_, lambda_i4_, lambda_uv_;
- int lambda_mode_, lambda_trellis_, tlambda_;
- int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_;
-} VP8SegmentInfo;
-
-// Handy transcient struct to accumulate score and info during RD-optimization
-// and mode evaluation.
-typedef struct {
- score_t D, SD, R, score; // Distortion, spectral distortion, rate, score.
- int16_t y_dc_levels[16]; // Quantized levels for luma-DC, luma-AC, chroma.
- int16_t y_ac_levels[16][16];
- int16_t uv_levels[4 + 4][16];
- int mode_i16; // mode number for intra16 prediction
- uint8_t modes_i4[16]; // mode numbers for intra4 predictions
- int mode_uv; // mode number of chroma prediction
- uint32_t nz; // non-zero blocks
-} VP8ModeScore;
-
-// Iterator structure to iterate through macroblocks, pointing to the
-// right neighbouring data (samples, predictions, contexts, ...)
-typedef struct {
- int x_, y_; // current macroblock
- int y_offset_, uv_offset_; // offset to the luma / chroma planes
- int y_stride_, uv_stride_; // respective strides
- uint8_t* yuv_in_; // borrowed from enc_ (for now)
- uint8_t* yuv_out_; // ''
- uint8_t* yuv_out2_; // ''
- uint8_t* yuv_p_; // ''
- VP8Encoder* enc_; // back-pointer
- VP8MBInfo* mb_; // current macroblock
- VP8BitWriter* bw_; // current bit-writer
- uint8_t* preds_; // intra mode predictors (4x4 blocks)
- uint32_t* nz_; // non-zero pattern
- uint8_t i4_boundary_[37]; // 32+5 boundary samples needed by intra4x4
- uint8_t* i4_top_; // pointer to the current top boundary sample
- int i4_; // current intra4x4 mode being tested
- int top_nz_[9]; // top-non-zero context.
- int left_nz_[9]; // left-non-zero. left_nz[8] is independent.
- uint64_t bit_count_[4][3]; // bit counters for coded levels.
- uint64_t luma_bits_; // macroblock bit-cost for luma
- uint64_t uv_bits_; // macroblock bit-cost for chroma
- LFStats* lf_stats_; // filter stats (borrowed from enc_)
- int do_trellis_; // if true, perform extra level optimisation
- int done_; // true when scan is finished
- int percent0_; // saved initial progress percent
-} VP8EncIterator;
-
- // in iterator.c
-// must be called first.
-void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it);
-// restart a scan.
-void VP8IteratorReset(VP8EncIterator* const it);
-// import samples from source
-void VP8IteratorImport(const VP8EncIterator* const it);
-// export decimated samples
-void VP8IteratorExport(const VP8EncIterator* const it);
-// go to next macroblock. Returns !done_. If *block_to_save is non-null, will
-// save the boundary values to top_/left_ arrays. block_to_save can be
-// it->yuv_out_ or it->yuv_in_.
-int VP8IteratorNext(VP8EncIterator* const it,
- const uint8_t* const block_to_save);
-// Report progression based on macroblock rows. Return 0 for user-abort request.
-int VP8IteratorProgress(const VP8EncIterator* const it,
- int final_delta_percent);
-// Intra4x4 iterations
-void VP8IteratorStartI4(VP8EncIterator* const it);
-// returns true if not done.
-int VP8IteratorRotateI4(VP8EncIterator* const it,
- const uint8_t* const yuv_out);
-
-// Non-zero context setup/teardown
-void VP8IteratorNzToBytes(VP8EncIterator* const it);
-void VP8IteratorBytesToNz(VP8EncIterator* const it);
-
-// Helper functions to set mode properties
-void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode);
-void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes);
-void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode);
-void VP8SetSkip(const VP8EncIterator* const it, int skip);
-void VP8SetSegment(const VP8EncIterator* const it, int segment);
-
-//------------------------------------------------------------------------------
-// Paginated token buffer
-
-typedef struct VP8Tokens VP8Tokens; // struct details in token.c
-
-typedef struct {
-#if !defined(DISABLE_TOKEN_BUFFER)
- VP8Tokens* pages_; // first page
- VP8Tokens** last_page_; // last page
- uint16_t* tokens_; // set to (*last_page_)->tokens_
- int left_; // how many free tokens left before the page is full.
-#endif
- int error_; // true in case of malloc error
-} VP8TBuffer;
-
-void VP8TBufferInit(VP8TBuffer* const b); // initialize an empty buffer
-void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory
-
-#if !defined(DISABLE_TOKEN_BUFFER)
-
-// Finalizes bitstream when probabilities are known.
-// Deletes the allocated token memory if final_pass is true.
-int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
- const uint8_t* const probas, int final_pass);
-
-// record the coding of coefficients without knowing the probabilities yet
-int VP8RecordCoeffTokens(int ctx, int coeff_type, int first, int last,
- const int16_t* const coeffs,
- VP8TBuffer* const tokens);
-
-// unused for now
-void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats);
-
-#endif // !DISABLE_TOKEN_BUFFER
-
-//------------------------------------------------------------------------------
-// VP8Encoder
-
-struct VP8Encoder {
- const WebPConfig* config_; // user configuration and parameters
- WebPPicture* pic_; // input / output picture
-
- // headers
- VP8FilterHeader filter_hdr_; // filtering information
- VP8SegmentHeader segment_hdr_; // segment information
-
- int profile_; // VP8's profile, deduced from Config.
-
- // dimension, in macroblock units.
- int mb_w_, mb_h_;
- int preds_w_; // stride of the *preds_ prediction plane (=4*mb_w + 1)
-
- // number of partitions (1, 2, 4 or 8 = MAX_NUM_PARTITIONS)
- int num_parts_;
-
- // per-partition boolean decoders.
- VP8BitWriter bw_; // part0
- VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions
- VP8TBuffer tokens_; // token buffer
-
- int percent_; // for progress
-
- // transparency blob
- int has_alpha_;
- uint8_t* alpha_data_; // non-NULL if transparency is present
- uint32_t alpha_data_size_;
- WebPWorker alpha_worker_;
-
- // enhancement layer
- int use_layer_;
- VP8BitWriter layer_bw_;
- uint8_t* layer_data_;
- size_t layer_data_size_;
-
- // quantization info (one set of DC/AC dequant factor per segment)
- VP8SegmentInfo dqm_[NUM_MB_SEGMENTS];
- int base_quant_; // nominal quantizer value. Only used
- // for relative coding of segments' quant.
- int alpha_; // global susceptibility (<=> complexity)
- int uv_alpha_; // U/V quantization susceptibility
- // global offset of quantizers, shared by all segments
- int dq_y1_dc_;
- int dq_y2_dc_, dq_y2_ac_;
- int dq_uv_dc_, dq_uv_ac_;
-
- // probabilities and statistics
- VP8Proba proba_;
- uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
- uint64_t sse_count_; // pixel count for the sse_[] stats
- int coded_size_;
- int residual_bytes_[3][4];
- int block_count_[3];
-
- // quality/speed settings
- int method_; // 0=fastest, 6=best/slowest.
- VP8RDLevel rd_opt_level_; // Deduced from method_.
- int max_i4_header_bits_; // partition #0 safeness factor
- int thread_level_; // derived from config->thread_level
- int do_search_; // derived from config->target_XXX
- int use_tokens_; // if true, use token buffer
-
- // Memory
- VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1)
- uint8_t* preds_; // predictions modes: (4*mb_w+1) * (4*mb_h+1)
- uint32_t* nz_; // non-zero bit context: mb_w+1
- uint8_t* yuv_in_; // input samples
- uint8_t* yuv_out_; // output samples
- uint8_t* yuv_out2_; // secondary scratch out-buffer. swapped with yuv_out_.
- uint8_t* yuv_p_; // scratch buffer for prediction
- uint8_t *y_top_; // top luma samples.
- uint8_t *uv_top_; // top u/v samples.
- // U and V are packed into 16 pixels (8 U + 8 V)
- uint8_t *y_left_; // left luma samples (adressable from index -1 to 15).
- uint8_t *u_left_; // left u samples (adressable from index -1 to 7)
- uint8_t *v_left_; // left v samples (adressable from index -1 to 7)
-
- LFStats *lf_stats_; // autofilter stats (if NULL, autofilter is off)
-};
-
-//------------------------------------------------------------------------------
-// internal functions. Not public.
-
- // in tree.c
-extern const uint8_t VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
-extern const uint8_t
- VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
-// Reset the token probabilities to their initial (default) values
-void VP8DefaultProbas(VP8Encoder* const enc);
-// Write the token probabilities
-void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas);
-// Writes the partition #0 modes (that is: all intra modes)
-void VP8CodeIntraModes(VP8Encoder* const enc);
-
- // in syntax.c
-// Generates the final bitstream by coding the partition0 and headers,
-// and appending an assembly of all the pre-coded token partitions.
-// Return true if everything is ok.
-int VP8EncWrite(VP8Encoder* const enc);
-// Release memory allocated for bit-writing in VP8EncLoop & seq.
-void VP8EncFreeBitWriters(VP8Encoder* const enc);
-
- // in frame.c
-extern const uint8_t VP8EncBands[16 + 1];
-extern const uint8_t VP8Cat3[];
-extern const uint8_t VP8Cat4[];
-extern const uint8_t VP8Cat5[];
-extern const uint8_t VP8Cat6[];
-
-// Form all the four Intra16x16 predictions in the yuv_p_ cache
-void VP8MakeLuma16Preds(const VP8EncIterator* const it);
-// Form all the four Chroma8x8 predictions in the yuv_p_ cache
-void VP8MakeChroma8Preds(const VP8EncIterator* const it);
-// Form all the ten Intra4x4 predictions in the yuv_p_ cache
-// for the 4x4 block it->i4_
-void VP8MakeIntra4Preds(const VP8EncIterator* const it);
-// Rate calculation
-int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd);
-int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]);
-int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd);
-// Main coding calls
-int VP8EncLoop(VP8Encoder* const enc);
-int VP8EncTokenLoop(VP8Encoder* const enc);
-
- // in webpenc.c
-// Assign an error code to a picture. Return false for convenience.
-int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error);
-int WebPReportProgress(const WebPPicture* const pic,
- int percent, int* const percent_store);
-
- // in analysis.c
-// Main analysis loop. Decides the segmentations and complexity.
-// Assigns a first guess for Intra16 and uvmode_ prediction modes.
-int VP8EncAnalyze(VP8Encoder* const enc);
-
- // in quant.c
-// Sets up segment's quantization values, base_quant_ and filter strengths.
-void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
-// Pick best modes and fills the levels. Returns true if skipped.
-int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
- VP8RDLevel rd_opt);
-
- // in alpha.c
-void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
-int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process
-int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
-int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
-
- // in layer.c
-void VP8EncInitLayer(VP8Encoder* const enc); // init everything
-void VP8EncCodeLayerBlock(VP8EncIterator* it); // code one more macroblock
-int VP8EncFinishLayer(VP8Encoder* const enc); // finalize coding
-void VP8EncDeleteLayer(VP8Encoder* enc); // reclaim memory
-
- // in filter.c
-
-// SSIM utils
-typedef struct {
- double w, xm, ym, xxm, xym, yym;
-} DistoStats;
-void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst);
-void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
- const uint8_t* src2, int stride2,
- int W, int H, DistoStats* const stats);
-double VP8SSIMGet(const DistoStats* const stats);
-double VP8SSIMGetSquaredError(const DistoStats* const stats);
-
-// autofilter
-void VP8InitFilter(VP8EncIterator* const it);
-void VP8StoreFilterStats(VP8EncIterator* const it);
-void VP8AdjustFilterStrength(VP8EncIterator* const it);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_ENC_VP8ENCI_H_ */
diff --git a/src/third_party/libwebp/enc/vp8l.c b/src/third_party/libwebp/enc/vp8l.c
deleted file mode 100644
index 34070b3..0000000
--- a/src/third_party/libwebp/enc/vp8l.c
+++ /dev/null
@@ -1,1180 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// main entry for the lossless encoder.
-//
-// Author: Vikas Arora (vikaas.arora@gmail.com)
-//
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#endif
-
-#include "./backward_references.h"
-#include "./vp8enci.h"
-#include "./vp8li.h"
-#include "../dsp/lossless.h"
-#include "../utils/bit_writer.h"
-#include "../utils/huffman_encode.h"
-#include "../utils/utils.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer.
-#define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024)
-#define MAX_COLORS_FOR_GRAPH 64
-
-// -----------------------------------------------------------------------------
-// Palette
-
-static int CompareColors(const void* p1, const void* p2) {
- const uint32_t a = *(const uint32_t*)p1;
- const uint32_t b = *(const uint32_t*)p2;
- SB_DCHECK(a != b);
- return (a < b) ? -1 : 1;
-}
-
-// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
-// creates a palette and returns true, else returns false.
-static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
- uint32_t palette[MAX_PALETTE_SIZE],
- int* const palette_size) {
- int i, x, y, key;
- int num_colors = 0;
- uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 };
- uint32_t colors[MAX_PALETTE_SIZE * 4];
- static const uint32_t kHashMul = 0x1e35a7bd;
- const uint32_t* argb = pic->argb;
- const int width = pic->width;
- const int height = pic->height;
- uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
-
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- if (argb[x] == last_pix) {
- continue;
- }
- last_pix = argb[x];
- key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT;
- while (1) {
- if (!in_use[key]) {
- colors[key] = last_pix;
- in_use[key] = 1;
- ++num_colors;
- if (num_colors > MAX_PALETTE_SIZE) {
- return 0;
- }
- break;
- } else if (colors[key] == last_pix) {
- // The color is already there.
- break;
- } else {
- // Some other color sits there.
- // Do linear conflict resolution.
- ++key;
- key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer.
- }
- }
- }
- argb += pic->argb_stride;
- }
-
- // TODO(skal): could we reuse in_use[] to speed up EncodePalette()?
- num_colors = 0;
- for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) {
- if (in_use[i]) {
- palette[num_colors] = colors[i];
- ++num_colors;
- }
- }
-
- SbSystemSort(palette, num_colors, sizeof(*palette), CompareColors);
- *palette_size = num_colors;
- return 1;
-}
-
-static int AnalyzeEntropy(const uint32_t* argb,
- int width, int height, int argb_stride,
- double* const nonpredicted_bits,
- double* const predicted_bits) {
- int x, y;
- const uint32_t* last_line = NULL;
- uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0
-
- VP8LHistogram* nonpredicted = NULL;
- VP8LHistogram* predicted =
- (VP8LHistogram*)SbMemoryAllocate(2 * sizeof(*predicted));
- if (predicted == NULL) return 0;
- nonpredicted = predicted + 1;
-
- VP8LHistogramInit(predicted, 0);
- VP8LHistogramInit(nonpredicted, 0);
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- const uint32_t pix = argb[x];
- const uint32_t pix_diff = VP8LSubPixels(pix, last_pix);
- if (pix_diff == 0) continue;
- if (last_line != NULL && pix == last_line[x]) {
- continue;
- }
- last_pix = pix;
- {
- const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix);
- const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff);
- VP8LHistogramAddSinglePixOrCopy(nonpredicted, &pix_token);
- VP8LHistogramAddSinglePixOrCopy(predicted, &pix_diff_token);
- }
- }
- last_line = argb;
- argb += argb_stride;
- }
- *nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted);
- *predicted_bits = VP8LHistogramEstimateBitsBulk(predicted);
- SbMemoryDeallocate(predicted);
- return 1;
-}
-
-static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) {
- const WebPPicture* const pic = enc->pic_;
- SB_DCHECK(pic != NULL && pic->argb != NULL);
-
- enc->use_palette_ =
- AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_);
-
- if (image_hint == WEBP_HINT_GRAPH) {
- if (enc->use_palette_ && enc->palette_size_ < MAX_COLORS_FOR_GRAPH) {
- enc->use_palette_ = 0;
- }
- }
-
- if (!enc->use_palette_) {
- if (image_hint == WEBP_HINT_PHOTO) {
- enc->use_predict_ = 1;
- enc->use_cross_color_ = 1;
- } else {
- double non_pred_entropy, pred_entropy;
- if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, pic->argb_stride,
- &non_pred_entropy, &pred_entropy)) {
- return 0;
- }
- if (pred_entropy < 0.95 * non_pred_entropy) {
- enc->use_predict_ = 1;
- // TODO(vikasa): Observed some correlation of cross_color transform with
- // predict. Need to investigate this further and add separate heuristic
- // for setting use_cross_color flag.
- enc->use_cross_color_ = 1;
- }
- }
- }
-
- return 1;
-}
-
-static int GetHuffBitLengthsAndCodes(
- const VP8LHistogramSet* const histogram_image,
- HuffmanTreeCode* const huffman_codes) {
- int i, k;
- int ok = 1;
- uint64_t total_length_size = 0;
- uint8_t* mem_buf = NULL;
- const int histogram_image_size = histogram_image->size;
-
- // Iterate over all histograms and get the aggregate number of codes used.
- for (i = 0; i < histogram_image_size; ++i) {
- const VP8LHistogram* const histo = histogram_image->histograms[i];
- HuffmanTreeCode* const codes = &huffman_codes[5 * i];
- for (k = 0; k < 5; ++k) {
- const int num_symbols = (k == 0) ? VP8LHistogramNumCodes(histo)
- : (k == 4) ? NUM_DISTANCE_CODES
- : 256;
- codes[k].num_symbols = num_symbols;
- total_length_size += num_symbols;
- }
- }
-
- // Allocate and Set Huffman codes.
- {
- uint16_t* codes;
- uint8_t* lengths;
- mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
- sizeof(*lengths) + sizeof(*codes));
- if (mem_buf == NULL) {
- ok = 0;
- goto End;
- }
- codes = (uint16_t*)mem_buf;
- lengths = (uint8_t*)&codes[total_length_size];
- for (i = 0; i < 5 * histogram_image_size; ++i) {
- const int bit_length = huffman_codes[i].num_symbols;
- huffman_codes[i].codes = codes;
- huffman_codes[i].code_lengths = lengths;
- codes += bit_length;
- lengths += bit_length;
- }
- }
-
- // Create Huffman trees.
- for (i = 0; ok && (i < histogram_image_size); ++i) {
- HuffmanTreeCode* const codes = &huffman_codes[5 * i];
- VP8LHistogram* const histo = histogram_image->histograms[i];
- ok = ok && VP8LCreateHuffmanTree(histo->literal_, 15, codes + 0);
- ok = ok && VP8LCreateHuffmanTree(histo->red_, 15, codes + 1);
- ok = ok && VP8LCreateHuffmanTree(histo->blue_, 15, codes + 2);
- ok = ok && VP8LCreateHuffmanTree(histo->alpha_, 15, codes + 3);
- ok = ok && VP8LCreateHuffmanTree(histo->distance_, 15, codes + 4);
- }
-
- End:
- if (!ok) {
- SbMemoryDeallocate(mem_buf);
- // If one VP8LCreateHuffmanTree() above fails, we need to clean up behind.
- SbMemorySet(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes));
- }
- return ok;
-}
-
-static void StoreHuffmanTreeOfHuffmanTreeToBitMask(
- VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) {
- // RFC 1951 will calm you down if you are worried about this funny sequence.
- // This sequence is tuned from that, but more weighted for lower symbol count,
- // and more spiking histograms.
- static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = {
- 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
- };
- int i;
- // Throw away trailing zeros:
- int codes_to_store = CODE_LENGTH_CODES;
- for (; codes_to_store > 4; --codes_to_store) {
- if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
- break;
- }
- }
- VP8LWriteBits(bw, 4, codes_to_store - 4);
- for (i = 0; i < codes_to_store; ++i) {
- VP8LWriteBits(bw, 3, code_length_bitdepth[kStorageOrder[i]]);
- }
-}
-
-static void ClearHuffmanTreeIfOnlyOneSymbol(
- HuffmanTreeCode* const huffman_code) {
- int k;
- int count = 0;
- for (k = 0; k < huffman_code->num_symbols; ++k) {
- if (huffman_code->code_lengths[k] != 0) {
- ++count;
- if (count > 1) return;
- }
- }
- for (k = 0; k < huffman_code->num_symbols; ++k) {
- huffman_code->code_lengths[k] = 0;
- huffman_code->codes[k] = 0;
- }
-}
-
-static void StoreHuffmanTreeToBitMask(
- VP8LBitWriter* const bw,
- const HuffmanTreeToken* const tokens, const int num_tokens,
- const HuffmanTreeCode* const huffman_code) {
- int i;
- for (i = 0; i < num_tokens; ++i) {
- const int ix = tokens[i].code;
- const int extra_bits = tokens[i].extra_bits;
- VP8LWriteBits(bw, huffman_code->code_lengths[ix], huffman_code->codes[ix]);
- switch (ix) {
- case 16:
- VP8LWriteBits(bw, 2, extra_bits);
- break;
- case 17:
- VP8LWriteBits(bw, 3, extra_bits);
- break;
- case 18:
- VP8LWriteBits(bw, 7, extra_bits);
- break;
- }
- }
-}
-
-static int StoreFullHuffmanCode(VP8LBitWriter* const bw,
- const HuffmanTreeCode* const tree) {
- int ok = 0;
- uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
- uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
- const int max_tokens = tree->num_symbols;
- int num_tokens;
- HuffmanTreeCode huffman_code;
- HuffmanTreeToken* const tokens =
- (HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens, sizeof(*tokens));
- if (tokens == NULL) return 0;
-
- huffman_code.num_symbols = CODE_LENGTH_CODES;
- huffman_code.code_lengths = code_length_bitdepth;
- huffman_code.codes = code_length_bitdepth_symbols;
-
- VP8LWriteBits(bw, 1, 0);
- num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
- {
- int histogram[CODE_LENGTH_CODES] = { 0 };
- int i;
- for (i = 0; i < num_tokens; ++i) {
- ++histogram[tokens[i].code];
- }
-
- if (!VP8LCreateHuffmanTree(histogram, 7, &huffman_code)) {
- goto End;
- }
- }
-
- StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
- ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code);
- {
- int trailing_zero_bits = 0;
- int trimmed_length = num_tokens;
- int write_trimmed_length;
- int length;
- int i = num_tokens;
- while (i-- > 0) {
- const int ix = tokens[i].code;
- if (ix == 0 || ix == 17 || ix == 18) {
- --trimmed_length; // discount trailing zeros
- trailing_zero_bits += code_length_bitdepth[ix];
- if (ix == 17) {
- trailing_zero_bits += 3;
- } else if (ix == 18) {
- trailing_zero_bits += 7;
- }
- } else {
- break;
- }
- }
- write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
- length = write_trimmed_length ? trimmed_length : num_tokens;
- VP8LWriteBits(bw, 1, write_trimmed_length);
- if (write_trimmed_length) {
- const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1);
- const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2;
- VP8LWriteBits(bw, 3, nbitpairs - 1);
- SB_DCHECK(trimmed_length >= 2);
- VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2);
- }
- StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
- }
- ok = 1;
- End:
- SbMemoryDeallocate(tokens);
- return ok;
-}
-
-static int StoreHuffmanCode(VP8LBitWriter* const bw,
- const HuffmanTreeCode* const huffman_code) {
- int i;
- int count = 0;
- int symbols[2] = { 0, 0 };
- const int kMaxBits = 8;
- const int kMaxSymbol = 1 << kMaxBits;
-
- // Check whether it's a small tree.
- for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) {
- if (huffman_code->code_lengths[i] != 0) {
- if (count < 2) symbols[count] = i;
- ++count;
- }
- }
-
- if (count == 0) { // emit minimal tree for empty cases
- // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
- VP8LWriteBits(bw, 4, 0x01);
- return 1;
- } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
- VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols.
- VP8LWriteBits(bw, 1, count - 1);
- if (symbols[0] <= 1) {
- VP8LWriteBits(bw, 1, 0); // Code bit for small (1 bit) symbol value.
- VP8LWriteBits(bw, 1, symbols[0]);
- } else {
- VP8LWriteBits(bw, 1, 1);
- VP8LWriteBits(bw, 8, symbols[0]);
- }
- if (count == 2) {
- VP8LWriteBits(bw, 8, symbols[1]);
- }
- return 1;
- } else {
- return StoreFullHuffmanCode(bw, huffman_code);
- }
-}
-
-static void WriteHuffmanCode(VP8LBitWriter* const bw,
- const HuffmanTreeCode* const code,
- int code_index) {
- const int depth = code->code_lengths[code_index];
- const int symbol = code->codes[code_index];
- VP8LWriteBits(bw, depth, symbol);
-}
-
-static void StoreImageToBitMask(
- VP8LBitWriter* const bw, int width, int histo_bits,
- const VP8LBackwardRefs* const refs,
- const uint16_t* histogram_symbols,
- const HuffmanTreeCode* const huffman_codes) {
- // x and y trace the position in the image.
- int x = 0;
- int y = 0;
- const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1;
- int i;
- for (i = 0; i < refs->size; ++i) {
- const PixOrCopy* const v = &refs->refs[i];
- const int histogram_ix = histogram_symbols[histo_bits ?
- (y >> histo_bits) * histo_xsize +
- (x >> histo_bits) : 0];
- const HuffmanTreeCode* const codes = huffman_codes + 5 * histogram_ix;
- if (PixOrCopyIsCacheIdx(v)) {
- const int code = PixOrCopyCacheIdx(v);
- const int literal_ix = 256 + NUM_LENGTH_CODES + code;
- WriteHuffmanCode(bw, codes, literal_ix);
- } else if (PixOrCopyIsLiteral(v)) {
- static const int order[] = { 1, 2, 0, 3 };
- int k;
- for (k = 0; k < 4; ++k) {
- const int code = PixOrCopyLiteral(v, order[k]);
- WriteHuffmanCode(bw, codes + k, code);
- }
- } else {
- int bits, n_bits;
- int code, distance;
-
- PrefixEncode(v->len, &code, &n_bits, &bits);
- WriteHuffmanCode(bw, codes, 256 + code);
- VP8LWriteBits(bw, n_bits, bits);
-
- distance = PixOrCopyDistance(v);
- PrefixEncode(distance, &code, &n_bits, &bits);
- WriteHuffmanCode(bw, codes + 4, code);
- VP8LWriteBits(bw, n_bits, bits);
- }
- x += PixOrCopyLength(v);
- while (x >= width) {
- x -= width;
- ++y;
- }
- }
-}
-
-// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
-static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
- const uint32_t* const argb,
- int width, int height, int quality) {
- int i;
- int ok = 0;
- VP8LBackwardRefs refs;
- HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
- const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
- VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0);
- if (histogram_image == NULL) return 0;
-
- // Calculate backward references from ARGB image.
- if (!VP8LGetBackwardReferences(width, height, argb, quality, 0, 1, &refs)) {
- goto Error;
- }
- // Build histogram image and symbols from backward references.
- VP8LHistogramStoreRefs(&refs, histogram_image->histograms[0]);
-
- // Create Huffman bit lengths and codes for each histogram image.
- SB_DCHECK(histogram_image->size == 1);
- if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
- goto Error;
- }
-
- // No color cache, no Huffman image.
- VP8LWriteBits(bw, 1, 0);
-
- // Store Huffman codes.
- for (i = 0; i < 5; ++i) {
- HuffmanTreeCode* const codes = &huffman_codes[i];
- if (!StoreHuffmanCode(bw, codes)) {
- goto Error;
- }
- ClearHuffmanTreeIfOnlyOneSymbol(codes);
- }
-
- // Store actual literals.
- StoreImageToBitMask(bw, width, 0, &refs, histogram_symbols, huffman_codes);
- ok = 1;
-
- Error:
- SbMemoryDeallocate(histogram_image);
- VP8LClearBackwardRefs(&refs);
- SbMemoryDeallocate(huffman_codes[0].codes);
- return ok;
-}
-
-static int EncodeImageInternal(VP8LBitWriter* const bw,
- const uint32_t* const argb,
- int width, int height, int quality,
- int cache_bits, int histogram_bits) {
- int ok = 0;
- const int use_2d_locality = 1;
- const int use_color_cache = (cache_bits > 0);
- const uint32_t histogram_image_xysize =
- VP8LSubSampleSize(width, histogram_bits) *
- VP8LSubSampleSize(height, histogram_bits);
- VP8LHistogramSet* histogram_image =
- VP8LAllocateHistogramSet(histogram_image_xysize, 0);
- int histogram_image_size = 0;
- size_t bit_array_size = 0;
- HuffmanTreeCode* huffman_codes = NULL;
- VP8LBackwardRefs refs;
- uint16_t* const histogram_symbols =
- (uint16_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
- sizeof(*histogram_symbols));
- SB_DCHECK(histogram_bits >= MIN_HUFFMAN_BITS);
- SB_DCHECK(histogram_bits <= MAX_HUFFMAN_BITS);
-
- if (histogram_image == NULL || histogram_symbols == NULL) {
- SbMemoryDeallocate(histogram_image);
- SbMemoryDeallocate(histogram_symbols);
- return 0;
- }
-
- // Calculate backward references from ARGB image.
- if (!VP8LGetBackwardReferences(width, height, argb, quality, cache_bits,
- use_2d_locality, &refs)) {
- goto Error;
- }
- // Build histogram image and symbols from backward references.
- if (!VP8LGetHistoImageSymbols(width, height, &refs,
- quality, histogram_bits, cache_bits,
- histogram_image,
- histogram_symbols)) {
- goto Error;
- }
- // Create Huffman bit lengths and codes for each histogram image.
- histogram_image_size = histogram_image->size;
- bit_array_size = 5 * histogram_image_size;
- huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
- sizeof(*huffman_codes));
- if (huffman_codes == NULL ||
- !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
- goto Error;
- }
- // Free combined histograms.
- SbMemoryDeallocate(histogram_image);
- histogram_image = NULL;
-
- // Color Cache parameters.
- VP8LWriteBits(bw, 1, use_color_cache);
- if (use_color_cache) {
- VP8LWriteBits(bw, 4, cache_bits);
- }
-
- // Huffman image + meta huffman.
- {
- const int write_histogram_image = (histogram_image_size > 1);
- VP8LWriteBits(bw, 1, write_histogram_image);
- if (write_histogram_image) {
- uint32_t* const histogram_argb =
- (uint32_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize,
- sizeof(*histogram_argb));
- int max_index = 0;
- uint32_t i;
- if (histogram_argb == NULL) goto Error;
- for (i = 0; i < histogram_image_xysize; ++i) {
- const int symbol_index = histogram_symbols[i] & 0xffff;
- histogram_argb[i] = 0xff000000 | (symbol_index << 8);
- if (symbol_index >= max_index) {
- max_index = symbol_index + 1;
- }
- }
- histogram_image_size = max_index;
-
- VP8LWriteBits(bw, 3, histogram_bits - 2);
- ok = EncodeImageNoHuffman(bw, histogram_argb,
- VP8LSubSampleSize(width, histogram_bits),
- VP8LSubSampleSize(height, histogram_bits),
- quality);
- SbMemoryDeallocate(histogram_argb);
- if (!ok) goto Error;
- }
- }
-
- // Store Huffman codes.
- {
- int i;
- for (i = 0; i < 5 * histogram_image_size; ++i) {
- HuffmanTreeCode* const codes = &huffman_codes[i];
- if (!StoreHuffmanCode(bw, codes)) goto Error;
- ClearHuffmanTreeIfOnlyOneSymbol(codes);
- }
- }
-
- // Store actual literals.
- StoreImageToBitMask(bw, width, histogram_bits, &refs,
- histogram_symbols, huffman_codes);
- ok = 1;
-
- Error:
- SbMemoryDeallocate(histogram_image);
-
- VP8LClearBackwardRefs(&refs);
- if (huffman_codes != NULL) {
- SbMemoryDeallocate(huffman_codes->codes);
- SbMemoryDeallocate(huffman_codes);
- }
- SbMemoryDeallocate(histogram_symbols);
- return ok;
-}
-
-// -----------------------------------------------------------------------------
-// Transforms
-
-// Check if it would be a good idea to subtract green from red and blue. We
-// only impact entropy in red/blue components, don't bother to look at others.
-static int EvalAndApplySubtractGreen(VP8LEncoder* const enc,
- int width, int height,
- VP8LBitWriter* const bw) {
- if (!enc->use_palette_) {
- int i;
- const uint32_t* const argb = enc->argb_;
- double bit_cost_before, bit_cost_after;
- VP8LHistogram* const histo = (VP8LHistogram*)SbMemoryAllocate(sizeof(*histo));
- if (histo == NULL) return 0;
-
- VP8LHistogramInit(histo, 1);
- for (i = 0; i < width * height; ++i) {
- const uint32_t c = argb[i];
- ++histo->red_[(c >> 16) & 0xff];
- ++histo->blue_[(c >> 0) & 0xff];
- }
- bit_cost_before = VP8LHistogramEstimateBits(histo);
-
- VP8LHistogramInit(histo, 1);
- for (i = 0; i < width * height; ++i) {
- const uint32_t c = argb[i];
- const int green = (c >> 8) & 0xff;
- ++histo->red_[((c >> 16) - green) & 0xff];
- ++histo->blue_[((c >> 0) - green) & 0xff];
- }
- bit_cost_after = VP8LHistogramEstimateBits(histo);
- SbMemoryDeallocate(histo);
-
- // Check if subtracting green yields low entropy.
- enc->use_subtract_green_ = (bit_cost_after < bit_cost_before);
- if (enc->use_subtract_green_) {
- VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
- VP8LWriteBits(bw, 2, SUBTRACT_GREEN);
- VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
- }
- }
- return 1;
-}
-
-static int ApplyPredictFilter(const VP8LEncoder* const enc,
- int width, int height, int quality,
- VP8LBitWriter* const bw) {
- const int pred_bits = enc->transform_bits_;
- const int transform_width = VP8LSubSampleSize(width, pred_bits);
- const int transform_height = VP8LSubSampleSize(height, pred_bits);
-
- VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->argb_scratch_,
- enc->transform_data_);
- VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
- VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM);
- SB_DCHECK(pred_bits >= 2);
- VP8LWriteBits(bw, 3, pred_bits - 2);
- if (!EncodeImageNoHuffman(bw, enc->transform_data_,
- transform_width, transform_height, quality)) {
- return 0;
- }
- return 1;
-}
-
-static int ApplyCrossColorFilter(const VP8LEncoder* const enc,
- int width, int height, int quality,
- VP8LBitWriter* const bw) {
- const int ccolor_transform_bits = enc->transform_bits_;
- const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
- const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
- const int step = (quality == 0) ? 32 : 8;
-
- VP8LColorSpaceTransform(width, height, ccolor_transform_bits, step,
- enc->argb_, enc->transform_data_);
- VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
- VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM);
- SB_DCHECK(ccolor_transform_bits >= 2);
- VP8LWriteBits(bw, 3, ccolor_transform_bits - 2);
- if (!EncodeImageNoHuffman(bw, enc->transform_data_,
- transform_width, transform_height, quality)) {
- return 0;
- }
- return 1;
-}
-
-// -----------------------------------------------------------------------------
-
-static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
- size_t riff_size, size_t vp8l_size) {
- uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
- 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
- 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
- };
- PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
- PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
- if (!pic->writer(riff, sizeof(riff), pic)) {
- return VP8_ENC_ERROR_BAD_WRITE;
- }
- return VP8_ENC_OK;
-}
-
-static int WriteImageSize(const WebPPicture* const pic,
- VP8LBitWriter* const bw) {
- const int width = pic->width - 1;
- const int height = pic->height - 1;
- SB_DCHECK(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION);
-
- VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, width);
- VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, height);
- return !bw->error_;
-}
-
-static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) {
- VP8LWriteBits(bw, 1, has_alpha);
- VP8LWriteBits(bw, VP8L_VERSION_BITS, VP8L_VERSION);
- return !bw->error_;
-}
-
-static WebPEncodingError WriteImage(const WebPPicture* const pic,
- VP8LBitWriter* const bw,
- size_t* const coded_size) {
- WebPEncodingError err = VP8_ENC_OK;
- const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
- const size_t webpll_size = VP8LBitWriterNumBytes(bw);
- const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
- const size_t pad = vp8l_size & 1;
- const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
-
- err = WriteRiffHeader(pic, riff_size, vp8l_size);
- if (err != VP8_ENC_OK) goto Error;
-
- if (!pic->writer(webpll_data, webpll_size, pic)) {
- err = VP8_ENC_ERROR_BAD_WRITE;
- goto Error;
- }
-
- if (pad) {
- const uint8_t pad_byte[1] = { 0 };
- if (!pic->writer(pad_byte, 1, pic)) {
- err = VP8_ENC_ERROR_BAD_WRITE;
- goto Error;
- }
- }
- *coded_size = CHUNK_HEADER_SIZE + riff_size;
- return VP8_ENC_OK;
-
- Error:
- return err;
-}
-
-// -----------------------------------------------------------------------------
-
-// Allocates the memory for argb (W x H) buffer, 2 rows of context for
-// prediction and transform data.
-static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
- int width, int height) {
- WebPEncodingError err = VP8_ENC_OK;
- const int tile_size = 1 << enc->transform_bits_;
- const uint64_t image_size = width * height;
- const uint64_t argb_scratch_size = tile_size * width + width;
- const uint64_t transform_data_size =
- (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) *
- (uint64_t)VP8LSubSampleSize(height, enc->transform_bits_);
- const uint64_t total_size =
- image_size + argb_scratch_size + transform_data_size;
- uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem));
- if (mem == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
- enc->argb_ = mem;
- mem += image_size;
- enc->argb_scratch_ = mem;
- mem += argb_scratch_size;
- enc->transform_data_ = mem;
- enc->current_width_ = width;
-
- Error:
- return err;
-}
-
-static void ApplyPalette(uint32_t* src, uint32_t* dst,
- uint32_t src_stride, uint32_t dst_stride,
- const uint32_t* palette, int palette_size,
- int width, int height, int xbits, uint8_t* row) {
- int i, x, y;
- int use_LUT = 1;
- for (i = 0; i < palette_size; ++i) {
- if ((palette[i] & 0xffff00ffu) != 0) {
- use_LUT = 0;
- break;
- }
- }
-
- if (use_LUT) {
- int inv_palette[MAX_PALETTE_SIZE] = { 0 };
- for (i = 0; i < palette_size; ++i) {
- const int color = (palette[i] >> 8) & 0xff;
- inv_palette[color] = i;
- }
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- const int color = (src[x] >> 8) & 0xff;
- row[x] = inv_palette[color];
- }
- VP8LBundleColorMap(row, width, xbits, dst);
- src += src_stride;
- dst += dst_stride;
- }
- } else {
- // Use 1 pixel cache for ARGB pixels.
- uint32_t last_pix = palette[0];
- int last_idx = 0;
- for (y = 0; y < height; ++y) {
- for (x = 0; x < width; ++x) {
- const uint32_t pix = src[x];
- if (pix != last_pix) {
- for (i = 0; i < palette_size; ++i) {
- if (pix == palette[i]) {
- last_idx = i;
- last_pix = pix;
- break;
- }
- }
- }
- row[x] = last_idx;
- }
- VP8LBundleColorMap(row, width, xbits, dst);
- src += src_stride;
- dst += dst_stride;
- }
- }
-}
-
-// Note: Expects "enc->palette_" to be set properly.
-// Also, "enc->palette_" will be modified after this call and should not be used
-// later.
-static WebPEncodingError EncodePalette(VP8LBitWriter* const bw,
- VP8LEncoder* const enc, int quality) {
- WebPEncodingError err = VP8_ENC_OK;
- int i;
- const WebPPicture* const pic = enc->pic_;
- uint32_t* src = pic->argb;
- uint32_t* dst;
- const int width = pic->width;
- const int height = pic->height;
- uint32_t* const palette = enc->palette_;
- const int palette_size = enc->palette_size_;
- uint8_t* row = NULL;
- int xbits;
-
- // Replace each input pixel by corresponding palette index.
- // This is done line by line.
- if (palette_size <= 4) {
- xbits = (palette_size <= 2) ? 3 : 2;
- } else {
- xbits = (palette_size <= 16) ? 1 : 0;
- }
-
- err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
- if (err != VP8_ENC_OK) goto Error;
- dst = enc->argb_;
-
- row = WebPSafeMalloc((uint64_t)width, sizeof(*row));
- if (row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
-
- ApplyPalette(src, dst, pic->argb_stride, enc->current_width_,
- palette, palette_size, width, height, xbits, row);
-
- // Save palette to bitstream.
- VP8LWriteBits(bw, 1, TRANSFORM_PRESENT);
- VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM);
- SB_DCHECK(palette_size >= 1);
- VP8LWriteBits(bw, 8, palette_size - 1);
- for (i = palette_size - 1; i >= 1; --i) {
- palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
- }
- if (!EncodeImageNoHuffman(bw, palette, palette_size, 1, quality)) {
- err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
- goto Error;
- }
-
- Error:
- SbMemoryDeallocate(row);
- return err;
-}
-
-// -----------------------------------------------------------------------------
-
-static int GetHistoBits(int method, int use_palette, int width, int height) {
- const uint64_t hist_size = sizeof(VP8LHistogram);
- // Make tile size a function of encoding method (Range: 0 to 6).
- int histo_bits = (use_palette ? 9 : 7) - method;
- while (1) {
- const uint64_t huff_image_size = VP8LSubSampleSize(width, histo_bits) *
- VP8LSubSampleSize(height, histo_bits) *
- hist_size;
- if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break;
- ++histo_bits;
- }
- return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS :
- (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits;
-}
-
-static void FinishEncParams(VP8LEncoder* const enc) {
- const WebPConfig* const config = enc->config_;
- const WebPPicture* const pic = enc->pic_;
- const int method = config->method;
- const float quality = config->quality;
- const int use_palette = enc->use_palette_;
- enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4;
- enc->histo_bits_ = GetHistoBits(method, use_palette, pic->width, pic->height);
- enc->cache_bits_ = (quality <= 25.f) ? 0 : 7;
-}
-
-// -----------------------------------------------------------------------------
-// VP8LEncoder
-
-static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config,
- const WebPPicture* const picture) {
- VP8LEncoder* const enc = (VP8LEncoder*)SbMemoryCalloc(1, sizeof(*enc));
- if (enc == NULL) {
- WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return NULL;
- }
- enc->config_ = config;
- enc->pic_ = picture;
- return enc;
-}
-
-static void VP8LEncoderDelete(VP8LEncoder* enc) {
- SbMemoryDeallocate(enc->argb_);
- SbMemoryDeallocate(enc);
-}
-
-// -----------------------------------------------------------------------------
-// Main call
-
-WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
- const WebPPicture* const picture,
- VP8LBitWriter* const bw) {
- WebPEncodingError err = VP8_ENC_OK;
- const int quality = (int)config->quality;
- const int width = picture->width;
- const int height = picture->height;
- VP8LEncoder* const enc = VP8LEncoderNew(config, picture);
- const size_t byte_position = VP8LBitWriterNumBytes(bw);
-
- if (enc == NULL) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- // ---------------------------------------------------------------------------
- // Analyze image (entropy, num_palettes etc)
-
- if (!VP8LEncAnalyze(enc, config->image_hint)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- FinishEncParams(enc);
-
- if (enc->use_palette_) {
- err = EncodePalette(bw, enc, quality);
- if (err != VP8_ENC_OK) goto Error;
- // Color cache is disabled for palette.
- enc->cache_bits_ = 0;
- }
-
- // In case image is not packed.
- if (enc->argb_ == NULL) {
- int y;
- err = AllocateTransformBuffer(enc, width, height);
- if (err != VP8_ENC_OK) goto Error;
- for (y = 0; y < height; ++y) {
- SbMemoryCopy(enc->argb_ + y * width,
- picture->argb + y * picture->argb_stride,
- width * sizeof(*enc->argb_));
- }
- enc->current_width_ = width;
- }
-
- // ---------------------------------------------------------------------------
- // Apply transforms and write transform data.
-
- if (!EvalAndApplySubtractGreen(enc, enc->current_width_, height, bw)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- if (enc->use_predict_) {
- if (!ApplyPredictFilter(enc, enc->current_width_, height, quality, bw)) {
- err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
- goto Error;
- }
- }
-
- if (enc->use_cross_color_) {
- if (!ApplyCrossColorFilter(enc, enc->current_width_, height, quality, bw)) {
- err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
- goto Error;
- }
- }
-
- VP8LWriteBits(bw, 1, !TRANSFORM_PRESENT); // No more transforms.
-
- // ---------------------------------------------------------------------------
- // Estimate the color cache size.
-
- if (enc->cache_bits_ > 0) {
- if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
- height, &enc->cache_bits_)) {
- err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
- goto Error;
- }
- }
-
- // ---------------------------------------------------------------------------
- // Encode and write the transformed image.
-
- if (!EncodeImageInternal(bw, enc->argb_, enc->current_width_, height,
- quality, enc->cache_bits_, enc->histo_bits_)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- if (picture->stats != NULL) {
- WebPAuxStats* const stats = picture->stats;
- stats->lossless_features = 0;
- if (enc->use_predict_) stats->lossless_features |= 1;
- if (enc->use_cross_color_) stats->lossless_features |= 2;
- if (enc->use_subtract_green_) stats->lossless_features |= 4;
- if (enc->use_palette_) stats->lossless_features |= 8;
- stats->histogram_bits = enc->histo_bits_;
- stats->transform_bits = enc->transform_bits_;
- stats->cache_bits = enc->cache_bits_;
- stats->palette_size = enc->palette_size_;
- stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position);
- }
-
- Error:
- VP8LEncoderDelete(enc);
- return err;
-}
-
-int VP8LEncodeImage(const WebPConfig* const config,
- const WebPPicture* const picture) {
- int width, height;
- int has_alpha;
- size_t coded_size;
- int percent = 0;
- WebPEncodingError err = VP8_ENC_OK;
- VP8LBitWriter bw;
-
- if (picture == NULL) return 0;
-
- if (config == NULL || picture->argb == NULL) {
- err = VP8_ENC_ERROR_NULL_PARAMETER;
- WebPEncodingSetError(picture, err);
- return 0;
- }
-
- width = picture->width;
- height = picture->height;
- if (!VP8LBitWriterInit(&bw, (width * height) >> 1)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- if (!WebPReportProgress(picture, 1, &percent)) {
- UserAbort:
- err = VP8_ENC_ERROR_USER_ABORT;
- goto Error;
- }
- // Reset stats (for pure lossless coding)
- if (picture->stats != NULL) {
- WebPAuxStats* const stats = picture->stats;
- SbMemorySet(stats, 0, sizeof(*stats));
- stats->PSNR[0] = 99.f;
- stats->PSNR[1] = 99.f;
- stats->PSNR[2] = 99.f;
- stats->PSNR[3] = 99.f;
- stats->PSNR[4] = 99.f;
- }
-
- // Write image size.
- if (!WriteImageSize(picture, &bw)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- has_alpha = WebPPictureHasTransparency(picture);
- // Write the non-trivial Alpha flag and lossless version.
- if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
- err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- goto Error;
- }
-
- if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
-
- // Encode main image stream.
- err = VP8LEncodeStream(config, picture, &bw);
- if (err != VP8_ENC_OK) goto Error;
-
- // TODO(skal): have a fine-grained progress report in VP8LEncodeStream().
- if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
-
- // Finish the RIFF chunk.
- err = WriteImage(picture, &bw, &coded_size);
- if (err != VP8_ENC_OK) goto Error;
-
- if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
-
- // Save size.
- if (picture->stats != NULL) {
- picture->stats->coded_size += (int)coded_size;
- picture->stats->lossless_size = (int)coded_size;
- }
-
- if (picture->extra_info != NULL) {
- const int mb_w = (width + 15) >> 4;
- const int mb_h = (height + 15) >> 4;
- SbMemorySet(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info));
- }
-
- Error:
- if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
- VP8LBitWriterDestroy(&bw);
- if (err != VP8_ENC_OK) {
- WebPEncodingSetError(picture, err);
- return 0;
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/enc/vp8li.h b/src/third_party/libwebp/enc/vp8li.h
deleted file mode 100644
index 01f01f5..0000000
--- a/src/third_party/libwebp/enc/vp8li.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Lossless encoder: internal header.
-//
-// Author: Vikas Arora (vikaas.arora@gmail.com)
-
-#ifndef WEBP_ENC_VP8LI_H_
-#define WEBP_ENC_VP8LI_H_
-
-#include "./histogram.h"
-#include "../utils/bit_writer.h"
-#include "../webp/encode.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-typedef struct {
- const WebPConfig* config_; // user configuration and parameters
- const WebPPicture* pic_; // input picture.
-
- uint32_t* argb_; // Transformed argb image data.
- uint32_t* argb_scratch_; // Scratch memory for argb rows
- // (used for prediction).
- uint32_t* transform_data_; // Scratch memory for transform data.
- int current_width_; // Corresponds to packed image width.
-
- // Encoding parameters derived from quality parameter.
- int histo_bits_;
- int transform_bits_;
- int cache_bits_; // If equal to 0, don't use color cache.
-
- // Encoding parameters derived from image characteristics.
- int use_cross_color_;
- int use_subtract_green_;
- int use_predict_;
- int use_palette_;
- int palette_size_;
- uint32_t palette_[MAX_PALETTE_SIZE];
-} VP8LEncoder;
-
-//------------------------------------------------------------------------------
-// internal functions. Not public.
-
-// Encodes the picture.
-// Returns 0 if config or picture is NULL or picture doesn't have valid argb
-// input.
-int VP8LEncodeImage(const WebPConfig* const config,
- const WebPPicture* const picture);
-
-// Encodes the main image stream using the supplied bit writer.
-WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
- const WebPPicture* const picture,
- VP8LBitWriter* const bw);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_ENC_VP8LI_H_ */
diff --git a/src/third_party/libwebp/enc/webpenc.c b/src/third_party/libwebp/enc/webpenc.c
deleted file mode 100644
index ae4fb3b..0000000
--- a/src/third_party/libwebp/enc/webpenc.c
+++ /dev/null
@@ -1,425 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// WebP encoder: main entry point
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#endif
-
-#include "./vp8enci.h"
-#include "./vp8li.h"
-#include "../utils/utils.h"
-
-// #define PRINT_MEMORY_INFO
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#ifdef PRINT_MEMORY_INFO
-#include <stdio.h>
-#endif
-
-//------------------------------------------------------------------------------
-
-int WebPGetEncoderVersion(void) {
- return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
-}
-
-//------------------------------------------------------------------------------
-// WebPPicture
-//------------------------------------------------------------------------------
-
-static int DummyWriter(const uint8_t* data, size_t data_size,
- const WebPPicture* const picture) {
- // The following are to prevent 'unused variable' error message.
- (void)data;
- (void)data_size;
- (void)picture;
- return 1;
-}
-
-int WebPPictureInitInternal(WebPPicture* picture, int version) {
- if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
- return 0; // caller/system version mismatch!
- }
- if (picture != NULL) {
- SbMemorySet(picture, 0, sizeof(*picture));
- picture->writer = DummyWriter;
- WebPEncodingSetError(picture, VP8_ENC_OK);
- }
- return 1;
-}
-
-//------------------------------------------------------------------------------
-// VP8Encoder
-//------------------------------------------------------------------------------
-
-static void ResetSegmentHeader(VP8Encoder* const enc) {
- VP8SegmentHeader* const hdr = &enc->segment_hdr_;
- hdr->num_segments_ = enc->config_->segments;
- hdr->update_map_ = (hdr->num_segments_ > 1);
- hdr->size_ = 0;
-}
-
-static void ResetFilterHeader(VP8Encoder* const enc) {
- VP8FilterHeader* const hdr = &enc->filter_hdr_;
- hdr->simple_ = 1;
- hdr->level_ = 0;
- hdr->sharpness_ = 0;
- hdr->i4x4_lf_delta_ = 0;
-}
-
-static void ResetBoundaryPredictions(VP8Encoder* const enc) {
- // init boundary values once for all
- // Note: actually, initializing the preds_[] is only needed for intra4.
- int i;
- uint8_t* const top = enc->preds_ - enc->preds_w_;
- uint8_t* const left = enc->preds_ - 1;
- for (i = -1; i < 4 * enc->mb_w_; ++i) {
- top[i] = B_DC_PRED;
- }
- for (i = 0; i < 4 * enc->mb_h_; ++i) {
- left[i * enc->preds_w_] = B_DC_PRED;
- }
- enc->nz_[-1] = 0; // constant
-}
-
-// Mapping from config->method_ to coding tools used.
-//-------------------+---+---+---+---+---+---+---+
-// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
-//-------------------+---+---+---+---+---+---+---+
-// fast probe | x | | | x | | | |
-//-------------------+---+---+---+---+---+---+---+
-// dynamic proba | ~ | x | x | x | x | x | x |
-//-------------------+---+---+---+---+---+---+---+
-// fast mode analysis| | | | | x | x | x |
-//-------------------+---+---+---+---+---+---+---+
-// basic rd-opt | | | | x | x | x | x |
-//-------------------+---+---+---+---+---+---+---+
-// disto-score i4/16 | | | x | | | | |
-//-------------------+---+---+---+---+---+---+---+
-// rd-opt i4/16 | | | ~ | x | x | x | x |
-//-------------------+---+---+---+---+---+---+---+
-// token buffer (opt)| | | | x | x | x | x |
-//-------------------+---+---+---+---+---+---+---+
-// Trellis | | | | | | x |Ful|
-//-------------------+---+---+---+---+---+---+---+
-// full-SNS | | | | | x | x | x |
-//-------------------+---+---+---+---+---+---+---+
-
-static void MapConfigToTools(VP8Encoder* const enc) {
- const WebPConfig* const config = enc->config_;
- const int method = config->method;
- const int limit = 100 - config->partition_limit;
- enc->method_ = method;
- enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL
- : (method >= 5) ? RD_OPT_TRELLIS
- : (method >= 3) ? RD_OPT_BASIC
- : RD_OPT_NONE;
- enc->max_i4_header_bits_ =
- 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block
- (limit * limit) / (100 * 100); // ... modulated with a quadratic curve.
-
- enc->thread_level_ = config->thread_level;
-
- enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
- if (!config->low_memory) {
-#if !defined(DISABLE_TOKEN_BUFFER)
- enc->use_tokens_ = (method >= 3) && !enc->do_search_;
-#endif
- if (enc->use_tokens_) {
- enc->num_parts_ = 1; // doesn't work with multi-partition
- }
- }
-}
-
-// Memory scaling with dimensions:
-// memory (bytes) ~= 2.25 * w + 0.0625 * w * h
-//
-// Typical memory footprint (768x510 picture)
-// Memory used:
-// encoder: 33919
-// block cache: 2880
-// info: 3072
-// preds: 24897
-// top samples: 1623
-// non-zero: 196
-// lf-stats: 2048
-// total: 68635
-// Transcient object sizes:
-// VP8EncIterator: 352
-// VP8ModeScore: 912
-// VP8SegmentInfo: 532
-// VP8Proba: 31032
-// LFStats: 2048
-// Picture size (yuv): 589824
-
-static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
- WebPPicture* const picture) {
- const int use_filter =
- (config->filter_strength > 0) || (config->autofilter > 0);
- const int mb_w = (picture->width + 15) >> 4;
- const int mb_h = (picture->height + 15) >> 4;
- const int preds_w = 4 * mb_w + 1;
- const int preds_h = 4 * mb_h + 1;
- const size_t preds_size = preds_w * preds_h * sizeof(uint8_t);
- const int top_stride = mb_w * 16;
- const size_t nz_size = (mb_w + 1) * sizeof(uint32_t);
- const size_t cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(uint8_t);
- const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo);
- const size_t samples_size = (2 * top_stride + // top-luma/u/v
- 16 + 16 + 16 + 8 + 1 + // left y/u/v
- 2 * ALIGN_CST) // align all
- * sizeof(uint8_t);
- const size_t lf_stats_size =
- config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
- VP8Encoder* enc;
- uint8_t* mem;
- const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct
- + ALIGN_CST // cache alignment
- + cache_size // working caches
- + info_size // modes info
- + preds_size // prediction modes
- + samples_size // top/left samples
- + nz_size // coeff context bits
- + lf_stats_size; // autofilter stats
-
-#ifdef PRINT_MEMORY_INFO
- printf("===================================\n");
- printf("Memory used:\n"
- " encoder: %ld\n"
- " block cache: %ld\n"
- " info: %ld\n"
- " preds: %ld\n"
- " top samples: %ld\n"
- " non-zero: %ld\n"
- " lf-stats: %ld\n"
- " total: %ld\n",
- sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size,
- preds_size, samples_size, nz_size, lf_stats_size, size);
- printf("Transcient object sizes:\n"
- " VP8EncIterator: %ld\n"
- " VP8ModeScore: %ld\n"
- " VP8SegmentInfo: %ld\n"
- " VP8Proba: %ld\n"
- " LFStats: %ld\n",
- sizeof(VP8EncIterator), sizeof(VP8ModeScore),
- sizeof(VP8SegmentInfo), sizeof(VP8Proba),
- sizeof(LFStats));
- printf("Picture size (yuv): %ld\n",
- mb_w * mb_h * 384 * sizeof(uint8_t));
- printf("===================================\n");
-#endif
- mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
- if (mem == NULL) {
- WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
- return NULL;
- }
- enc = (VP8Encoder*)mem;
- mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc));
- SbMemorySet(enc, 0, sizeof(*enc));
- enc->num_parts_ = 1 << config->partitions;
- enc->mb_w_ = mb_w;
- enc->mb_h_ = mb_h;
- enc->preds_w_ = preds_w;
- enc->yuv_in_ = (uint8_t*)mem;
- mem += YUV_SIZE;
- enc->yuv_out_ = (uint8_t*)mem;
- mem += YUV_SIZE;
- enc->yuv_out2_ = (uint8_t*)mem;
- mem += YUV_SIZE;
- enc->yuv_p_ = (uint8_t*)mem;
- mem += PRED_SIZE;
- enc->mb_info_ = (VP8MBInfo*)mem;
- mem += info_size;
- enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_;
- mem += preds_w * preds_h * sizeof(uint8_t);
- enc->nz_ = 1 + (uint32_t*)mem;
- mem += nz_size;
- enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL;
- mem += lf_stats_size;
-
- // top samples (all 16-aligned)
- mem = (uint8_t*)DO_ALIGN(mem);
- enc->y_top_ = (uint8_t*)mem;
- enc->uv_top_ = enc->y_top_ + top_stride;
- mem += 2 * top_stride;
- mem = (uint8_t*)DO_ALIGN(mem + 1);
- enc->y_left_ = (uint8_t*)mem;
- mem += 16 + 16;
- enc->u_left_ = (uint8_t*)mem;
- mem += 16;
- enc->v_left_ = (uint8_t*)mem;
- mem += 8;
-
- enc->config_ = config;
- enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
- enc->pic_ = picture;
- enc->percent_ = 0;
-
- MapConfigToTools(enc);
- VP8EncDspInit();
- VP8DefaultProbas(enc);
- ResetSegmentHeader(enc);
- ResetFilterHeader(enc);
- ResetBoundaryPredictions(enc);
-
- VP8EncInitAlpha(enc);
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- VP8EncInitLayer(enc);
-#endif
-
- VP8TBufferInit(&enc->tokens_);
- return enc;
-}
-
-static int DeleteVP8Encoder(VP8Encoder* enc) {
- int ok = 1;
- if (enc != NULL) {
- ok = VP8EncDeleteAlpha(enc);
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- VP8EncDeleteLayer(enc);
-#endif
- VP8TBufferClear(&enc->tokens_);
- SbMemoryDeallocate(enc);
- }
- return ok;
-}
-
-//------------------------------------------------------------------------------
-
-static double GetPSNR(uint64_t err, uint64_t size) {
- return err ? 10. * log10(255. * 255. * size / err) : 99.;
-}
-
-static void FinalizePSNR(const VP8Encoder* const enc) {
- WebPAuxStats* stats = enc->pic_->stats;
- const uint64_t size = enc->sse_count_;
- const uint64_t* const sse = enc->sse_;
- stats->PSNR[0] = (float)GetPSNR(sse[0], size);
- stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
- stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
- stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
- stats->PSNR[4] = (float)GetPSNR(sse[3], size);
-}
-
-static void StoreStats(VP8Encoder* const enc) {
- WebPAuxStats* const stats = enc->pic_->stats;
- if (stats != NULL) {
- int i, s;
- for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
- stats->segment_level[i] = enc->dqm_[i].fstrength_;
- stats->segment_quant[i] = enc->dqm_[i].quant_;
- for (s = 0; s <= 2; ++s) {
- stats->residual_bytes[s][i] = enc->residual_bytes_[s][i];
- }
- }
- FinalizePSNR(enc);
- stats->coded_size = enc->coded_size_;
- for (i = 0; i < 3; ++i) {
- stats->block_count[i] = enc->block_count_[i];
- }
- }
- WebPReportProgress(enc->pic_, 100, &enc->percent_); // done!
-}
-
-int WebPEncodingSetError(const WebPPicture* const pic,
- WebPEncodingError error) {
- SB_DCHECK((int)error < VP8_ENC_ERROR_LAST);
- SB_DCHECK((int)error >= VP8_ENC_OK);
- ((WebPPicture*)pic)->error_code = error;
- return 0;
-}
-
-int WebPReportProgress(const WebPPicture* const pic,
- int percent, int* const percent_store) {
- if (percent_store != NULL && percent != *percent_store) {
- *percent_store = percent;
- if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
- // user abort requested
- WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
- return 0;
- }
- }
- return 1; // ok
-}
-//------------------------------------------------------------------------------
-
-int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
- int ok = 0;
-
- if (pic == NULL)
- return 0;
- WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far
- if (config == NULL) // bad params
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
- if (!WebPValidateConfig(config))
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
- if (pic->width <= 0 || pic->height <= 0)
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
- if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
-
- if (pic->stats != NULL) SbMemorySet(pic->stats, 0, sizeof(*pic->stats));
-
- if (!config->lossless) {
- VP8Encoder* enc = NULL;
- if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
- // Make sure we have YUVA samples.
- if (!WebPPictureARGBToYUVA(pic, WEBP_YUV420)) return 0;
- }
-
- enc = InitVP8Encoder(config, pic);
- if (enc == NULL) return 0; // pic->error is already set.
- // Note: each of the tasks below account for 20% in the progress report.
- ok = VP8EncAnalyze(enc);
-
- // Analysis is done, proceed to actual coding.
- ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel
- if (!enc->use_tokens_) {
- ok = ok && VP8EncLoop(enc);
- } else {
- ok = ok && VP8EncTokenLoop(enc);
- }
- ok = ok && VP8EncFinishAlpha(enc);
-#ifdef WEBP_EXPERIMENTAL_FEATURES
- ok = ok && VP8EncFinishLayer(enc);
-#endif
-
- ok = ok && VP8EncWrite(enc);
- StoreStats(enc);
- if (!ok) {
- VP8EncFreeBitWriters(enc);
- }
- ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok
- } else {
- // Make sure we have ARGB samples.
- if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {
- return 0;
- }
-
- ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.
- }
-
- return ok;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/examples/Android.mk b/src/third_party/libwebp/examples/Android.mk
new file mode 100644
index 0000000..486b8b8
--- /dev/null
+++ b/src/third_party/libwebp/examples/Android.mk
@@ -0,0 +1,98 @@
+LOCAL_PATH := $(call my-dir)
+
+################################################################################
+# libexample_util
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ example_util.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+
+LOCAL_MODULE := example_util
+
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+# cwebp
+
+include $(CLEAR_VARS)
+
+# Note: to enable jpeg/png encoding the sources from AOSP can be used with
+# minor modification to their Android.mk files.
+LOCAL_SRC_FILES := \
+ cwebp.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpdemux webp
+
+LOCAL_MODULE := cwebp
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# dwebp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ dwebp.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := example_util imagedec imageenc webpdemux webp
+LOCAL_MODULE := dwebp
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# webpmux
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ webpmux.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := example_util imageio_util webpmux webp
+
+LOCAL_MODULE := webpmux_example
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# img2webp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ img2webp.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpmux webpdemux \
+ webp
+
+LOCAL_MODULE := img2webp_example
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# webpinfo
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ webpinfo.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := example_util imageio_util webp
+
+LOCAL_MODULE := webpinfo_example
+
+include $(BUILD_EXECUTABLE)
diff --git a/src/third_party/libwebp/examples/Makefile.am b/src/third_party/libwebp/examples/Makefile.am
new file mode 100644
index 0000000..e10872f
--- /dev/null
+++ b/src/third_party/libwebp/examples/Makefile.am
@@ -0,0 +1,119 @@
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+
+bin_PROGRAMS =
+if WANT_DEMUX
+ bin_PROGRAMS += dwebp cwebp
+endif
+if BUILD_ANIMDIFF
+ noinst_PROGRAMS = anim_diff anim_dump
+endif
+if BUILD_GIF2WEBP
+ bin_PROGRAMS += gif2webp
+endif
+if BUILD_IMG2WEBP
+ bin_PROGRAMS += img2webp
+endif
+if WANT_MUX
+ bin_PROGRAMS += webpmux
+endif
+if BUILD_VWEBP
+ bin_PROGRAMS += vwebp
+endif
+if BUILD_WEBPINFO
+ bin_PROGRAMS += webpinfo
+endif
+
+noinst_LTLIBRARIES = libexample_util.la
+
+libexample_util_la_SOURCES = example_util.c example_util.h
+libexample_util_la_LIBADD = ../src/libwebp.la
+
+anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h
+anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
+anim_diff_LDADD =
+anim_diff_LDADD += ../src/demux/libwebpdemux.la
+anim_diff_LDADD += libexample_util.la
+anim_diff_LDADD += ../imageio/libimageio_util.la
+anim_diff_LDADD += $(GIF_LIBS) -lm
+
+anim_dump_SOURCES = anim_dump.c anim_util.c anim_util.h
+anim_dump_CPPFLAGS = $(AM_CPPFLAGS) $(PNG_INCLUDES)
+anim_dump_CPPFLAGS += $(GIF_INCLUDES)
+anim_dump_LDADD =
+anim_dump_LDADD += ../src/demux/libwebpdemux.la
+anim_dump_LDADD += libexample_util.la
+anim_dump_LDADD += ../imageio/libimageio_util.la
+anim_dump_LDADD += ../imageio/libimageenc.la
+anim_dump_LDADD += $(PNG_LIBS) $(GIF_LIBS) $(TIFF_LIBS) -lm
+
+cwebp_SOURCES = cwebp.c stopwatch.h
+cwebp_CPPFLAGS = $(AM_CPPFLAGS)
+cwebp_LDADD =
+cwebp_LDADD += libexample_util.la
+cwebp_LDADD += ../imageio/libimageio_util.la
+cwebp_LDADD += ../imageio/libimagedec.la
+cwebp_LDADD += ../src/libwebp.la
+cwebp_LDADD += $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS)
+
+dwebp_SOURCES = dwebp.c stopwatch.h
+dwebp_CPPFLAGS = $(AM_CPPFLAGS)
+dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES)
+dwebp_LDADD =
+dwebp_LDADD += libexample_util.la
+dwebp_LDADD += ../imageio/libimagedec.la
+dwebp_LDADD += ../imageio/libimageenc.la
+dwebp_LDADD += ../imageio/libimageio_util.la
+dwebp_LDADD += ../src/libwebp.la
+dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS)
+
+gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h
+gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
+gif2webp_LDADD =
+gif2webp_LDADD += libexample_util.la
+gif2webp_LDADD += ../imageio/libimageio_util.la
+gif2webp_LDADD += ../src/mux/libwebpmux.la
+gif2webp_LDADD += ../src/libwebp.la
+gif2webp_LDADD += $(GIF_LIBS)
+
+vwebp_SOURCES = vwebp.c
+vwebp_CPPFLAGS = $(AM_CPPFLAGS) $(GL_INCLUDES)
+vwebp_LDADD =
+vwebp_LDADD += libexample_util.la
+vwebp_LDADD += ../imageio/libimageio_util.la
+vwebp_LDADD += ../src/demux/libwebpdemux.la
+vwebp_LDADD += $(GL_LIBS)
+
+webpmux_SOURCES = webpmux.c
+webpmux_CPPFLAGS = $(AM_CPPFLAGS)
+webpmux_LDADD =
+webpmux_LDADD += libexample_util.la
+webpmux_LDADD += ../imageio/libimageio_util.la
+webpmux_LDADD += ../src/mux/libwebpmux.la
+webpmux_LDADD += ../src/libwebp.la
+
+img2webp_SOURCES = img2webp.c
+img2webp_CPPFLAGS = $(AM_CPPFLAGS)
+img2webp_LDADD =
+img2webp_LDADD += libexample_util.la
+img2webp_LDADD += ../imageio/libimageio_util.la
+img2webp_LDADD += ../imageio/libimagedec.la
+img2webp_LDADD += ../src/mux/libwebpmux.la
+img2webp_LDADD += ../src/libwebp.la
+img2webp_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
+
+webpinfo_SOURCES = webpinfo.c
+webpinfo_CPPFLAGS = $(AM_CPPFLAGS)
+webpinfo_LDADD =
+webpinfo_LDADD += libexample_util.la
+webpinfo_LDADD += ../imageio/libimageio_util.la
+webpinfo_LDADD += ../src/libwebp.la
+
+if BUILD_LIBWEBPDECODER
+ anim_diff_LDADD += ../src/libwebpdecoder.la
+ anim_dump_LDADD += ../src/libwebpdecoder.la
+ vwebp_LDADD += ../src/libwebpdecoder.la
+else
+ anim_diff_LDADD += ../src/libwebp.la
+ anim_dump_LDADD += ../src/libwebp.la
+ vwebp_LDADD += ../src/libwebp.la
+endif
diff --git a/src/third_party/libwebp/examples/anim_diff.c b/src/third_party/libwebp/examples/anim_diff.c
new file mode 100644
index 0000000..e74a915
--- /dev/null
+++ b/src/third_party/libwebp/examples/anim_diff.c
@@ -0,0 +1,314 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Checks if given pair of animated GIF/WebP images are identical:
+// That is: their reconstructed canvases match pixel-by-pixel and their other
+// animation properties (loop count etc) also match.
+//
+// example: anim_diff foo.gif bar.webp
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h> // for 'strtod'.
+#include <string.h> // for 'strcmp'.
+
+#include "./anim_util.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+// Returns true if 'a + b' will overflow.
+static int AdditionWillOverflow(int a, int b) {
+ return (b > 0) && (a > INT_MAX - b);
+}
+
+static int FramesAreEqual(const uint8_t* const rgba1,
+ const uint8_t* const rgba2, int width, int height) {
+ const int stride = width * 4; // Always true for 'DecodedFrame.rgba'.
+ return !memcmp(rgba1, rgba2, stride * height);
+}
+
+static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
+ int max_allowed_diff) {
+ const int src_a = (src >> 24) & 0xff;
+ const int src_r = (src >> 16) & 0xff;
+ const int src_g = (src >> 8) & 0xff;
+ const int src_b = (src >> 0) & 0xff;
+ const int dst_a = (dst >> 24) & 0xff;
+ const int dst_r = (dst >> 16) & 0xff;
+ const int dst_g = (dst >> 8) & 0xff;
+ const int dst_b = (dst >> 0) & 0xff;
+
+ return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) &&
+ (abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) &&
+ (abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) &&
+ (abs(src_a - dst_a) <= max_allowed_diff);
+}
+
+static int FramesAreSimilar(const uint8_t* const rgba1,
+ const uint8_t* const rgba2,
+ int width, int height, int max_allowed_diff) {
+ int i, j;
+ assert(max_allowed_diff > 0);
+ for (j = 0; j < height; ++j) {
+ for (i = 0; i < width; ++i) {
+ const int stride = width * 4;
+ const size_t offset = j * stride + i;
+ if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+// Minimize number of frames by combining successive frames that have at max
+// 'max_diff' difference per channel between corresponding pixels.
+static void MinimizeAnimationFrames(AnimatedImage* const img, int max_diff) {
+ uint32_t i;
+ for (i = 1; i < img->num_frames; ++i) {
+ DecodedFrame* const frame1 = &img->frames[i - 1];
+ DecodedFrame* const frame2 = &img->frames[i];
+ const uint8_t* const rgba1 = frame1->rgba;
+ const uint8_t* const rgba2 = frame2->rgba;
+ int should_merge_frames = 0;
+ // If merging frames will result in integer overflow for 'duration',
+ // skip merging.
+ if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
+ if (max_diff > 0) {
+ should_merge_frames = FramesAreSimilar(rgba1, rgba2, img->canvas_width,
+ img->canvas_height, max_diff);
+ } else {
+ should_merge_frames =
+ FramesAreEqual(rgba1, rgba2, img->canvas_width, img->canvas_height);
+ }
+ if (should_merge_frames) { // Merge 'i+1'th frame into 'i'th frame.
+ frame1->duration += frame2->duration;
+ if (i + 1 < img->num_frames) {
+ memmove(&img->frames[i], &img->frames[i + 1],
+ (img->num_frames - i - 1) * sizeof(*img->frames));
+ }
+ --img->num_frames;
+ --i;
+ }
+ }
+}
+
+static int CompareValues(uint32_t a, uint32_t b, const char* output_str) {
+ if (a != b) {
+ fprintf(stderr, "%s: %d vs %d\n", output_str, a, b);
+ return 0;
+ }
+ return 1;
+}
+
+static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
+ if (premultiply) {
+ const int alpha1 = (bg1 >> 24) & 0xff;
+ const int alpha2 = (bg2 >> 24) & 0xff;
+ if (alpha1 == 0 && alpha2 == 0) return 1;
+ }
+ if (bg1 != bg2) {
+ fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n",
+ bg1, bg2);
+ return 0;
+ }
+ return 1;
+}
+
+// Note: As long as frame durations and reconstructed frames are identical, it
+// is OK for other aspects like offsets, dispose/blend method to vary.
+static int CompareAnimatedImagePair(const AnimatedImage* const img1,
+ const AnimatedImage* const img2,
+ int premultiply,
+ double min_psnr) {
+ int ok = 1;
+ const int is_multi_frame_image = (img1->num_frames > 1);
+ uint32_t i;
+
+ ok = CompareValues(img1->canvas_width, img2->canvas_width,
+ "Canvas width mismatch") && ok;
+ ok = CompareValues(img1->canvas_height, img2->canvas_height,
+ "Canvas height mismatch") && ok;
+ ok = CompareValues(img1->num_frames, img2->num_frames,
+ "Frame count mismatch") && ok;
+ if (!ok) return 0; // These are fatal failures, can't proceed.
+
+ if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
+ ok = CompareValues(img1->loop_count, img2->loop_count,
+ "Loop count mismatch") && ok;
+ ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
+ premultiply) && ok;
+ }
+
+ for (i = 0; i < img1->num_frames; ++i) {
+ // Pixel-by-pixel comparison.
+ const uint8_t* const rgba1 = img1->frames[i].rgba;
+ const uint8_t* const rgba2 = img2->frames[i].rgba;
+ int max_diff;
+ double psnr;
+ if (is_multi_frame_image) { // Check relevant for multi-frame images only.
+ const char format[] = "Frame #%d, duration mismatch";
+ char tmp[sizeof(format) + 8];
+ ok = ok && (snprintf(tmp, sizeof(tmp), format, i) >= 0);
+ ok = ok && CompareValues(img1->frames[i].duration,
+ img2->frames[i].duration, tmp);
+ }
+ GetDiffAndPSNR(rgba1, rgba2, img1->canvas_width, img1->canvas_height,
+ premultiply, &max_diff, &psnr);
+ if (min_psnr > 0.) {
+ if (psnr < min_psnr) {
+ fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i,
+ psnr, min_psnr);
+ ok = 0;
+ }
+ } else {
+ if (max_diff != 0) {
+ fprintf(stderr, "Frame #%d, max pixel diff: %d\n", i, max_diff);
+ ok = 0;
+ }
+ }
+ }
+ return ok;
+}
+
+static void Help(void) {
+ printf("Usage: anim_diff <image1> <image2> [options]\n");
+ printf("\nOptions:\n");
+ printf(" -dump_frames <folder> dump decoded frames in PAM format\n");
+ printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
+ printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
+ printf(" premultiplied before comparison\n");
+ printf(" -max_diff <int> ..... maximum allowed difference per channel\n"
+ " between corresponding pixels in subsequent\n"
+ " frames\n");
+ printf(" -h .................. this help\n");
+ printf(" -version ............ print version number and exit\n");
+}
+
+int main(int argc, const char* argv[]) {
+ int return_code = -1;
+ int dump_frames = 0;
+ const char* dump_folder = NULL;
+ double min_psnr = 0.;
+ int got_input1 = 0;
+ int got_input2 = 0;
+ int premultiply = 1;
+ int max_diff = 0;
+ int i, c;
+ const char* files[2] = { NULL, NULL };
+ AnimatedImage images[2];
+
+ for (c = 1; c < argc; ++c) {
+ int parse_error = 0;
+ if (!strcmp(argv[c], "-dump_frames")) {
+ if (c < argc - 1) {
+ dump_frames = 1;
+ dump_folder = argv[++c];
+ } else {
+ parse_error = 1;
+ }
+ } else if (!strcmp(argv[c], "-min_psnr")) {
+ if (c < argc - 1) {
+ const char* const v = argv[++c];
+ char* end = NULL;
+ const double d = strtod(v, &end);
+ if (end == v) {
+ parse_error = 1;
+ fprintf(stderr, "Error! '%s' is not a floating point number.\n", v);
+ }
+ min_psnr = d;
+ } else {
+ parse_error = 1;
+ }
+ } else if (!strcmp(argv[c], "-raw_comparison")) {
+ premultiply = 0;
+ } else if (!strcmp(argv[c], "-max_diff")) {
+ if (c < argc - 1) {
+ const char* const v = argv[++c];
+ char* end = NULL;
+ const int n = (int)strtol(v, &end, 10);
+ if (end == v) {
+ parse_error = 1;
+ fprintf(stderr, "Error! '%s' is not an integer.\n", v);
+ }
+ max_diff = n;
+ } else {
+ parse_error = 1;
+ }
+ } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ Help();
+ return 0;
+ } else if (!strcmp(argv[c], "-version")) {
+ int dec_version, demux_version;
+ GetAnimatedImageVersions(&dec_version, &demux_version);
+ printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
+ (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
+ (dec_version >> 0) & 0xff,
+ (demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
+ (demux_version >> 0) & 0xff);
+ return 0;
+ } else {
+ if (!got_input1) {
+ files[0] = argv[c];
+ got_input1 = 1;
+ } else if (!got_input2) {
+ files[1] = argv[c];
+ got_input2 = 1;
+ } else {
+ parse_error = 1;
+ }
+ }
+ if (parse_error) {
+ Help();
+ return -1;
+ }
+ }
+ if (argc < 3) {
+ Help();
+ return -1;
+ }
+
+
+ if (!got_input2) {
+ Help();
+ return -1;
+ }
+
+ if (dump_frames) {
+ printf("Dumping decoded frames in: %s\n", dump_folder);
+ }
+
+ memset(images, 0, sizeof(images));
+ for (i = 0; i < 2; ++i) {
+ printf("Decoding file: %s\n", files[i]);
+ if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) {
+ fprintf(stderr, "Error decoding file: %s\n Aborting.\n", files[i]);
+ return_code = -2;
+ goto End;
+ } else {
+ MinimizeAnimationFrames(&images[i], max_diff);
+ }
+ }
+
+ if (!CompareAnimatedImagePair(&images[0], &images[1],
+ premultiply, min_psnr)) {
+ fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]);
+ return_code = -3;
+ } else {
+ printf("\nFiles %s and %s are identical.\n", files[0], files[1]);
+ return_code = 0;
+ }
+ End:
+ ClearAnimatedImage(&images[0]);
+ ClearAnimatedImage(&images[1]);
+ return return_code;
+}
diff --git a/src/third_party/libwebp/examples/anim_dump.c b/src/third_party/libwebp/examples/anim_dump.c
new file mode 100644
index 0000000..7b96cfe
--- /dev/null
+++ b/src/third_party/libwebp/examples/anim_dump.c
@@ -0,0 +1,118 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Decodes an animated WebP file and dumps the decoded frames as PNG or TIFF.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdio.h>
+#include <string.h> // for 'strcmp'.
+
+#include "./anim_util.h"
+#include "webp/decode.h"
+#include "../imageio/image_enc.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+static void Help(void) {
+ printf("Usage: anim_dump [options] files...\n");
+ printf("\nOptions:\n");
+ printf(" -folder <string> .... dump folder (default: '.')\n");
+ printf(" -prefix <string> .... prefix for dumped frames "
+ "(default: 'dump_')\n");
+ printf(" -tiff ............... save frames as TIFF\n");
+ printf(" -pam ................ save frames as PAM\n");
+ printf(" -h .................. this help\n");
+ printf(" -version ............ print version number and exit\n");
+}
+
+int main(int argc, const char* argv[]) {
+ int error = 0;
+ const char* dump_folder = ".";
+ const char* prefix = "dump_";
+ const char* suffix = "png";
+ WebPOutputFileFormat format = PNG;
+ int c;
+
+ if (argc < 2) {
+ Help();
+ return -1;
+ }
+
+ for (c = 1; !error && c < argc; ++c) {
+ if (!strcmp(argv[c], "-folder")) {
+ if (c + 1 == argc) {
+ fprintf(stderr, "missing argument after option '%s'\n", argv[c]);
+ error = 1;
+ break;
+ }
+ dump_folder = argv[++c];
+ } else if (!strcmp(argv[c], "-prefix")) {
+ if (c + 1 == argc) {
+ fprintf(stderr, "missing argument after option '%s'\n", argv[c]);
+ error = 1;
+ break;
+ }
+ prefix = argv[++c];
+ } else if (!strcmp(argv[c], "-tiff")) {
+ format = TIFF;
+ suffix = "tiff";
+ } else if (!strcmp(argv[c], "-pam")) {
+ format = PAM;
+ suffix = "pam";
+ } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ Help();
+ return 0;
+ } else if (!strcmp(argv[c], "-version")) {
+ int dec_version, demux_version;
+ GetAnimatedImageVersions(&dec_version, &demux_version);
+ printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
+ (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
+ (dec_version >> 0) & 0xff,
+ (demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
+ (demux_version >> 0) & 0xff);
+ return 0;
+ } else {
+ uint32_t i;
+ AnimatedImage image;
+ const char* const file = argv[c];
+ memset(&image, 0, sizeof(image));
+ printf("Decoding file: %s as %s/%sxxxx.%s\n",
+ file, dump_folder, prefix, suffix);
+ if (!ReadAnimatedImage(file, &image, 0, NULL)) {
+ fprintf(stderr, "Error decoding file: %s\n Aborting.\n", file);
+ error = 1;
+ break;
+ }
+ for (i = 0; !error && i < image.num_frames; ++i) {
+ char out_file[1024];
+ WebPDecBuffer buffer;
+ WebPInitDecBuffer(&buffer);
+ buffer.colorspace = MODE_RGBA;
+ buffer.is_external_memory = 1;
+ buffer.width = image.canvas_width;
+ buffer.height = image.canvas_height;
+ buffer.u.RGBA.rgba = image.frames[i].rgba;
+ buffer.u.RGBA.stride = buffer.width * sizeof(uint32_t);
+ buffer.u.RGBA.size = buffer.u.RGBA.stride * buffer.height;
+ snprintf(out_file, sizeof(out_file), "%s/%s%.4d.%s",
+ dump_folder, prefix, i, suffix);
+ if (!WebPSaveImage(&buffer, format, out_file)) {
+ fprintf(stderr, "Error while saving image '%s'\n", out_file);
+ error = 1;
+ }
+ WebPFreeDecBuffer(&buffer);
+ }
+ ClearAnimatedImage(&image);
+ }
+ }
+ return error ? 1 : 0;
+}
diff --git a/src/third_party/libwebp/examples/anim_util.c b/src/third_party/libwebp/examples/anim_util.c
new file mode 100644
index 0000000..c7a05c7
--- /dev/null
+++ b/src/third_party/libwebp/examples/anim_util.c
@@ -0,0 +1,794 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for animated images
+
+#include "./anim_util.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(WEBP_HAVE_GIF)
+#include <gif_lib.h>
+#endif
+#include "webp/format_constants.h"
+#include "webp/decode.h"
+#include "webp/demux.h"
+#include "../imageio/imageio_util.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+static const int kNumChannels = 4;
+
+// -----------------------------------------------------------------------------
+// Common utilities.
+
+#if defined(WEBP_HAVE_GIF)
+// Returns true if the frame covers the full canvas.
+static int IsFullFrame(int width, int height,
+ int canvas_width, int canvas_height) {
+ return (width == canvas_width && height == canvas_height);
+}
+#endif // WEBP_HAVE_GIF
+
+static int CheckSizeForOverflow(uint64_t size) {
+ return (size == (size_t)size);
+}
+
+static int AllocateFrames(AnimatedImage* const image, uint32_t num_frames) {
+ uint32_t i;
+ uint8_t* mem = NULL;
+ DecodedFrame* frames = NULL;
+ const uint64_t rgba_size =
+ (uint64_t)image->canvas_width * kNumChannels * image->canvas_height;
+ const uint64_t total_size = (uint64_t)num_frames * rgba_size * sizeof(*mem);
+ const uint64_t total_frame_size = (uint64_t)num_frames * sizeof(*frames);
+ if (!CheckSizeForOverflow(total_size) ||
+ !CheckSizeForOverflow(total_frame_size)) {
+ return 0;
+ }
+ mem = (uint8_t*)malloc((size_t)total_size);
+ frames = (DecodedFrame*)malloc((size_t)total_frame_size);
+
+ if (mem == NULL || frames == NULL) {
+ free(mem);
+ free(frames);
+ return 0;
+ }
+ free(image->raw_mem);
+ image->num_frames = num_frames;
+ image->frames = frames;
+ for (i = 0; i < num_frames; ++i) {
+ frames[i].rgba = mem + i * rgba_size;
+ frames[i].duration = 0;
+ frames[i].is_key_frame = 0;
+ }
+ image->raw_mem = mem;
+ return 1;
+}
+
+void ClearAnimatedImage(AnimatedImage* const image) {
+ if (image != NULL) {
+ free(image->raw_mem);
+ free(image->frames);
+ image->num_frames = 0;
+ image->frames = NULL;
+ image->raw_mem = NULL;
+ }
+}
+
+#if defined(WEBP_HAVE_GIF)
+// Clear the canvas to transparent.
+static void ZeroFillCanvas(uint8_t* rgba,
+ uint32_t canvas_width, uint32_t canvas_height) {
+ memset(rgba, 0, canvas_width * kNumChannels * canvas_height);
+}
+
+// Clear given frame rectangle to transparent.
+static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset,
+ int y_offset, int width, int height) {
+ int j;
+ assert(width * kNumChannels <= rgba_stride);
+ rgba += y_offset * rgba_stride + x_offset * kNumChannels;
+ for (j = 0; j < height; ++j) {
+ memset(rgba, 0, width * kNumChannels);
+ rgba += rgba_stride;
+ }
+}
+
+// Copy width * height pixels from 'src' to 'dst'.
+static void CopyCanvas(const uint8_t* src, uint8_t* dst,
+ uint32_t width, uint32_t height) {
+ assert(src != NULL && dst != NULL);
+ memcpy(dst, src, width * kNumChannels * height);
+}
+
+// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'.
+static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
+ int x_offset, int y_offset,
+ int width, int height) {
+ int j;
+ const int width_in_bytes = width * kNumChannels;
+ const size_t offset = y_offset * stride + x_offset * kNumChannels;
+ assert(width_in_bytes <= stride);
+ src += offset;
+ dst += offset;
+ for (j = 0; j < height; ++j) {
+ memcpy(dst, src, width_in_bytes);
+ src += stride;
+ dst += stride;
+ }
+}
+#endif // WEBP_HAVE_GIF
+
+// Canonicalize all transparent pixels to transparent black to aid comparison.
+static void CleanupTransparentPixels(uint32_t* rgba,
+ uint32_t width, uint32_t height) {
+ const uint32_t* const rgba_end = rgba + width * height;
+ while (rgba < rgba_end) {
+ const uint8_t alpha = (*rgba >> 24) & 0xff;
+ if (alpha == 0) {
+ *rgba = 0;
+ }
+ ++rgba;
+ }
+}
+
+// Dump frame to a PAM file. Returns true on success.
+static int DumpFrame(const char filename[], const char dump_folder[],
+ uint32_t frame_num, const uint8_t rgba[],
+ int canvas_width, int canvas_height) {
+ int ok = 0;
+ size_t max_len;
+ int y;
+ const char* base_name = NULL;
+ char* file_name = NULL;
+ FILE* f = NULL;
+ const char* row;
+
+ if (dump_folder == NULL) dump_folder = ".";
+
+ base_name = strrchr(filename, '/');
+ base_name = (base_name == NULL) ? filename : base_name + 1;
+ max_len = strlen(dump_folder) + 1 + strlen(base_name)
+ + strlen("_frame_") + strlen(".pam") + 8;
+ file_name = (char*)malloc(max_len * sizeof(*file_name));
+ if (file_name == NULL) goto End;
+
+ if (snprintf(file_name, max_len, "%s/%s_frame_%d.pam",
+ dump_folder, base_name, frame_num) < 0) {
+ fprintf(stderr, "Error while generating file name\n");
+ goto End;
+ }
+
+ f = fopen(file_name, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "Error opening file for writing: %s\n", file_name);
+ ok = 0;
+ goto End;
+ }
+ if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n"
+ "DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
+ canvas_width, canvas_height) < 0) {
+ fprintf(stderr, "Write error for file %s\n", file_name);
+ goto End;
+ }
+ row = (const char*)rgba;
+ for (y = 0; y < canvas_height; ++y) {
+ if (fwrite(row, canvas_width * kNumChannels, 1, f) != 1) {
+ fprintf(stderr, "Error writing to file: %s\n", file_name);
+ goto End;
+ }
+ row += canvas_width * kNumChannels;
+ }
+ ok = 1;
+ End:
+ if (f != NULL) fclose(f);
+ free(file_name);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+// WebP Decoding.
+
+// Returns true if this is a valid WebP bitstream.
+static int IsWebP(const WebPData* const webp_data) {
+ return (WebPGetInfo(webp_data->bytes, webp_data->size, NULL, NULL) != 0);
+}
+
+// Read animated WebP bitstream 'webp_data' into 'AnimatedImage' struct.
+static int ReadAnimatedWebP(const char filename[],
+ const WebPData* const webp_data,
+ AnimatedImage* const image, int dump_frames,
+ const char dump_folder[]) {
+ int ok = 0;
+ int dump_ok = 1;
+ uint32_t frame_index = 0;
+ int prev_frame_timestamp = 0;
+ WebPAnimDecoder* dec;
+ WebPAnimInfo anim_info;
+
+ memset(image, 0, sizeof(*image));
+
+ dec = WebPAnimDecoderNew(webp_data, NULL);
+ if (dec == NULL) {
+ fprintf(stderr, "Error parsing image: %s\n", filename);
+ goto End;
+ }
+
+ if (!WebPAnimDecoderGetInfo(dec, &anim_info)) {
+ fprintf(stderr, "Error getting global info about the animation\n");
+ goto End;
+ }
+
+ // Animation properties.
+ image->canvas_width = anim_info.canvas_width;
+ image->canvas_height = anim_info.canvas_height;
+ image->loop_count = anim_info.loop_count;
+ image->bgcolor = anim_info.bgcolor;
+
+ // Allocate frames.
+ if (!AllocateFrames(image, anim_info.frame_count)) return 0;
+
+ // Decode frames.
+ while (WebPAnimDecoderHasMoreFrames(dec)) {
+ DecodedFrame* curr_frame;
+ uint8_t* curr_rgba;
+ uint8_t* frame_rgba;
+ int timestamp;
+
+ if (!WebPAnimDecoderGetNext(dec, &frame_rgba, ×tamp)) {
+ fprintf(stderr, "Error decoding frame #%u\n", frame_index);
+ goto End;
+ }
+ assert(frame_index < anim_info.frame_count);
+ curr_frame = &image->frames[frame_index];
+ curr_rgba = curr_frame->rgba;
+ curr_frame->duration = timestamp - prev_frame_timestamp;
+ curr_frame->is_key_frame = 0; // Unused.
+ memcpy(curr_rgba, frame_rgba,
+ image->canvas_width * kNumChannels * image->canvas_height);
+
+ // Needed only because we may want to compare with GIF later.
+ CleanupTransparentPixels((uint32_t*)curr_rgba,
+ image->canvas_width, image->canvas_height);
+
+ if (dump_frames && dump_ok) {
+ dump_ok = DumpFrame(filename, dump_folder, frame_index, curr_rgba,
+ image->canvas_width, image->canvas_height);
+ if (!dump_ok) { // Print error once, but continue decode loop.
+ fprintf(stderr, "Error dumping frames to %s\n", dump_folder);
+ }
+ }
+
+ ++frame_index;
+ prev_frame_timestamp = timestamp;
+ }
+ ok = dump_ok;
+
+ End:
+ WebPAnimDecoderDelete(dec);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+// GIF Decoding.
+
+#if defined(WEBP_HAVE_GIF)
+
+// Returns true if this is a valid GIF bitstream.
+static int IsGIF(const WebPData* const data) {
+ return data->size > GIF_STAMP_LEN &&
+ (!memcmp(GIF_STAMP, data->bytes, GIF_STAMP_LEN) ||
+ !memcmp(GIF87_STAMP, data->bytes, GIF_STAMP_LEN) ||
+ !memcmp(GIF89_STAMP, data->bytes, GIF_STAMP_LEN));
+}
+
+// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
+#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
+# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
+# define LOCAL_GIF_PREREQ(maj, min) \
+ (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
+#else
+# define LOCAL_GIF_VERSION 0
+# define LOCAL_GIF_PREREQ(maj, min) 0
+#endif
+
+#if !LOCAL_GIF_PREREQ(5, 0)
+
+// Added in v5.0
+typedef struct {
+ int DisposalMode;
+#define DISPOSAL_UNSPECIFIED 0 // No disposal specified
+#define DISPOSE_DO_NOT 1 // Leave image in place
+#define DISPOSE_BACKGROUND 2 // Set area to background color
+#define DISPOSE_PREVIOUS 3 // Restore to previous content
+ int UserInputFlag; // User confirmation required before disposal
+ int DelayTime; // Pre-display delay in 0.01sec units
+ int TransparentColor; // Palette index for transparency, -1 if none
+#define NO_TRANSPARENT_COLOR -1
+} GraphicsControlBlock;
+
+static int DGifExtensionToGCB(const size_t GifExtensionLength,
+ const GifByteType* GifExtension,
+ GraphicsControlBlock* gcb) {
+ if (GifExtensionLength != 4) {
+ return GIF_ERROR;
+ }
+ gcb->DisposalMode = (GifExtension[0] >> 2) & 0x07;
+ gcb->UserInputFlag = (GifExtension[0] & 0x02) != 0;
+ gcb->DelayTime = GifExtension[1] | (GifExtension[2] << 8);
+ if (GifExtension[0] & 0x01) {
+ gcb->TransparentColor = (int)GifExtension[3];
+ } else {
+ gcb->TransparentColor = NO_TRANSPARENT_COLOR;
+ }
+ return GIF_OK;
+}
+
+static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
+ GraphicsControlBlock* gcb) {
+ int i;
+ if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
+ return GIF_ERROR;
+ }
+ gcb->DisposalMode = DISPOSAL_UNSPECIFIED;
+ gcb->UserInputFlag = 0;
+ gcb->DelayTime = 0;
+ gcb->TransparentColor = NO_TRANSPARENT_COLOR;
+
+ for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
+ ExtensionBlock* ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
+ if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
+ return DGifExtensionToGCB(
+ ep->ByteCount, (const GifByteType*)ep->Bytes, gcb);
+ }
+ }
+ return GIF_ERROR;
+}
+
+#define CONTINUE_EXT_FUNC_CODE 0x00
+
+// Signature was changed in v5.0
+#define DGifOpenFileName(a, b) DGifOpenFileName(a)
+
+#endif // !LOCAL_GIF_PREREQ(5, 0)
+
+// Signature changed in v5.1
+#if !LOCAL_GIF_PREREQ(5, 1)
+#define DGifCloseFile(a, b) DGifCloseFile(a)
+#endif
+
+static void GIFDisplayError(const GifFileType* const gif, int gif_error) {
+ // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
+#if LOCAL_GIF_PREREQ(4, 2)
+#if LOCAL_GIF_PREREQ(5, 0)
+ const char* error_str =
+ GifErrorString((gif == NULL) ? gif_error : gif->Error);
+#else
+ const char* error_str = GifErrorString();
+ (void)gif;
+#endif
+ if (error_str == NULL) error_str = "Unknown error";
+ fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
+#else
+ (void)gif;
+ fprintf(stderr, "GIFLib Error %d: ", gif_error);
+ PrintGifError();
+ fprintf(stderr, "\n");
+#endif
+}
+
+static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose,
+ const DecodedFrame* const prev_frame,
+ int canvas_width, int canvas_height) {
+ if (prev_frame == NULL) return 1;
+ if (prev_dispose == DISPOSE_BACKGROUND) {
+ if (IsFullFrame(prev_desc->Width, prev_desc->Height,
+ canvas_width, canvas_height)) {
+ return 1;
+ }
+ if (prev_frame->is_key_frame) return 1;
+ }
+ return 0;
+}
+
+static int GetTransparentIndexGIF(GifFileType* gif) {
+ GraphicsControlBlock first_gcb;
+ memset(&first_gcb, 0, sizeof(first_gcb));
+ DGifSavedExtensionToGCB(gif, 0, &first_gcb);
+ return first_gcb.TransparentColor;
+}
+
+static uint32_t GetBackgroundColorGIF(GifFileType* gif) {
+ const int transparent_index = GetTransparentIndexGIF(gif);
+ const ColorMapObject* const color_map = gif->SColorMap;
+ if (transparent_index != NO_TRANSPARENT_COLOR &&
+ gif->SBackGroundColor == transparent_index) {
+ return 0x00000000; // Special case: transparent black.
+ } else if (color_map == NULL || color_map->Colors == NULL
+ || gif->SBackGroundColor >= color_map->ColorCount) {
+ return 0xffffffff; // Invalid: assume white.
+ } else {
+ const GifColorType color = color_map->Colors[gif->SBackGroundColor];
+ return (0xff << 24) |
+ (color.Red << 16) |
+ (color.Green << 8) |
+ (color.Blue << 0);
+ }
+}
+
+// Find appropriate app extension and get loop count from the next extension.
+// We use Chrome's interpretation of the 'loop_count' semantics:
+// if not present -> loop once
+// if present and loop_count == 0, return 0 ('infinite').
+// if present and loop_count != 0, it's the number of *extra* loops
+// so we need to return loop_count + 1 as total loop number.
+static uint32_t GetLoopCountGIF(const GifFileType* const gif) {
+ int i;
+ for (i = 0; i < gif->ImageCount; ++i) {
+ const SavedImage* const image = &gif->SavedImages[i];
+ int j;
+ for (j = 0; (j + 1) < image->ExtensionBlockCount; ++j) {
+ const ExtensionBlock* const eb1 = image->ExtensionBlocks + j;
+ const ExtensionBlock* const eb2 = image->ExtensionBlocks + j + 1;
+ const char* const signature = (const char*)eb1->Bytes;
+ const int signature_is_ok =
+ (eb1->Function == APPLICATION_EXT_FUNC_CODE) &&
+ (eb1->ByteCount == 11) &&
+ (!memcmp(signature, "NETSCAPE2.0", 11) ||
+ !memcmp(signature, "ANIMEXTS1.0", 11));
+ if (signature_is_ok &&
+ eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 &&
+ eb2->Bytes[0] == 1) {
+ const uint32_t extra_loop = ((uint32_t)(eb2->Bytes[2]) << 8) +
+ ((uint32_t)(eb2->Bytes[1]) << 0);
+ return (extra_loop > 0) ? extra_loop + 1 : 0;
+ }
+ }
+ }
+ return 1; // Default.
+}
+
+// Get duration of 'n'th frame in milliseconds.
+static int GetFrameDurationGIF(GifFileType* gif, int n) {
+ GraphicsControlBlock gcb;
+ memset(&gcb, 0, sizeof(gcb));
+ DGifSavedExtensionToGCB(gif, n, &gcb);
+ return gcb.DelayTime * 10;
+}
+
+// Returns true if frame 'target' completely covers 'covered'.
+static int CoversFrameGIF(const GifImageDesc* const target,
+ const GifImageDesc* const covered) {
+ return target->Left <= covered->Left &&
+ covered->Left + covered->Width <= target->Left + target->Width &&
+ target->Top <= covered->Top &&
+ covered->Top + covered->Height <= target->Top + target->Height;
+}
+
+static void RemapPixelsGIF(const uint8_t* const src,
+ const ColorMapObject* const cmap,
+ int transparent_color, int len, uint8_t* dst) {
+ int i;
+ for (i = 0; i < len; ++i) {
+ if (src[i] != transparent_color) {
+ // If a pixel in the current frame is transparent, we don't modify it, so
+ // that we can see-through the corresponding pixel from an earlier frame.
+ const GifColorType c = cmap->Colors[src[i]];
+ dst[4 * i + 0] = c.Red;
+ dst[4 * i + 1] = c.Green;
+ dst[4 * i + 2] = c.Blue;
+ dst[4 * i + 3] = 0xff;
+ }
+ }
+}
+
+static int ReadFrameGIF(const SavedImage* const gif_image,
+ const ColorMapObject* cmap, int transparent_color,
+ int out_stride, uint8_t* const dst) {
+ const GifImageDesc* image_desc = &gif_image->ImageDesc;
+ const uint8_t* in;
+ uint8_t* out;
+ int j;
+
+ if (image_desc->ColorMap) cmap = image_desc->ColorMap;
+
+ if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+ fprintf(stderr, "Potentially corrupt color map.\n");
+ return 0;
+ }
+
+ in = (const uint8_t*)gif_image->RasterBits;
+ out = dst + image_desc->Top * out_stride + image_desc->Left * kNumChannels;
+
+ for (j = 0; j < image_desc->Height; ++j) {
+ RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out);
+ in += image_desc->Width;
+ out += out_stride;
+ }
+ return 1;
+}
+
+// Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct.
+static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
+ int dump_frames, const char dump_folder[]) {
+ uint32_t frame_count;
+ uint32_t canvas_width, canvas_height;
+ uint32_t i;
+ int gif_error;
+ GifFileType* gif;
+
+ gif = DGifOpenFileName(filename, NULL);
+ if (gif == NULL) {
+ fprintf(stderr, "Could not read file: %s.\n", filename);
+ return 0;
+ }
+
+ gif_error = DGifSlurp(gif);
+ if (gif_error != GIF_OK) {
+ fprintf(stderr, "Could not parse image: %s.\n", filename);
+ GIFDisplayError(gif, gif_error);
+ DGifCloseFile(gif, NULL);
+ return 0;
+ }
+
+ // Animation properties.
+ image->canvas_width = (uint32_t)gif->SWidth;
+ image->canvas_height = (uint32_t)gif->SHeight;
+ if (image->canvas_width > MAX_CANVAS_SIZE ||
+ image->canvas_height > MAX_CANVAS_SIZE) {
+ fprintf(stderr, "Invalid canvas dimension: %d x %d\n",
+ image->canvas_width, image->canvas_height);
+ DGifCloseFile(gif, NULL);
+ return 0;
+ }
+ image->loop_count = GetLoopCountGIF(gif);
+ image->bgcolor = GetBackgroundColorGIF(gif);
+
+ frame_count = (uint32_t)gif->ImageCount;
+ if (frame_count == 0) {
+ DGifCloseFile(gif, NULL);
+ return 0;
+ }
+
+ if (image->canvas_width == 0 || image->canvas_height == 0) {
+ image->canvas_width = gif->SavedImages[0].ImageDesc.Width;
+ image->canvas_height = gif->SavedImages[0].ImageDesc.Height;
+ gif->SavedImages[0].ImageDesc.Left = 0;
+ gif->SavedImages[0].ImageDesc.Top = 0;
+ if (image->canvas_width == 0 || image->canvas_height == 0) {
+ fprintf(stderr, "Invalid canvas size in GIF.\n");
+ DGifCloseFile(gif, NULL);
+ return 0;
+ }
+ }
+ // Allocate frames.
+ AllocateFrames(image, frame_count);
+
+ canvas_width = image->canvas_width;
+ canvas_height = image->canvas_height;
+
+ // Decode and reconstruct frames.
+ for (i = 0; i < frame_count; ++i) {
+ const int canvas_width_in_bytes = canvas_width * kNumChannels;
+ const SavedImage* const curr_gif_image = &gif->SavedImages[i];
+ GraphicsControlBlock curr_gcb;
+ DecodedFrame* curr_frame;
+ uint8_t* curr_rgba;
+
+ memset(&curr_gcb, 0, sizeof(curr_gcb));
+ DGifSavedExtensionToGCB(gif, i, &curr_gcb);
+
+ curr_frame = &image->frames[i];
+ curr_rgba = curr_frame->rgba;
+ curr_frame->duration = GetFrameDurationGIF(gif, i);
+ // Force frames with a small or no duration to 100ms to be consistent
+ // with web browsers and other transcoding tools (like gif2webp itself).
+ if (curr_frame->duration <= 10) curr_frame->duration = 100;
+
+ if (i == 0) { // Initialize as transparent.
+ curr_frame->is_key_frame = 1;
+ ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
+ } else {
+ DecodedFrame* const prev_frame = &image->frames[i - 1];
+ const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc;
+ GraphicsControlBlock prev_gcb;
+ memset(&prev_gcb, 0, sizeof(prev_gcb));
+ DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb);
+
+ curr_frame->is_key_frame =
+ IsKeyFrameGIF(prev_desc, prev_gcb.DisposalMode, prev_frame,
+ canvas_width, canvas_height);
+
+ if (curr_frame->is_key_frame) { // Initialize as transparent.
+ ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
+ } else {
+ int prev_frame_disposed, curr_frame_opaque;
+ int prev_frame_completely_covered;
+ // Initialize with previous canvas.
+ uint8_t* const prev_rgba = image->frames[i - 1].rgba;
+ CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);
+
+ // Dispose previous frame rectangle.
+ prev_frame_disposed =
+ (prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
+ prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
+ curr_frame_opaque =
+ (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
+ prev_frame_completely_covered =
+ curr_frame_opaque &&
+ CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc);
+
+ if (prev_frame_disposed && !prev_frame_completely_covered) {
+ switch (prev_gcb.DisposalMode) {
+ case DISPOSE_BACKGROUND: {
+ ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
+ prev_desc->Left, prev_desc->Top,
+ prev_desc->Width, prev_desc->Height);
+ break;
+ }
+ case DISPOSE_PREVIOUS: {
+ int src_frame_num = i - 2;
+ while (src_frame_num >= 0) {
+ GraphicsControlBlock src_frame_gcb;
+ memset(&src_frame_gcb, 0, sizeof(src_frame_gcb));
+ DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb);
+ if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break;
+ --src_frame_num;
+ }
+ if (src_frame_num >= 0) {
+ // Restore pixels inside previous frame rectangle to
+ // corresponding pixels in source canvas.
+ uint8_t* const src_frame_rgba =
+ image->frames[src_frame_num].rgba;
+ CopyFrameRectangle(src_frame_rgba, curr_rgba,
+ canvas_width_in_bytes,
+ prev_desc->Left, prev_desc->Top,
+ prev_desc->Width, prev_desc->Height);
+ } else {
+ // Source canvas doesn't exist. So clear previous frame
+ // rectangle to background.
+ ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
+ prev_desc->Left, prev_desc->Top,
+ prev_desc->Width, prev_desc->Height);
+ }
+ break;
+ }
+ default:
+ break; // Nothing to do.
+ }
+ }
+ }
+ }
+
+ // Decode current frame.
+ if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor,
+ canvas_width_in_bytes, curr_rgba)) {
+ DGifCloseFile(gif, NULL);
+ return 0;
+ }
+
+ if (dump_frames) {
+ if (!DumpFrame(filename, dump_folder, i, curr_rgba,
+ canvas_width, canvas_height)) {
+ DGifCloseFile(gif, NULL);
+ return 0;
+ }
+ }
+ }
+ DGifCloseFile(gif, NULL);
+ return 1;
+}
+
+#else
+
+static int IsGIF(const WebPData* const data) {
+ (void)data;
+ return 0;
+}
+
+static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
+ int dump_frames, const char dump_folder[]) {
+ (void)filename;
+ (void)image;
+ (void)dump_frames;
+ (void)dump_folder;
+ fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
+ "package before building.\n");
+ return 0;
+}
+
+#endif // WEBP_HAVE_GIF
+
+// -----------------------------------------------------------------------------
+
+int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
+ int dump_frames, const char dump_folder[]) {
+ int ok = 0;
+ WebPData webp_data;
+
+ WebPDataInit(&webp_data);
+ memset(image, 0, sizeof(*image));
+
+ if (!ImgIoUtilReadFile(filename, &webp_data.bytes, &webp_data.size)) {
+ fprintf(stderr, "Error reading file: %s\n", filename);
+ return 0;
+ }
+
+ if (IsWebP(&webp_data)) {
+ ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames,
+ dump_folder);
+ } else if (IsGIF(&webp_data)) {
+ ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder);
+ } else {
+ fprintf(stderr,
+ "Unknown file type: %s. Supported file types are WebP and GIF\n",
+ filename);
+ ok = 0;
+ }
+ if (!ok) ClearAnimatedImage(image);
+ WebPDataClear(&webp_data);
+ return ok;
+}
+
+static void Accumulate(double v1, double v2, double* const max_diff,
+ double* const sse) {
+ const double diff = fabs(v1 - v2);
+ if (diff > *max_diff) *max_diff = diff;
+ *sse += diff * diff;
+}
+
+void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
+ uint32_t width, uint32_t height, int premultiply,
+ int* const max_diff, double* const psnr) {
+ const uint32_t stride = width * kNumChannels;
+ const int kAlphaChannel = kNumChannels - 1;
+ double f_max_diff = 0.;
+ double sse = 0.;
+ uint32_t x, y;
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < stride; x += kNumChannels) {
+ int k;
+ const size_t offset = (size_t)y * stride + x;
+ const int alpha1 = rgba1[offset + kAlphaChannel];
+ const int alpha2 = rgba2[offset + kAlphaChannel];
+ Accumulate(alpha1, alpha2, &f_max_diff, &sse);
+ if (!premultiply) {
+ for (k = 0; k < kAlphaChannel; ++k) {
+ Accumulate(rgba1[offset + k], rgba2[offset + k], &f_max_diff, &sse);
+ }
+ } else {
+ // premultiply R/G/B channels with alpha value
+ for (k = 0; k < kAlphaChannel; ++k) {
+ Accumulate(rgba1[offset + k] * alpha1 / 255.,
+ rgba2[offset + k] * alpha2 / 255.,
+ &f_max_diff, &sse);
+ }
+ }
+ }
+ }
+ *max_diff = (int)f_max_diff;
+ if (*max_diff == 0) {
+ *psnr = 99.; // PSNR when images are identical.
+ } else {
+ sse /= stride * height;
+ *psnr = 4.3429448 * log(255. * 255. / sse);
+ }
+}
+
+void GetAnimatedImageVersions(int* const decoder_version,
+ int* const demux_version) {
+ *decoder_version = WebPGetDecoderVersion();
+ *demux_version = WebPGetDemuxVersion();
+}
diff --git a/src/third_party/libwebp/examples/anim_util.h b/src/third_party/libwebp/examples/anim_util.h
new file mode 100644
index 0000000..8063121
--- /dev/null
+++ b/src/third_party/libwebp/examples/anim_util.h
@@ -0,0 +1,67 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for animated images
+
+#ifndef WEBP_EXAMPLES_ANIM_UTIL_H_
+#define WEBP_EXAMPLES_ANIM_UTIL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ uint8_t* rgba; // Decoded and reconstructed full frame.
+ int duration; // Frame duration in milliseconds.
+ int is_key_frame; // True if this frame is a key-frame.
+} DecodedFrame;
+
+typedef struct {
+ uint32_t canvas_width;
+ uint32_t canvas_height;
+ uint32_t bgcolor;
+ uint32_t loop_count;
+ DecodedFrame* frames;
+ uint32_t num_frames;
+ void* raw_mem;
+} AnimatedImage;
+
+// Deallocate everything in 'image' (but not the object itself).
+void ClearAnimatedImage(AnimatedImage* const image);
+
+// Read animated image file into 'AnimatedImage' struct.
+// If 'dump_frames' is true, dump frames to 'dump_folder'.
+// Previous content of 'image' is obliterated.
+// Upon successful return, content of 'image' must be deleted by
+// calling 'ClearAnimatedImage'.
+int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
+ int dump_frames, const char dump_folder[]);
+
+// Given two RGBA buffers, calculate max pixel difference and PSNR.
+// If 'premultiply' is true, R/G/B values will be pre-multiplied by the
+// transparency before comparison.
+void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
+ uint32_t width, uint32_t height, int premultiply,
+ int* const max_diff, double* const psnr);
+
+// Return library versions used by anim_util.
+void GetAnimatedImageVersions(int* const decoder_version,
+ int* const demux_version);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_EXAMPLES_ANIM_UTIL_H_
diff --git a/src/third_party/libwebp/examples/cwebp.c b/src/third_party/libwebp/examples/cwebp.c
new file mode 100644
index 0000000..de7190b
--- /dev/null
+++ b/src/third_party/libwebp/examples/cwebp.c
@@ -0,0 +1,1131 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// simple command line calling the WebPEncode function.
+// Encodes a raw .YUV into WebP bitstream
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../examples/example_util.h"
+#include "../imageio/image_dec.h"
+#include "../imageio/imageio_util.h"
+#include "./stopwatch.h"
+#include "webp/encode.h"
+
+#ifndef WEBP_DLL
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void* VP8GetCPUInfo; // opaque forward declaration.
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#endif // WEBP_DLL
+
+//------------------------------------------------------------------------------
+
+static int verbose = 0;
+
+static int ReadYUV(const uint8_t* const data, size_t data_size,
+ WebPPicture* const pic) {
+ const int use_argb = pic->use_argb;
+ const int uv_width = (pic->width + 1) / 2;
+ const int uv_height = (pic->height + 1) / 2;
+ const int y_plane_size = pic->width * pic->height;
+ const int uv_plane_size = uv_width * uv_height;
+ const size_t expected_data_size = y_plane_size + 2 * uv_plane_size;
+
+ if (data_size != expected_data_size) {
+ fprintf(stderr,
+ "input data doesn't have the expected size (%d instead of %d)\n",
+ (int)data_size, (int)expected_data_size);
+ return 0;
+ }
+
+ pic->use_argb = 0;
+ if (!WebPPictureAlloc(pic)) return 0;
+ ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride,
+ pic->width, pic->height);
+ ImgIoUtilCopyPlane(data + y_plane_size, uv_width,
+ pic->u, pic->uv_stride, uv_width, uv_height);
+ ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width,
+ pic->v, pic->uv_stride, uv_width, uv_height);
+ return use_argb ? WebPPictureYUVAToARGB(pic) : 1;
+}
+
+#ifdef HAVE_WINCODEC_H
+
+static int ReadPicture(const char* const filename, WebPPicture* const pic,
+ int keep_alpha, Metadata* const metadata) {
+ int ok = 0;
+ const uint8_t* data = NULL;
+ size_t data_size = 0;
+ if (pic->width != 0 && pic->height != 0) {
+ ok = ImgIoUtilReadFile(filename, &data, &data_size);
+ ok = ok && ReadYUV(data, data_size, pic);
+ } else {
+ // If no size specified, try to decode it using WIC.
+ ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata);
+ if (!ok) {
+ ok = ImgIoUtilReadFile(filename, &data, &data_size);
+ ok = ok && ReadWebP(data, data_size, pic, keep_alpha, metadata);
+ }
+ }
+ if (!ok) {
+ fprintf(stderr, "Error! Could not process file %s\n", filename);
+ }
+ free((void*)data);
+ return ok;
+}
+
+#else // !HAVE_WINCODEC_H
+
+static int ReadPicture(const char* const filename, WebPPicture* const pic,
+ int keep_alpha, Metadata* const metadata) {
+ const uint8_t* data = NULL;
+ size_t data_size = 0;
+ int ok = 0;
+
+ ok = ImgIoUtilReadFile(filename, &data, &data_size);
+ if (!ok) goto End;
+
+ if (pic->width == 0 || pic->height == 0) {
+ WebPImageReader reader = WebPGuessImageReader(data, data_size);
+ ok = reader(data, data_size, pic, keep_alpha, metadata);
+ } else {
+ // If image size is specified, infer it as YUV format.
+ ok = ReadYUV(data, data_size, pic);
+ }
+ End:
+ if (!ok) {
+ fprintf(stderr, "Error! Could not process file %s\n", filename);
+ }
+ free((void*)data);
+ return ok;
+}
+
+#endif // !HAVE_WINCODEC_H
+
+static void AllocExtraInfo(WebPPicture* const pic) {
+ const int mb_w = (pic->width + 15) / 16;
+ const int mb_h = (pic->height + 15) / 16;
+ pic->extra_info = (uint8_t*)malloc(mb_w * mb_h * sizeof(*pic->extra_info));
+}
+
+static void PrintByteCount(const int bytes[4], int total_size,
+ int* const totals) {
+ int s;
+ int total = 0;
+ for (s = 0; s < 4; ++s) {
+ fprintf(stderr, "| %7d ", bytes[s]);
+ total += bytes[s];
+ if (totals) totals[s] += bytes[s];
+ }
+ fprintf(stderr, "| %7d (%.1f%%)\n", total, 100.f * total / total_size);
+}
+
+static void PrintPercents(const int counts[4]) {
+ int s;
+ const int total = counts[0] + counts[1] + counts[2] + counts[3];
+ for (s = 0; s < 4; ++s) {
+ fprintf(stderr, "| %2d%%", (int)(100. * counts[s] / total + .5));
+ }
+ fprintf(stderr, "| %7d\n", total);
+}
+
+static void PrintValues(const int values[4]) {
+ int s;
+ for (s = 0; s < 4; ++s) {
+ fprintf(stderr, "| %7d ", values[s]);
+ }
+ fprintf(stderr, "|\n");
+}
+
+static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
+ const char* const description) {
+ fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
+ description, stats->lossless_size);
+ fprintf(stderr, " * Header size: %d bytes, image data size: %d\n",
+ stats->lossless_hdr_size, stats->lossless_data_size);
+ if (stats->lossless_features) {
+ fprintf(stderr, " * Lossless features used:");
+ if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
+ if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
+ if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
+ if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, " * Precision Bits: histogram=%d transform=%d cache=%d\n",
+ stats->histogram_bits, stats->transform_bits, stats->cache_bits);
+ if (stats->palette_size > 0) {
+ fprintf(stderr, " * Palette size: %d\n", stats->palette_size);
+ }
+}
+
+static void PrintExtraInfoLossless(const WebPPicture* const pic,
+ int short_output,
+ const char* const file_name) {
+ const WebPAuxStats* const stats = pic->stats;
+ if (short_output) {
+ fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
+ } else {
+ fprintf(stderr, "File: %s\n", file_name);
+ fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
+ fprintf(stderr, "Output: %d bytes (%.2f bpp)\n", stats->coded_size,
+ 8.f * stats->coded_size / pic->width / pic->height);
+ PrintFullLosslessInfo(stats, "ARGB");
+ }
+}
+
+static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
+ int full_details,
+ const char* const file_name) {
+ const WebPAuxStats* const stats = pic->stats;
+ if (short_output) {
+ fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
+ } else {
+ const int num_i4 = stats->block_count[0];
+ const int num_i16 = stats->block_count[1];
+ const int num_skip = stats->block_count[2];
+ const int total = num_i4 + num_i16;
+ fprintf(stderr, "File: %s\n", file_name);
+ fprintf(stderr, "Dimension: %d x %d%s\n",
+ pic->width, pic->height,
+ stats->alpha_data_size ? " (with alpha)" : "");
+ fprintf(stderr, "Output: "
+ "%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f %2.2f dB\n"
+ " (%.2f bpp)\n",
+ stats->coded_size,
+ stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3],
+ 8.f * stats->coded_size / pic->width / pic->height);
+ if (total > 0) {
+ int totals[4] = { 0, 0, 0, 0 };
+ fprintf(stderr, "block count: intra4: %6d (%.2f%%)\n"
+ " intra16: %6d (%.2f%%)\n"
+ " skipped: %6d (%.2f%%)\n",
+ num_i4, 100.f * num_i4 / total,
+ num_i16, 100.f * num_i16 / total,
+ num_skip, 100.f * num_skip / total);
+ fprintf(stderr, "bytes used: header: %6d (%.1f%%)\n"
+ " mode-partition: %6d (%.1f%%)\n",
+ stats->header_bytes[0],
+ 100.f * stats->header_bytes[0] / stats->coded_size,
+ stats->header_bytes[1],
+ 100.f * stats->header_bytes[1] / stats->coded_size);
+ if (stats->alpha_data_size > 0) {
+ fprintf(stderr, " transparency: %6d (%.1f dB)\n",
+ stats->alpha_data_size, stats->PSNR[4]);
+ }
+ fprintf(stderr, " Residuals bytes "
+ "|segment 1|segment 2|segment 3"
+ "|segment 4| total\n");
+ if (full_details) {
+ fprintf(stderr, " intra4-coeffs: ");
+ PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals);
+ fprintf(stderr, " intra16-coeffs: ");
+ PrintByteCount(stats->residual_bytes[1], stats->coded_size, totals);
+ fprintf(stderr, " chroma coeffs: ");
+ PrintByteCount(stats->residual_bytes[2], stats->coded_size, totals);
+ }
+ fprintf(stderr, " macroblocks: ");
+ PrintPercents(stats->segment_size);
+ fprintf(stderr, " quantizer: ");
+ PrintValues(stats->segment_quant);
+ fprintf(stderr, " filter level: ");
+ PrintValues(stats->segment_level);
+ if (full_details) {
+ fprintf(stderr, "------------------+---------");
+ fprintf(stderr, "+---------+---------+---------+-----------------\n");
+ fprintf(stderr, " segments total: ");
+ PrintByteCount(totals, stats->coded_size, NULL);
+ }
+ }
+ if (stats->lossless_size > 0) {
+ PrintFullLosslessInfo(stats, "alpha");
+ }
+ }
+}
+
+static void PrintMapInfo(const WebPPicture* const pic) {
+ if (pic->extra_info != NULL) {
+ const int mb_w = (pic->width + 15) / 16;
+ const int mb_h = (pic->height + 15) / 16;
+ const int type = pic->extra_info_type;
+ int x, y;
+ for (y = 0; y < mb_h; ++y) {
+ for (x = 0; x < mb_w; ++x) {
+ const int c = pic->extra_info[x + y * mb_w];
+ if (type == 1) { // intra4/intra16
+ fprintf(stderr, "%c", "+."[c]);
+ } else if (type == 2) { // segments
+ fprintf(stderr, "%c", ".-*X"[c]);
+ } else if (type == 3) { // quantizers
+ fprintf(stderr, "%.2d ", c);
+ } else if (type == 6 || type == 7) {
+ fprintf(stderr, "%3d ", c);
+ } else {
+ fprintf(stderr, "0x%.2x ", c);
+ }
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+static int MyWriter(const uint8_t* data, size_t data_size,
+ const WebPPicture* const pic) {
+ FILE* const out = (FILE*)pic->custom_ptr;
+ return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
+}
+
+// Dumps a picture as a PGM file using the IMC4 layout.
+static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
+ int y;
+ const int uv_width = (picture->width + 1) / 2;
+ const int uv_height = (picture->height + 1) / 2;
+ const int stride = (picture->width + 1) & ~1;
+ const uint8_t* src_y = picture->y;
+ const uint8_t* src_u = picture->u;
+ const uint8_t* src_v = picture->v;
+ const uint8_t* src_a = picture->a;
+ const int alpha_height =
+ WebPPictureHasTransparency(picture) ? picture->height : 0;
+ const int height = picture->height + uv_height + alpha_height;
+ FILE* const f = fopen(PGM_name, "wb");
+ if (f == NULL) return 0;
+ fprintf(f, "P5\n%d %d\n255\n", stride, height);
+ for (y = 0; y < picture->height; ++y) {
+ if (fwrite(src_y, picture->width, 1, f) != 1) return 0;
+ if (picture->width & 1) fputc(0, f); // pad
+ src_y += picture->y_stride;
+ }
+ for (y = 0; y < uv_height; ++y) {
+ if (fwrite(src_u, uv_width, 1, f) != 1) return 0;
+ if (fwrite(src_v, uv_width, 1, f) != 1) return 0;
+ src_u += picture->uv_stride;
+ src_v += picture->uv_stride;
+ }
+ for (y = 0; y < alpha_height; ++y) {
+ if (fwrite(src_a, picture->width, 1, f) != 1) return 0;
+ if (picture->width & 1) fputc(0, f); // pad
+ src_a += picture->a_stride;
+ }
+ fclose(f);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+// Metadata writing.
+
+enum {
+ METADATA_EXIF = (1 << 0),
+ METADATA_ICC = (1 << 1),
+ METADATA_XMP = (1 << 2),
+ METADATA_ALL = METADATA_EXIF | METADATA_ICC | METADATA_XMP
+};
+
+static const int kChunkHeaderSize = 8;
+static const int kTagSize = 4;
+
+static void PrintMetadataInfo(const Metadata* const metadata,
+ int metadata_written) {
+ if (metadata == NULL || metadata_written == 0) return;
+
+ fprintf(stderr, "Metadata:\n");
+ if (metadata_written & METADATA_ICC) {
+ fprintf(stderr, " * ICC profile: %6d bytes\n", (int)metadata->iccp.size);
+ }
+ if (metadata_written & METADATA_EXIF) {
+ fprintf(stderr, " * EXIF data: %6d bytes\n", (int)metadata->exif.size);
+ }
+ if (metadata_written & METADATA_XMP) {
+ fprintf(stderr, " * XMP data: %6d bytes\n", (int)metadata->xmp.size);
+ }
+}
+
+// Outputs, in little endian, 'num' bytes from 'val' to 'out'.
+static int WriteLE(FILE* const out, uint32_t val, int num) {
+ uint8_t buf[4];
+ int i;
+ for (i = 0; i < num; ++i) {
+ buf[i] = (uint8_t)(val & 0xff);
+ val >>= 8;
+ }
+ return (fwrite(buf, num, 1, out) == 1);
+}
+
+static int WriteLE24(FILE* const out, uint32_t val) {
+ return WriteLE(out, val, 3);
+}
+
+static int WriteLE32(FILE* const out, uint32_t val) {
+ return WriteLE(out, val, 4);
+}
+
+static int WriteMetadataChunk(FILE* const out, const char fourcc[4],
+ const MetadataPayload* const payload) {
+ const uint8_t zero = 0;
+ const size_t need_padding = payload->size & 1;
+ int ok = (fwrite(fourcc, kTagSize, 1, out) == 1);
+ ok = ok && WriteLE32(out, (uint32_t)payload->size);
+ ok = ok && (fwrite(payload->bytes, payload->size, 1, out) == 1);
+ return ok && (fwrite(&zero, need_padding, need_padding, out) == need_padding);
+}
+
+// Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the
+// chunk if there is metadata and 'keep' is true.
+static int UpdateFlagsAndSize(const MetadataPayload* const payload,
+ int keep, int flag,
+ uint32_t* vp8x_flags, uint64_t* metadata_size) {
+ if (keep && payload->bytes != NULL && payload->size > 0) {
+ *vp8x_flags |= flag;
+ *metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1);
+ return 1;
+ }
+ return 0;
+}
+
+// Writes a WebP file using the image contained in 'memory_writer' and the
+// metadata from 'metadata'. Metadata is controlled by 'keep_metadata' and the
+// availability in 'metadata'. Returns true on success.
+// For details see doc/webp-container-spec.txt#extended-file-format.
+static int WriteWebPWithMetadata(FILE* const out,
+ const WebPPicture* const picture,
+ const WebPMemoryWriter* const memory_writer,
+ const Metadata* const metadata,
+ int keep_metadata,
+ int* const metadata_written) {
+ const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00";
+ const int kAlphaFlag = 0x10;
+ const int kEXIFFlag = 0x08;
+ const int kICCPFlag = 0x20;
+ const int kXMPFlag = 0x04;
+ const size_t kRiffHeaderSize = 12;
+ const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1;
+ const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize;
+ uint32_t flags = 0;
+ uint64_t metadata_size = 0;
+ const int write_exif = UpdateFlagsAndSize(&metadata->exif,
+ !!(keep_metadata & METADATA_EXIF),
+ kEXIFFlag, &flags, &metadata_size);
+ const int write_iccp = UpdateFlagsAndSize(&metadata->iccp,
+ !!(keep_metadata & METADATA_ICC),
+ kICCPFlag, &flags, &metadata_size);
+ const int write_xmp = UpdateFlagsAndSize(&metadata->xmp,
+ !!(keep_metadata & METADATA_XMP),
+ kXMPFlag, &flags, &metadata_size);
+ uint8_t* webp = memory_writer->mem;
+ size_t webp_size = memory_writer->size;
+
+ *metadata_written = 0;
+
+ if (webp_size < kMinSize) return 0;
+ if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) {
+ fprintf(stderr, "Error! Addition of metadata would exceed "
+ "container size limit.\n");
+ return 0;
+ }
+
+ if (metadata_size > 0) {
+ const int kVP8XChunkSize = 18;
+ const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize);
+ const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize +
+ (has_vp8x ? 0 : kVP8XChunkSize) +
+ metadata_size);
+ // RIFF
+ int ok = (fwrite(webp, kTagSize, 1, out) == 1);
+ // RIFF size (file header size is not recorded)
+ ok = ok && WriteLE32(out, riff_size);
+ webp += kChunkHeaderSize;
+ webp_size -= kChunkHeaderSize;
+ // WEBP
+ ok = ok && (fwrite(webp, kTagSize, 1, out) == 1);
+ webp += kTagSize;
+ webp_size -= kTagSize;
+ if (has_vp8x) { // update the existing VP8X flags
+ webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff);
+ ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1);
+ webp += kVP8XChunkSize;
+ webp_size -= kVP8XChunkSize;
+ } else {
+ const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
+ if (is_lossless) {
+ // Presence of alpha is stored in the 37th bit (29th after the
+ // signature) of VP8L data.
+ if (webp[kChunkHeaderSize + 4] & (1 << 4)) flags |= kAlphaFlag;
+ }
+ ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
+ ok = ok && WriteLE32(out, flags);
+ ok = ok && WriteLE24(out, picture->width - 1);
+ ok = ok && WriteLE24(out, picture->height - 1);
+ }
+ if (write_iccp) {
+ ok = ok && WriteMetadataChunk(out, "ICCP", &metadata->iccp);
+ *metadata_written |= METADATA_ICC;
+ }
+ // Image
+ ok = ok && (fwrite(webp, webp_size, 1, out) == 1);
+ if (write_exif) {
+ ok = ok && WriteMetadataChunk(out, "EXIF", &metadata->exif);
+ *metadata_written |= METADATA_EXIF;
+ }
+ if (write_xmp) {
+ ok = ok && WriteMetadataChunk(out, "XMP ", &metadata->xmp);
+ *metadata_written |= METADATA_XMP;
+ }
+ return ok;
+ }
+
+ // No metadata, just write the original image file.
+ return (fwrite(webp, webp_size, 1, out) == 1);
+}
+
+//------------------------------------------------------------------------------
+
+static int ProgressReport(int percent, const WebPPicture* const picture) {
+ fprintf(stderr, "[%s]: %3d %% \r",
+ (char*)picture->user_data, percent);
+ return 1; // all ok
+}
+
+//------------------------------------------------------------------------------
+
+static void HelpShort(void) {
+ printf("Usage:\n\n");
+ printf(" cwebp [options] -q quality input.png -o output.webp\n\n");
+ printf("where quality is between 0 (poor) to 100 (very good).\n");
+ printf("Typical value is around 80.\n\n");
+ printf("Try -longhelp for an exhaustive list of advanced options.\n");
+}
+
+static void HelpLong(void) {
+ printf("Usage:\n");
+ printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
+ printf("If input size (-s) for an image is not specified, it is\n"
+ "assumed to be a PNG, JPEG, TIFF or WebP file.\n");
+#ifdef HAVE_WINCODEC_H
+ printf("Windows builds can take as input any of the files handled by WIC.\n");
+#endif
+ printf("\nOptions:\n");
+ printf(" -h / -help ............. short help\n");
+ printf(" -H / -longhelp ......... long help\n");
+ printf(" -q <float> ............. quality factor (0:small..100:big), "
+ "default=75\n");
+ printf(" -alpha_q <int> ......... transparency-compression quality (0..100),"
+ "\n default=100\n");
+ printf(" -preset <string> ....... preset setting, one of:\n");
+ printf(" default, photo, picture,\n");
+ printf(" drawing, icon, text\n");
+ printf(" -preset must come first, as it overwrites other parameters\n");
+ printf(" -z <int> ............... activates lossless preset with given\n"
+ " level in [0:fast, ..., 9:slowest]\n");
+ printf("\n");
+ printf(" -m <int> ............... compression method (0=fast, 6=slowest), "
+ "default=4\n");
+ printf(" -segments <int> ........ number of segments to use (1..4), "
+ "default=4\n");
+ printf(" -size <int> ............ target size (in bytes)\n");
+ printf(" -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
+ printf("\n");
+ printf(" -s <int> <int> ......... input size (width x height) for YUV\n");
+ printf(" -sns <int> ............. spatial noise shaping (0:off, 100:max), "
+ "default=50\n");
+ printf(" -f <int> ............... filter strength (0=off..100), "
+ "default=60\n");
+ printf(" -sharpness <int> ....... "
+ "filter sharpness (0:most .. 7:least sharp), default=0\n");
+ printf(" -strong ................ use strong filter instead "
+ "of simple (default)\n");
+ printf(" -nostrong .............. use simple filter instead of strong\n");
+ printf(" -sharp_yuv ............. use sharper (and slower) RGB->YUV "
+ "conversion\n");
+ printf(" -partition_limit <int> . limit quality to fit the 512k limit on\n");
+ printf(" "
+ "the first partition (0=no degradation ... 100=full)\n");
+ printf(" -pass <int> ............ analysis pass number (1..10)\n");
+ printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
+ printf(" -resize <w> <h> ........ resize picture (after any cropping)\n");
+ printf(" -mt .................... use multi-threading if available\n");
+ printf(" -low_memory ............ reduce memory usage (slower encoding)\n");
+ printf(" -map <int> ............. print map of extra info\n");
+ printf(" -print_psnr ............ prints averaged PSNR distortion\n");
+ printf(" -print_ssim ............ prints averaged SSIM distortion\n");
+ printf(" -print_lsim ............ prints local-similarity distortion\n");
+ printf(" -d <file.pgm> .......... dump the compressed output (PGM file)\n");
+ printf(" -alpha_method <int> .... transparency-compression method (0..1), "
+ "default=1\n");
+ printf(" -alpha_filter <string> . predictive filtering for alpha plane,\n");
+ printf(" one of: none, fast (default) or best\n");
+ printf(" -exact ................. preserve RGB values in transparent area, "
+ "default=off\n");
+ printf(" -blend_alpha <hex> ..... blend colors against background color\n"
+ " expressed as RGB values written in\n"
+ " hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
+ " green=0xe0 and blue=0xd0\n");
+ printf(" -noalpha ............... discard any transparency information\n");
+ printf(" -lossless .............. encode image losslessly, default=off\n");
+ printf(" -near_lossless <int> ... use near-lossless image\n"
+ " preprocessing (0..100=off), "
+ "default=100\n");
+ printf(" -hint <string> ......... specify image characteristics hint,\n");
+ printf(" one of: photo, picture or graph\n");
+
+ printf("\n");
+ printf(" -metadata <string> ..... comma separated list of metadata to\n");
+ printf(" ");
+ printf("copy from the input to the output if present.\n");
+ printf(" "
+ "Valid values: all, none (default), exif, icc, xmp\n");
+
+ printf("\n");
+ printf(" -short ................. condense printed message\n");
+ printf(" -quiet ................. don't print anything\n");
+ printf(" -version ............... print version number and exit\n");
+#ifndef WEBP_DLL
+ printf(" -noasm ................. disable all assembly optimizations\n");
+#endif
+ printf(" -v ..................... verbose, e.g. print encoding/decoding "
+ "times\n");
+ printf(" -progress .............. report encoding progress\n");
+ printf("\n");
+ printf("Experimental Options:\n");
+ printf(" -jpeg_like ............. roughly match expected JPEG size\n");
+ printf(" -af .................... auto-adjust filter strength\n");
+ printf(" -pre <int> ............. pre-processing filter\n");
+ printf("\n");
+}
+
+//------------------------------------------------------------------------------
+// Error messages
+
+static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = {
+ "OK",
+ "OUT_OF_MEMORY: Out of memory allocating objects",
+ "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
+ "NULL_PARAMETER: NULL parameter passed to function",
+ "INVALID_CONFIGURATION: configuration is invalid",
+ "BAD_DIMENSION: Bad picture dimension. Maximum width and height "
+ "allowed is 16383 pixels.",
+ "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
+ "To reduce the size of this partition, try using less segments "
+ "with the -segments option, and eventually reduce the number of "
+ "header bits using -partition_limit. More details are available "
+ "in the manual (`man cwebp`)",
+ "PARTITION_OVERFLOW: Partition is too big to fit 16M",
+ "BAD_WRITE: Picture writer returned an I/O error",
+ "FILE_TOO_BIG: File would be too big to fit in 4G",
+ "USER_ABORT: encoding abort requested by user"
+};
+
+//------------------------------------------------------------------------------
+
+int main(int argc, const char *argv[]) {
+ int return_value = -1;
+ const char *in_file = NULL, *out_file = NULL, *dump_file = NULL;
+ FILE *out = NULL;
+ int c;
+ int short_output = 0;
+ int quiet = 0;
+ int keep_alpha = 1;
+ int blend_alpha = 0;
+ uint32_t background_color = 0xffffffu;
+ int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
+ int resize_w = 0, resize_h = 0;
+ int lossless_preset = 6;
+ int use_lossless_preset = -1; // -1=unset, 0=don't use, 1=use it
+ int show_progress = 0;
+ int keep_metadata = 0;
+ int metadata_written = 0;
+ WebPPicture picture;
+ int print_distortion = -1; // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
+ WebPPicture original_picture; // when PSNR or SSIM is requested
+ WebPConfig config;
+ WebPAuxStats stats;
+ WebPMemoryWriter memory_writer;
+ Metadata metadata;
+ Stopwatch stop_watch;
+
+ MetadataInit(&metadata);
+ WebPMemoryWriterInit(&memory_writer);
+ if (!WebPPictureInit(&picture) ||
+ !WebPPictureInit(&original_picture) ||
+ !WebPConfigInit(&config)) {
+ fprintf(stderr, "Error! Version mismatch!\n");
+ return -1;
+ }
+
+ if (argc == 1) {
+ HelpShort();
+ return 0;
+ }
+
+ for (c = 1; c < argc; ++c) {
+ int parse_error = 0;
+ if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ HelpShort();
+ return 0;
+ } else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
+ HelpLong();
+ return 0;
+ } else if (!strcmp(argv[c], "-o") && c < argc - 1) {
+ out_file = argv[++c];
+ } else if (!strcmp(argv[c], "-d") && c < argc - 1) {
+ dump_file = argv[++c];
+ config.show_compressed = 1;
+ } else if (!strcmp(argv[c], "-print_psnr")) {
+ config.show_compressed = 1;
+ print_distortion = 0;
+ } else if (!strcmp(argv[c], "-print_ssim")) {
+ config.show_compressed = 1;
+ print_distortion = 1;
+ } else if (!strcmp(argv[c], "-print_lsim")) {
+ config.show_compressed = 1;
+ print_distortion = 2;
+ } else if (!strcmp(argv[c], "-short")) {
+ ++short_output;
+ } else if (!strcmp(argv[c], "-s") && c < argc - 2) {
+ picture.width = ExUtilGetInt(argv[++c], 0, &parse_error);
+ picture.height = ExUtilGetInt(argv[++c], 0, &parse_error);
+ if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
+ picture.height > WEBP_MAX_DIMENSION || picture.height < 0) {
+ fprintf(stderr,
+ "Specified dimension (%d x %d) is out of range.\n",
+ picture.width, picture.height);
+ goto Error;
+ }
+ } else if (!strcmp(argv[c], "-m") && c < argc - 1) {
+ config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
+ use_lossless_preset = 0; // disable -z option
+ } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
+ config.quality = ExUtilGetFloat(argv[++c], &parse_error);
+ use_lossless_preset = 0; // disable -z option
+ } else if (!strcmp(argv[c], "-z") && c < argc - 1) {
+ lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
+ if (use_lossless_preset != 0) use_lossless_preset = 1;
+ } else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
+ config.alpha_quality = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
+ config.alpha_compression = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-alpha_cleanup")) {
+ // This flag is obsolete, does opposite of -exact.
+ config.exact = 0;
+ } else if (!strcmp(argv[c], "-exact")) {
+ config.exact = 1;
+ } else if (!strcmp(argv[c], "-blend_alpha") && c < argc - 1) {
+ blend_alpha = 1;
+ // background color is given in hex with an optional '0x' prefix
+ background_color = ExUtilGetInt(argv[++c], 16, &parse_error);
+ background_color = background_color & 0x00ffffffu;
+ } else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
+ ++c;
+ if (!strcmp(argv[c], "none")) {
+ config.alpha_filtering = 0;
+ } else if (!strcmp(argv[c], "fast")) {
+ config.alpha_filtering = 1;
+ } else if (!strcmp(argv[c], "best")) {
+ config.alpha_filtering = 2;
+ } else {
+ fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]);
+ goto Error;
+ }
+ } else if (!strcmp(argv[c], "-noalpha")) {
+ keep_alpha = 0;
+ } else if (!strcmp(argv[c], "-lossless")) {
+ config.lossless = 1;
+ } else if (!strcmp(argv[c], "-near_lossless") && c < argc - 1) {
+ config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
+ config.lossless = 1; // use near-lossless only with lossless
+ } else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
+ ++c;
+ if (!strcmp(argv[c], "photo")) {
+ config.image_hint = WEBP_HINT_PHOTO;
+ } else if (!strcmp(argv[c], "picture")) {
+ config.image_hint = WEBP_HINT_PICTURE;
+ } else if (!strcmp(argv[c], "graph")) {
+ config.image_hint = WEBP_HINT_GRAPH;
+ } else {
+ fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
+ goto Error;
+ }
+ } else if (!strcmp(argv[c], "-size") && c < argc - 1) {
+ config.target_size = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
+ config.target_PSNR = ExUtilGetFloat(argv[++c], &parse_error);
+ } else if (!strcmp(argv[c], "-sns") && c < argc - 1) {
+ config.sns_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-f") && c < argc - 1) {
+ config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-af")) {
+ config.autofilter = 1;
+ } else if (!strcmp(argv[c], "-jpeg_like")) {
+ config.emulate_jpeg_size = 1;
+ } else if (!strcmp(argv[c], "-mt")) {
+ ++config.thread_level; // increase thread level
+ } else if (!strcmp(argv[c], "-low_memory")) {
+ config.low_memory = 1;
+ } else if (!strcmp(argv[c], "-strong")) {
+ config.filter_type = 1;
+ } else if (!strcmp(argv[c], "-nostrong")) {
+ config.filter_type = 0;
+ } else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
+ config.filter_sharpness = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-sharp_yuv")) {
+ config.use_sharp_yuv = 1;
+ } else if (!strcmp(argv[c], "-pass") && c < argc - 1) {
+ config.pass = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-pre") && c < argc - 1) {
+ config.preprocessing = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-segments") && c < argc - 1) {
+ config.segments = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
+ config.partition_limit = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-map") && c < argc - 1) {
+ picture.extra_info_type = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
+ crop = 1;
+ crop_x = ExUtilGetInt(argv[++c], 0, &parse_error);
+ crop_y = ExUtilGetInt(argv[++c], 0, &parse_error);
+ crop_w = ExUtilGetInt(argv[++c], 0, &parse_error);
+ crop_h = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-resize") && c < argc - 2) {
+ resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
+ resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
+#ifndef WEBP_DLL
+ } else if (!strcmp(argv[c], "-noasm")) {
+ VP8GetCPUInfo = NULL;
+#endif
+ } else if (!strcmp(argv[c], "-version")) {
+ const int version = WebPGetEncoderVersion();
+ printf("%d.%d.%d\n",
+ (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+ return 0;
+ } else if (!strcmp(argv[c], "-progress")) {
+ show_progress = 1;
+ } else if (!strcmp(argv[c], "-quiet")) {
+ quiet = 1;
+ } else if (!strcmp(argv[c], "-preset") && c < argc - 1) {
+ WebPPreset preset;
+ ++c;
+ if (!strcmp(argv[c], "default")) {
+ preset = WEBP_PRESET_DEFAULT;
+ } else if (!strcmp(argv[c], "photo")) {
+ preset = WEBP_PRESET_PHOTO;
+ } else if (!strcmp(argv[c], "picture")) {
+ preset = WEBP_PRESET_PICTURE;
+ } else if (!strcmp(argv[c], "drawing")) {
+ preset = WEBP_PRESET_DRAWING;
+ } else if (!strcmp(argv[c], "icon")) {
+ preset = WEBP_PRESET_ICON;
+ } else if (!strcmp(argv[c], "text")) {
+ preset = WEBP_PRESET_TEXT;
+ } else {
+ fprintf(stderr, "Error! Unrecognized preset: %s\n", argv[c]);
+ goto Error;
+ }
+ if (!WebPConfigPreset(&config, preset, config.quality)) {
+ fprintf(stderr, "Error! Could initialize configuration with preset.\n");
+ goto Error;
+ }
+ } else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
+ static const struct {
+ const char* option;
+ int flag;
+ } kTokens[] = {
+ { "all", METADATA_ALL },
+ { "none", 0 },
+ { "exif", METADATA_EXIF },
+ { "icc", METADATA_ICC },
+ { "xmp", METADATA_XMP },
+ };
+ const size_t kNumTokens = sizeof(kTokens) / sizeof(kTokens[0]);
+ const char* start = argv[++c];
+ const char* const end = start + strlen(start);
+
+ while (start < end) {
+ size_t i;
+ const char* token = strchr(start, ',');
+ if (token == NULL) token = end;
+
+ for (i = 0; i < kNumTokens; ++i) {
+ if ((size_t)(token - start) == strlen(kTokens[i].option) &&
+ !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
+ if (kTokens[i].flag != 0) {
+ keep_metadata |= kTokens[i].flag;
+ } else {
+ keep_metadata = 0;
+ }
+ break;
+ }
+ }
+ if (i == kNumTokens) {
+ fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
+ (int)(token - start), start);
+ HelpLong();
+ return -1;
+ }
+ start = token + 1;
+ }
+#ifdef HAVE_WINCODEC_H
+ if (keep_metadata != 0 && keep_metadata != METADATA_ICC) {
+ // TODO(jzern): remove when -metadata is supported on all platforms.
+ fprintf(stderr, "Warning: only ICC profile extraction is currently"
+ " supported on this platform!\n");
+ }
+#endif
+ } else if (!strcmp(argv[c], "-v")) {
+ verbose = 1;
+ } else if (!strcmp(argv[c], "--")) {
+ if (c < argc - 1) in_file = argv[++c];
+ break;
+ } else if (argv[c][0] == '-') {
+ fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
+ HelpLong();
+ return -1;
+ } else {
+ in_file = argv[c];
+ }
+
+ if (parse_error) {
+ HelpLong();
+ return -1;
+ }
+ }
+ if (in_file == NULL) {
+ fprintf(stderr, "No input file specified!\n");
+ HelpShort();
+ goto Error;
+ }
+
+ if (use_lossless_preset == 1) {
+ if (!WebPConfigLosslessPreset(&config, lossless_preset)) {
+ fprintf(stderr, "Invalid lossless preset (-z %d)\n", lossless_preset);
+ goto Error;
+ }
+ }
+
+ // Check for unsupported command line options for lossless mode and log
+ // warning for such options.
+ if (!quiet && config.lossless == 1) {
+ if (config.target_size > 0 || config.target_PSNR > 0) {
+ fprintf(stderr, "Encoding for specified size or PSNR is not supported"
+ " for lossless encoding. Ignoring such option(s)!\n");
+ }
+ if (config.partition_limit > 0) {
+ fprintf(stderr, "Partition limit option is not required for lossless"
+ " encoding. Ignoring this option!\n");
+ }
+ }
+ // If a target size or PSNR was given, but somehow the -pass option was
+ // omitted, force a reasonable value.
+ if (config.target_size > 0 || config.target_PSNR > 0) {
+ if (config.pass == 1) config.pass = 6;
+ }
+
+ if (!WebPValidateConfig(&config)) {
+ fprintf(stderr, "Error! Invalid configuration.\n");
+ goto Error;
+ }
+
+ // Read the input. We need to decide if we prefer ARGB or YUVA
+ // samples, depending on the expected compression mode (this saves
+ // some conversion steps).
+ picture.use_argb = (config.lossless || config.use_sharp_yuv ||
+ config.preprocessing > 0 ||
+ crop || (resize_w | resize_h) > 0);
+ if (verbose) {
+ StopwatchReset(&stop_watch);
+ }
+ if (!ReadPicture(in_file, &picture, keep_alpha,
+ (keep_metadata == 0) ? NULL : &metadata)) {
+ fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
+ goto Error;
+ }
+ picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
+
+ if (blend_alpha) {
+ WebPBlendAlpha(&picture, background_color);
+ }
+
+ if (verbose) {
+ const double read_time = StopwatchReadAndReset(&stop_watch);
+ fprintf(stderr, "Time to read input: %.3fs\n", read_time);
+ }
+
+ // Open the output
+ if (out_file != NULL) {
+ const int use_stdout = !strcmp(out_file, "-");
+ out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(out_file, "wb");
+ if (out == NULL) {
+ fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
+ goto Error;
+ } else {
+ if (!short_output && !quiet) {
+ fprintf(stderr, "Saving file '%s'\n", out_file);
+ }
+ }
+ if (keep_metadata == 0) {
+ picture.writer = MyWriter;
+ picture.custom_ptr = (void*)out;
+ } else {
+ picture.writer = WebPMemoryWrite;
+ picture.custom_ptr = (void*)&memory_writer;
+ }
+ } else {
+ out = NULL;
+ if (!quiet && !short_output) {
+ fprintf(stderr, "No output file specified (no -o flag). Encoding will\n");
+ fprintf(stderr, "be performed, but its results discarded.\n\n");
+ }
+ }
+ if (!quiet) {
+ picture.stats = &stats;
+ picture.user_data = (void*)in_file;
+ }
+
+ // Crop & resize.
+ if (verbose) {
+ StopwatchReset(&stop_watch);
+ }
+ if (crop != 0) {
+ // We use self-cropping using a view.
+ if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) {
+ fprintf(stderr, "Error! Cannot crop picture\n");
+ goto Error;
+ }
+ }
+ if ((resize_w | resize_h) > 0) {
+ if (!WebPPictureRescale(&picture, resize_w, resize_h)) {
+ fprintf(stderr, "Error! Cannot resize picture\n");
+ goto Error;
+ }
+ }
+ if (verbose && (crop != 0 || (resize_w | resize_h) > 0)) {
+ const double preproc_time = StopwatchReadAndReset(&stop_watch);
+ fprintf(stderr, "Time to crop/resize picture: %.3fs\n", preproc_time);
+ }
+
+ if (picture.extra_info_type > 0) {
+ AllocExtraInfo(&picture);
+ }
+ if (print_distortion >= 0) { // Save original picture for later comparison
+ WebPPictureCopy(&picture, &original_picture);
+ }
+
+ // Compress.
+ if (verbose) {
+ StopwatchReset(&stop_watch);
+ }
+ if (!WebPEncode(&config, &picture)) {
+ fprintf(stderr, "Error! Cannot encode picture as WebP\n");
+ fprintf(stderr, "Error code: %d (%s)\n",
+ picture.error_code, kErrorMessages[picture.error_code]);
+ goto Error;
+ }
+ if (verbose) {
+ const double encode_time = StopwatchReadAndReset(&stop_watch);
+ fprintf(stderr, "Time to encode picture: %.3fs\n", encode_time);
+ }
+
+ // Write info
+ if (dump_file) {
+ if (picture.use_argb) {
+ fprintf(stderr, "Warning: can't dump file (-d option) in lossless mode.");
+ } else if (!DumpPicture(&picture, dump_file)) {
+ fprintf(stderr, "Warning, couldn't dump picture %s\n", dump_file);
+ }
+ }
+
+ if (keep_metadata != 0) {
+ if (out != NULL) {
+ if (!WriteWebPWithMetadata(out, &picture, &memory_writer,
+ &metadata, keep_metadata, &metadata_written)) {
+ fprintf(stderr, "Error writing WebP file with metadata!\n");
+ goto Error;
+ }
+ } else { // output is disabled, just display the metadata stats.
+ const struct {
+ const MetadataPayload* const payload;
+ int flag;
+ } *iter, info[] = {
+ { &metadata.exif, METADATA_EXIF },
+ { &metadata.iccp, METADATA_ICC },
+ { &metadata.xmp, METADATA_XMP },
+ { NULL, 0 }
+ };
+ uint32_t unused1 = 0;
+ uint64_t unused2 = 0;
+
+ for (iter = info; iter->payload != NULL; ++iter) {
+ if (UpdateFlagsAndSize(iter->payload, !!(keep_metadata & iter->flag),
+ 0, &unused1, &unused2)) {
+ metadata_written |= iter->flag;
+ }
+ }
+ }
+ }
+
+ if (!quiet) {
+ if (!short_output || print_distortion < 0) {
+ if (config.lossless) {
+ PrintExtraInfoLossless(&picture, short_output, in_file);
+ } else {
+ PrintExtraInfoLossy(&picture, short_output, config.low_memory, in_file);
+ }
+ }
+ if (!short_output && picture.extra_info_type > 0) {
+ PrintMapInfo(&picture);
+ }
+ if (print_distortion >= 0) { // print distortion
+ static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
+ float values[5];
+ if (!WebPPictureDistortion(&picture, &original_picture,
+ print_distortion, values)) {
+ fprintf(stderr, "Error while computing the distortion.\n");
+ goto Error;
+ }
+ if (!short_output) {
+ fprintf(stderr, "%s: ", distortion_names[print_distortion]);
+ fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f Total:%.2f\n",
+ values[0], values[1], values[2], values[3], values[4]);
+ } else {
+ fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
+ }
+ }
+ if (!short_output) {
+ PrintMetadataInfo(&metadata, metadata_written);
+ }
+ }
+ return_value = 0;
+
+ Error:
+ WebPMemoryWriterClear(&memory_writer);
+ free(picture.extra_info);
+ MetadataFree(&metadata);
+ WebPPictureFree(&picture);
+ WebPPictureFree(&original_picture);
+ if (out != NULL && out != stdout) {
+ fclose(out);
+ }
+
+ return return_value;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/examples/dwebp.c b/src/third_party/libwebp/examples/dwebp.c
new file mode 100644
index 0000000..154069a
--- /dev/null
+++ b/src/third_party/libwebp/examples/dwebp.c
@@ -0,0 +1,417 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Command-line tool for decoding a WebP image.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../examples/example_util.h"
+#include "../imageio/image_enc.h"
+#include "../imageio/webpdec.h"
+#include "./stopwatch.h"
+
+static int verbose = 0;
+static int quiet = 0;
+#ifndef WEBP_DLL
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void* VP8GetCPUInfo; // opaque forward declaration.
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#endif // WEBP_DLL
+
+
+static int SaveOutput(const WebPDecBuffer* const buffer,
+ WebPOutputFileFormat format, const char* const out_file) {
+ const int use_stdout = (out_file != NULL) && !strcmp(out_file, "-");
+ int ok = 1;
+ Stopwatch stop_watch;
+
+ if (verbose) {
+ StopwatchReset(&stop_watch);
+ }
+ ok = WebPSaveImage(buffer, format, out_file);
+
+ if (ok) {
+ if (!quiet) {
+ if (use_stdout) {
+ fprintf(stderr, "Saved to stdout\n");
+ } else {
+ fprintf(stderr, "Saved file %s\n", out_file);
+ }
+ }
+ if (verbose) {
+ const double write_time = StopwatchReadAndReset(&stop_watch);
+ fprintf(stderr, "Time to write output: %.3fs\n", write_time);
+ }
+ } else {
+ if (use_stdout) {
+ fprintf(stderr, "Error writing to stdout !!\n");
+ } else {
+ fprintf(stderr, "Error writing file %s !!\n", out_file);
+ }
+ }
+ return ok;
+}
+
+static void Help(void) {
+ printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
+ "Decodes the WebP image file to PNG format [Default]\n"
+ "Use following options to convert into alternate image formats:\n"
+ " -pam ......... save the raw RGBA samples as a color PAM\n"
+ " -ppm ......... save the raw RGB samples as a color PPM\n"
+ " -bmp ......... save as uncompressed BMP format\n"
+ " -tiff ........ save as uncompressed TIFF format\n"
+ " -pgm ......... save the raw YUV samples as a grayscale PGM\n"
+ " file with IMC4 layout\n"
+ " -yuv ......... save the raw YUV samples in flat layout\n"
+ "\n"
+ " Other options are:\n"
+ " -version ..... print version number and exit\n"
+ " -nofancy ..... don't use the fancy YUV420 upscaler\n"
+ " -nofilter .... disable in-loop filtering\n"
+ " -nodither .... disable dithering\n"
+ " -dither <d> .. dithering strength (in 0..100)\n"
+ " -alpha_dither use alpha-plane dithering if needed\n"
+ " -mt .......... use multi-threading\n"
+ " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
+ " -resize <w> <h> ......... scale the output (*after* any cropping)\n"
+ " -flip ........ flip the output vertically\n"
+ " -alpha ....... only save the alpha plane\n"
+ " -incremental . use incremental decoding (useful for tests)\n"
+ " -h ........... this help message\n"
+ " -v ........... verbose (e.g. print encoding/decoding times)\n"
+ " -quiet ....... quiet mode, don't print anything\n"
+#ifndef WEBP_DLL
+ " -noasm ....... disable all assembly optimizations\n"
+#endif
+ );
+}
+
+static const char* const kFormatType[] = {
+ "unspecified", "lossy", "lossless"
+};
+
+static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
+ WebPOutputFileFormat format,
+ int use_external_memory) {
+ uint8_t* external_buffer = NULL;
+ WebPDecBuffer* const output_buffer = &config->output;
+ int w = config->input.width;
+ int h = config->input.height;
+ if (config->options.use_scaling) {
+ w = config->options.scaled_width;
+ h = config->options.scaled_height;
+ } else if (config->options.use_cropping) {
+ w = config->options.crop_width;
+ h = config->options.crop_height;
+ }
+ if (format >= RGB && format <= rgbA_4444) {
+ const int bpp = (format == RGB || format == BGR) ? 3
+ : (format == RGBA_4444 || format == rgbA_4444 ||
+ format == RGB_565) ? 2
+ : 4;
+ uint32_t stride = bpp * w + 7; // <- just for exercising
+ external_buffer = (uint8_t*)malloc(stride * h);
+ if (external_buffer == NULL) return NULL;
+ output_buffer->u.RGBA.stride = stride;
+ output_buffer->u.RGBA.size = stride * h;
+ output_buffer->u.RGBA.rgba = external_buffer;
+ } else { // YUV and YUVA
+ const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace);
+ uint8_t* tmp;
+ uint32_t stride = w + 3;
+ uint32_t uv_stride = (w + 1) / 2 + 13;
+ uint32_t total_size = stride * h * (has_alpha ? 2 : 1)
+ + 2 * uv_stride * (h + 1) / 2;
+ assert(format >= YUV && format <= YUVA);
+ external_buffer = (uint8_t*)malloc(total_size);
+ if (external_buffer == NULL) return NULL;
+ tmp = external_buffer;
+ output_buffer->u.YUVA.y = tmp;
+ output_buffer->u.YUVA.y_stride = stride;
+ output_buffer->u.YUVA.y_size = stride * h;
+ tmp += output_buffer->u.YUVA.y_size;
+ if (has_alpha) {
+ output_buffer->u.YUVA.a = tmp;
+ output_buffer->u.YUVA.a_stride = stride;
+ output_buffer->u.YUVA.a_size = stride * h;
+ tmp += output_buffer->u.YUVA.a_size;
+ } else {
+ output_buffer->u.YUVA.a = NULL;
+ output_buffer->u.YUVA.a_stride = 0;
+ }
+ output_buffer->u.YUVA.u = tmp;
+ output_buffer->u.YUVA.u_stride = uv_stride;
+ output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2;
+ tmp += output_buffer->u.YUVA.u_size;
+
+ output_buffer->u.YUVA.v = tmp;
+ output_buffer->u.YUVA.v_stride = uv_stride;
+ output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2;
+ tmp += output_buffer->u.YUVA.v_size;
+ assert(tmp <= external_buffer + total_size);
+ }
+ output_buffer->is_external_memory = use_external_memory;
+ return external_buffer;
+}
+
+int main(int argc, const char *argv[]) {
+ int ok = 0;
+ const char *in_file = NULL;
+ const char *out_file = NULL;
+
+ WebPDecoderConfig config;
+ WebPDecBuffer* const output_buffer = &config.output;
+ WebPBitstreamFeatures* const bitstream = &config.input;
+ WebPOutputFileFormat format = PNG;
+ uint8_t* external_buffer = NULL;
+ int use_external_memory = 0;
+ const uint8_t* data = NULL;
+
+ int incremental = 0;
+ int c;
+
+ if (!WebPInitDecoderConfig(&config)) {
+ fprintf(stderr, "Library version mismatch!\n");
+ return -1;
+ }
+
+ for (c = 1; c < argc; ++c) {
+ int parse_error = 0;
+ if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ Help();
+ return 0;
+ } else if (!strcmp(argv[c], "-o") && c < argc - 1) {
+ out_file = argv[++c];
+ } else if (!strcmp(argv[c], "-alpha")) {
+ format = ALPHA_PLANE_ONLY;
+ } else if (!strcmp(argv[c], "-nofancy")) {
+ config.options.no_fancy_upsampling = 1;
+ } else if (!strcmp(argv[c], "-nofilter")) {
+ config.options.bypass_filtering = 1;
+ } else if (!strcmp(argv[c], "-pam")) {
+ format = PAM;
+ } else if (!strcmp(argv[c], "-ppm")) {
+ format = PPM;
+ } else if (!strcmp(argv[c], "-bmp")) {
+ format = BMP;
+ } else if (!strcmp(argv[c], "-tiff")) {
+ format = TIFF;
+ } else if (!strcmp(argv[c], "-quiet")) {
+ quiet = 1;
+ } else if (!strcmp(argv[c], "-version")) {
+ const int version = WebPGetDecoderVersion();
+ printf("%d.%d.%d\n",
+ (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+ return 0;
+ } else if (!strcmp(argv[c], "-pgm")) {
+ format = PGM;
+ } else if (!strcmp(argv[c], "-yuv")) {
+ format = RAW_YUV;
+ } else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) {
+ const char* const fmt = argv[++c];
+ if (!strcmp(fmt, "RGB")) format = RGB;
+ else if (!strcmp(fmt, "RGBA")) format = RGBA;
+ else if (!strcmp(fmt, "BGR")) format = BGR;
+ else if (!strcmp(fmt, "BGRA")) format = BGRA;
+ else if (!strcmp(fmt, "ARGB")) format = ARGB;
+ else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444;
+ else if (!strcmp(fmt, "RGB_565")) format = RGB_565;
+ else if (!strcmp(fmt, "rgbA")) format = rgbA;
+ else if (!strcmp(fmt, "bgrA")) format = bgrA;
+ else if (!strcmp(fmt, "Argb")) format = Argb;
+ else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444;
+ else if (!strcmp(fmt, "YUV")) format = YUV;
+ else if (!strcmp(fmt, "YUVA")) format = YUVA;
+ else {
+ fprintf(stderr, "Can't parse pixel_format %s\n", fmt);
+ parse_error = 1;
+ }
+ } else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) {
+ use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error);
+ parse_error |= (use_external_memory > 2 || use_external_memory < 0);
+ if (parse_error) {
+ fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]);
+ }
+ } else if (!strcmp(argv[c], "-mt")) {
+ config.options.use_threads = 1;
+ } else if (!strcmp(argv[c], "-alpha_dither")) {
+ config.options.alpha_dithering_strength = 100;
+ } else if (!strcmp(argv[c], "-nodither")) {
+ config.options.dithering_strength = 0;
+ } else if (!strcmp(argv[c], "-dither") && c < argc - 1) {
+ config.options.dithering_strength =
+ ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
+ config.options.use_cropping = 1;
+ config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error);
+ config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error);
+ config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error);
+ config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) &&
+ c < argc - 2) { // '-scale' is left for compatibility
+ config.options.use_scaling = 1;
+ config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error);
+ config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-flip")) {
+ config.options.flip = 1;
+ } else if (!strcmp(argv[c], "-v")) {
+ verbose = 1;
+#ifndef WEBP_DLL
+ } else if (!strcmp(argv[c], "-noasm")) {
+ VP8GetCPUInfo = NULL;
+#endif
+ } else if (!strcmp(argv[c], "-incremental")) {
+ incremental = 1;
+ } else if (!strcmp(argv[c], "--")) {
+ if (c < argc - 1) in_file = argv[++c];
+ break;
+ } else if (argv[c][0] == '-') {
+ fprintf(stderr, "Unknown option '%s'\n", argv[c]);
+ Help();
+ return -1;
+ } else {
+ in_file = argv[c];
+ }
+
+ if (parse_error) {
+ Help();
+ return -1;
+ }
+ }
+
+ if (in_file == NULL) {
+ fprintf(stderr, "missing input file!!\n");
+ Help();
+ return -1;
+ }
+
+ if (quiet) verbose = 0;
+
+ {
+ VP8StatusCode status = VP8_STATUS_OK;
+ size_t data_size = 0;
+ if (!LoadWebP(in_file, &data, &data_size, bitstream)) {
+ return -1;
+ }
+
+ switch (format) {
+ case PNG:
+#ifdef HAVE_WINCODEC_H
+ output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
+#else
+ output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
+#endif
+ break;
+ case PAM:
+ output_buffer->colorspace = MODE_RGBA;
+ break;
+ case PPM:
+ output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
+ break;
+ case BMP:
+ output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
+ break;
+ case TIFF:
+ output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
+ break;
+ case PGM:
+ case RAW_YUV:
+ output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
+ break;
+ case ALPHA_PLANE_ONLY:
+ output_buffer->colorspace = MODE_YUVA;
+ break;
+ // forced modes:
+ case RGB: output_buffer->colorspace = MODE_RGB; break;
+ case RGBA: output_buffer->colorspace = MODE_RGBA; break;
+ case BGR: output_buffer->colorspace = MODE_BGR; break;
+ case BGRA: output_buffer->colorspace = MODE_BGRA; break;
+ case ARGB: output_buffer->colorspace = MODE_ARGB; break;
+ case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break;
+ case RGB_565: output_buffer->colorspace = MODE_RGB_565; break;
+ case rgbA: output_buffer->colorspace = MODE_rgbA; break;
+ case bgrA: output_buffer->colorspace = MODE_bgrA; break;
+ case Argb: output_buffer->colorspace = MODE_Argb; break;
+ case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break;
+ case YUV: output_buffer->colorspace = MODE_YUV; break;
+ case YUVA: output_buffer->colorspace = MODE_YUVA; break;
+ default: goto Exit;
+ }
+
+ if (use_external_memory > 0 && format >= RGB) {
+ external_buffer = AllocateExternalBuffer(&config, format,
+ use_external_memory);
+ if (external_buffer == NULL) goto Exit;
+ }
+
+ {
+ Stopwatch stop_watch;
+ if (verbose) StopwatchReset(&stop_watch);
+
+ if (incremental) {
+ status = DecodeWebPIncremental(data, data_size, &config);
+ } else {
+ status = DecodeWebP(data, data_size, &config);
+ }
+ if (verbose) {
+ const double decode_time = StopwatchReadAndReset(&stop_watch);
+ fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
+ }
+ }
+
+ ok = (status == VP8_STATUS_OK);
+ if (!ok) {
+ PrintWebPError(in_file, status);
+ goto Exit;
+ }
+ }
+
+ if (out_file != NULL) {
+ if (!quiet) {
+ fprintf(stderr, "Decoded %s. Dimensions: %d x %d %s. Format: %s. "
+ "Now saving...\n",
+ in_file, output_buffer->width, output_buffer->height,
+ bitstream->has_alpha ? " (with alpha)" : "",
+ kFormatType[bitstream->format]);
+ }
+ ok = SaveOutput(output_buffer, format, out_file);
+ } else {
+ if (!quiet) {
+ fprintf(stderr, "File %s can be decoded "
+ "(dimensions: %d x %d %s. Format: %s).\n",
+ in_file, output_buffer->width, output_buffer->height,
+ bitstream->has_alpha ? " (with alpha)" : "",
+ kFormatType[bitstream->format]);
+ fprintf(stderr, "Nothing written; "
+ "use -o flag to save the result as e.g. PNG.\n");
+ }
+ }
+ Exit:
+ WebPFreeDecBuffer(output_buffer);
+ free((void*)external_buffer);
+ free((void*)data);
+ return ok ? 0 : -1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/examples/example_util.c b/src/third_party/libwebp/examples/example_util.c
new file mode 100644
index 0000000..825a123
--- /dev/null
+++ b/src/third_party/libwebp/examples/example_util.c
@@ -0,0 +1,127 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utility functions used by the example programs.
+//
+
+#include "./example_util.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/mux_types.h"
+#include "../imageio/imageio_util.h"
+
+//------------------------------------------------------------------------------
+// String parsing
+
+uint32_t ExUtilGetUInt(const char* const v, int base, int* const error) {
+ char* end = NULL;
+ const uint32_t n = (v != NULL) ? (uint32_t)strtoul(v, &end, base) : 0u;
+ if (end == v && error != NULL && !*error) {
+ *error = 1;
+ fprintf(stderr, "Error! '%s' is not an integer.\n",
+ (v != NULL) ? v : "(null)");
+ }
+ return n;
+}
+
+int ExUtilGetInt(const char* const v, int base, int* const error) {
+ return (int)ExUtilGetUInt(v, base, error);
+}
+
+int ExUtilGetInts(const char* v, int base, int max_output, int output[]) {
+ int n, error = 0;
+ for (n = 0; v != NULL && n < max_output; ++n) {
+ const int value = ExUtilGetInt(v, base, &error);
+ if (error) return -1;
+ output[n] = value;
+ v = strchr(v, ',');
+ if (v != NULL) ++v; // skip over the trailing ','
+ }
+ return n;
+}
+
+float ExUtilGetFloat(const char* const v, int* const error) {
+ char* end = NULL;
+ const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f;
+ if (end == v && error != NULL && !*error) {
+ *error = 1;
+ fprintf(stderr, "Error! '%s' is not a floating point number.\n",
+ (v != NULL) ? v : "(null)");
+ }
+ return f;
+}
+
+//------------------------------------------------------------------------------
+
+static void ResetCommandLineArguments(int argc, const char* argv[],
+ CommandLineArguments* const args) {
+ assert(args != NULL);
+ args->argc_ = argc;
+ args->argv_ = argv;
+ args->own_argv_ = 0;
+ WebPDataInit(&args->argv_data_);
+}
+
+void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args) {
+ if (args != NULL) {
+ if (args->own_argv_) {
+ free((void*)args->argv_);
+ WebPDataClear(&args->argv_data_);
+ }
+ ResetCommandLineArguments(0, NULL, args);
+ }
+}
+
+#define MAX_ARGC 16384
+int ExUtilInitCommandLineArguments(int argc, const char* argv[],
+ CommandLineArguments* const args) {
+ if (args == NULL || argv == NULL) return 0;
+ ResetCommandLineArguments(argc, argv, args);
+ if (argc == 1 && argv[0][0] != '-') {
+ char* cur;
+ const char sep[] = " \t\r\n\f\v";
+ if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data_)) {
+ return 0;
+ }
+ args->own_argv_ = 1;
+ args->argv_ = (const char**)malloc(MAX_ARGC * sizeof(*args->argv_));
+ if (args->argv_ == NULL) return 0;
+
+ argc = 0;
+ for (cur = strtok((char*)args->argv_data_.bytes, sep);
+ cur != NULL;
+ cur = strtok(NULL, sep)) {
+ if (argc == MAX_ARGC) {
+ fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC);
+ return 0;
+ }
+ assert(strlen(cur) != 0);
+ args->argv_[argc++] = cur;
+ }
+ args->argc_ = argc;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+int ExUtilReadFileToWebPData(const char* const filename,
+ WebPData* const webp_data) {
+ const uint8_t* data;
+ size_t size;
+ if (webp_data == NULL) return 0;
+ if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
+ webp_data->bytes = data;
+ webp_data->size = size;
+ return 1;
+}
diff --git a/src/third_party/libwebp/examples/example_util.h b/src/third_party/libwebp/examples/example_util.h
new file mode 100644
index 0000000..fe762a4
--- /dev/null
+++ b/src/third_party/libwebp/examples/example_util.h
@@ -0,0 +1,70 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utility functions used by the example programs.
+//
+
+#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_
+#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_
+
+#include "webp/types.h"
+#include "webp/mux_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// String parsing
+
+// Parses 'v' using strto(ul|l|d)(). If error is non-NULL, '*error' is set to
+// true on failure while on success it is left unmodified to allow chaining of
+// calls. An error is only printed on the first occurrence.
+uint32_t ExUtilGetUInt(const char* const v, int base, int* const error);
+int ExUtilGetInt(const char* const v, int base, int* const error);
+float ExUtilGetFloat(const char* const v, int* const error);
+
+// This variant of ExUtilGetInt() will parse multiple integers from a
+// comma-separated list. Up to 'max_output' integers are parsed.
+// The result is placed in the output[] array, and the number of integers
+// actually parsed is returned, or -1 if an error occurred.
+int ExUtilGetInts(const char* v, int base, int max_output, int output[]);
+
+// Reads a file named 'filename' into a WebPData structure. The content of
+// webp_data is overwritten. Returns false in case of error.
+int ExUtilReadFileToWebPData(const char* const filename,
+ WebPData* const webp_data);
+
+//------------------------------------------------------------------------------
+// Command-line arguments
+
+typedef struct {
+ int argc_;
+ const char** argv_;
+ WebPData argv_data_;
+ int own_argv_;
+} CommandLineArguments;
+
+// Initializes the structure from the command-line parameters. If there is
+// only one parameter and it does not start with a '-', then it is assumed to
+// be a file name. This file will be read and tokenized into command-line
+// arguments. The content of 'args' is overwritten.
+// Returns false in case of error (memory allocation failure, non
+// existing file, too many arguments, ...).
+int ExUtilInitCommandLineArguments(int argc, const char* argv[],
+ CommandLineArguments* const args);
+
+// Deallocate all memory and reset 'args'.
+void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_EXAMPLES_EXAMPLE_UTIL_H_
diff --git a/src/third_party/libwebp/examples/gif2webp.c b/src/third_party/libwebp/examples/gif2webp.c
new file mode 100644
index 0000000..b61f273
--- /dev/null
+++ b/src/third_party/libwebp/examples/gif2webp.c
@@ -0,0 +1,607 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// simple tool to convert animated GIFs to WebP
+//
+// Authors: Skal (pascal.massimino@gmail.com)
+// Urvang (urvang@google.com)
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#ifdef WEBP_HAVE_GIF
+
+#if defined(HAVE_UNISTD_H) && HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gif_lib.h>
+#include "webp/encode.h"
+#include "webp/mux.h"
+#include "../examples/example_util.h"
+#include "../imageio/imageio_util.h"
+#include "./gifdec.h"
+
+#if !defined(STDIN_FILENO)
+#define STDIN_FILENO 0
+#endif
+
+//------------------------------------------------------------------------------
+
+static int transparent_index = GIF_INDEX_INVALID; // Opaque by default.
+
+static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
+ "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
+ "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
+};
+
+static const char* ErrorString(WebPMuxError err) {
+ assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
+ return kErrorMessages[-err];
+}
+
+enum {
+ METADATA_ICC = (1 << 0),
+ METADATA_XMP = (1 << 1),
+ METADATA_ALL = METADATA_ICC | METADATA_XMP
+};
+
+//------------------------------------------------------------------------------
+
+static void Help(void) {
+ printf("Usage:\n");
+ printf(" gif2webp [options] gif_file -o webp_file\n");
+ printf("Options:\n");
+ printf(" -h / -help ............. this help\n");
+ printf(" -lossy ................. encode image using lossy compression\n");
+ printf(" -mixed ................. for each frame in the image, pick lossy\n"
+ " or lossless compression heuristically\n");
+ printf(" -q <float> ............. quality factor (0:small..100:big)\n");
+ printf(" -m <int> ............... compression method (0=fast, 6=slowest)\n");
+ printf(" -min_size .............. minimize output size (default:off)\n"
+ " lossless compression by default; can be\n"
+ " combined with -q, -m, -lossy or -mixed\n"
+ " options\n");
+ printf(" -kmin <int> ............ min distance between key frames\n");
+ printf(" -kmax <int> ............ max distance between key frames\n");
+ printf(" -f <int> ............... filter strength (0=off..100)\n");
+ printf(" -metadata <string> ..... comma separated list of metadata to\n");
+ printf(" ");
+ printf("copy from the input to the output if present\n");
+ printf(" ");
+ printf("Valid values: all, none, icc, xmp (default)\n");
+ printf(" -loop_compatibility .... use compatibility mode for Chrome\n");
+ printf(" version prior to M62 (inclusive)\n");
+ printf(" -mt .................... use multi-threading if available\n");
+ printf("\n");
+ printf(" -version ............... print version number and exit\n");
+ printf(" -v ..................... verbose\n");
+ printf(" -quiet ................. don't print anything\n");
+ printf("\n");
+}
+
+//------------------------------------------------------------------------------
+
+int main(int argc, const char *argv[]) {
+ int verbose = 0;
+ int gif_error = GIF_ERROR;
+ WebPMuxError err = WEBP_MUX_OK;
+ int ok = 0;
+ const char *in_file = NULL, *out_file = NULL;
+ FILE* out = NULL;
+ GifFileType* gif = NULL;
+ int frame_duration = 0;
+ int frame_timestamp = 0;
+ GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE;
+
+ WebPPicture frame; // Frame rectangle only (not disposed).
+ WebPPicture curr_canvas; // Not disposed.
+ WebPPicture prev_canvas; // Disposed.
+
+ WebPAnimEncoder* enc = NULL;
+ WebPAnimEncoderOptions enc_options;
+ WebPConfig config;
+
+ int frame_number = 0; // Whether we are processing the first frame.
+ int done;
+ int c;
+ int quiet = 0;
+ WebPData webp_data;
+
+ int keep_metadata = METADATA_XMP; // ICC not output by default.
+ WebPData icc_data;
+ int stored_icc = 0; // Whether we have already stored an ICC profile.
+ WebPData xmp_data;
+ int stored_xmp = 0; // Whether we have already stored an XMP profile.
+ int loop_count = 0; // default: infinite
+ int stored_loop_count = 0; // Whether we have found an explicit loop count.
+ int loop_compatibility = 0;
+ WebPMux* mux = NULL;
+
+ int default_kmin = 1; // Whether to use default kmin value.
+ int default_kmax = 1;
+
+ if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) ||
+ !WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) ||
+ !WebPPictureInit(&prev_canvas)) {
+ fprintf(stderr, "Error! Version mismatch!\n");
+ return -1;
+ }
+ config.lossless = 1; // Use lossless compression by default.
+
+ WebPDataInit(&webp_data);
+ WebPDataInit(&icc_data);
+ WebPDataInit(&xmp_data);
+
+ if (argc == 1) {
+ Help();
+ return 0;
+ }
+
+ for (c = 1; c < argc; ++c) {
+ int parse_error = 0;
+ if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ Help();
+ return 0;
+ } else if (!strcmp(argv[c], "-o") && c < argc - 1) {
+ out_file = argv[++c];
+ } else if (!strcmp(argv[c], "-lossy")) {
+ config.lossless = 0;
+ } else if (!strcmp(argv[c], "-mixed")) {
+ enc_options.allow_mixed = 1;
+ config.lossless = 0;
+ } else if (!strcmp(argv[c], "-loop_compatibility")) {
+ loop_compatibility = 1;
+ } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
+ config.quality = ExUtilGetFloat(argv[++c], &parse_error);
+ } else if (!strcmp(argv[c], "-m") && c < argc - 1) {
+ config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-min_size")) {
+ enc_options.minimize_size = 1;
+ } else if (!strcmp(argv[c], "-kmax") && c < argc - 1) {
+ enc_options.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
+ default_kmax = 0;
+ } else if (!strcmp(argv[c], "-kmin") && c < argc - 1) {
+ enc_options.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
+ default_kmin = 0;
+ } else if (!strcmp(argv[c], "-f") && c < argc - 1) {
+ config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
+ static const struct {
+ const char* option;
+ int flag;
+ } kTokens[] = {
+ { "all", METADATA_ALL },
+ { "none", 0 },
+ { "icc", METADATA_ICC },
+ { "xmp", METADATA_XMP },
+ };
+ const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens);
+ const char* start = argv[++c];
+ const char* const end = start + strlen(start);
+
+ keep_metadata = 0;
+ while (start < end) {
+ size_t i;
+ const char* token = strchr(start, ',');
+ if (token == NULL) token = end;
+
+ for (i = 0; i < kNumTokens; ++i) {
+ if ((size_t)(token - start) == strlen(kTokens[i].option) &&
+ !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
+ if (kTokens[i].flag != 0) {
+ keep_metadata |= kTokens[i].flag;
+ } else {
+ keep_metadata = 0;
+ }
+ break;
+ }
+ }
+ if (i == kNumTokens) {
+ fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
+ (int)(token - start), start);
+ Help();
+ return -1;
+ }
+ start = token + 1;
+ }
+ } else if (!strcmp(argv[c], "-mt")) {
+ ++config.thread_level;
+ } else if (!strcmp(argv[c], "-version")) {
+ const int enc_version = WebPGetEncoderVersion();
+ const int mux_version = WebPGetMuxVersion();
+ printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
+ (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
+ enc_version & 0xff, (mux_version >> 16) & 0xff,
+ (mux_version >> 8) & 0xff, mux_version & 0xff);
+ return 0;
+ } else if (!strcmp(argv[c], "-quiet")) {
+ quiet = 1;
+ enc_options.verbose = 0;
+ } else if (!strcmp(argv[c], "-v")) {
+ verbose = 1;
+ enc_options.verbose = 1;
+ } else if (!strcmp(argv[c], "--")) {
+ if (c < argc - 1) in_file = argv[++c];
+ break;
+ } else if (argv[c][0] == '-') {
+ fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
+ Help();
+ return -1;
+ } else {
+ in_file = argv[c];
+ }
+
+ if (parse_error) {
+ Help();
+ return -1;
+ }
+ }
+
+ // Appropriate default kmin, kmax values for lossy and lossless.
+ if (default_kmin) {
+ enc_options.kmin = config.lossless ? 9 : 3;
+ }
+ if (default_kmax) {
+ enc_options.kmax = config.lossless ? 17 : 5;
+ }
+
+ if (!WebPValidateConfig(&config)) {
+ fprintf(stderr, "Error! Invalid configuration.\n");
+ goto End;
+ }
+
+ if (in_file == NULL) {
+ fprintf(stderr, "No input file specified!\n");
+ Help();
+ goto End;
+ }
+
+ // Start the decoder object
+#if LOCAL_GIF_PREREQ(5,0)
+ gif = !strcmp(in_file, "-") ? DGifOpenFileHandle(STDIN_FILENO, &gif_error)
+ : DGifOpenFileName(in_file, &gif_error);
+#else
+ gif = !strcmp(in_file, "-") ? DGifOpenFileHandle(STDIN_FILENO)
+ : DGifOpenFileName(in_file);
+#endif
+ if (gif == NULL) goto End;
+
+ // Loop over GIF images
+ done = 0;
+ do {
+ GifRecordType type;
+ if (DGifGetRecordType(gif, &type) == GIF_ERROR) goto End;
+
+ switch (type) {
+ case IMAGE_DESC_RECORD_TYPE: {
+ GIFFrameRect gif_rect;
+ GifImageDesc* const image_desc = &gif->Image;
+
+ if (!DGifGetImageDesc(gif)) goto End;
+
+ if (frame_number == 0) {
+ if (verbose) {
+ printf("Canvas screen: %d x %d\n", gif->SWidth, gif->SHeight);
+ }
+ // Fix some broken GIF global headers that report
+ // 0 x 0 screen dimension.
+ if (gif->SWidth == 0 || gif->SHeight == 0) {
+ image_desc->Left = 0;
+ image_desc->Top = 0;
+ gif->SWidth = image_desc->Width;
+ gif->SHeight = image_desc->Height;
+ if (gif->SWidth <= 0 || gif->SHeight <= 0) {
+ goto End;
+ }
+ if (verbose) {
+ printf("Fixed canvas screen dimension to: %d x %d\n",
+ gif->SWidth, gif->SHeight);
+ }
+ }
+ // Allocate current buffer.
+ frame.width = gif->SWidth;
+ frame.height = gif->SHeight;
+ frame.use_argb = 1;
+ if (!WebPPictureAlloc(&frame)) goto End;
+ GIFClearPic(&frame, NULL);
+ WebPPictureCopy(&frame, &curr_canvas);
+ WebPPictureCopy(&frame, &prev_canvas);
+
+ // Background color.
+ GIFGetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
+ transparent_index,
+ &enc_options.anim_params.bgcolor);
+
+ // Initialize encoder.
+ enc = WebPAnimEncoderNew(curr_canvas.width, curr_canvas.height,
+ &enc_options);
+ if (enc == NULL) {
+ fprintf(stderr,
+ "Error! Could not create encoder object. Possibly due to "
+ "a memory error.\n");
+ goto End;
+ }
+ }
+
+ // Some even more broken GIF can have sub-rect with zero width/height.
+ if (image_desc->Width == 0 || image_desc->Height == 0) {
+ image_desc->Width = gif->SWidth;
+ image_desc->Height = gif->SHeight;
+ }
+
+ if (!GIFReadFrame(gif, transparent_index, &gif_rect, &frame)) {
+ goto End;
+ }
+ // Blend frame rectangle with previous canvas to compose full canvas.
+ // Note that 'curr_canvas' is same as 'prev_canvas' at this point.
+ GIFBlendFrames(&frame, &gif_rect, &curr_canvas);
+
+ if (!WebPAnimEncoderAdd(enc, &curr_canvas, frame_timestamp, &config)) {
+ fprintf(stderr, "Error while adding frame #%d: %s\n", frame_number,
+ WebPAnimEncoderGetError(enc));
+ goto End;
+ } else {
+ ++frame_number;
+ }
+
+ // Update canvases.
+ GIFDisposeFrame(orig_dispose, &gif_rect, &prev_canvas, &curr_canvas);
+ GIFCopyPixels(&curr_canvas, &prev_canvas);
+
+ // Force frames with a small or no duration to 100ms to be consistent
+ // with web browsers and other transcoding tools. This also avoids
+ // incorrect durations between frames when padding frames are
+ // discarded.
+ if (frame_duration <= 10) {
+ frame_duration = 100;
+ }
+
+ // Update timestamp (for next frame).
+ frame_timestamp += frame_duration;
+
+ // In GIF, graphic control extensions are optional for a frame, so we
+ // may not get one before reading the next frame. To handle this case,
+ // we reset frame properties to reasonable defaults for the next frame.
+ orig_dispose = GIF_DISPOSE_NONE;
+ frame_duration = 0;
+ transparent_index = GIF_INDEX_INVALID;
+ break;
+ }
+ case EXTENSION_RECORD_TYPE: {
+ int extension;
+ GifByteType *data = NULL;
+ if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) {
+ goto End;
+ }
+ if (data == NULL) continue;
+
+ switch (extension) {
+ case COMMENT_EXT_FUNC_CODE: {
+ break; // Do nothing for now.
+ }
+ case GRAPHICS_EXT_FUNC_CODE: {
+ if (!GIFReadGraphicsExtension(data, &frame_duration, &orig_dispose,
+ &transparent_index)) {
+ goto End;
+ }
+ break;
+ }
+ case PLAINTEXT_EXT_FUNC_CODE: {
+ break;
+ }
+ case APPLICATION_EXT_FUNC_CODE: {
+ if (data[0] != 11) break; // Chunk is too short
+ if (!memcmp(data + 1, "NETSCAPE2.0", 11) ||
+ !memcmp(data + 1, "ANIMEXTS1.0", 11)) {
+ if (!GIFReadLoopCount(gif, &data, &loop_count)) {
+ goto End;
+ }
+ if (verbose) {
+ fprintf(stderr, "Loop count: %d\n", loop_count);
+ }
+ stored_loop_count = loop_compatibility ? (loop_count != 0) : 1;
+ } else { // An extension containing metadata.
+ // We only store the first encountered chunk of each type, and
+ // only if requested by the user.
+ const int is_xmp = (keep_metadata & METADATA_XMP) &&
+ !stored_xmp &&
+ !memcmp(data + 1, "XMP DataXMP", 11);
+ const int is_icc = (keep_metadata & METADATA_ICC) &&
+ !stored_icc &&
+ !memcmp(data + 1, "ICCRGBG1012", 11);
+ if (is_xmp || is_icc) {
+ if (!GIFReadMetadata(gif, &data,
+ is_xmp ? &xmp_data : &icc_data)) {
+ goto End;
+ }
+ if (is_icc) {
+ stored_icc = 1;
+ } else if (is_xmp) {
+ stored_xmp = 1;
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ break; // skip
+ }
+ }
+ while (data != NULL) {
+ if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End;
+ }
+ break;
+ }
+ case TERMINATE_RECORD_TYPE: {
+ done = 1;
+ break;
+ }
+ default: {
+ if (verbose) {
+ fprintf(stderr, "Skipping over unknown record type %d\n", type);
+ }
+ break;
+ }
+ }
+ } while (!done);
+
+ // Last NULL frame.
+ if (!WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL)) {
+ fprintf(stderr, "Error flushing WebP muxer.\n");
+ fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
+ }
+
+ if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
+ fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
+ goto End;
+ }
+
+ if (!loop_compatibility) {
+ if (!stored_loop_count) {
+ // if no loop-count element is seen, the default is '1' (loop-once)
+ // and we need to signal it explicitly in WebP. Note however that
+ // in case there's a single frame, we still don't need to store it.
+ if (frame_number > 1) {
+ stored_loop_count = 1;
+ loop_count = 1;
+ }
+ } else if (loop_count > 0) {
+ // adapt GIF's semantic to WebP's (except in the infinite-loop case)
+ loop_count += 1;
+ }
+ }
+ // loop_count of 0 is the default (infinite), so no need to signal it
+ if (loop_count == 0) stored_loop_count = 0;
+
+ if (stored_loop_count || stored_icc || stored_xmp) {
+ // Re-mux to add loop count and/or metadata as needed.
+ mux = WebPMuxCreate(&webp_data, 1);
+ if (mux == NULL) {
+ fprintf(stderr, "ERROR: Could not re-mux to add loop count/metadata.\n");
+ goto End;
+ }
+ WebPDataClear(&webp_data);
+
+ if (stored_loop_count) { // Update loop count.
+ WebPMuxAnimParams new_params;
+ err = WebPMuxGetAnimationParams(mux, &new_params);
+ if (err != WEBP_MUX_OK) {
+ fprintf(stderr, "ERROR (%s): Could not fetch loop count.\n",
+ ErrorString(err));
+ goto End;
+ }
+ new_params.loop_count = loop_count;
+ err = WebPMuxSetAnimationParams(mux, &new_params);
+ if (err != WEBP_MUX_OK) {
+ fprintf(stderr, "ERROR (%s): Could not update loop count.\n",
+ ErrorString(err));
+ goto End;
+ }
+ }
+
+ if (stored_icc) { // Add ICCP chunk.
+ err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1);
+ if (verbose) {
+ fprintf(stderr, "ICC size: %d\n", (int)icc_data.size);
+ }
+ if (err != WEBP_MUX_OK) {
+ fprintf(stderr, "ERROR (%s): Could not set ICC chunk.\n",
+ ErrorString(err));
+ goto End;
+ }
+ }
+
+ if (stored_xmp) { // Add XMP chunk.
+ err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1);
+ if (verbose) {
+ fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size);
+ }
+ if (err != WEBP_MUX_OK) {
+ fprintf(stderr, "ERROR (%s): Could not set XMP chunk.\n",
+ ErrorString(err));
+ goto End;
+ }
+ }
+
+ err = WebPMuxAssemble(mux, &webp_data);
+ if (err != WEBP_MUX_OK) {
+ fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add "
+ "loop count/metadata.\n", ErrorString(err));
+ goto End;
+ }
+ }
+
+ if (out_file != NULL) {
+ if (!ImgIoUtilWriteFile(out_file, webp_data.bytes, webp_data.size)) {
+ fprintf(stderr, "Error writing output file: %s\n", out_file);
+ goto End;
+ }
+ if (!quiet) {
+ if (!strcmp(out_file, "-")) {
+ fprintf(stderr, "Saved %d bytes to STDIO\n",
+ (int)webp_data.size);
+ } else {
+ fprintf(stderr, "Saved output file (%d bytes): %s\n",
+ (int)webp_data.size, out_file);
+ }
+ }
+ } else {
+ if (!quiet) {
+ fprintf(stderr, "Nothing written; use -o flag to save the result "
+ "(%d bytes).\n", (int)webp_data.size);
+ }
+ }
+
+ // All OK.
+ ok = 1;
+ gif_error = GIF_OK;
+
+ End:
+ WebPDataClear(&icc_data);
+ WebPDataClear(&xmp_data);
+ WebPMuxDelete(mux);
+ WebPDataClear(&webp_data);
+ WebPPictureFree(&frame);
+ WebPPictureFree(&curr_canvas);
+ WebPPictureFree(&prev_canvas);
+ WebPAnimEncoderDelete(enc);
+ if (out != NULL && out_file != NULL) fclose(out);
+
+ if (gif_error != GIF_OK) {
+ GIFDisplayError(gif, gif_error);
+ }
+ if (gif != NULL) {
+#if LOCAL_GIF_PREREQ(5,1)
+ DGifCloseFile(gif, &gif_error);
+#else
+ DGifCloseFile(gif);
+#endif
+ }
+
+ return !ok;
+}
+
+#else // !WEBP_HAVE_GIF
+
+int main(int argc, const char *argv[]) {
+ fprintf(stderr, "GIF support not enabled in %s.\n", argv[0]);
+ (void)argc;
+ return 0;
+}
+
+#endif
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/examples/gifdec.c b/src/third_party/libwebp/examples/gifdec.c
new file mode 100644
index 0000000..4219352
--- /dev/null
+++ b/src/third_party/libwebp/examples/gifdec.c
@@ -0,0 +1,416 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// GIF decode.
+
+#include "./gifdec.h"
+
+#include <stdio.h>
+
+#ifdef WEBP_HAVE_GIF
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "webp/mux_types.h"
+
+#define GIF_TRANSPARENT_COLOR 0x00000000u
+#define GIF_WHITE_COLOR 0xffffffffu
+#define GIF_TRANSPARENT_MASK 0x01
+#define GIF_DISPOSE_MASK 0x07
+#define GIF_DISPOSE_SHIFT 2
+
+// from utils/utils.h
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void WebPCopyPlane(const uint8_t* src, int src_stride,
+ uint8_t* dst, int dst_stride,
+ int width, int height);
+extern void WebPCopyPixels(const WebPPicture* const src,
+ WebPPicture* const dst);
+#ifdef __cplusplus
+}
+#endif
+
+void GIFGetBackgroundColor(const ColorMapObject* const color_map,
+ int bgcolor_index, int transparent_index,
+ uint32_t* const bgcolor) {
+ if (transparent_index != GIF_INDEX_INVALID &&
+ bgcolor_index == transparent_index) {
+ *bgcolor = GIF_TRANSPARENT_COLOR; // Special case.
+ } else if (color_map == NULL || color_map->Colors == NULL
+ || bgcolor_index >= color_map->ColorCount) {
+ *bgcolor = GIF_WHITE_COLOR;
+ fprintf(stderr,
+ "GIF decode warning: invalid background color index. Assuming "
+ "white background.\n");
+ } else {
+ const GifColorType color = color_map->Colors[bgcolor_index];
+ *bgcolor = (0xffu << 24)
+ | (color.Red << 16)
+ | (color.Green << 8)
+ | (color.Blue << 0);
+ }
+}
+
+int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
+ GIFDisposeMethod* const dispose,
+ int* const transparent_index) {
+ const int flags = buf[1];
+ const int dispose_raw = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
+ const int duration_raw = buf[2] | (buf[3] << 8); // In 10 ms units.
+ if (buf[0] != 4) return 0;
+ *duration = duration_raw * 10; // Duration is in 1 ms units.
+ switch (dispose_raw) {
+ case 3:
+ *dispose = GIF_DISPOSE_RESTORE_PREVIOUS;
+ break;
+ case 2:
+ *dispose = GIF_DISPOSE_BACKGROUND;
+ break;
+ case 1:
+ case 0:
+ default:
+ *dispose = GIF_DISPOSE_NONE;
+ break;
+ }
+ *transparent_index =
+ (flags & GIF_TRANSPARENT_MASK) ? buf[4] : GIF_INDEX_INVALID;
+ return 1;
+}
+
+static int Remap(const GifFileType* const gif, const uint8_t* const src,
+ int len, int transparent_index, uint32_t* dst) {
+ int i;
+ const GifColorType* colors;
+ const ColorMapObject* const cmap =
+ gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
+ if (cmap == NULL) return 1;
+ if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0;
+ colors = cmap->Colors;
+
+ for (i = 0; i < len; ++i) {
+ if (src[i] == transparent_index) {
+ dst[i] = GIF_TRANSPARENT_COLOR;
+ } else if (src[i] < cmap->ColorCount) {
+ const GifColorType c = colors[src[i]];
+ dst[i] = c.Blue | (c.Green << 8) | (c.Red << 16) | (0xffu << 24);
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int GIFReadFrame(GifFileType* const gif, int transparent_index,
+ GIFFrameRect* const gif_rect, WebPPicture* const picture) {
+ WebPPicture sub_image;
+ const GifImageDesc* const image_desc = &gif->Image;
+ uint32_t* dst = NULL;
+ uint8_t* tmp = NULL;
+ const GIFFrameRect rect = {
+ image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
+ };
+ const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
+ int ok = 0;
+ *gif_rect = rect;
+
+ if (memory_needed != (size_t)memory_needed || memory_needed > (4ULL << 32)) {
+ fprintf(stderr, "Image is too large (%d x %d).", rect.width, rect.height);
+ return 0;
+ }
+
+ // Use a view for the sub-picture:
+ if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
+ rect.width, rect.height, &sub_image)) {
+ fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
+ rect.width, rect.height, rect.x_offset, rect.y_offset);
+ return 0;
+ }
+ dst = sub_image.argb;
+
+ tmp = (uint8_t*)malloc(rect.width * sizeof(*tmp));
+ if (tmp == NULL) goto End;
+
+ if (image_desc->Interlace) { // Interlaced image.
+ // We need 4 passes, with the following offsets and jumps.
+ const int interlace_offsets[] = { 0, 4, 2, 1 };
+ const int interlace_jumps[] = { 8, 8, 4, 2 };
+ int pass;
+ for (pass = 0; pass < 4; ++pass) {
+ const size_t stride = (size_t)sub_image.argb_stride;
+ int y = interlace_offsets[pass];
+ uint32_t* row = dst + y * stride;
+ const size_t jump = interlace_jumps[pass] * stride;
+ for (; y < rect.height; y += interlace_jumps[pass], row += jump) {
+ if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
+ if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End;
+ }
+ }
+ } else { // Non-interlaced image.
+ int y;
+ uint32_t* ptr = dst;
+ for (y = 0; y < rect.height; ++y, ptr += sub_image.argb_stride) {
+ if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
+ if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End;
+ }
+ }
+ ok = 1;
+
+ End:
+ if (!ok) picture->error_code = sub_image.error_code;
+ WebPPictureFree(&sub_image);
+ free(tmp);
+ return ok;
+}
+
+int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
+ int* const loop_count) {
+ assert(!memcmp(*buf + 1, "NETSCAPE2.0", 11) ||
+ !memcmp(*buf + 1, "ANIMEXTS1.0", 11));
+ if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
+ return 0;
+ }
+ if (*buf == NULL) {
+ return 0; // Loop count sub-block missing.
+ }
+ if ((*buf)[0] < 3 || (*buf)[1] != 1) {
+ return 0; // wrong size/marker
+ }
+ *loop_count = (*buf)[2] | ((*buf)[3] << 8);
+ return 1;
+}
+
+int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
+ WebPData* const metadata) {
+ const int is_xmp = !memcmp(*buf + 1, "XMP DataXMP", 11);
+ const int is_icc = !memcmp(*buf + 1, "ICCRGBG1012", 11);
+ assert(is_xmp || is_icc);
+ (void)is_icc; // silence unused warning.
+ // Construct metadata from sub-blocks.
+ // Usual case (including ICC profile): In each sub-block, the
+ // first byte specifies its size in bytes (0 to 255) and the
+ // rest of the bytes contain the data.
+ // Special case for XMP data: In each sub-block, the first byte
+ // is also part of the XMP payload. XMP in GIF also has a 257
+ // byte padding data. See the XMP specification for details.
+ while (1) {
+ WebPData subblock;
+ const uint8_t* tmp;
+ if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
+ return 0;
+ }
+ if (*buf == NULL) break; // Finished.
+ subblock.size = is_xmp ? (*buf)[0] + 1 : (*buf)[0];
+ assert(subblock.size > 0);
+ subblock.bytes = is_xmp ? *buf : *buf + 1;
+ // Note: We store returned value in 'tmp' first, to avoid
+ // leaking old memory in metadata->bytes on error.
+ tmp = (uint8_t*)realloc((void*)metadata->bytes,
+ metadata->size + subblock.size);
+ if (tmp == NULL) {
+ return 0;
+ }
+ memcpy((void*)(tmp + metadata->size),
+ subblock.bytes, subblock.size);
+ metadata->bytes = tmp;
+ metadata->size += subblock.size;
+ }
+ if (is_xmp) {
+ // XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
+ const size_t xmp_pading_size = 257;
+ if (metadata->size > xmp_pading_size) {
+ metadata->size -= xmp_pading_size;
+ }
+ }
+ return 1;
+}
+
+static void ClearRectangle(WebPPicture* const picture,
+ int left, int top, int width, int height) {
+ int i, j;
+ const size_t stride = picture->argb_stride;
+ uint32_t* dst = picture->argb + top * stride + left;
+ for (j = 0; j < height; ++j, dst += stride) {
+ for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR;
+ }
+}
+
+void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
+ if (rect != NULL) {
+ ClearRectangle(pic, rect->x_offset, rect->y_offset,
+ rect->width, rect->height);
+ } else {
+ ClearRectangle(pic, 0, 0, pic->width, pic->height);
+ }
+}
+
+void GIFCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
+ WebPCopyPixels(src, dst);
+}
+
+void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
+ const WebPPicture* const prev_canvas,
+ WebPPicture* const curr_canvas) {
+ assert(rect != NULL);
+ if (dispose == GIF_DISPOSE_BACKGROUND) {
+ GIFClearPic(curr_canvas, rect);
+ } else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
+ const size_t src_stride = prev_canvas->argb_stride;
+ const uint32_t* const src = prev_canvas->argb + rect->x_offset
+ + rect->y_offset * src_stride;
+ const size_t dst_stride = curr_canvas->argb_stride;
+ uint32_t* const dst = curr_canvas->argb + rect->x_offset
+ + rect->y_offset * dst_stride;
+ assert(prev_canvas != NULL);
+ WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride),
+ (uint8_t*)dst, (int)(4 * dst_stride),
+ 4 * rect->width, rect->height);
+ }
+}
+
+void GIFBlendFrames(const WebPPicture* const src,
+ const GIFFrameRect* const rect, WebPPicture* const dst) {
+ int i, j;
+ const size_t src_stride = src->argb_stride;
+ const size_t dst_stride = dst->argb_stride;
+ assert(src->width == dst->width && src->height == dst->height);
+ for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
+ for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
+ const uint32_t src_pixel = src->argb[j * src_stride + i];
+ const int src_alpha = src_pixel >> 24;
+ if (src_alpha != 0) {
+ dst->argb[j * dst_stride + i] = src_pixel;
+ }
+ }
+ }
+}
+
+void GIFDisplayError(const GifFileType* const gif, int gif_error) {
+ // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
+#if LOCAL_GIF_PREREQ(4,2)
+#if LOCAL_GIF_PREREQ(5,0)
+ // Static string actually, hence the const char* cast.
+ const char* error_str = (const char*)GifErrorString(
+ (gif == NULL) ? gif_error : gif->Error);
+#else
+ const char* error_str = (const char*)GifErrorString();
+ (void)gif;
+#endif
+ if (error_str == NULL) error_str = "Unknown error";
+ fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
+#else
+ (void)gif;
+ fprintf(stderr, "GIFLib Error %d: ", gif_error);
+ PrintGifError();
+ fprintf(stderr, "\n");
+#endif
+}
+
+#else // !WEBP_HAVE_GIF
+
+static void ErrorGIFNotAvailable() {
+ fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
+ "package before building.\n");
+}
+
+void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
+ int bgcolor_index, int transparent_index,
+ uint32_t* const bgcolor) {
+ (void)color_map;
+ (void)bgcolor_index;
+ (void)transparent_index;
+ (void)bgcolor;
+ ErrorGIFNotAvailable();
+}
+
+int GIFReadGraphicsExtension(const GifByteType* const data, int* const duration,
+ GIFDisposeMethod* const dispose,
+ int* const transparent_index) {
+ (void)data;
+ (void)duration;
+ (void)dispose;
+ (void)transparent_index;
+ ErrorGIFNotAvailable();
+ return 0;
+}
+
+int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
+ GIFFrameRect* const gif_rect,
+ struct WebPPicture* const picture) {
+ (void)gif;
+ (void)transparent_index;
+ (void)gif_rect;
+ (void)picture;
+ ErrorGIFNotAvailable();
+ return 0;
+}
+
+int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
+ int* const loop_count) {
+ (void)gif;
+ (void)buf;
+ (void)loop_count;
+ ErrorGIFNotAvailable();
+ return 0;
+}
+
+int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
+ struct WebPData* const metadata) {
+ (void)gif;
+ (void)buf;
+ (void)metadata;
+ ErrorGIFNotAvailable();
+ return 0;
+}
+
+void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
+ const struct WebPPicture* const prev_canvas,
+ struct WebPPicture* const curr_canvas) {
+ (void)dispose;
+ (void)rect;
+ (void)prev_canvas;
+ (void)curr_canvas;
+ ErrorGIFNotAvailable();
+}
+
+void GIFBlendFrames(const struct WebPPicture* const src,
+ const GIFFrameRect* const rect,
+ struct WebPPicture* const dst) {
+ (void)src;
+ (void)rect;
+ (void)dst;
+ ErrorGIFNotAvailable();
+}
+
+void GIFDisplayError(const struct GifFileType* const gif, int gif_error) {
+ (void)gif;
+ (void)gif_error;
+ ErrorGIFNotAvailable();
+}
+
+void GIFClearPic(struct WebPPicture* const pic,
+ const GIFFrameRect* const rect) {
+ (void)pic;
+ (void)rect;
+ ErrorGIFNotAvailable();
+}
+
+void GIFCopyPixels(const struct WebPPicture* const src,
+ struct WebPPicture* const dst) {
+ (void)src;
+ (void)dst;
+ ErrorGIFNotAvailable();
+}
+
+#endif // WEBP_HAVE_GIF
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/examples/gifdec.h b/src/third_party/libwebp/examples/gifdec.h
new file mode 100644
index 0000000..5eba9dd
--- /dev/null
+++ b/src/third_party/libwebp/examples/gifdec.h
@@ -0,0 +1,116 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// GIF decode.
+
+#ifndef WEBP_EXAMPLES_GIFDEC_H_
+#define WEBP_EXAMPLES_GIFDEC_H_
+
+#include <stdio.h>
+#include "webp/types.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#ifdef WEBP_HAVE_GIF
+#include <gif_lib.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
+#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
+# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
+# define LOCAL_GIF_PREREQ(maj, min) \
+ (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
+#else
+# define LOCAL_GIF_VERSION 0
+# define LOCAL_GIF_PREREQ(maj, min) 0
+#endif
+
+#define GIF_INDEX_INVALID (-1)
+
+typedef enum GIFDisposeMethod {
+ GIF_DISPOSE_NONE,
+ GIF_DISPOSE_BACKGROUND,
+ GIF_DISPOSE_RESTORE_PREVIOUS
+} GIFDisposeMethod;
+
+typedef struct {
+ int x_offset, y_offset, width, height;
+} GIFFrameRect;
+
+struct WebPData;
+struct WebPPicture;
+
+#ifndef WEBP_HAVE_GIF
+struct ColorMapObject;
+struct GifFileType;
+typedef unsigned char GifByteType;
+#endif
+
+// Given the index of background color and transparent color, returns the
+// corresponding background color (in BGRA format) in 'bgcolor'.
+void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
+ int bgcolor_index, int transparent_index,
+ uint32_t* const bgcolor);
+
+// Parses the given graphics extension data to get frame duration (in 1ms
+// units), dispose method and transparent color index.
+// Returns true on success.
+int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
+ GIFDisposeMethod* const dispose,
+ int* const transparent_index);
+
+// Reads the next GIF frame from 'gif' into 'picture'. Also, returns the GIF
+// frame dimensions and offsets in 'rect'.
+// Returns true on success.
+int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
+ GIFFrameRect* const gif_rect,
+ struct WebPPicture* const picture);
+
+// Parses loop count from the given Netscape extension data.
+int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
+ int* const loop_count);
+
+// Parses the given ICC or XMP extension data and stores it into 'metadata'.
+// Returns true on success.
+int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
+ struct WebPData* const metadata);
+
+// Dispose the pixels within 'rect' of 'curr_canvas' based on 'dispose' method
+// and 'prev_canvas'.
+void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
+ const struct WebPPicture* const prev_canvas,
+ struct WebPPicture* const curr_canvas);
+
+// Given 'src' picture and its frame rectangle 'rect', blend it into 'dst'.
+void GIFBlendFrames(const struct WebPPicture* const src,
+ const GIFFrameRect* const rect,
+ struct WebPPicture* const dst);
+
+// Prints an error string based on 'gif_error'.
+void GIFDisplayError(const struct GifFileType* const gif, int gif_error);
+
+// In the given 'pic', clear the pixels in 'rect' to transparent color.
+void GIFClearPic(struct WebPPicture* const pic, const GIFFrameRect* const rect);
+
+// Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed
+// to be already allocated.
+void GIFCopyPixels(const struct WebPPicture* const src,
+ struct WebPPicture* const dst);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_EXAMPLES_GIFDEC_H_
diff --git a/src/third_party/libwebp/examples/img2webp.c b/src/third_party/libwebp/examples/img2webp.c
new file mode 100644
index 0000000..2f750c5
--- /dev/null
+++ b/src/third_party/libwebp/examples/img2webp.c
@@ -0,0 +1,314 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// generate an animated WebP out of a sequence of images
+// (PNG, JPEG, ...)
+//
+// Example usage:
+// img2webp -o out.webp -q 40 -mixed -duration 40 input??.png
+//
+// Author: skal@google.com (Pascal Massimino)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../examples/example_util.h"
+#include "../imageio/image_dec.h"
+#include "../imageio/imageio_util.h"
+#include "./stopwatch.h"
+#include "webp/encode.h"
+#include "webp/mux.h"
+
+//------------------------------------------------------------------------------
+
+static void Help(void) {
+ printf("Usage:\n\n");
+ printf(" img2webp [file-level options] [image files...] "
+ "[per-frame options...]\n");
+ printf("\n");
+
+ printf("File-level options (only used at the start of compression):\n");
+ printf(" -min_size ............ minimize size\n");
+ printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
+ printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
+ " (0=only keyframes)\n");
+ printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
+ " (0=disable key-frames altogether)\n");
+ printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
+ printf(" -v ................... verbose mode\n");
+ printf(" -h ................... this help\n");
+ printf(" -version ............. print version number and exit\n");
+ printf("\n");
+
+ printf("Per-frame options (only used for subsequent images input):\n");
+ printf(" -d <int> ............. frame duration in ms (default: 100)\n");
+ printf(" -lossless ........... use lossless mode (default)\n");
+ printf(" -lossy ... ........... use lossy mode\n");
+ printf(" -q <float> ........... quality\n");
+ printf(" -m <int> ............. method to use\n");
+
+ printf("\n");
+ printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
+ " -d 80 in2.tiff -o out.webp\n");
+}
+
+//------------------------------------------------------------------------------
+
+static int ReadImage(const char filename[], WebPPicture* const pic) {
+ const uint8_t* data = NULL;
+ size_t data_size = 0;
+ WebPImageReader reader;
+ int ok;
+#ifdef HAVE_WINCODEC_H
+ // Try to decode the file using WIC falling back to the other readers for
+ // e.g., WebP.
+ ok = ReadPictureWithWIC(filename, pic, 1, NULL);
+ if (ok) return 1;
+#endif
+ if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0;
+ reader = WebPGuessImageReader(data, data_size);
+ ok = reader(data, data_size, pic, 1, NULL);
+ free((void*)data);
+ return ok;
+}
+
+static int SetLoopCount(int loop_count, WebPData* const webp_data) {
+ int ok = 1;
+ WebPMuxError err;
+ uint32_t features;
+ WebPMuxAnimParams new_params;
+ WebPMux* const mux = WebPMuxCreate(webp_data, 1);
+ if (mux == NULL) return 0;
+
+ err = WebPMuxGetFeatures(mux, &features);
+ ok = (err == WEBP_MUX_OK);
+ if (!ok || !(features & ANIMATION_FLAG)) goto End;
+
+ err = WebPMuxGetAnimationParams(mux, &new_params);
+ ok = (err == WEBP_MUX_OK);
+ if (ok) {
+ new_params.loop_count = loop_count;
+ err = WebPMuxSetAnimationParams(mux, &new_params);
+ ok = (err == WEBP_MUX_OK);
+ }
+ if (ok) {
+ WebPDataClear(webp_data);
+ err = WebPMuxAssemble(mux, webp_data);
+ ok = (err == WEBP_MUX_OK);
+ }
+
+ End:
+ WebPMuxDelete(mux);
+ if (!ok) {
+ fprintf(stderr, "Error during loop-count setting\n");
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+
+int main(int argc, const char* argv[]) {
+ const char* output = NULL;
+ WebPAnimEncoder* enc = NULL;
+ int verbose = 0;
+ int pic_num = 0;
+ int duration = 100;
+ int timestamp_ms = 0;
+ int loop_count = 0;
+ int width = 0, height = 0;
+ WebPAnimEncoderOptions anim_config;
+ WebPConfig config;
+ WebPPicture pic;
+ WebPData webp_data;
+ int c;
+ int have_input = 0;
+ CommandLineArguments cmd_args;
+ int ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
+ if (!ok) return 1;
+ argc = cmd_args.argc_;
+ argv = cmd_args.argv_;
+
+ WebPDataInit(&webp_data);
+ if (!WebPAnimEncoderOptionsInit(&anim_config) ||
+ !WebPConfigInit(&config) ||
+ !WebPPictureInit(&pic)) {
+ fprintf(stderr, "Library version mismatch!\n");
+ ok = 0;
+ goto End;
+ }
+
+ // 1st pass of option parsing
+ for (c = 0; ok && c < argc; ++c) {
+ if (argv[c][0] == '-') {
+ int parse_error = 0;
+ if (!strcmp(argv[c], "-o") && c + 1 < argc) {
+ argv[c] = NULL;
+ output = argv[++c];
+ } else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) {
+ argv[c] = NULL;
+ anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) {
+ argv[c] = NULL;
+ anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-loop") && c + 1 < argc) {
+ argv[c] = NULL;
+ loop_count = ExUtilGetInt(argv[++c], 0, &parse_error);
+ if (loop_count < 0) {
+ fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count);
+ parse_error = 1;
+ }
+ } else if (!strcmp(argv[c], "-min_size")) {
+ anim_config.minimize_size = 1;
+ } else if (!strcmp(argv[c], "-mixed")) {
+ anim_config.allow_mixed = 1;
+ config.lossless = 0;
+ } else if (!strcmp(argv[c], "-v")) {
+ verbose = 1;
+ } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ Help();
+ goto End;
+ } else if (!strcmp(argv[c], "-version")) {
+ const int enc_version = WebPGetEncoderVersion();
+ const int mux_version = WebPGetMuxVersion();
+ printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
+ (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
+ enc_version & 0xff, (mux_version >> 16) & 0xff,
+ (mux_version >> 8) & 0xff, mux_version & 0xff);
+ goto End;
+ } else {
+ continue;
+ }
+ ok = !parse_error;
+ if (!ok) goto End;
+ argv[c] = NULL; // mark option as 'parsed' during 1st pass
+ } else {
+ have_input |= 1;
+ }
+ }
+ if (!have_input) {
+ fprintf(stderr, "No input file(s) for generating animation!\n");
+ goto End;
+ }
+
+ // image-reading pass
+ pic_num = 0;
+ config.lossless = 1;
+ for (c = 0; ok && c < argc; ++c) {
+ if (argv[c] == NULL) continue;
+ if (argv[c][0] == '-') { // parse local options
+ int parse_error = 0;
+ if (!strcmp(argv[c], "-lossy")) {
+ if (!anim_config.allow_mixed) config.lossless = 0;
+ } else if (!strcmp(argv[c], "-lossless")) {
+ if (!anim_config.allow_mixed) config.lossless = 1;
+ } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
+ config.quality = ExUtilGetFloat(argv[++c], &parse_error);
+ } else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
+ config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
+ duration = ExUtilGetInt(argv[++c], 0, &parse_error);
+ if (duration <= 0) {
+ fprintf(stderr, "Invalid negative duration (%d)\n", duration);
+ parse_error = 1;
+ }
+ } else {
+ parse_error = 1; // shouldn't be here.
+ fprintf(stderr, "Unknown option [%s]\n", argv[c]);
+ }
+ ok = !parse_error;
+ if (!ok) goto End;
+ continue;
+ }
+
+ if (ok) {
+ ok = WebPValidateConfig(&config);
+ if (!ok) {
+ fprintf(stderr, "Invalid configuration.\n");
+ goto End;
+ }
+ }
+
+ // read next input image
+ pic.use_argb = 1;
+ ok = ReadImage(argv[c], &pic);
+ if (!ok) goto End;
+
+ if (enc == NULL) {
+ width = pic.width;
+ height = pic.height;
+ enc = WebPAnimEncoderNew(width, height, &anim_config);
+ ok = (enc != NULL);
+ if (!ok) {
+ fprintf(stderr, "Could not create WebPAnimEncoder object.\n");
+ }
+ }
+
+ if (ok) {
+ ok = (width == pic.width && height == pic.height);
+ if (!ok) {
+ fprintf(stderr, "Frame #%d dimension mismatched! "
+ "Got %d x %d. Was expecting %d x %d.\n",
+ pic_num, pic.width, pic.height, width, height);
+ }
+ }
+
+ if (ok) {
+ ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config);
+ if (!ok) {
+ fprintf(stderr, "Error while adding frame #%d\n", pic_num);
+ }
+ }
+ WebPPictureFree(&pic);
+ if (!ok) goto End;
+
+ if (verbose) {
+ fprintf(stderr, "Added frame #%3d at time %4d (file: %s)\n",
+ pic_num, timestamp_ms, argv[c]);
+ }
+ timestamp_ms += duration;
+ ++pic_num;
+ }
+
+ // add a last fake frame to signal the last duration
+ ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
+ ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
+ if (!ok) {
+ fprintf(stderr, "Error during final animation assembly.\n");
+ }
+
+ End:
+ // free resources
+ WebPAnimEncoderDelete(enc);
+
+ if (ok && loop_count > 0) { // Re-mux to add loop count.
+ ok = SetLoopCount(loop_count, &webp_data);
+ }
+
+ if (ok) {
+ if (output != NULL) {
+ ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size);
+ if (ok) fprintf(stderr, "output file: %s ", output);
+ } else {
+ fprintf(stderr, "[no output file specified] ");
+ }
+ }
+
+ if (ok) {
+ fprintf(stderr, "[%d frames, %u bytes].\n",
+ pic_num, (unsigned int)webp_data.size);
+ }
+ WebPDataClear(&webp_data);
+ ExUtilDeleteCommandLineArguments(&cmd_args);
+ return ok ? 0 : 1;
+}
diff --git a/src/third_party/libwebp/examples/stopwatch.h b/src/third_party/libwebp/examples/stopwatch.h
new file mode 100644
index 0000000..6ce3827
--- /dev/null
+++ b/src/third_party/libwebp/examples/stopwatch.h
@@ -0,0 +1,63 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Helper functions to measure elapsed time.
+//
+// Author: Mikolaj Zalewski (mikolajz@google.com)
+
+#ifndef WEBP_EXAMPLES_STOPWATCH_H_
+#define WEBP_EXAMPLES_STOPWATCH_H_
+
+#include "webp/types.h"
+
+#if defined _WIN32 && !defined __GNUC__
+#include <windows.h>
+
+typedef LARGE_INTEGER Stopwatch;
+
+static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
+ QueryPerformanceCounter(watch);
+}
+
+static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
+ const LARGE_INTEGER old_value = *watch;
+ LARGE_INTEGER freq;
+ if (!QueryPerformanceCounter(watch))
+ return 0.0;
+ if (!QueryPerformanceFrequency(&freq))
+ return 0.0;
+ if (freq.QuadPart == 0)
+ return 0.0;
+ return (watch->QuadPart - old_value.QuadPart) / (double)freq.QuadPart;
+}
+
+
+#else /* !_WIN32 */
+#include <string.h> // memcpy
+#include <sys/time.h>
+
+typedef struct timeval Stopwatch;
+
+static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
+ gettimeofday(watch, NULL);
+}
+
+static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
+ struct timeval old_value;
+ double delta_sec, delta_usec;
+ SbMemoryCopy(&old_value, watch, sizeof(old_value));
+ gettimeofday(watch, NULL);
+ delta_sec = (double)watch->tv_sec - old_value.tv_sec;
+ delta_usec = (double)watch->tv_usec - old_value.tv_usec;
+ return delta_sec + delta_usec / 1000000.0;
+}
+
+#endif /* _WIN32 */
+
+#endif /* WEBP_EXAMPLES_STOPWATCH_H_ */
diff --git a/src/third_party/libwebp/examples/test.webp b/src/third_party/libwebp/examples/test.webp
new file mode 100644
index 0000000..3e4bca1
--- /dev/null
+++ b/src/third_party/libwebp/examples/test.webp
Binary files differ
diff --git a/src/third_party/libwebp/examples/test_ref.ppm b/src/third_party/libwebp/examples/test_ref.ppm
new file mode 100644
index 0000000..97719f0
--- /dev/null
+++ b/src/third_party/libwebp/examples/test_ref.ppm
@@ -0,0 +1,4 @@
+P6
+128 128
+255
+ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐãýÐãýÐãýÐãýÒäþÒäþÒäþÒäþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÏäýÐãýÐãýÐãýÒäþÒäþÒäþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÏäûÏäûÏäûÏäûÐåýÐåýÐåýÐåýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÎåûÎåûÎåûÎåûÏæýÏæýÏæýÏæýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÎåûÎåùÎåùÎåùÏæúÏæúÏæúÏæýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåýÒèûÒèûÒèûÒèûÐæúÐæúÐæúÐæúÐåýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåýÐæúÒåøÒåøÒåøÒåøÒåøÒåøÐæúÐåýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÐãÿÐãÿÐãÿÏäýÏäýÏäýÐãýÒäþÒåýÒåýÒåýÏáóÏáóÏáóÏáñÔåöÔåöÔåöÔåøÒåúÒåýÒåýÐåýÐåýÐåýÐåýÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒæÿÐåþÒäþÐãýÎáúÒâûÐáúÕåÿÎáýÔæÿÔæÿÓãýÏßøÉÚð·Æܪ©±»Îª»©³ÅÌÖèÖáòÓáñÖèøÔåö×éûÒåøÒåúÖêÿÐæúÒæþÓæþÓæþÓæþÓæþÔèÿÔèÿÔèÿÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÓãý×èÿÕãþÒßúÕäÿÌÛøÈÙò«Åm{dqhtlvs{
y¨¤ªº°¶Æ½ÆÕÎÜêÓäóÕæ÷ÔåöÖèúÓæùÐäùÔêþÓæûÓæûÓæûÓæûÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÌáùÎãûÓåÿÕèÿÒâûÓãýÔâýÙæÿÓáû©·Òlz\jmzhqmw¡¥°³Å¸½Ë©¿ËÙÓãòÖæö×æøÔåöÖèúÕéûÐä÷ÓæûÓæûÓæûÓæûÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓèÿÒæÿÐãýÏâûÓãýÕåÿ±¿ÚªrWd~\i~¤¢½jqªz
²
©²´Á¯³¿¬±½ËÆÔäÔâòÙèùÔãôÖèúÕæùÓæûÓæûÓæûÓæûÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓèÿÉÞ÷ÚìÿÔæÿÈÙòy£M[vXfVc}[deoipªfj¬xz}}~{qo¥©¤¡ÆÌÚ½ÆÕ¢±¯¹ËÖáò×å÷Úèù×æúÖæýÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒæÿÒäþÅ×ñfwM]wLZtMZsOXq_fjq~rvrs©
¤ssvs}²¬¥¤¨²·»Ç¨¡¶ÈÓÝïÚä÷ÛéúÖèúÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒæÿÏäý½ÏéXk
IZsL\vP^yN[tbisx¬~¢©¨Ë¡}}qq~²
±´¿¤¨²¨¬º{¯³»Î×ßò×å÷ÖèúÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåýÐåýÐåþÐåþÍßù[mGWqLZtIWrFQmox´¡¤¸ªyzss¤¤Â½¹Ù²°Ì£lmz{vx««¯¹ÌÏÚ©¨£ª°ÀÔÚê×âó×æúÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏåÿÏåÿÏæþÏæþÎãúÏåùÖêÿÒåýºÍæk~JXvLWvLTqFNkQXtlqÆÈÚ£¥²áäï··Ä
£¢·ÎÍâÝÜïìëû¸¸Å£¢²yy£²²»ººÆ¤·£
¡¥±¬±¿ÕÞíÖå÷×éûÔèúÓæùÔêþÔêþÔéÿÕéþÖêÿÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íÿ×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íýÙíýÙíýÙíýÙíý×ìû×ìû×ìû×ìû×íýÖìûÕëúÔêùÔêùÔêùÔêùÔêùÓéøÓéøÓéøÓéøÒè÷Òè÷Òè÷Òè÷ÐæöÐæöÐæöÐæöÐæöÐæöÐæöÐæöÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏåÿÏåÿÏæþÐåýÕêÿÐäùÏãø¬ÀÕViIXvN\yHPpLSpHOlei¨¢²²»ÚÛáÒÒÛ£º¹ÉÌÌÙ°°»£yy··À££¬ª¡¯´ªª³»»Åª¸ªª£©·ÌÚêÒãóÚëûÚíÿÕéûÔêþÔéÿÕéþÙêýÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏæþÏæþÐåþÐåýÐäûÐäû½ÍãSc{FVpIWtS^}MUtLSpHOllp¡©jly
wyikxjlysv
ÉÍ×
xz¦¥¯©¨±£·²¶À£¦«¶¦²ÀÈÖæ×è÷×éùÙêýÕéþÕéþÕéþÙêýÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏæþÐåýÐåýÒåýÒåý£³ÌTd}IWqIWrQ]{P\zOWwLTqFMjcfzil~x{y}
z~]_o~¥¨´kmzikxehw^artw~sv
iit£¢«ÂÂÌ}¡©ÌÕâÖâðÝëûÜëý×éûÕéûÕéþÙêýÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓèÿÎãúÕéÿÈÜóqIZrJXrIWrO[yJVtPXxT\{FNkV]zv}osX\pqvfkwfj{sxy}hkei}X]ktycewbbo°°»½½È±´½ÇËÓ·½Ä¡¦·»Æ¶ºÅÀÇÒ×ãïÙæ÷ÙèùÖèúÙìÿÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåýÎäøÓæûÄ×ï^oHXqM[vDQlP\xNZxIUsLTsMUtHPm]ekrSZsSWoW[pfj{qttxjm]arZ^lmq^bv[^sdh}fj{dhyilehy~~£¢¢©©´¢¥»¿Çª°·±·¾ÁÅËÌÏ׶ºÅÁÈÓÚåóÚèøÛìýÖêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåýÕéþ²ÆÛTdzEUmM[tAOjWcMXwJSrS[zIQqJSr[c^fNWrLSoPTqINhW\simX]tae}V\oX^qU[m]bwW\s]bykqflV[pTWkqp¡ªª¶¤
²¶»·¸»ÌËÏÚÝå½Á̹ÂÏÔâòÛêûÕéûÖêýÕëýÕëýÕëýÖìþÖìþÖìþÖìþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒåý³ÄÚN^tIZpIWqHVqQ_zJXvLWvNZxEPoLWvQZyV^{W_}U\yOVsQUsTWt{\azSWqPUoPUlOTiV[pmrTXrOTmmrx}_d{X\qz}¨¢z~{wwÆÅÌÌÌÕ¥ª´°·ÁÅÐÞ×åöÛëúÙêúÖêýÖêýÕëýÖìþÖìþÖìþÖìþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÓèÿÒäþÔæÿÕèÿÐãýÐãýÕèÿÕèÿ¨¸ÒZhN[tN[tO]xQ_zM[vLZtL\vJZwBQoP^{Q]{W_W]~W[{UXwQUs\_}hlTXrLOmIMkDGdDH_EI^lqTXr~TWtaeqv¦ª´~qp¡©ÅÅг·Á¡¥°¤©³ÅÌÕÙâìÜêúÞíÿÚëûÖëúÖìû×íý×ðþÙñÿ×íýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×íý×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÔëøÔëøÔëøÓê÷Óê÷ÒéöÒéöÓê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÓèÿÒäþÖéÿÒäþÓåÿÐãýÐãý}©O_yNZvNVsLWsQ]yLWsN\wN\wHXrIZsHWtSa~S^}[abehiWXxW[yVZwZ^xTWtPTrOSqNQoBGaINc{^c}[_y²
X\yQVpjo}¢¤±°¿wvªrp}£¿¿Ë¢¥»ÁÆÉÐÚÝé÷ÙæøÜëýÝïÿÚïþÙïþÖïýÕíû×íýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×ïû×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÕìùÕìùÔëøÓê÷Óê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÓèÿÔæÿÓãýÖæÿÕåÿÛëÿw¡M]wDQlLWsLTqS[xV^{O[wS^zLWsBPkLZtO]xUa}Zbls_cop^bUXv\azW\vGJiHLjMPmFJbIObX^qZ^vy~¥¸w{UZq[_ttyty
£¡±¬ªº»»Èvv«¦ª²©¬´¤¨°¹¾ÈÛæôÛéùÛëúÙêùÛðþÙðýÖïûÕíú×íýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×ïû×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÕìùÕìùÕìùÔëøÓê÷Óê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÔèÿÖæÿÔäþÓãý×èÿ~¨HXrHXrIWrIUqMUrQZwNVsQZwPXvNVsJVrS_yO\vU^y^eVZw^bsv¥y}UZs]byX]wPTqPTqMQkNSjPViLQdSWo~¤·jp]bw[_trvUZhZ\kkm}¦±¯½À¾Ì©©¶ffsxx
¢¤£ÆÏÞÝëûÜêúÛëúÙíûÖíúÙðýÚñþÙíýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×ïû×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÕìùÕìùÕìùÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÒæþÒæþÒæþÒæþÒæþÒæþÒæþÓæþÚêÿÏÝøÎÜ÷hvHVqHVqHVqGUpHTpOWtU]zLTqLTqOVsLSpS\wOXqV]wU\v]b{ptjokmz}kpX]wLPhNSjTWtOSpW\vLPhMQfPUjV[rae}U[mU[mkp
X]r^bv^bvehybdseer«©¦³¢¬km}fixtw¢¤±¥³ÎÚèÜêøÙéöÞñýÙíùÙðúÕìùÚïþÚïþÚïþÚïþÚïþÚïþÚïþÚïþÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÕìùÔëøÔëøÔëøÔëøÔëøÔëøÓê÷ÒéöÒéöÒéöÒæþÒæþÒæþÒæþÒæþÒæþÒæþÓæþÕåÿÎÜ÷drFToIWrESmFToMXtS^zU]zHPmLSpJQoU\y\csw[_w\ax^czdichchdf_bzSWoNSjX]r\ax\azOTmPUlae}QVmHMdLPjINhMQfdj}y~ejVZojm¨¦°¨y{
z}}{¨ª·©ÀÉÙÙäòÙæôÚê÷ÜïúÙíùÛòýÚïýÚïþÚïþÚïþÚïþÚïþÚïþÚïþÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷ÒéöÒéöÒæþÒæþÒæþÒæþÒæþÒæþÒæþÓæþÕãþwHTpMXtO[wLWsNZvT_{P\xLTqIQoLSpOSq\_~il¡mpSVkdh}
W[p[^sUZoNShV[r_h~^fzwqxPWqPXockDJdGNjNUqFMiXawt}V[rSUmtw¦¨ª¤±°·xz}{£¨´´Á¤¦°½ËÖâÜëöÝíøÝðù×íöÚïúÚïýÚïþÚïþÚïþÚïþÚïþÚïþÚïþÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÔëøÔëøÔëøÔëøÔëøÔëøÓê÷ÒéöÒæÿÒæþÒæþÒæþÒæþÓæþÓæþÕåþ}¥DOkIUqMXtJVrMXtP\xS^zLWvHPpGNkELiNQodh
ª¦©Ä³[^r{ilSVkQUjMQfGLaJSf_h{ltW_vT[t^eJSiGNhNUqLSoFMjFMiIPjrzV^tLPhOQlxz°
¸·¾xwts¨´´Á«º¨©°ºÍÙâÛêòÚêôßòûÜòúÚïýÚïþÚïþÚïþÚïþÚïþÚïþÚïýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïù×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷ÓèÿÐåþÓåÿÓåÿÒåýÓãûÔäúm{LWsJSpJSpNVsT\yZbXa~NVvGOqFLoGMmIPlQXrbj~¢´´ºÍ¢fkim\axNSjJOfQVmLPeV[p^cxchFIfMTqHOlFOjNUqZa}FIfQUrMPmTWtejMQkMQkWZt}xz[\rbasfev¤°]\oa_r¬ÄÄл»Ç¤¤¡¤¯¦·Ë×ÞÝìôÝð÷ÝðùÜïúÜíýÜíýÜíýÜñÿÜñÿÛòÿÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóûÛòúÛòúÚñùÚñùÚñùÚñùÚñùÚñùÙðøÙðúÙðúÙðú×ïù×ïù×ïù×ïù×ïù×ïù×ïù×ïùÖíøÖíøÖíøÖíøÕì÷Õì÷Õì÷Õì÷ÔëöÔëöÔëöÓêôÐãýÖéÿÓæþÔäýÔäý×åÿp~TazNVsLTsLTsNVvV^~V^~NVvEMlGMpNTwLSpFMi[cypx¦¶«´Ä^cx_d{\axW\sTXpNSjNSj\avioZ^sdiFIfGNkIOpGOlSZvfk
^c}}[_yPTqeiOTmNSlbdª]_x]^t{zzyª¤¯]Zm[Zlkj}¬³³¿º¹Â¾½Ä¬¬¶¨´±»ÄÐÝäßñøÚêôÝíúÝíúÜïúÜïúÜñýÜñýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÕìôÕìôÕìôÕìôÔëóÔëóÔëóÓêò×éûÒãöÕåû×æýÖäþªIVpW_}PXxMUwMUwPXzU]NVxGOqDLmNTwIOpBIfJQmLTjmv¯·ÉpxW\schZ^vMQiSWoOTkPUjaez\btioae}AEbLSpLSpGOlJQkW\ssw£¥¾twX[vikJMjQToprMOh\]syx£¡±¨sproa_r^]p¯¯º³²»·¶½ÀÀÉ£ª¯¶¿¿ÌÓÛêòÜì÷ÝíúÝíúÜïúÜïúÜñýÜñýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÕìôÕìôÕìôÕìôÔëóÔëóÔëóÓêòÕåô×è÷Öå÷×äù°PZtS\wLTqQZyNVxNVxQZ{OWyHPrFNpIQqQWxW^{LSoMTmHOiS[qz[cwX]wV[tHMf@E\INebf~PUjrxiodjzPUjHMfOVrHOlMUrFMiHMdX\qjmei~PSkikwyloTWl_av{z¢¡«¡rqZXkmp¡¡¬º¹ÀËËÔ¦¥¯¬·¿ËÖßÛêôÝíúÝíúÜïúÜïúÜñýÜñýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÕìôÕìôÕìôÕìôÔëóÔëóÔëóÓêò¤¯¯¾È½ËÙz
S\rV]wQXtV]zS[zT\{PXxLTsFNmIQqOWwT\{W^zQXtJQkELeELeNUoU]sGNhGLePTqNSlUZqMQipt[_tzjq{}X^oHMdT[wOVsNVvSZwUWrHI_TUkrswxhi~stUXmMPeUXmst¢«¹¤¯ª¥·¨¤¸¢²¡²qpmp¦¡©¬«²´´¾³·¿¤¯«²»ª±»ÙäðáïýÜìùáóÿáóÿÜñýÜñýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔëóeqzit~NZeOXhPXlX]tX]w[^{QXvS[xS[xPXvNVsQZwT\yT\yMTmMTmBIcAHbMTmELeBIc@GcIMjOSpOTmjorwhldj}MScPWaahrMScV[pT[tQXvOWtT[wVXqVWlijqs
actqs
vxSVjTWlW[ohj{¡¦¦³¦¨¶±À£°¢«¢¢¹¹ÂÅÅΦª´¯³¾¦«·¶¿ÌáïýÞïûáóÿáóÿÜñýÜñýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔëóybkxNTdV\oX\qW[p^aySZsU^yU^yQ[vS\wU^yS\wNWpFNdAI_BJa>E^GNhGNj7>Z;B_?Ba?B_BGaX]wINech}¢GM]DIWOUeafy_dyJSiMTmWawdl[]ofevsrhfwfevml}«¡²qt_cxadx[]o¦¦³¨¦©¸£}yzw{z
¦©©²ÀÀɲ¶À¸»Æ´·Ä¢°ÁÏÝÜìùáóÿÞñýÜñýÜñýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔëó
t{ptlqhlzehyTVh_avZ^sXatU]sPXoQXrT[tNUoELeAHbDH_AF_@GaELh=Da=Da@Gd>A_>A^JNkHMfAF]PUjOTiOUhX^qsyejOTiS[qFNdLViT\pX\pZ\m^arihzdcvjiy£¢´rsy}~xzwy£~{
£¢¸·¾»»Å¾¾Ç¿¿Ë½½È¶¶Áª¯º¦ÓãðáñþÜïúÞñýÜñýÜñýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔëó£ÆÉÔ±±½¥¨svhjyps
VZmUZqPUoLOlFIhFIhGHhEFeAEc@Db@Dd>Dd>Eb>Eb?FbHOkFMiAHbAHbBIcNVlXatS[m]eydlU]sLSlJQkLSlTWtHJhOQj]^tcdyxz±±¾mp}~£¡¥
vvss
©¤¬¶º¹Â¾½Æ¤£¬¹¸Á¾½Æ²¶À©¨³ßïùáñûÛíùßòþÝòÿÚïúÞóÿÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓìó¢¢£©³´ºÙÚßÈÉϬ³½¾Ä··À¨ª·[]obc{bdMNmIMkJLkLMlIJjFGi@Dd@Dd>Dd>Eb?Fb>EaGNjDJfBIeAHd?FbFMfDLb@H\IQeckQZpIPjFMfOVrMPoLOmPSm]_x]^spr
~}~vx¢¥°£¥²prvx
ss~~ª¤¤¤£¬ÓÒÛ¤£¬¬¶³²»°°¹±¶ÀÒÝéßïùáñþáóÿßôÿÝòþÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓìóÁÂƤ¥©ÞßãÎÏÓÅÆÉ¿ÀÄ´¶»··Â{zcd}hhVWyLMoILiJMjHJhEFe@Db@Db>Eb>Eb?Fb>EaELhAHdAHdAHd>Ea@GcELh>E^ELe]e{JSiGOcNVjV^rPUoNSlOQjX\qQShbdvom{z{}mpQTc[]j
ddqbboyy¨£¦²±º¦¦¥¯¡©´¹Äª¬¶ÀâíùáñûÞñýÛðûÝòþÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓìóÉËÍ´¶¸¦¨ª·¸ºÜÝßÚÛݸ¹»²³·ÇÇЪ©¹zxdd__NOoDFaDFaAD^?A^@Da@Db>Eb>Eb@Gc>EaDJf?Fb=D_@Gc>Ea@GcFMi@GcJQkXawPXoJSfXasPXlGLcQVm\_tQUjSTi\^ptsvtik}UWfikxx{
¡^^kzz¡£¢«¡©±°¹
£¢«¢¡ª©²¶À²½ÚãíãòýáóýÞóÿáöÿÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓì󩫪¾À¿mpoz}{ËÍÌßâáÖÖÖÒÒÔÁÀÇÀ¾Ë²±Ä«de~OOkHLaGJ_HJcFHa@E^=@]:A]=D_>Ea?Fb>Ea;B^>Ea?Fb@GcAHdMTmSZsHPfFNdS[qOWmQZpLTjJOi_d{JNc_cw]_qjl{llyy{VXjWZirtlpz¦¢¤³¡ww}}¡¡¡¥£ÆÅΣ¢«¦ª©²°¯¸ßß黻ǣºÄÎÝìôáóýßöþÛòúÞôýÞôýÞôýÞôýßöþßöþßöþßöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö~±²´
deh¯°²ÎÎθ¶·ÓÐÔÜÚÞÒÐ×°ºª©¹wvWXmOSdNQeNQeLOd@E\>B\:A]>Ea?Fb>Ea;B^=D_@Gc?Fb>Ea=D]=E[FNdDLbMUkS[qQZpT[tU\vV[rejTWlX\pcewjlyyy
y{\^plo~y{£vx~¢¢£¥ª¡©¦¥¯¨±°¹£¢«©¨±ª©²±°¹´³½¬°ºßëôÛíôÞôýß÷ÿÞôýÞôýÞôýÞôýßöþßöþßöþßöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö¡¢¥{}dei
ÅÀÁÛÙÜÓÐÔÍÌÐÆÅÌ°°¹eisV[fTXfQUfOSfAF]>B\;B^?Fb>Ea;B^=D_=D_?Fb>Ea=D_;B\BJaEMa@H^MUkIQhJSiNUoV^tdi~{W[odhysv
z}¢¢¯qpVXjxzrtvx
¢fixdfvww££°¾¾Ë¤¨¦¢¯·ÁÀÉ¥¤´³½¬¶°¯¸¦£«ÍÉÔ½½Æ£©¶½Þð÷ÞôúÝöúÞôýÞôýÞôýÞôýßöþßöþßöþßöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö°±´°±´¥¦ª{}efjmor_^c¯¬°ÕÓÖ¦¤¨^blV[fSWePTeBF[?D[?B_>Ea;B^=D_=D_>Ea:A];B^=D_>EaFNdAI_;DZJQkHOiBIc;B\AHbV[pafy]arhlzy{jiywybdvjl{}fiv~oq~zz¤~~««¸¤¡©¡£¢«¥ª©²¨¶´»¥¢ªÒÐ×ÀÁǯ´»¥°¶Úéðßñ÷ãöýÞôúÞôýÞôýÞôýßöþßöûßöûßöûÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜôùÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö¶´¹¨¦«£~}zy~tsxqpt«ª¯ÄÂÉVU^WV_ljwyy
lpzTWbSVaUWfGJ^GIb@Da>Eb?Fc>Gb=Fa;E];E];E_;E_;E_?Ha?Ha?Ha?Hc@Id>Gb>GbAHdMQiNSh_ctqvvt
srzykjzvt
~yy¢¢±°¹©¨¯¦¡«¢¡ª£¢©ÐÏÖª©°¥¤«°¯³±°´ª«¯³¸º²¶»°¶½´»ÂÖáæßïöáóúÝöúÞ÷ûÞ÷ûßøýáùýâøýá÷ûá÷ýá÷ýá÷ýá÷ýâøþâøýâøýâøýáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×ñö×ñö×ñö×ñöÖðôÖðôÖðôÕïórqv¥¤©{zwvz¥¤«º¹Â¡a^k_]jb_mdbpss\_hNQ\\^kNQeGIbDGd>A_?Fc>Fc=Fa;E];E];E_;Da;Da>Gb>G_>Gb>Gb=Fa=Fa?GdELhUZsOTi]ar
svyxkjzom~dcs~~~}}¥°¯¶¬«°¨¦¡¬«´¥¤¢¡¨´³¸ÅÄÈÓÒÖ½»À¬°ª«º»¿»¿Å¤¨°ÀÆͤ«äï÷âôûÞ÷ûÝöúÜôùâúþÞ÷úß÷øãùþá÷ýá÷ýá÷ýá÷ýâøýâøýâùúâùúáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÜôøÜôøÜôøÜôøÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóÕðò}{wvzrqvvty
°¯³¡¤¢a^lZWe^^j[^iZ]h]_lPTeJNcGLe@Db?Fc>Fc=Fa;E_;E_;E_;Da;Da=Fa=F^=Fa=Fa=Fa=Fa?GdELh\azOTicfx
jly\^k{z_^oVUebaqqp~}}ss©¨¯«ª¯¯´£¡©ÆÅΨ¥²¬¶¤¥¤«¡¤ÂÁÆëêïÌËÏÆÆȱ±³ÅÆɾ¿Å¯²º½ÀÈÁÌÔßñøßöûÞôúßöûßöúãùþâúûÞ÷úá÷ýá÷ýá÷ýá÷ýâøýâøýâùúâùúáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÜôøÜôøÜôøÜôøÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóÕðò¿¾Â\[_edi±°´¶´¹[Zc
¿½Écao]]iVZdkoy_boNQcNQfGLeGJh?Fb>Fc=Fa;E_;E_;E_;Da;E_=F^=F^=Fa=Fa@Id>Gb>FcAHdGLeX]rcfxdiw]_leht~baq\[ksrxw{zyy£¢©ª©³²¹¤ª©²¡«¤¢¯·¶¿¤£ªª©°¦¥ª©¨¬ÇÆËÁÀŽ½¿¿¿Áº¹¾ÆÇ͹¹Â¾ÁɾÁÉÒáéÞñøä÷þäúÿâøýá÷ûßøùáùýá÷ýá÷ýá÷ýá÷ýâøýâøýâùúâùúáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÜôøÜôøÜôøÜôøÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóÕðò©¨¬´³¸edi~¡¤¬±¬«°¦~}WUb¬ª·ÍËÙhesbbmps~Z]ecfq_drMPdINeJOiAHd=Fa=Eb;Da;Da;Da;E_;E_;E];E];E_;E_>Gb;E_AIfJQmJOiW\qZ]olq_boz}ml}ihxrq~}vt
tttt¦¬«²¬«°ª©°¦¥£°½ºÇ¦¥¯¦¥ª¿¾ÂÆÅÉÀ¾ÁÅÂÆ¿¾ÂÔÓÚ²²»»»Å¾Áɤ¯
ÉÙáäöýÜïöá÷ûãùþãûýäýÿâøþâøþâøþâøþâøýâøýâùúâùúáùýáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛó÷Ûó÷Ûó÷Ûó÷ÚòöÚòöÚòöÚòö×òô×òô×òô×òôÖñóÖñóÖñóÕðò»º¿ÂÁÆ¡¤©¨¬£¢¦¨¦¢¦°¯¸[Xe^\j¸¶Ä]]iX\f_ck\_jlq}fj{DH]GLcDJd>Gb=Eb;Da;Da;Da;E_;E_;E];E];E_;E_=Fa;E_?GdGNjPUoQVkSVhlqvx
y{edtedtxwxwsrqq~¨¨±ÆÅ̽»À±°·½»Â¨{y¤£ª¦¥ª»º¿ËÉÎÈÆÉËÈÌÂÀÅÀ¿Æ»ºÄ¿¿ÈÂÂÌ°´¿}¥ßï÷èùÿáôùá÷ûáùúáùýâøþâøþâøþâøþâøýâøýâùúâùúáùýáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛó÷Ûó÷Ûó÷Ûó÷ÚòöÚòöÚòöÚòö×òô×òô×òô×òôÖñóÖñóÖñóÕðòÐÏÔ¯²¥¤©¦¥ª¡¸·ÀÆÄÐ^\ib_mqo}SS^Z]h[^fadohlx}FL^=AVFMf@Id=Eb=Eb;Da;Da;E_;E];E];E];E_;E_:D^:D^=EbAHdFJdNShMPb]bpxwyxtt±°·º¹¾¦¥¬¹¸¿¬¶¥£°¡¢©¨¬»º¿ËÉÎâßãÔÎÓÄÁÆÌÈÐÏÎ×ÎÍÖ»»Å¦ª´£ª´y¬¸Áè÷ÿåùþßöúßøùßøûâøþâøþâøþâøþâøýâøýâùúâùúáùýáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛóôÛóôÛóôÛóôÚòóÚòóÚòóÚòó×òô×òô×òô×òôÖñóÖñóÖñóÕðòÈÇ̪©¯²©¨¬£¢¦³²·¬±£¢¦{z¤¤¢¯ljwVTbVVcNP]Z]hilwbepcfqimyrvFL^BJaDJd?Fb>Eb=Da;Da;E_;E];E];E_;E_;E_9B]:D^;Da=D_@Ga?D[FI]ps
«¤xw~vv¿¾Å²±¶ª©°¡©¸·À©¨±»ºÄ¬³¸·¾¡¦¬±¾½ÁÄÂÇÍËÎÍËι·»½º¿°¯¶³²»ÁÁËÁÁ˾ÁÌ¢¦²Ë×ÞåôûèûÿâøýßøûáùýáùþâøþâøýâøýâøýáùúáùúáùúáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛóôÛóôÛóôÛóôÚòóÚòóÚòóÚòó×òô×òô×òô×òôÖñóÖñóÖñóÕðòÇÇÉÏÏÒÀÀÂ¥¥¨¨¨ª··¹¯¯±
rsy¢hhtNM]NP_SUdacrllxffrllxrt}fizAEZHJcJNkDGd>A_9@];B^@Ga?Fb?Fc>Eb>Eb=D_;B^;B^=D_;B\AF_DHb@B[LOcmp¨zzrr~xxtt¶·½¹ºÀ°±·¥¦¬«¬²³´º±²¸¯´²±¶·¶ºº¹¾£¡¤½»ÀÇÆËÁÀŲ±¶´³¸º¹¾³²·ÐÏÖÆÅÌÄÂɾ½Æ´´À¥Ýêñè÷ÿåøÿá÷ýáûþáûþâúþãúûäûýãûýâýýâýýâúûâúûâúûâúûáùúáùúáùúáùúßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÞÞáÈÈ˽½¿³³¶ÀÀÂÇÇÉ«««¯¯¯º»¾[^fTVcPSbX[lVXjbdsoo{±±½¦~~hhtkjz^_tHI_HJeMOlFIf?B_9@\;B^>Eb>Eb>Eb>Eb>Ea=D_;B^;B^;B\@Ga>A^@E^QUjNQcUWd
¡
iitffrllx°°¹´¶»±²¸£¤ª
¯°¶¦¨²³¹¹¸½²±¶¸·»ÁÀŹ¸½¢¡¥»º¿ÉÈ͹¸½¬«°·¶º´³¸Ù×ÜÄÂÇÈÇÎÀ½Ç¥¤²²»¥¯¹ßëôæ÷ÿäúÿáûþáûûâúûãúûäûýãûýâýýâýýâûùâûùâûùâûùáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðßßâÒÒÔ££¥°³³¶¯¯¯¯°³z~jlyNP_QTewyhjyzz¡ÈÈÔ\\hllytsy{PQfEG_HJeGJhAEb=D_>Ea>Eb>Eb=Da=Da>Ea>Ea=D_;B^=D]9@Z@Da?D]PTi^bsdfswz
]]immy{{jjvzz¢¢«³´º¹ºÀ²³¹³´º©ª°¸·»²±¶¬±¯²¾½Á¸·»±°´¥¤©¾½ÁÇÆ˺¹¾½»À·¶ºÀ¿ÄÎÍÔÇÆÏ°¯¸½½Æ©x±½Æéøÿä÷þâúþáûûâúûâúûãûýãûýâýýâýýâûùâûùâûùâûùáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÌÌÎ××Ú³³¶ªª¬°°²¬¬¯¡¡¡¥¥¥£¤¦¤¥©¤
WZiTVhpr\^m~ffsZZe¡¡¤¦¶twEH]FHaLPjGJh?Fb;B^=Da=Da=Da=Da=D_>Ea>Ea=D_>E^:A[;?\@E^NQfcfx
£¦±ww{{¢´¶»³´º³´º¤¥«¤¥«¡¢¨´³¸³²·±°´·¶ºÌËϽ»À¾½Á¹¸½¿¾Â»º¿ÆÅÉÌËÏÂÁÆìëðáßæÄÂÌÇÆ϶¶¿°³¾yë÷ÿãôûãùþáûûâúûâúûãûýãûýâýýâýýâûùâûùâûùâûùáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÄÄÆÌÌÎÏÏÒ¸¸ººº½³³¶ÂÂŬ¬¯¨LN]IL]qslo~kjzNM]FFSOO\eeq~~y{
_cw>@XOTmFIfAHd>Ea>Eb>Eb>Eb>Eb9@\:A]>Ea>Ea=D]=D];?\HMfHLa\_q¬°º¯¯º£¦¥¦¬¯°¶²³¹¸¹¿¡°±·´³¸¹¸½±°´º¹¾ÅÄÈÇÆ˽»À¹¸½ÅÄÈÈÇÌÅÄȤ£¨¨¦«ÖÕÚãâæÝÜãÔÓÜËÉÓÀÀÉÍÐÛ¦¢ÈÎÜë÷ÿãùþãûýâýýâýýâýýâýýãûýãûýãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÀÀ··¹ËË;¾À¨¨ª¶¶¸ÈÈËÇÇɲ²²ªªª¬°¡¤¤ªGIXIL]SUdehwXWhML\^^k]]ipp{¯±ÀªILdINhHLiAHdAHd?Fc>Eb>Eb>Eb:A]:A]=D_>Ea>E^@Ga>A^AF_AEZ]arrt{}}
£¥¥±«¦¦°ª«±¸¹¿¶·½¯´±²¸ª«±»º¿ÌËÏÂÁÆ«ª¯¹¸½ÍÌÐÄÂǺ¹¾·¶º½»À¾½Á±°´¿¾ÂßÞãÄÂÇÝÜãíìöÂÁËÇÇÐÐÐÜÅÇÔª¯½ëôÿåùþâúûâýýâýýâýýâýýãûýãûýãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÁÁĺº½ÅÅǾ¾À¥¥¨»»¾ÁÁÄÅÅž¾¾³´·°±´¬°¸£LN]OQc\^macr
ji{TScbboZZejjs¯²ºº½É_ctVZo@E^>A^FMiBIeBIf?Fc>Eb>Eb:A];B^=D_=D_?F_>E^@Da@E^GJ_\_q{~z~ss
¤¤¤ÍÎÔ»½Â³´º¤¥«¡¦¯´¤±°´¿¾ÂÏÎÓ¬±±°´¢¯²ÂÁÆ¿¾Â³²·¸·»«ª¯º¹¾¿¾ÂÍÌÐ÷öýÙ×áÄÂ̽½ÆÆÆÒ××ä©¢ÉÐÛèùÿâúûâýýßþýßþýâýýãûýäûýãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÇÇÉÄÄƽ½¿½½¿¦¦©¹¹»±±³³³³ÁÁ¿ÇÇDz³¶°±´ªª³¡¡NM_IL]UWiik}PObqpedt^^kaaljjs·©¬·}{}SUmFHcHLiGJhDJfAJe>Gb>Ea:A];B^>Ea=D_?F_9@Z@DaFJd=?WLOc~wz
ooz£¢
³´º¿ÀÆ¡¢¨½½Æ¦¨¨¦«ÅÄÈÎÍÒ´³¸¬«°·¶ºÀ¿Ä²±¶»º¿ÅÄÈÇÆ˶´¹À¿ÄÎÍÒÜÛßÔÓ××ÖÝãâé¿ÀƬ¬¶ÀÀÌ´´Á¡¬áð÷åùûâúûâýýâýýâýýãûýãýúãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÀÀÀ«««ËË˾¾¾ÁÁÁ¹¹¹½½½ÄÄÁ¿¿¿ÎÎЬ³¯¬¹PMaNMbHF]qo\[pzy\\illv}~¤¬½²NLeNLhJMjJTlGTkANe=F^=D]:A[;?\=@]?D]?D]?B_?B_AF_?DXfmx~swvv~~ww«¥¤«£¢«¤¯¬¹¡©¢¡¥²²´ÏÏÒÀÀÂÆÆȱ±³££¥±±³½½¿ÈÈËÇÇÉÂÂÅÄÄÆÇÇÉÍÍÏÌÌÎñðôöôùÈÇ̬³¿¾ÅÅÄͤ¸ÂËæöýäöùëÿÿãýúãþûÞýùâþøäþùãýøâû÷âû÷áúöáúöáúöáúøßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïxxxÌÌÌÒÒÒ¶¶¶ÌÌÌ°°°¦¦¤¢¢¢¸¸º²±¸°º¢OL_LH^VTkWUlMLahfy¥ddp¥ss}iho
¥¤±¯½ÉÆÚXToTTpDMeN[rDPh>G_:A[:A[>A^>A^?D]?D]?B_?B_BFc=AXZ_my~txppyzz¥¶³À»ºÄ²²´ÁÁĸ¸º¿¿ÁÇÇɬ¬¯¡¡£»»¾ÆÆȾ¾ÀÁÁÄÀÀÂÅÅÇÌÌÎÇÇÉããåÂÂÅÒÐÕ·¶ºÀ¿Æ¿¾Å«ª³¢©²áëóåöùéúþåýûáúøâÿúÞý÷ãÿùãýøâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïlljÏÏÍ¿¿½¸¸¶~~{
¿¿½¡¡££¥±°´©¨±«©¶HFTOM]XWjXWjSQb\\ittww{{
fhmomt¯·¾»È½ºË²¯Â[WmVWpBIcQ[vISmBIe=D_>A^?B_>A^?D]?D]?D]?D]?B_BG^MSasxxzzz£{{£
zx
£¥ª©°ÇÆÍ°¯³³³¶««±±³²²´««¿¿ÁÆÆȳ³¶ÁÁÄÇÇÉÁÁĹ¹»½½¿ÈÈËÈÈ˽½¿ÇÇɸ·»ÁÀŹ¸¿ÉÈϹ¸ÁÆÐÙíúÿèùýåùùåÿýßûöáÿùãÿùãýøâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïaa^yywÙÙÖËËÈÈÈÆ
wwt¡¡¡´´²¢¦¥¬¤VTaLIVNN[UUbPP\EEPaalMMVMNThiokjo¢½»Â£¢«ÄÁÏ»¹ÉwsQPeFJdMTpNUqHLiEHeAEb>A^=A[?D[?D[?D]?D]?B_DHbBHXqvsvqq~wv¤
qo}¥¤¨¦°¨£»º¿¸¸º¸¸º»»¾ÄÄÆ»»¾´´·²²´íí𸸺ÅÅÇÂÂÅÅÅǽ½¿´´···¹¹¹»¨¨ªÉÉÌÄÂǺ¹¾±°·¸·¾ÈÇШ£Üéíâòöæúúãúùãÿùâþøäþùãýøâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïVWSopk½¾¹½¾¹vwr}²³¯³³±¡¡£kjqddmTT]TT]OOXZ[aTU[NOSdeiomr¥¤©£¢©ÂÁ˦¤±qo}edtSVk?D]OTmOTmHLiBFc;?\>B\?DX?DX>BZ>B\AEb>B\?EWzsvoo{ml}¦rp}±°¹°º¦±±³ÁÁÄÄÄƺº½ÆÆÈÆÆÈÉÉÌÌÌθ¸ºÌÌÎÇÇÉÅÅÇÀÀÂÀÀºº½²²´ºº½èèêÜÛß¿¾Â½»Â¸·¾ÍÌÕÈÈÒy©³¹íúÿä÷÷åýûäþùáý÷åÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïTUPbc^}~y«¬¨¢£bc^stpklh~°°²°¯³ijm]^bXZ]deiZ[^\]aTUXefjooqwwy¹¹»¹¸¿°¯¸cewLOdPTiPSkOQlIMj?B_>B\?DX@EZ?D[?D]?B_?D]BH[jo}{{ddqxx
¨¤£¡xw°¯¸±°¹
«ª¯»»¾ÄÄÆÉÉ̾¾ÀÌÌÎÈÈËââäÍÍϸ¸ºËËÍÉÉÌÇÇÉÂÂÅÁÁÄÂÂÅ»»¾ÀÀÂÄÂÇÈÇÌÇÆͨ¦ÒÐÚÅÅΣ{Ôáååøøåùùãýøãÿùåÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïUVOef_lmf©ª£WXQ
~mohde^stpjkfddbaa^eeeijljkm^_b]^aefi
LMOijlrrtkkkjimfel
}}fivUWiPQfPSkOTmDGd;B\@EZAGZ@E\?D]?B_@E^BH[SWey}yymmwww
¥¥¨³²»¤¤£¨»»¾ÅÅÇÕÕ×ÏÏÒÆÆÈÔÔÖÈÈËßßâ»»¾ÍÍÏÇÇÉÈÈËÌÌÎËËÍÆÆÈÂÂÅÎÎÐÇÆˬ«°¶´»«ª±º¹ÂÎÎ×¾ÁÌ{¬±ãóôæúúæþúåÿúåÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïUVQ[\Wfhclmffha^_Xef_cd][\U^_[^_[Z[Viifccattrceddfe]_^TVUehf{}bcekmlsssiiilll}}}vvxxxz{zddpVXhTUjTVoINh@E^AGZAGZ@EZ?D[?B_@E^>BWBFWkmzllvxxjiytt¨¨¦°zy
¤¤¦··¹ÄÄÆÎÎÐÎÎÐÐÐÓ××ÚÔÔÖÛÛÝÏÏÒÎÎÐÏÏÒÌÌÎËËÍÍÍÏÅÅÇÈÈËÂÁƾ½Ä´³º·¶½²±ºÒÐÚÒÒÝ~Þïðêýýêÿþãýøåÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷áø÷áø÷áø÷ß÷öß÷öß÷öß÷öÝôóÝôóÝôóÝôóÝôóÝôóÝôóÜóòÛòñÛòñÛòñÛòñÚñðÚñðÚñðÙðïOOM[[Xiif
klhefbcd]cd]NOJTTQUUSQQOppmppmXXVddbjjhbb_[[XWWUffdjjjjjjffdhheddbaa^^^\iifxxxqqqttrmmmomr__iPO_LMbGIbAD\AEVLOaNQf?AZAD\?AZ?BWBFZacr}}kkxddq¢ªª¶¥¡°°²ÀÀÂËËÍÀÀÂÏÏÒÔÔÖÈÈËÛÛÝÅÅÇÒÒÔÆÆÈÇÇÉÆÆÈÍÍÏÆÆÆËËÍÉÈϾ½Ä¬±¥¤©³²»ÆÄÐÒÐÚ·¶¿ê÷ùéûùéþúäý÷æÿùåþøäý÷äý÷ãûöãûöãûöãú÷áøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíÜñíSSSNNNkkissqqrm]^Zbc\ijeQQOLLIZZWMMJbb_^^\ZZWiifbb_\\Z[[Xbb_hheaa^ddbeecVVTddbaa^__]eeceecmmksrojieiii
__kTScTVhQUfLP\UZeUXjEH\AEZ?AZ?BWBDXIHXlly}}©©´¤¤°__kppy¢¡¡¡ªª³¡´´···¹ËËÍÉÉÌÅÅÇÎÎÐÇÇÉÛÛÝÍÍÏÓÓÕÄÄÆÉÉÌÓÓÕÝÝßÚÚÚÆÆÈÆÅÌÄÂÉËÉΡ¯·¿½ÉËÇÒÖÕÞ£ßíìëþûéþùæÿùæÿùåþøäý÷äý÷ãûöãûöãûöãûöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíÜñíJJJMMM]][hhefhcbc^_aZfhcTTQOOM__]TTQUUS~~{SSPbc^]^Z[\W\]Xbc^efb^_[efbiifPPN]][eechhemmkjjhddbppmbb____{}^^hZZeX\dU[_\biZ^jFI[@DW@DXBF[HI^TSeiivzzww£¤ª²³¹ÅÆÌbboZZf¶¶¿¶´¹¸¸º¸¸ºÂÂÅÒÒÔÂÂÅÐÐÓÔÔÖÔÔÖÓÓÕÐÐÓÇÇÉÆÆÈÈÈË¥¥¨···»»¾ÄÂÇÉÈÍ¿¾ÂÇÆË£²±ºÍÉÔ¾½ÆÀÁÇÜêéåøöéþùäý÷æÿùåþøäý÷äý÷ãûöãûöãûöãûöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíAAALLLUUSccabc^Z[V^_Xab]WWUXXVUUSNNLXXVeecLLI[\WWXTWXTZ[V_a\\]X\]X_a\^^\ZZW]][eeceecmmkjjhZZWffd[]\^a_vxwklo]^bZ[]X]\]bcX\dDFS@BTDEZLMbTVhLJ[aam~~
¡¢¥¶·¹ÆÇËÆÆÒ
XWhbbo
¡¤¥«¢¥¥¨¨¨ªÀÀ¶¶¸»»¾ËËÍÌÌÎÀÀÂÌÌÎÆÆÈÆÆÈÎÎÐÉÉÌyy{¨¨ªÂÂÅÀ¿Ä¿¾ÂÕÔÛ«ª±¹¶ÀÏÎ×Ö×ÝÍÛÚèúøêÿúèÿúæÿùåþøäý÷äý÷ãûöãûöãûöãûöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíHHHNNNVVTffd]^ZNOJQSL_a\WWU\\ZPPNPPN^^\cca}}zVVTXZU_aZWXQ_aZab[\]VZ[T_a\bb_]][ZZW^^\eeceecddbUUSX[ZX[Z^a_ceddddiiihheTVSTVQ[]Z^_cQQ[IIVEDTML\^]m]]jaaliir}~¯±°¾¿ÁÉÉÓÎÎÛQQ^qqz¢~}ÅÅǬ¬¯¿¿ÁËËÍÀÀž¾ÀÍÍÏÆÆȸ¸º½½¿ÎÎÐÍÍÏÓÓÕÎÎÐÌÌί¯±¾¾ÀÉÈ͸·¾½»ÂÐÏÖ¹¶ÀÍÌÕÜÝãÍÓ×Õãâéûùæû÷ãûöæÿùåþøäý÷äý÷ãûöãûöãûöãûöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñÞóðßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïOOOGGGQQO]][VWSMNIUVOab]]][__]__][[XVVTaa^aa^]][TUPhibUVObc\bc\]^WWXQ^_[[[X]][VVT]][eecddbddbVXWSWXX]^[\^bdcbbbccalljWXTZ[Vbd_abdTU[JJTFFQJJVWWcaajijp\]cz{~}~¥¨¦±²´ÁÁËÐÐÝÍÍÚss^^j¤¤¢¢«¿¿Á³³¶ÅÅdz³¶ÈÈËÂÂÅËËÍÓÓÕÒÒÔªª¬¢¹¹»ËËÍÓÓÕÔÔÖããåééë··¹ÄÂÇËÉв±ºÂÁȯ´¯«¶ÁÀÉÎÏÕÙÞãÛéèèúøæû÷äý÷æÿùåþøäý÷äý÷ãûöãûöãûöãûöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñÞóðßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïZZZPPPQQOOOMPQMNOJWXQ^_[[[X^^\__]\\ZVVTcca^^\ZZW_a\de^XZSde^ab[_aZbc\TUPZZW]][\\Z__]cca__]aa^VXWUZ[PUWNSUcdfiik]]]cab[ZWVWSccaddd\[_WV]TSZSQVTSW]^bvwycdfqrttwvwyx©ª°ÉÉÓ××á¾¾É[[hxx
½½¿ÝÝߢ¢¤°°²¾¾ÀÌÌÎÂÂÅËËÍÒÒÔ¦¦©½½¿ÆÆÈÕÕ×ÚÚÜææéææéÛÛÛ¿¿ÁÍÌÕ¿¾ÇÍÌÓÈÇΣª·¶¿ÀÁÇÝãèáïíèúøèýøåþøæÿùåþøäý÷äý÷ãûöãûöãûöãûöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñÞóðßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïXXXaa^XXVTTQOPLOPLWXQZ[V[\Weec__]bb_[[X__]XXVUVQbc\de^STMXZSde^cd]]^WUVOWXTSSP]][XXV]][XXV[[XTVSNSTGLMEIJ_acaac]]]b_a_^\VVTcca^^^VVXTTVWWZZZ\WWWZ\[fih_ba_bafihqsr
ÅÆÌÖ×ÝÒÓÙ¥¥±vveeqâáå´´·ÅÅÇÍÍÏÈÈËÈÈËÒÒÔÍÍÏææéÒÒÔÍÍÏÌÌÎßßâååèññóäääÇÇÉÆÅÌÐÏÖÈÇÎÄÂǽ¹Á¬³ÅÆÉÎÔÖßëëé÷ôêûøêýùéþùåþøäý÷äý÷ãûöãûöãûöåúöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßóóßóóÞòòßôñßôñÞóðÞóðÝòïÝññÝññÝññUUS\]X_a\UVQQSNIJFOPL\]XZ[V]^Z^_[]^Z]^ZWXTZ[VXZUab[de^[\UMNG^_XWXQZ[TZ[TVWSWXT_a\XZUab]QSNXZU\\ZNPMVXWGIHXXXeee^^\aa^ccaUUSeecddbZZZSSS___[[[OOO[[[hhhXXVddbjjhyyw¿¾ÅÒÒÔÈÈ˶´¹£¢¦~}^]fvs¨¢ÞÞáÒÒÔÌÌÌÇÇÉÅÅÇßßâÉÉÌÖÖÙÐÐÓÒÒÔÙÙÛÞÞÞêêêêêêÕÕÕÅÅÇÈÈË××ÚÀÀÂÀÀ··¹½¾ÀÉÎÏËÒÒÙßÝìöòìøôêýùéþùèýöåúóéþ÷åúóäùòäùôãööä÷÷ä÷ôåøöãöòåøôåøôâôñâôñâôñâôòáóñáóóáòöáòöáòöáóñáóðáóñáóñßòòßñôßñôßñôOPLUVQ]^ZZ[VHIEEFAMNIXZUTUP]^Z^_[WXT^_[XZUTUP]^Z\]V]^WXZSEF?WXQUVOZ[TZ[TXZU]^Z[\WZ[V^_[STOQSN]^ZSSP\\ZPPNVVTZZWQQOWWU\\ZPPNXXViif^^\LLIUUSWWUSSP^^^cca_a\cd_rsowxsttrqqs{z¤¤¦¹¹»dbp}z
©¨¬ÂÂÅÀÀÀÇÇÉÐÐÓÖÖÙºº½ÏÏÒÛÛÝÈÈËÉÉÌÝÝÝ×××ßßßÖÖÖÄÄÄÍÍÍÌÌÌÅÅÅÁÁÁÇÇÇ°²±ÅÇÆÅÉÈÂÇÆìñðïøôìþúêÿúéþ÷éþ÷åúöèýøåúóâ÷òæù÷åøøãöóâôòæùöãöòâöïåøôâôñâôòáóñßòòáóóáòöáòøáòöáóñáóñáóñáóóßòòßñôßñ÷ßñ÷IJFQSN^_[^_[JLGBD?GHDTUPSTO\]X]^ZVWS^_[XZUTUP]^ZZ[T[\U]^WGHAVWPVWP[\UXZSXZS^_XZ[TXZS\]VNOHOPI^_[]^ZZ[VQSNSTO^_[QSNSTOXZUQSNWXTjkfXZUMNIQSNZ[V[[X__]eec_a\de^fhadeappmlllihl~}rrp~¥¥¥£¢©ecpb_lljw´³½´³º¡±±³´´·ÕÕ×ÛÛÝÜÜÞÍÍÏÖÖÙÈÈËÐÐÓÓÓÓÐÐÐÜÜÜÓÓÓÍÍÍÔÔÔÆÆÆÈÈÈÆÆƾ¾¾¹»ºÎÓÒÁÆÅ×ÜÛôþúìþøèûôåøôåøôéûùåøöä÷ôæù÷âôòãöóåøôæùöãöòåøôåøôâôñä÷ôãöóâôôâôôáóóáòöáòöáòöáóñáóñáóóáóóßòòßñôßñôßñôHIETUPbc^^_[MNI>?:EFA[\WXZU\]X]^Z\]X]^ZWXTZ[VXZU^_X^_Xbc\STM[\U[\U]^WVWPZ[T]^WZ[TUVO[\UHIBQSL_aZUVQWXTWXTQSN\]XOPLSTO]^ZPQM\]XijeXZUVWSUVQbc^^_[ccaab]fhacd]bc\de^efb___aacqqsmmmhheqqo{{~ÂÁÆ{z\[d[Zcdcjjim¨¨ª««ËËÍÇÇÉßßâÔÔÖÕÕ×ÕÕ×ÓÓÕÙÙÙÙÙÙßßßáááÔÔÔÍÍÍÕÕÕÌÌ̹¹¹ÉÉ̲³¶³¸¹ÐÕÖÌÐÒéððíûùëûúìýûéùúéùýéøÿè÷ÿåôûå÷úä÷÷ä÷÷ãöóä÷ôâôòâôñä÷ôä÷ôãööãööâôôáóóáóóáóóáóóáóóáóóáóóáóóßòòßòòßòòßñôIJFIJFZ[V\]XLMH9:6BD?]^ZVWSUVQXZU\]X[\WZ[V^_[WXTab[\]V]^WXZSNOHTUNXZSXZSZ[Q]^UbcZ\]T\]TTULWXOZ[TSTMXZS]^WLMFZ[T[\U_aZbc\OPI]^Wde^bc\^_XWXQab[]^Z[\W^_[hibde\abX_aZ]^Zbb_VVXjjljjh_a\jkfccahhj¦¥ªÌËÏzy~~ªª¬¥¥¨bbd°¾¾À¤¤¦¾¾ÀÀÀÂÍÍÏÓÓÕÒÒÔÜÜÜåååßßßâââÜÜÜÔÔÔÈÈÈÁÁÁÆÆÈÅÄÈ¢°±´²´ÂÇÉÛßââèêêôøéöúÕâé·ÂÌ£±¢´£¹¡«¾·ÅÓ°À˺ÌÓÍÞäâó÷áóóÝðíä÷ôÝððáóóáóóßòòáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóLMHLMH\]X^_[QSN?@;EFA[\WOPLQSNZ[VVWSZ[VZ[VZ[VUVQ\]V[\U]^W\]VJLEPQJUVOUVOWXO\]TabXZ[QQSITULXZP[\SOPIWXQbc\UVOTUNWXQ[\U[\UNOHde^ab[[\U\]VUVO]^WXZS\]Xcd]Z[Q]^Ucd[]^UZ[T__]ZZZfffbb_Z[V_a\bb____¡¶´¹²±¶~~¸¸ºkkm
¨¨ª´´·²²´½½¿ÍÍÏÐÐÐÚÚÚßßßéééæææÛÛÛÌÌ̸¸ºÎÍÒ¿¾Â¡¢¥©ª«°²´¹»ÎÓÕáæëÏÖÝÇÎ×´¿¥¶¬±Æ¢»¤¦¥¨¨²¨¹ÀÆ×ÝÜíñÞðóäöùâó÷ßñôÞññáóóáóóáóñáóñáòöáòöáòöáóóáóóáóóáóñáóñVWSQSN[\WZ[V[\WGHDHIEXZUNOJSTOZ[VXZUZ[VVWSXZUOPLWXQXZSWXQXZSGHAMNGPQJQSLSTJ[\S_aWUVMMNEQSIUVMZ[Q\]VQSLXZSXZSVWPZ[TXZSUVOOPIde^^_XWXQ\]VTUN[\UUVOZ[VZ[T]^U_bU^aTbcZUVOaa^\\\bbb\\ZXZUbc^iif^^^iiikkk´´·¡¡£¥¥¨jjlÄÄƹ¹»¹¹»ÒÒÒðððÔÔÔÙÙÙåååÙÙÙÆÆÈÂÁÆÎÍÔ¡¬³°³¹¥©¯»¿ÅÞâèëñóÖÜáÇÌÖ¸½Ëßãø±³Î¨~¢z}¡¢²¿¾ÎÙÙêñãôúáòöÝððáóóáóñáóñáóñáòöáòøáòöáóóáóóáóñáóñáóðWXTTUP]^Z\]Xab]MNIJLGWXTIJFPQMNOJ[\W[\WPQMZ[VIJFUVOVWPOPIPQMDE@IJFNOJNOJPQJZ[Q^_VSTJOPGOPGNOFXZP[\UNOHUVOVWPIJDSTMWXQUVOPQJ\]V_aZZ[T^_XUVO[\UTUP\]XZ[V_aWcd[WXO\]Tab]^^\]][aa^\]X^_[dealmieecbbbTTQddb¢¢¢}}}«««SSUyy{»»¾
ppr¾¾Àppp¶¶¶äääíííæææâââÝÝÝÏÏÏÄÂÇÄÂÇÁÂƱ²¶¨©¬ÒÖÙèìïÓ×Ú»¿Ç½Á͸»Ï·
¢}{{yx
}ª£´¡¯½ËÜãáòøáòöáóóáóñáóðáóðáóóáòöáòöáóóáóóáóñáóñáóñSTOVWSWXTUVQUVQVWSPQMXZUGHDJLGOPLab]LMH[\WPQMIJFQSNMNISTOTTQIIGNNLLLLMMJSTOSTMUVOUVOQSLWXQSTMVWPVWPPQJOPISTMGHAQSLPQJSTMUVMUVM\]V^_XXZUUVQUVQMMJTTQZ[V]^W_aZab[[\UXZU^^\[[X\\Zdeade^ab[lmiffd__]UVQZ[Tjkf}~yooliifXXXlll¡¡¡~~~hhhddb¸¸¶ÎÎÌååãÕÕÓÖÖÔ¯¯¬ÇÇǺº½ÐÐÓ±±³©©«¢¥¦©ÍÎÒæèí¯¯¸©«¸¯¥¡¤¤»¤©À¯¨t{
¢¤²ÂÕåðáòøÞòòßôðßôíãøñâôñãóòãóòãóôâòóâòöâòöâòöJLGPQMVWSVWS[\WXZUNOJSTOGHDIJFUVQ_a\LMHWXTNOJEFAQSNLMHNNLUUSLLLLLLGGIJJJOPLPQJTUNTUNSTMMNGZ[T[\UVWPPQJOPISTMNOHLMFSTMOPIUVMUVMXZS]^WVWSTUPOOMHHFPPNXXV]^Zab[cd]^_[XXV]][^^\__]^_[^_XUVO^_[XXVTUPQSLVWNcd]mohbc^xytffd\\Z___qqqvvvmmm\\\^^\qrm¦¨£×ÙÔ«¬¨ÄÅÀÌÌÌ···ÎÎÎÐÐÐÛÛÛ½½½ºººªª¬ÝÜãÒÐÚ¾¾É¬«»¨¢£
¡«¶¢¤½±©y¦£±¿ÕæíÞðóÛðìáöïßôíâôñãóòãóòãóôâòóâòöâñøâñøFGBNOJTUPVWSVWSTUPJLGOPLPQMLMHZ[VTUPJLGVWSVWSNOJOPLMNIMMJSSPPPPGGG@@BGGGIJFQSLMNGUVONOHWXQ]^W^_XVWPPQJOPISTMUVOHIBOPINOHUVMUVMVWN[\UQSLUVQQSNPQMOOMVVTWXTZ[Tef_deaZZW^^\[[Xaa^XZU^_X[\U]^ZPPNJLGUVO\]Tab[bc\TUPefb[[XQQO\\Z__]eeeqqqxxxiiiqqsVVV\\Zffdlljyyw¶¶³{{yrrp´´´³³³ÆÆÆÁÁÁÈÈȬ¬¬½½½»»¾âáèÇÅÒÍÌܨ©{{¢wy~ª£¦»²¶É£´©{¡¬»ÆÚëñæùùãøôáöñâôòãóòãóôãóôâòóâòöâòöâòö@@>FFDJJHJJHVVTPPNBB@DE@QSNJLG[\WTUPJLGTUPUVQIJFIJFPQMNNLOOMSSSEEE::=EEEHIEHIEQSNQSNNOJQSNTUPZ[VVWSPQMOPLSTOXZUIJFJLGNOHJLBTULXZP]^WNOHSTOMNIJLGPPNPPNOPLPQJ]^Wbc^XXVVVT\\ZccaPQMVWPWXQWXTJJHGHDVWP]^U^_XXZSPQMcd_bb_]][TTQ\\Z___hhh[[[TTV^^aLLNNNNbb_ddbiifeecmmk]][³³³ººº¾¾¾
{{{ÂÂÅÙ×ÜÆÅÎÆÆÒ°¯¿Ö×ì¿ÀÙ±¤wxxy}£©¢°©tw¥ÏÞæÜíóßñôä÷÷ãóôãóôãóôãóôâòóâòóâòóâòö>>>AAAMMMFFFHHHJJJAAA@@>PQMPQMXZUMNI@A=HIENOJEFAGHDFGBFFDGGENNNAAA99;>>>DDA@@>LLIMMJLLIJJHNNLOOMUUSMMJIIGPPN]][SSPJJHMNISTJWXO^_X^_XPQMEFAHHFIIGLLIPPNVWSSTM^_X]^ZSSPTTQUUS]][UVQUVOZ[TUVQHHFMNIWXQ_aWab[]^WUVQ\]XZZWPPNUVQXXVQQO[[[UUUMMOONSGGIPPPZZZZZZ\\\TTT[[[\\\iii°°°sss½½ºÉÉÇÀÁÅÅÇÖÐÒæ²¥{}xyxz}ª¨»¢ª½¦r}s¨·ÆÐÜíôÛìòãó÷äôöãóôäôöãóôâòóâòóâòó666;;;GGG>>>DDDAAA>>>AAAMMJJLGPQMNOJBD?HIELMHDE@DE@EFAEEBDDASSSFFF88:::=;;;@@>??=IIGGGELLIGGEFFDOOMLLIFFDQQOXXVSSPNNLHIELMFSTJ\]V^_XWXTGHDFFDDDAGGEJJHQSNMNG[\U]^ZSSPOOMVVTXXVUVQPQJVWPLMHIIGSTO^_Xfh^fhabc\VWSQSNSSPXXVNOJTUPWWUPPPQQTGGIHGLA@ENNP[[[UUU___WWWSSS]]]ZZZbbb~~~jjjlllooo±²ÇÈĪ¬«xy¦ª´}xyrty{{~wz
¢¬£¨¶¢¨º¬rzlwr}ª²ÀÎãóþâñøãó÷äôøâòóãóôãóôâòñâòñ>>@::=FFHFFHJJMHHJBBE999MMJIJFGHDDE@BD?FGBLMHGHDBD?FGBFFDBB@TTTJJJ88:99;888777>>>BBBLLLEEEGGGGGGJJJFFFGGGQQQQQQUUUOOODDAMNGPQHVWPVWPWXTIJFLLILLINNLLLIMNIHIB^_Xfhc\\ZTTQWWU[[XZ[VUVOWXQOPLHHFTUPbc\ijahibab[XZUFGBJJHXXVOPLXZUUUSNNNSSUPPSQPUDBGJJMUUWPPS^^aJJMHHJPPSbbd^^acceXX[VVX[[]ddf~~
¯¯¯£¤¦motz~oq~qsps{{rvtx¦±¦iq
fos~¥¥·ÄÒâÜëóâñøâòöâòóãóôãóòãóòâóð???>>>IIIGGI??A;;>AADAAAEEBHIEEFA?@;?@;BD?LMHMNIDE@IJFIIGDDAQQQNNN:::;;;777666===AAAJJJNNNMMMNNNGGG>>>JJJPPPMMMVVVLLLFFDGHDMNGUVOVWP]^ZOPLQQOQQOJJHEEBHIEFGBUVQbc^VVTJJHOOMZ[VVWSXZUQSNXZULLI]][^_Xde\cd]\]Vab]NOJNNLSSP\]XXZUXXVJJHQQQPPSUTXLJOFEIIHMNMQZZ\PPSMMOOOQ[[][[]]]___bWWZTTViikooq
yy{fhkz~kotfjrilwoq~vxrtfizwx
~£
¡vz¤qypyª¥ºÅÔÞÛêñßðóßðñâòóãóòãóòãóò480794BEALNM>?A236687FHE@@>FFDFFD>>;GGEAA?FFDHHFDDALLIJJHJJHVVT]][TTQFFD442997;;9@@>DDALLIJJHBB@BEAEGFILJEFHFGINPO@BAGIFDDAIIGUVQ[\WOPLLMH997???GGEBB@FFDEEB]][[[XQQONNLLMHSTONOJWWUSSPMMMIIIUUSef_de\ab[ef_cd_TUPMMJNNLWWU\\Z[[XTUPMMJNNLUUUSSUDEJEFLFGMVW[MNQIJNIJMUVX[[[QQQUUUVVVOOO]]]eeeeeeefi^_bijlabdcdhopshilefjdekqqzssjjwrq¬oqcfzw}{s{t{~¢¬ÌÙßßìïâðïäôöáñòãööáóóAE:AE=EGBDFE9:=013687ILHEEBLLIGGE886663@@>GGEEEB??=MMJPPNQQOaa^bb_XXVTTQ??=@@>;;9DDAFFDLLIJJHAA?JMIEGFGHJFGIDEGIJMJMLFHEFFFGGGVVTXZUQSNTTQ???;;;FFD@@>FFDNNLiif__]SSPSSPMNIOPLOOMJJHQQQFFFDDFBBBVWPab[ab[TUP\]XVVTMMJMMJSSSWWWPPNXZUIJFNNLNNNOOQFGMDDMGHNTU[NOSEFIEFHVWZSSSMMMTTTXXXQQQVVV[[[WWW^_bUVX^_b]^a]^abcebce]^abcfdei{}ffpvv«ª½¡¥ijadvsx£¦º{txt{¤ÛèêäòñãóôäôöÛííä÷÷BF;=@8>@;>@?78:-/1243BEAMMJOOMFFD220220??=EEBGGE@@>IIGJJHLLIUUSbb_ccaaa^OOMEEB997BB@DDALLIJJHBB@DFB>@??@B?@B:;>@ADNPOILHLLLNNNZZW\]XUVQOOMPPP@@@LLIMMJIIGJJHbb___]TTQLLIUVQMNIOOMNNLNNN===::=???PQMXZS[\WGHD\\ZLLIFFFIIIQQQWWWLLIXZUJLGOOMOOOJJMGHL@AGEFLNOSOPTFGIFGIWZXMMMHHHNNNTTTVVVUUUSSSWWWTUWUVX\]_VWZbce]^a]^abcebcfdeibci^^hhhsllyzy¢qrikz©¬°Á¯qsrwr{vÅÒÔâðïáñôãó÷åö÷ßðñTWMNQI?A=@BA@AD347243?A>NNLIIG??=774>>;;;9==:JJHGGEHHFHHFQQOJJHQQOSSPXXV]][HHF774AA???=IIGMMJGGE:=9;>=@AD=>@;=?;=?EGFDFBGGGOOOOOMXZUWXTJJHJJJ???LLIHHFAA?GGEZZWTTQJJHJJHHIEHIEHHFGGE@@@;;;88:AAA\]XXZUXZUUUSLLI???IIIGGGJJJQQQQQO_a\QSNQQOSSSFFH@AE;=BBDGGHLPQTMNPMONX[ZJJJEEEIIINNNIIITTTTTTVVVOPSNOQTUWVWZXZ\_acXZ\[\^^_cefjfhmQSXeeoddpxx
¨z}_bomq{~txqw
wªÜéëäñøâñøãó÷áñò[^TOSJ8:68:9@AD124364>@=LLIJJHAA?::8HHF;;9??=QQOBB@??=QQOSSPNNLOOMOOMNNLZZWEEB::8EEBEEBFFDLLIPPN9;80219:=46801389;>@?@B?FFFQQQSSPNOJXZUSSP>>>BBBOOMLLIGGEFFDOOMHHFBB@BB@EFA?@;AA?DDADDD???::=EEEPPNPQM[[XAA?>>>:::FFHPPSFFFMMMNNLXZUSTOMMJSSSIIL?@D:;?GHLSTVIJMMONNPOUWTOOONNNFFFMMMHHHQQQOOOQQQLMONOQOPSMNPXZ\]^aVWZPQTVWZVW[^_c]^b[\b_afffpmmyz}acp[^feiqmp}{zoqlq{ipwÅÏÕáëóÞëòßìñéöúTWMLOGDFA;>=@AD679132@B?LLIQQOGGE774MMJEEBAA?NNLEEBAA?PPNLLIWWULLIFFDIIGVVTAA?442??=BB@IIGJJHTTQ>@=79889;/02124>?A?A@GIFGGGEEEHHFQSN[\W^^\LLLLLLLLIIIGJJHEEBIIGIIGAA?GGE@A=DE@HHF@@>BBBHHHEEGBBBJJHMMJZZWBBB@@@NNPGGIMMODDDAAA>>;NOJXZUMMJPPPIIIBDFBDGNOQZ[]LNMLNMJMIQTPVVVQQQEEELLLHHHNNNSSSVVVPQTOPSPQTFGITUW[\^UVXTUWQSUTUW\]_^_cOPTVW[cdhklrdhpSV^W\^efjss}vt
oo{eiqekpv~¶½ÄÜãì¸ÁÌâìòâïóMPFMPHNPL=?>=>@78:8:9LNJGGENNLHHF>>;PPNNNL>>;JJHBB@@@>MMJFFDUUSTTQNNLHHFWWU@@>331;;9DDAQQOIIGUUSFHE;>=89;34789;@AD:=;>@=HHH>>>HHF]^Zab]\\ZZZZLLLLLIMMJHHF@@>DDAEEB774IIG@A=;=8FFDBB@DDDJJJNNPGGGGGELLI[[[EEENNPQQT@?DBBE;;;>>>??=QSN^_[LLIJJJIIIGHJHILMNPUWVNPOHJGEGDMOLSSSVVVLLLOOOMMMJJJWWWWWWVWZMNPIJMIJMPQTPQTZ[]WX[LMONOQ[\^_acUVXUVX^_bbceX]_TX[dehdehdcjvt~wtjirbfibhjjpt£©°¿ÆÐÌÖÜÙãæIMBSVNPSN=?>=>@1249;:HJGMMJFFD??=@@>AA?UUS>>;TTQFFDGGEUUSMMJLLIOOMOOMLLI\\ZEEB663>>;FFD[[XFFDNNLBEA8:92364682369;:HJGILHDDDFFDIJFJLG[\WPQMJJH??=GGETTQJJH886>>;MMJ;;9HHFFGB?@;NNLIIGFFFSSSUUU???GGGMMM^^^JJMIILEDHDBG;:?88:>>>==:HIE^_[GGEFFFHHJBDFFGIGHJSUTOQPGIF@B?ILHGGEXXXWWWVVVSSSHHHXXXUUUQTSQSUGHJMNPMNPNOQXZ\\]_QSUPQTVXWX[Z[]\UWV^a_abd^cdPUVacbbdcbaehfmmlsbah^cdejkilrv{}»ÂÉÙãæGH?PQJWXTMMM@@B//1BBBBB@JMH:=8362798>@?TVSFHDTVQDE@MNIVWSOPLNOJJLGMNIHIEQQOOOM9:6FGBVWSab]GHDLMHJJJ>>@224224DDDIII??=DDALLIHHFMNINOJVWP^_XSTMHIEJJHMMJDDA442::8886886EEBNNL@@>EEBTTQAA?LLIXXVIIIOOOEEETTVPPSVUZFEIBAH:9>88:???@@>JJH]][AA????JJMABFFGJHIMFGIXZ\MONILJMOLIIGTTQ^^\MMJTTQPPNNNLTTQSUTQTSNPOLNMILJILJQTS\^][\^UVXUVXVWZ^_b[\^[\^]^a\]__ac\]_^_babd^_c^_c_ac^_bijlhioeiq_dp^co£@A8MNGOPLIIIDDF113:::;;9PSNAD?3644768:9NPO@B>OSJNOJLMHOPLLMHPQMFGBLMHAB>UUS[[XGHD?@;HIBZ[THIBJLGFFH327224BBELLLPPPAA???=??=??=HHFNOJPQM\]VUVONOHJLGPPNNNL;;9442663774GGEPPNBB@AA?QQOIIGEEBPPNSSPWWWIIIGGI??AFEI@?D=;B98=779??ADDDZZWaa^LLL@@BJJM9:>?@DDEHFGJOPSGHJGIHJMLJJHOPLZ[VUVQXZUVWSQSNVVTPSQX[ZWZXLNMHJIFHGJMLUWV\]_TUWQSUTUWZ[]WX[STVXZ\[\^XZ\[\^Z[]^_bQSUVWZ\]_bbbcce_afadlX\fX]hsy}GH?LMFTUPFFFQQT113111;;9MOJAD@/10/02468JML=?;JMHNOJIJFOPLMNIGHD?@;IJFEFAFFDUUSWXTPQMNOHWXQJLEQSNLLL99;,,/;;;UUUUUSNNL@@>@@>>>>EEBGGELMHVWSNOHGHABD?MMJPPNHHF886220997@@>SSPDDA==:NNLJJHFFDPPNTTQWWUJJJNNNHHJ@@B;:??>BIHM@@B??AMMMZZWZZWQQQ;;>GGI:;?;=@?@DBDGMNPHILGIHHJIMMJNNLUUSZZWVVTUUSPPNTTQQTSWZXVXWOQPHJIGIHGIHOQPXZ\QSUNOQVWZXZ\Z[]PQTWX[XZ\VWZXZ\WX[]^aPQTQSU]^a_acWX[TU[[^fSV^LOW_ej_ejLMDQSLNOJ@@@SSU>>@777;;9LNJFHE236347679LMO>@=MOLPPNIIGOOMEEBBB@AA?FFDDDAMMJVVTVWSFGBFG@VWPQSLVWSFFF447;;;777LLITTQHIEEFADDDAAAGGGIIGFFDTUPQSNMNIMMJUUSMMJPPN==:++)997??=TTQDDA>>;LLILLIIIGNNLPPN\\ZIIGUUULLLWWZMMOFEI87;668==?HHHQQO\\ZJJJMMOHHJ89=9:>@AEHIMIJMEFHADB?A@OOOQQQSSSVVVNNNQQQNNNPPPSUTLNMMONSUTNPOILJHJIMONWX[TUWJLNMNPTUWTUWIJMTUWTUWWX[TUWVWZUVXSTVJLNXZ\WX[OTVIMSPTZQU]OSXZ^aW\^VWNUVOMNIDDDOOQTTVHHH::8>@==?>*+-&(+67:@AD@BAEGFFFF>>>FFFFFFMMMPPPPPPMMJAA?QQOWXTHIEIJDNOHTUNPQMJJHGGGTTTFFDIIGVWSSTO>?:888668>>>AAA>>;HHFPPNSSPSSPMMJNNLQQOIIG774442??=VVTOOMEEBFFDUUSMMJIIGMMJXZUXXVWWULLLTTTLLN--0336113>>@HHHUUS\\ZOOOLLN??A67::;?=>AGHLDEGFGI=?>EGFLLLTTVLLNOOQLLNNNPGGIQQQSUTOQPQTSOQPWZXPSQOQPZ\[VWZUVXGHJLMOPQTXZ\FGIHILPQTQSUTUWWX[STVZ[]MNPPQTMQSPUWJNTBFLLOUVZ_TXZTUWWXOLMFHIE???DDFGGI>>>11/79878:)*-*+/237469468:=;DDD======AAADDD@@@@@@DDDEEBUUSVWS?@;@A:FG@VWP[\WQQO[[[SSPEEBNOJQSNOPI894AAA>>@>>@======JJHPPNJJHIIGMMJHHFNNLOOM@@>AA?BB@UUSVVTNNLHHFSSPIIGMMJJJHUVQSTOLLIQQONNNOOO;;>IIL88:;;>QQQOOM[[X\\\BBEDDF?@D?@D;=@>?BDEGDEG9;:;>=EEGPPSJJMGGIGGIJJMFFHLLNJMLMONQTSPSQSUTSUTUWVTVUOPSNOQEFHIJMIJMPQTBDFFGILMOPQTSTVTUWMNPVWZTUWOTUFMMPVXPTZEHNIMSTUXQTSQTSUVMPQJEFABBB??AEEGBBB886243236(),/0634:67:78:=?>FFH::=::=??AAAD::=::=AAATTQVVTWXTMNIJLEFG@NOHOPLTTQ^^\]][GHDMNIQSLEF?9:6IILGFJ??A::=999HHHMMJGGEGGEJJHGGENNLSSPGGE??=;;9OOMWWUTTQGGEIIGJJHNNLFFDQSNLMHMNILLINNLNNNFFF@@@GGIMMOUUUGGEXXVNNNAAD88:78;9:>?@D@AE:;>>?A?A@@BAEEGNNPLLNFFHEEGGGIEEGEEGFHGMONOQPSUTTVUQTSVXWOQPTUWNOQEFHEFHBDFMNPHILNOQHILJLNPQTQSUHILPQTWX[MQSAGIMSUQW\IMSMQTTUWTTQVVTJLEIJDHIE??=BBBQQQIII444-/1236+,0/03-/29:>;=??A@JJM447@@B@@B>>@::=::=>>@@@@UUS[\WGHD?@;BD?OPLWXTPPN\\Z\\ZLMHGHDLMHDE@==:LLNMMOAAD==?777AAAJJHLLIHHFBB@>>;@@>@@>BB@>>>999HHHPPPMMJFFDJJHFFDFFDLLIMNINOJJJHIIGMMJ[[[XXXUUULLNPPSQQQDDAUUSPPP99;44778;=>AIJNIJM9:=?@BDEGBDFBBEEEGFFHBBBBBBEEGFFHAADEGFNPONPOSUTPSQILJVXWVXWUVXOPSJLNJLNEFHJLNFGIFGIFGIHILLMOPQTHILMNPWX[GLM=BEJPSOUWJOQLPQPSQNPMTTQNOJOPLPQMGHDDE@GGEBBE87;:;A01778;013124468:;>>?AEEG779779AADIIL99;336::=HHHZZWUUSEEB>>;FFDLLI[[XPPNZZZ\\\PPNBB@MMJBB@BBBDFEILJ;>=FHG=?>8:9MONFHGLNM@B?9;8=?;?A>;>=78:348=>AGHJHJIILHLNIJMIHJGFHGHJINPOFHGFHGFHGLMO?@B:;>ABEIJMEGF?A>TVSOQP@AD89;/039:>FGIUWV=?>;=?:;>?@D=>AEFHSUTOQNEGD=?>@AD?@BLNMNPOPSQQTSQTSJMLNPOX[ZTUWPQTIJMNOQJLNGHJGHJIJMDFEGIHGIHMONFHGLNMVXWMNPINOHMNMQSLPQHMNINOMQPQTSPPNMMJLMHDE>;=8AA?>>@32911:/06;=@6796879;:9:=:;>>>@224779??A==?224//1668===IIGVVTNNL>>;EEBPPNTTQZZZXXX\\\IIIAAAGGGFFF@@@?A@BEDDFEBED:=;8:9JMLEGFILJ>@?362362:=;89;46923967=@AEDFEEGDGIEFHEEGFDFEJLNNOQDEGABEFGIQSUDEG9:=BDFABEDFE@B?OQNTVUIJMABE2379:>EFHVXWEGF;=?78;:;?>?BIJMTVSTVQJMHBEADEG?@DGHJHJILNMQTSMONHJILNMTVUWX[OPSIJMMNPPQTJLNEFHABEGIHHJINPOLNMJMLSUTTVUMONFJLEIJINOHMNEIJGLMINOLPQIIGQQOTUPFG@9:6??===?329,,6-/4?@D?@B243=?>;=?>?A?@D12667:78;-/2+,0126;=?>>>HHFUUSQQOAA?AA?JJHUUSUUUZZZWWWPPP@@@JJJEEEEEEADB@BAHJIADB9;:9;:HJIEGFMONDFE9;8796=?>9:=46912889??@D=?>;>:?A=BEAFHGILJGHJJLN?@B?@B?@BQSUNOQLMO>?AEFHILJ?A>NPMSUTHIL@AD:;?ABFGHJVXWSUT>?A78;89=469GHJQTPWZUOQMILHEFH>?BJLNHILFGIGHJEFHBDFFGILMONOQIJMGHJEFHJLNJLNHILEFHEGFFHGJMLEGFBEDMONQTSNPOHMNGLMLPQJOPBGHDHIGLMINOQQOUUSUVQGHA=>9BB@AAD98?//8+,2>?BEFH687ADB:;>:;>>?B126:;?:;?237014348:;>???MMJLLIDDA>>;FFDHHFQQOWWWUUUZZZPPPEEEIIINNNGGG@BA8:9=?>:=;7989;:FHGDFEHJIDFE=?;9;8DFE>?A469/0667=>?B>@?=?;FHDFHEFHGFHG>?AFGI?@BBDF;=?EFH;=?9:=78:?@B=?>=?>PSQLNM=>@67:67:ABFDEGJML\^]GHJ>?B89=:;?MNPPSOTVQOQMILHGHJABFHILGHJEFHEFH?@B?@BBDFFGIHILMNPNOQ?@BIJMJLNIJMEFHFHGNPOJMLFHGILJMONVXWLNMEIJDHIHMNGLM?DEAFGDHIFJLSSPWWUWXTPQJ=>9??=DDFA@GEEN12878;347798@BA@AD4687;>&*037=14:-17*-3*-3-/2:::NNLQQOIIG??=AA?GGELLINNNUUUWWWNNNAAAHHHQQQGGGEGF?A@9;:ADB8:99;:DFE@BAEGFBED@B?=?;GIH9:=126-/478>;=@ADB>@=BE@?A>:=;798>?A;=?67978:67989;78:3479:>:;?9:=>?AGHJPQTFGJ46989=?@DEFHEGF\^]LMO=>A12667:FGIQTPX[V^a\JMIHIL;=@?@D>?B?@D?@D;=@:;?@AEHIMGHJ?@BJLN9:=ABELMODEG=>@HJIOQPJMLFHGFHGEGFNPOHJIAFG@EFDHIAFG>BD;@ABGHGLMOOMUUSUVQOPI894886==?>=D88A/0689=6799;:ADB>?A12637=$(-,06(+1*-3(+1*-3014;;;LLINNLHHF@@>==:??=HHFJJJUUUTTTQQQ===GGGMMMIII@BA=?>9;:HJI?A@>@?DFEBEDADBBEDEGDGIFILJ;=?46912834:78;>@?>@=GIEEGDBEDADB>?A9:=12434767989;89;67:78>;=B89=89;DEGNOSDEJ12878;=>AABEDFE[]\PQTBDG9:>FGJMNPSUQWZU]_[PSOLMO?@D;=@:;??@D>?B78;9:>ABFIJNMNPGHJPQT@ADGHJIJMGHJGHJPSQOQPNPOEGFGIHEGFMONMONFJLDHIFJLDHI;@A;@ABGHEIJSSPUUSPQMGHA340997;;>218++4,-378;679364>@?>?A469-17&*2/2:,08%)1%)1*-6239PPPQQOBB@442==:??=;;9==:IIIOOOUUUSSS;;;AAAIIIDDD@BA=?>>@?JMLEGF?A@?A@?A@9;:?A@GIFLNJILJ;=?469128/060149;::=9FHDBEA=?>8:99:=67901334746878:78:46934:89?46967:HIMIJN?@F67=34889=?@BDFESUTPQTDEH>?BGHLIJMOQNSUPZ\WUWTOPS?@D9:@9:@>?E:;A78>89?=>D@AEABEABENOQEFHEFHFGIJLNLMOGIHGIHMONJMLGIHADBFHGHJIBGH?DE@EF=AB9>?;@ABGHBGHNNLQSNOPIFG@:;7::8779,+2%&,,-3469124687>?A;=?014),4#&/&*2"%-"%-#&,*-3348DFELLIFFD??=886DDALLIGGEBBBNNNPPNTTQ;;9IIG@@@>>>9;:@BAGIHPSQOQPEGFDFE?A@=?>BEDLNJQTPGIH9:=126-/467=237:=;=?;@B?=?;9;:78:89=67:01434812634834812622;33=12878;IJNNOSDEJ78>12889==>@ABESTVVWZJLNFGJNOSHILILHJMHQTOTVSMON;=?9:>67=:;A9:@12878>>?EABH>?B?@BHILABE89;?@BJLNIJMFHGJMLLNMQTSNPOADBFHGIJMDHI?DE?DE;@A7;=;@ABGH@EFPQMMNGOPISTMMNIBBB=;@43:)*0()/78;3474689:=78;/06**6%%1$$-%%/()/126236013>@?BED@BA9;:;>=ADBFHGLNMPPPHHFQSNQSNHIELMHHHFBBB78:@ADFHEMOLLNJJMIIJM@AD>?ADFEOQNNPMADB78:34:,,612812689;9;::=;89;78;78>14:14=03;/2=-1;69A26>03;009009/0678;GHJJLO@AG34:1289:@?@D=>AFGJVWZEFHEFH@@BLLNFFFIIGUUSUUSNNLGGGEFH89=34:67=11:88A66?88A?@DEFHHILBDF=>@=>@GHJEFHEFHBDFIJMNOQGHJGHJEFHHIL:;?=>A>?B:;?9:>9:>=>A:;?PQJIJDZ[TMNIDDA===76:218*+1,-3:;?236-/1348128((1))4%%2%%1&&0&(--/2468687?A@:=;798798?A@?A@BEDADB???GGGJLGOPI892>?:???;;>89=ABEFHENPLQTOGIFFGIFGJBDFABEGIHNPMJML=>@239,,634:67=9:>236?@B?@D;=B46;37?,0:+-:02?13@79F14?,08--922;017237:;>QSVFGM67=34:78>EFL=>DBDGNOSGHJ>?A??ALLNHHHFFFSSPWWUPQMEEBNOQ?@D34:239//866?11=>>G?@DDEGGHJ@AD:;>=>@DEGDEG>?A=>@HILNOQMNPFGIABEGHJDEH@AE;=@78;=>A=>A>?B=>AQSLGHAWXQOPLBB@III649107+,2128>?B013+,/126/06%%/&&2$$1%%1))2%&,*+/9:=>@?>@?9;:364243:=;BED>@?>@?GGIGGGNNLLMHAB>IIG;;;==?9:>>?A>@=DFASUP@B?;=?@AE@AD9:=:=;GIFNPO?@B017//834:89?=>A89;>?A89=34:34:26>+/9*,9/1>13@79F14?,0822>//8,-3348?@BOPT@AG23923967=DEJ;=BABFMNQHIL;=?JJMIILFFFHHHVVTTTQMNIHHFX[ZWX\89=67=34:66?11:88A=>A@AEABF:;?78;;=@?@DABF?@B>?AHILNOQMNPFGIABEGHJ?@D9:>67:469BDGEFIBDGDEHLMFQSLTUNNOJMMJ@@@327/-4()/128=>A)*,*+-/03,-3$$-%%1$$1&&2**3#$*&(+;=?BEDEGF679/02679679:;>ABE@ADDDFDDFNNLPPNOOMMMJJJMAAD>?BDEG>@=>@;NPL:=9124014@AD>?A:=;BEALNM=>@-/400967=23934812478:78;67=34:/2:-1;,/;+-:-0=68E26@03;11=11:,-3014FGISTWIJP67=-/467==>D9:@ABFQSVIJM=>@>>@@@BEEEFFFTTQVVTOPLFFDZ\[abdABF237/0612899B67=BDIEFIEFI=>A469:;?;=@@AE?@B=>@DEGHILGHJGHJEFHHILGHL=>A469126>?BBDG@AE?@DSTMIJDQSLVWSEEB@@@98=/-4()/128;=@+,/*+-)*-#$*$$-%%1##0$$0&&0&(-)*-BDFILJ236/03,-1348;=@67:78;34898===?GGGLLLAAA???336-,1+,0236AD@HJFTVQGIF124/03=>@>?AADBEGDBED89;,-3++412846;78;01378:89=89?78>,08(+6-0=02?+-:+-:+/9+/7--9,,6,-3469LMOLMPNOU:;A/0646;89?9:@@AEIJN@AD9:=AAD==?:::AAATTQ[[XLMHLLIZ\[TUWMNP=>A126/06*+19:@;=B;=B>?E46;67==>D?@F>?B;=?;=?BDFEFHIJMIJMHILFGIDEGEFH679124?@BHILHILDEG?@9@A:PQJUVQTTQFFF438(&-()/78>BDG/02)*,)*-#$*%%/##/""/$$0**3()/014GHJTUW89=/03+,0-/2/03014:;?237,+0;:?BBEHHHEEE@@B76:0/34699:=:=9?A=OQMLNJ:;>78;89;;=?>@?=?;>@?78:-/4,,646;46;469,-089;67:128-/4+/7),7,/;,/;*,9*,9*-8*-6--9,,6&(-9:>JLNJLOQSXLMS0172399:@46;BDGDEH>?A;=?HHJ99;888>>>MMJOOMIJFMNIPSOTVUUVXNOQ:;?014/0646;89?67=78>34:01789?;=B:;A9:>468=>@EFHGHJGHJEFHBDFDEGABE9:=89;=>@DEGEFH>?A:;4>?8PQJWXTMMJBBB327%$+*+1?@FJLO347,-0,-1)*0++4((3$$1**666?$%+126BDFSTV:;?-/4,-3+,2239-/4017)*00/698=HHJHHHEEE??A98=/-4:;?;=?8:79;7NPLJMI9:=67:89;@AD@BA9;88:9679/06--746;128126,-078:4699:@ABH+/7),7,/;(*7)+8)+8),7),4))4009+,278;BDFFGJFGM:;A9:@01723934:>?B@AE;=?:;>>>@::=>>>===IIGOOMSTOQSNLNJQTSNPOIJM469-/2126;=B:;A66?33=22;11:77@;;E;=B@AE9:=;=?@AD?@BDEGGHJIJMHJI=?>687798=?>ADBFHGADB>?8:;4IJDTUPIIGHHH:9>&%,+,2DEJQSV78:124237/0622;%%1""/**699B()/78;?@BQSU9:>+,2/06,-3,-39:@89?67=43::9>AADHHHBBB::=76:+*189=78:;>::=8QTOEGD-/1+,0236>?A>@?241364468017--7017,-3014/0223678;78>34:*-6*-8+-:$&3(*7(*7(+6(+3++7))2,-39:>GHJIJNOPVQSX@AG017*+146;;=@?@D89;89;??A==?777;;;FFDOOMTUPPQMLNJSUQLNMGHJ/03-/2+,0;=B??H66?22;33=33=22;33=67=78;4689:=9:=@ADEFHHILIJMDFE4763648:9@BA@BAFHGHJI
\ No newline at end of file
diff --git a/src/third_party/libwebp/examples/vwebp.c b/src/third_party/libwebp/examples/vwebp.c
new file mode 100644
index 0000000..a79c72c
--- /dev/null
+++ b/src/third_party/libwebp/examples/vwebp.c
@@ -0,0 +1,597 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple OpenGL-based WebP file viewer.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#if defined(__unix__) || defined(__CYGWIN__)
+#define _POSIX_C_SOURCE 200112L // for setenv
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(WEBP_HAVE_GL)
+
+#if defined(HAVE_GLUT_GLUT_H)
+#include <GLUT/glut.h>
+#else
+#include <GL/glut.h>
+#ifdef FREEGLUT
+#include <GL/freeglut.h>
+#endif
+#endif
+
+#ifdef WEBP_HAVE_QCMS
+#include <qcms.h>
+#endif
+
+#include "webp/decode.h"
+#include "webp/demux.h"
+
+#include "../examples/example_util.h"
+#include "../imageio/imageio_util.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+// Unfortunate global variables. Gathered into a struct for comfort.
+static struct {
+ int has_animation;
+ int has_color_profile;
+ int done;
+ int decoding_error;
+ int print_info;
+ int only_deltas;
+ int use_color_profile;
+
+ int canvas_width, canvas_height;
+ int loop_count;
+ uint32_t bg_color;
+
+ const char* file_name;
+ WebPData data;
+ WebPDecoderConfig config;
+ const WebPDecBuffer* pic;
+ WebPDemuxer* dmux;
+ WebPIterator curr_frame;
+ WebPIterator prev_frame;
+ WebPChunkIterator iccp;
+ int viewport_width, viewport_height;
+} kParams;
+
+static void ClearPreviousPic(void) {
+ WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic);
+ kParams.pic = NULL;
+}
+
+static void ClearParams(void) {
+ ClearPreviousPic();
+ WebPDataClear(&kParams.data);
+ WebPDemuxReleaseIterator(&kParams.curr_frame);
+ WebPDemuxReleaseIterator(&kParams.prev_frame);
+ WebPDemuxReleaseChunkIterator(&kParams.iccp);
+ WebPDemuxDelete(kParams.dmux);
+ kParams.dmux = NULL;
+}
+
+// Sets the previous frame to the dimensions of the canvas and has it dispose
+// to background to cause the canvas to be cleared.
+static void ClearPreviousFrame(void) {
+ WebPIterator* const prev = &kParams.prev_frame;
+ prev->width = kParams.canvas_width;
+ prev->height = kParams.canvas_height;
+ prev->x_offset = prev->y_offset = 0;
+ prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
+}
+
+// -----------------------------------------------------------------------------
+// Color profile handling
+static int ApplyColorProfile(const WebPData* const profile,
+ WebPDecBuffer* const rgba) {
+#ifdef WEBP_HAVE_QCMS
+ int i, ok = 0;
+ uint8_t* line;
+ uint8_t major_revision;
+ qcms_profile* input_profile = NULL;
+ qcms_profile* output_profile = NULL;
+ qcms_transform* transform = NULL;
+ const qcms_data_type input_type = QCMS_DATA_RGBA_8;
+ const qcms_data_type output_type = QCMS_DATA_RGBA_8;
+ const qcms_intent intent = QCMS_INTENT_DEFAULT;
+
+ if (profile == NULL || rgba == NULL) return 0;
+ if (profile->bytes == NULL || profile->size < 10) return 1;
+ major_revision = profile->bytes[8];
+
+ qcms_enable_iccv4();
+ input_profile = qcms_profile_from_memory(profile->bytes, profile->size);
+ // qcms_profile_is_bogus() is broken with ICCv4.
+ if (input_profile == NULL ||
+ (major_revision < 4 && qcms_profile_is_bogus(input_profile))) {
+ fprintf(stderr, "Color profile is bogus!\n");
+ goto Error;
+ }
+
+ output_profile = qcms_profile_sRGB();
+ if (output_profile == NULL) {
+ fprintf(stderr, "Error creating output color profile!\n");
+ goto Error;
+ }
+
+ qcms_profile_precache_output_transform(output_profile);
+ transform = qcms_transform_create(input_profile, input_type,
+ output_profile, output_type,
+ intent);
+ if (transform == NULL) {
+ fprintf(stderr, "Error creating color transform!\n");
+ goto Error;
+ }
+
+ line = rgba->u.RGBA.rgba;
+ for (i = 0; i < rgba->height; ++i, line += rgba->u.RGBA.stride) {
+ qcms_transform_data(transform, line, line, rgba->width);
+ }
+ ok = 1;
+
+ Error:
+ if (input_profile != NULL) qcms_profile_release(input_profile);
+ if (output_profile != NULL) qcms_profile_release(output_profile);
+ if (transform != NULL) qcms_transform_release(transform);
+ return ok;
+#else
+ (void)profile;
+ (void)rgba;
+ return 1;
+#endif // WEBP_HAVE_QCMS
+}
+
+//------------------------------------------------------------------------------
+// File decoding
+
+static int Decode(void) { // Fills kParams.curr_frame
+ const WebPIterator* const curr = &kParams.curr_frame;
+ WebPDecoderConfig* const config = &kParams.config;
+ WebPDecBuffer* const output_buffer = &config->output;
+ int ok = 0;
+
+ ClearPreviousPic();
+ output_buffer->colorspace = MODE_RGBA;
+ ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size,
+ config) == VP8_STATUS_OK);
+ if (!ok) {
+ fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num);
+ } else {
+ kParams.pic = output_buffer;
+ if (kParams.use_color_profile) {
+ ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer);
+ if (!ok) {
+ fprintf(stderr, "Applying color profile to frame #%d failed!\n",
+ curr->frame_num);
+ }
+ }
+ }
+ return ok;
+}
+
+static void decode_callback(int what) {
+ if (what == 0 && !kParams.done) {
+ int duration = 0;
+ if (kParams.dmux != NULL) {
+ WebPIterator* const curr = &kParams.curr_frame;
+ if (!WebPDemuxNextFrame(curr)) {
+ WebPDemuxReleaseIterator(curr);
+ if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) {
+ --kParams.loop_count;
+ kParams.done = (kParams.loop_count == 0);
+ if (kParams.done) return;
+ ClearPreviousFrame();
+ } else {
+ kParams.decoding_error = 1;
+ kParams.done = 1;
+ return;
+ }
+ }
+ duration = curr->duration;
+ // Behavior copied from Chrome, cf:
+ // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/
+ // platform/graphics/DeferredImageDecoder.cpp?
+ // rcl=b4c33049f096cd283f32be9a58b9a9e768227c26&l=246
+ if (duration <= 10) duration = 100;
+ }
+ if (!Decode()) {
+ kParams.decoding_error = 1;
+ kParams.done = 1;
+ } else {
+ glutPostRedisplay();
+ glutTimerFunc(duration, decode_callback, what);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Callbacks
+
+static void HandleKey(unsigned char key, int pos_x, int pos_y) {
+ (void)pos_x;
+ (void)pos_y;
+ if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
+#ifdef FREEGLUT
+ glutLeaveMainLoop();
+#else
+ ClearParams();
+ exit(0);
+#endif
+ } else if (key == 'c') {
+ if (kParams.has_color_profile && !kParams.decoding_error) {
+ kParams.use_color_profile = 1 - kParams.use_color_profile;
+
+ if (kParams.has_animation) {
+ // Restart the completed animation to pickup the color profile change.
+ if (kParams.done && kParams.loop_count == 0) {
+ kParams.loop_count =
+ (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT) + 1;
+ kParams.done = 0;
+ // Start the decode loop immediately.
+ glutTimerFunc(0, decode_callback, 0);
+ }
+ } else {
+ Decode();
+ glutPostRedisplay();
+ }
+ }
+ } else if (key == 'i') {
+ // Note: doesn't handle refresh of animation's last-frame (it's quite
+ // more involved to do, since you need to save the previous frame).
+ kParams.print_info = 1 - kParams.print_info;
+ if (!kParams.has_animation) ClearPreviousFrame();
+ glutPostRedisplay();
+ } else if (key == 'd') {
+ kParams.only_deltas = 1 - kParams.only_deltas;
+ glutPostRedisplay();
+ }
+}
+
+static void HandleReshape(int width, int height) {
+ // Note: reshape doesn't preserve aspect ratio, and might
+ // be handling larger-than-screen pictures incorrectly.
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ kParams.viewport_width = width;
+ kParams.viewport_height = height;
+ if (!kParams.has_animation) ClearPreviousFrame();
+}
+
+static void PrintString(const char* const text) {
+ void* const font = GLUT_BITMAP_9_BY_15;
+ int i;
+ for (i = 0; text[i]; ++i) {
+ glutBitmapCharacter(font, text[i]);
+ }
+}
+
+static float GetColorf(uint32_t color, int shift) {
+ return (color >> shift) / 255.f;
+}
+
+static void DrawCheckerBoard(void) {
+ const int square_size = 8; // must be a power of 2
+ int x, y;
+ GLint viewport[4]; // x, y, width, height
+
+ glPushMatrix();
+
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ // shift to integer coordinates with (0,0) being top-left.
+ glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
+ for (y = 0; y < viewport[3]; y += square_size) {
+ for (x = 0; x < viewport[2]; x += square_size) {
+ const GLubyte color = 128 + 64 * (!((x + y) & square_size));
+ glColor3ub(color, color, color);
+ glRecti(x, y, x + square_size, y + square_size);
+ }
+ }
+ glPopMatrix();
+}
+
+static void HandleDisplay(void) {
+ const WebPDecBuffer* const pic = kParams.pic;
+ const WebPIterator* const curr = &kParams.curr_frame;
+ WebPIterator* const prev = &kParams.prev_frame;
+ GLfloat xoff, yoff;
+ if (pic == NULL) return;
+ glPushMatrix();
+ glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width),
+ (GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height));
+ xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
+ yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
+ glRasterPos2f(-1.f + xoff, 1.f - yoff);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
+
+ if (kParams.only_deltas) {
+ DrawCheckerBoard();
+ } else if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ||
+ curr->blend_method == WEBP_MUX_NO_BLEND) {
+ // glScissor() takes window coordinates (0,0 at bottom left).
+ int window_x, window_y;
+ int frame_w, frame_h;
+ if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
+ // Clear the previous frame rectangle.
+ window_x = prev->x_offset;
+ window_y = kParams.canvas_height - prev->y_offset - prev->height;
+ frame_w = prev->width;
+ frame_h = prev->height;
+ } else { // curr->blend_method == WEBP_MUX_NO_BLEND.
+ // We simulate no-blending behavior by first clearing the current frame
+ // rectangle (to a checker-board) and then alpha-blending against it.
+ window_x = curr->x_offset;
+ window_y = kParams.canvas_height - curr->y_offset - curr->height;
+ frame_w = curr->width;
+ frame_h = curr->height;
+ }
+ glEnable(GL_SCISSOR_TEST);
+ // Only update the requested area, not the whole canvas.
+ window_x = window_x * kParams.viewport_width / kParams.canvas_width;
+ window_y = window_y * kParams.viewport_height / kParams.canvas_height;
+ frame_w = frame_w * kParams.viewport_width / kParams.canvas_width;
+ frame_h = frame_h * kParams.viewport_height / kParams.canvas_height;
+ glScissor(window_x, window_y, frame_w, frame_h);
+
+ glClear(GL_COLOR_BUFFER_BIT); // use clear color
+ DrawCheckerBoard();
+
+ glDisable(GL_SCISSOR_TEST);
+ }
+
+ *prev = *curr;
+
+ glDrawPixels(pic->width, pic->height,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (GLvoid*)pic->u.RGBA.rgba);
+ if (kParams.print_info) {
+ char tmp[32];
+
+ glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
+ glRasterPos2f(-0.95f, 0.90f);
+ PrintString(kParams.file_name);
+
+ snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
+ glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
+ glRasterPos2f(-0.95f, 0.80f);
+ PrintString(tmp);
+ if (curr->x_offset != 0 || curr->y_offset != 0) {
+ snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
+ curr->x_offset, curr->y_offset);
+ glRasterPos2f(-0.95f, 0.70f);
+ PrintString(tmp);
+ }
+ }
+ glPopMatrix();
+#if defined(__APPLE__) || defined(_WIN32)
+ glFlush();
+#else
+ glutSwapBuffers();
+#endif
+}
+
+static void StartDisplay(void) {
+ const int width = kParams.canvas_width;
+ const int height = kParams.canvas_height;
+ // TODO(webp:365) GLUT_DOUBLE results in flickering / old frames to be
+ // partially displayed with animated webp + alpha.
+#if defined(__APPLE__) || defined(_WIN32)
+ glutInitDisplayMode(GLUT_RGBA);
+#else
+ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
+#endif
+ glutInitWindowSize(width, height);
+ glutCreateWindow("WebP viewer");
+ glutDisplayFunc(HandleDisplay);
+ glutReshapeFunc(HandleReshape);
+ glutIdleFunc(NULL);
+ glutKeyboardFunc(HandleKey);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glClearColor(GetColorf(kParams.bg_color, 0),
+ GetColorf(kParams.bg_color, 8),
+ GetColorf(kParams.bg_color, 16),
+ GetColorf(kParams.bg_color, 24));
+ glClear(GL_COLOR_BUFFER_BIT);
+ DrawCheckerBoard();
+}
+
+//------------------------------------------------------------------------------
+// Main
+
+static void Help(void) {
+ printf("Usage: vwebp in_file [options]\n\n"
+ "Decodes the WebP image file and visualize it using OpenGL\n"
+ "Options are:\n"
+ " -version ..... print version number and exit\n"
+ " -noicc ....... don't use the icc profile if present\n"
+ " -nofancy ..... don't use the fancy YUV420 upscaler\n"
+ " -nofilter .... disable in-loop filtering\n"
+ " -dither <int> dithering strength (0..100), default=50\n"
+ " -noalphadither disable alpha plane dithering\n"
+ " -mt .......... use multi-threading\n"
+ " -info ........ print info\n"
+ " -h ........... this help message\n"
+ "\n"
+ "Keyboard shortcuts:\n"
+ " 'c' ................ toggle use of color profile\n"
+ " 'i' ................ overlay file information\n"
+ " 'd' ................ disable blending & disposal (debug)\n"
+ " 'q' / 'Q' / ESC .... quit\n"
+ );
+}
+
+int main(int argc, char *argv[]) {
+ int c;
+ WebPDecoderConfig* const config = &kParams.config;
+ WebPIterator* const curr = &kParams.curr_frame;
+
+ if (!WebPInitDecoderConfig(config)) {
+ fprintf(stderr, "Library version mismatch!\n");
+ return -1;
+ }
+ config->options.dithering_strength = 50;
+ config->options.alpha_dithering_strength = 100;
+ kParams.use_color_profile = 1;
+
+ for (c = 1; c < argc; ++c) {
+ int parse_error = 0;
+ if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ Help();
+ return 0;
+ } else if (!strcmp(argv[c], "-noicc")) {
+ kParams.use_color_profile = 0;
+ } else if (!strcmp(argv[c], "-nofancy")) {
+ config->options.no_fancy_upsampling = 1;
+ } else if (!strcmp(argv[c], "-nofilter")) {
+ config->options.bypass_filtering = 1;
+ } else if (!strcmp(argv[c], "-noalphadither")) {
+ config->options.alpha_dithering_strength = 0;
+ } else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
+ config->options.dithering_strength =
+ ExUtilGetInt(argv[++c], 0, &parse_error);
+ } else if (!strcmp(argv[c], "-info")) {
+ kParams.print_info = 1;
+ } else if (!strcmp(argv[c], "-version")) {
+ const int dec_version = WebPGetDecoderVersion();
+ const int dmux_version = WebPGetDemuxVersion();
+ printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
+ (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
+ dec_version & 0xff, (dmux_version >> 16) & 0xff,
+ (dmux_version >> 8) & 0xff, dmux_version & 0xff);
+ return 0;
+ } else if (!strcmp(argv[c], "-mt")) {
+ config->options.use_threads = 1;
+ } else if (!strcmp(argv[c], "--")) {
+ if (c < argc - 1) kParams.file_name = argv[++c];
+ break;
+ } else if (argv[c][0] == '-') {
+ printf("Unknown option '%s'\n", argv[c]);
+ Help();
+ return -1;
+ } else {
+ kParams.file_name = argv[c];
+ }
+
+ if (parse_error) {
+ Help();
+ return -1;
+ }
+ }
+
+ if (kParams.file_name == NULL) {
+ printf("missing input file!!\n");
+ Help();
+ return 0;
+ }
+
+ if (!ImgIoUtilReadFile(kParams.file_name,
+ &kParams.data.bytes, &kParams.data.size)) {
+ goto Error;
+ }
+
+ if (!WebPGetInfo(kParams.data.bytes, kParams.data.size, NULL, NULL)) {
+ fprintf(stderr, "Input file doesn't appear to be WebP format.\n");
+ goto Error;
+ }
+
+ kParams.dmux = WebPDemux(&kParams.data);
+ if (kParams.dmux == NULL) {
+ fprintf(stderr, "Could not create demuxing object!\n");
+ goto Error;
+ }
+
+ kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
+ kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
+ if (kParams.print_info) {
+ printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
+ }
+
+ ClearPreviousFrame();
+
+ memset(&kParams.iccp, 0, sizeof(kParams.iccp));
+ kParams.has_color_profile =
+ !!(WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG);
+ if (kParams.has_color_profile) {
+#ifdef WEBP_HAVE_QCMS
+ if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error;
+ printf("VP8X: Found color profile\n");
+#else
+ fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n"
+ "Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS "
+ "before building.\n");
+#endif
+ }
+
+ if (!WebPDemuxGetFrame(kParams.dmux, 1, curr)) goto Error;
+
+ kParams.has_animation = (curr->num_frames > 1);
+ kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
+ kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
+ printf("VP8X: Found %d images in file (loop count = %d)\n",
+ curr->num_frames, kParams.loop_count);
+
+ // Decode first frame
+ if (!Decode()) goto Error;
+
+ // Position iterator to last frame. Next call to HandleDisplay will wrap over.
+ // We take this into account by bumping up loop_count.
+ WebPDemuxGetFrame(kParams.dmux, 0, curr);
+ if (kParams.loop_count) ++kParams.loop_count;
+
+#if defined(__unix__) || defined(__CYGWIN__)
+ // Work around GLUT compositor bug.
+ // https://bugs.launchpad.net/ubuntu/+source/freeglut/+bug/369891
+ setenv("XLIB_SKIP_ARGB_VISUALS", "1", 1);
+#endif
+
+ // Start display (and timer)
+ glutInit(&argc, argv);
+#ifdef FREEGLUT
+ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
+#endif
+ StartDisplay();
+
+ if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
+ glutMainLoop();
+
+ // Should only be reached when using FREEGLUT:
+ ClearParams();
+ return 0;
+
+ Error:
+ ClearParams();
+ return -1;
+}
+
+#else // !WEBP_HAVE_GL
+
+int main(int argc, const char *argv[]) {
+ fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
+ (void)argc;
+ return 0;
+}
+
+#endif
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/examples/webpinfo.c b/src/third_party/libwebp/examples/webpinfo.c
new file mode 100644
index 0000000..2dcd277
--- /dev/null
+++ b/src/third_party/libwebp/examples/webpinfo.c
@@ -0,0 +1,1178 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Command-line tool to print out the chunk level structure of WebP files
+// along with basic integrity checks.
+//
+// Author: Hui Su (huisu@google.com)
+
+#include <assert.h>
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../imageio/imageio_util.h"
+#include "webp/decode.h"
+#include "webp/format_constants.h"
+#include "webp/mux_types.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+#define LOG_ERROR(MESSAGE) \
+ do { \
+ if (webp_info->show_diagnosis_) { \
+ fprintf(stderr, "Error: %s\n", MESSAGE); \
+ } \
+ } while (0)
+
+#define LOG_WARN(MESSAGE) \
+ do { \
+ if (webp_info->show_diagnosis_) { \
+ fprintf(stderr, "Warning: %s\n", MESSAGE); \
+ } \
+ } while (0)
+
+static const char* const kFormats[3] = {
+ "Unknown",
+ "Lossy",
+ "Lossless"
+};
+
+static const char* const kLosslessTransforms[4] = {
+ "Predictor",
+ "Cross Color",
+ "Subtract Green",
+ "Color Indexing"
+};
+
+static const char* const kAlphaFilterMethods[4] = {
+ "None",
+ "Horizontal",
+ "Vertical",
+ "Gradient"
+};
+
+typedef enum {
+ WEBP_INFO_OK = 0,
+ WEBP_INFO_TRUNCATED_DATA,
+ WEBP_INFO_PARSE_ERROR,
+ WEBP_INFO_INVALID_PARAM,
+ WEBP_INFO_BITSTREAM_ERROR,
+ WEBP_INFO_MISSING_DATA,
+ WEBP_INFO_INVALID_COMMAND
+} WebPInfoStatus;
+
+typedef enum ChunkID {
+ CHUNK_VP8,
+ CHUNK_VP8L,
+ CHUNK_VP8X,
+ CHUNK_ALPHA,
+ CHUNK_ANIM,
+ CHUNK_ANMF,
+ CHUNK_ICCP,
+ CHUNK_EXIF,
+ CHUNK_XMP,
+ CHUNK_UNKNOWN,
+ CHUNK_TYPES = CHUNK_UNKNOWN
+} ChunkID;
+
+typedef struct {
+ size_t start_;
+ size_t end_;
+ const uint8_t* buf_;
+} MemBuffer;
+
+typedef struct {
+ size_t offset_;
+ size_t size_;
+ const uint8_t* payload_;
+ ChunkID id_;
+} ChunkData;
+
+typedef struct WebPInfo {
+ int canvas_width_;
+ int canvas_height_;
+ int loop_count_;
+ int num_frames_;
+ int chunk_counts_[CHUNK_TYPES];
+ int anmf_subchunk_counts_[3]; // 0 VP8; 1 VP8L; 2 ALPH.
+ uint32_t bgcolor_;
+ int feature_flags_;
+ int has_alpha_;
+ // Used for parsing ANMF chunks.
+ int frame_width_, frame_height_;
+ size_t anim_frame_data_size_;
+ int is_processing_anim_frame_, seen_alpha_subchunk_, seen_image_subchunk_;
+ // Print output control.
+ int quiet_, show_diagnosis_, show_summary_;
+ int parse_bitstream_;
+} WebPInfo;
+
+static void WebPInfoInit(WebPInfo* const webp_info) {
+ memset(webp_info, 0, sizeof(*webp_info));
+}
+
+static const char kWebPChunkTags[CHUNK_TYPES][4] = {
+ { 'V', 'P', '8', ' ' },
+ { 'V', 'P', '8', 'L' },
+ { 'V', 'P', '8', 'X' },
+ { 'A', 'L', 'P', 'H' },
+ { 'A', 'N', 'I', 'M' },
+ { 'A', 'N', 'M', 'F' },
+ { 'I', 'C', 'C', 'P' },
+ { 'E', 'X', 'I', 'F' },
+ { 'X', 'M', 'P', ' ' },
+};
+
+// -----------------------------------------------------------------------------
+// Data reading.
+
+static int GetLE16(const uint8_t* const data) {
+ return (data[0] << 0) | (data[1] << 8);
+}
+
+static int GetLE24(const uint8_t* const data) {
+ return GetLE16(data) | (data[2] << 16);
+}
+
+static uint32_t GetLE32(const uint8_t* const data) {
+ return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16);
+}
+
+static int ReadLE16(const uint8_t** data) {
+ const int val = GetLE16(*data);
+ *data += 2;
+ return val;
+}
+
+static int ReadLE24(const uint8_t** data) {
+ const int val = GetLE24(*data);
+ *data += 3;
+ return val;
+}
+
+static uint32_t ReadLE32(const uint8_t** data) {
+ const uint32_t val = GetLE32(*data);
+ *data += 4;
+ return val;
+}
+
+static int ReadFileToWebPData(const char* const filename,
+ WebPData* const webp_data) {
+ const uint8_t* data;
+ size_t size;
+ if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
+ webp_data->bytes = data;
+ webp_data->size = size;
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+// MemBuffer object.
+
+static void InitMemBuffer(MemBuffer* const mem, const WebPData* webp_data) {
+ mem->buf_ = webp_data->bytes;
+ mem->start_ = 0;
+ mem->end_ = webp_data->size;
+}
+
+static size_t MemDataSize(const MemBuffer* const mem) {
+ return (mem->end_ - mem->start_);
+}
+
+static const uint8_t* GetBuffer(MemBuffer* const mem) {
+ return mem->buf_ + mem->start_;
+}
+
+static void Skip(MemBuffer* const mem, size_t size) {
+ mem->start_ += size;
+}
+
+static uint32_t ReadMemBufLE32(MemBuffer* const mem) {
+ const uint8_t* const data = mem->buf_ + mem->start_;
+ const uint32_t val = GetLE32(data);
+ assert(MemDataSize(mem) >= 4);
+ Skip(mem, 4);
+ return val;
+}
+
+// -----------------------------------------------------------------------------
+// Lossy bitstream analysis.
+
+static int GetBits(const uint8_t* const data, size_t data_size, size_t nb,
+ int* val, uint64_t* const bit_pos) {
+ *val = 0;
+ while (nb-- > 0) {
+ const uint64_t p = (*bit_pos)++;
+ if ((p >> 3) >= data_size) {
+ return 0;
+ } else {
+ const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
+ *val = (*val << 1) | bit;
+ }
+ }
+ return 1;
+}
+
+static int GetSignedBits(const uint8_t* const data, size_t data_size, size_t nb,
+ int* val, uint64_t* const bit_pos) {
+ int sign;
+ if (!GetBits(data, data_size, nb, val, bit_pos)) return 0;
+ if (!GetBits(data, data_size, 1, &sign, bit_pos)) return 0;
+ if (sign) *val = -(*val);
+ return 1;
+}
+
+#define GET_BITS(v, n) \
+ do { \
+ if (!GetBits(data, data_size, n, &(v), bit_pos)) { \
+ LOG_ERROR("Truncated lossy bitstream."); \
+ return WEBP_INFO_TRUNCATED_DATA; \
+ } \
+ } while (0)
+
+#define GET_SIGNED_BITS(v, n) \
+ do { \
+ if (!GetSignedBits(data, data_size, n, &(v), bit_pos)) { \
+ LOG_ERROR("Truncated lossy bitstream."); \
+ return WEBP_INFO_TRUNCATED_DATA; \
+ } \
+ } while (0)
+
+static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
+ const uint8_t* const data,
+ size_t data_size,
+ uint64_t* const bit_pos) {
+ int use_segment;
+ GET_BITS(use_segment, 1);
+ printf(" Use segment: %d\n", use_segment);
+ if (use_segment) {
+ int update_map, update_data;
+ GET_BITS(update_map, 1);
+ GET_BITS(update_data, 1);
+ printf(" Update map: %d\n"
+ " Update data: %d\n",
+ update_map, update_data);
+ if (update_data) {
+ int i, a_delta;
+ int quantizer[4] = {0, 0, 0, 0};
+ int filter_strength[4] = {0, 0, 0, 0};
+ GET_BITS(a_delta, 1);
+ printf(" Absolute delta: %d\n", a_delta);
+ for (i = 0; i < 4; ++i) {
+ int bit;
+ GET_BITS(bit, 1);
+ if (bit) GET_SIGNED_BITS(quantizer[i], 7);
+ }
+ for (i = 0; i < 4; ++i) {
+ int bit;
+ GET_BITS(bit, 1);
+ if (bit) GET_SIGNED_BITS(filter_strength[i], 6);
+ }
+ printf(" Quantizer: %d %d %d %d\n", quantizer[0], quantizer[1],
+ quantizer[2], quantizer[3]);
+ printf(" Filter strength: %d %d %d %d\n", filter_strength[0],
+ filter_strength[1], filter_strength[2], filter_strength[3]);
+ }
+ if (update_map) {
+ int i;
+ int prob_segment[3] = {255, 255, 255};
+ for (i = 0; i < 3; ++i) {
+ int bit;
+ GET_BITS(bit, 1);
+ if (bit) GET_BITS(prob_segment[i], 8);
+ }
+ printf(" Prob segment: %d %d %d\n",
+ prob_segment[0], prob_segment[1], prob_segment[2]);
+ }
+ }
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseLossyFilterHeader(const WebPInfo* const webp_info,
+ const uint8_t* const data,
+ size_t data_size,
+ uint64_t* const bit_pos) {
+ int simple_filter, level, sharpness, use_lf_delta;
+ GET_BITS(simple_filter, 1);
+ GET_BITS(level, 6);
+ GET_BITS(sharpness, 3);
+ GET_BITS(use_lf_delta, 1);
+ printf(" Simple filter: %d\n", simple_filter);
+ printf(" Level: %d\n", level);
+ printf(" Sharpness: %d\n", sharpness);
+ printf(" Use lf delta: %d\n", use_lf_delta);
+ if (use_lf_delta) {
+ int update;
+ GET_BITS(update, 1);
+ printf(" Update lf delta: %d\n", update);
+ if (update) {
+ int i;
+ for (i = 0; i < 4 + 4; ++i) {
+ int temp;
+ GET_BITS(temp, 1);
+ if (temp) GET_BITS(temp, 7);
+ }
+ }
+ }
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
+ const WebPInfo* const webp_info) {
+ const uint8_t* data = chunk_data->payload_;
+ size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+ const uint32_t bits = (uint32_t)data[0] | (data[1] << 8) | (data[2] << 16);
+ const int key_frame = !(bits & 1);
+ const int profile = (bits >> 1) & 7;
+ const int display = (bits >> 4) & 1;
+ const uint32_t partition0_length = (bits >> 5);
+ WebPInfoStatus status = WEBP_INFO_OK;
+ uint64_t bit_position = 0;
+ uint64_t* const bit_pos = &bit_position;
+ int colorspace, clamp_type;
+ printf(" Parsing lossy bitstream...\n");
+ // Calling WebPGetFeatures() in ProcessImageChunk() should ensure this.
+ assert(chunk_data->size_ >= CHUNK_HEADER_SIZE + 10);
+ if (profile > 3) {
+ LOG_ERROR("Unknown profile.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ if (!display) {
+ LOG_ERROR("Frame is not displayable.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ data += 3;
+ data_size -= 3;
+ printf(" Key frame: %s\n"
+ " Profile: %d\n"
+ " Display: %s\n"
+ " Part. 0 length: %d\n",
+ key_frame ? "Yes" : "No", profile,
+ display ? "Yes" : "No", partition0_length);
+ if (key_frame) {
+ if (!(data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a)) {
+ LOG_ERROR("Invalid lossy bitstream signature.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ printf(" Width: %d\n"
+ " X scale: %d\n"
+ " Height: %d\n"
+ " Y scale: %d\n",
+ ((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
+ ((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
+ data += 7;
+ data_size -= 7;
+ } else {
+ LOG_ERROR("Non-keyframe detected in lossy bitstream.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ if (partition0_length >= data_size) {
+ LOG_ERROR("Bad partition length.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ GET_BITS(colorspace, 1);
+ GET_BITS(clamp_type, 1);
+ printf(" Color space: %d\n", colorspace);
+ printf(" Clamp type: %d\n", clamp_type);
+ status = ParseLossySegmentHeader(webp_info, data, data_size, bit_pos);
+ if (status != WEBP_INFO_OK) return status;
+ status = ParseLossyFilterHeader(webp_info, data, data_size, bit_pos);
+ if (status != WEBP_INFO_OK) return status;
+ { // Partition number and size.
+ const uint8_t* part_size = data + partition0_length;
+ int num_parts, i;
+ size_t part_data_size;
+ GET_BITS(num_parts, 2);
+ num_parts = 1 << num_parts;
+ if ((int)(data_size - partition0_length) < (num_parts - 1) * 3) {
+ LOG_ERROR("Truncated lossy bitstream.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ part_data_size = data_size - partition0_length - (num_parts - 1) * 3;
+ printf(" Total partitions: %d\n", num_parts);
+ for (i = 1; i < num_parts; ++i) {
+ const size_t psize =
+ part_size[0] | (part_size[1] << 8) | (part_size[2] << 16);
+ if (psize > part_data_size) {
+ LOG_ERROR("Truncated partition.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ printf(" Part. %d length: %d\n", i, (int)psize);
+ part_data_size -= psize;
+ part_size += 3;
+ }
+ }
+ // Quantizer.
+ {
+ int base_q, bit;
+ int dq_y1_dc = 0, dq_y2_dc = 0, dq_y2_ac = 0, dq_uv_dc = 0, dq_uv_ac = 0;
+ GET_BITS(base_q, 7);
+ GET_BITS(bit, 1);
+ if (bit) GET_SIGNED_BITS(dq_y1_dc, 4);
+ GET_BITS(bit, 1);
+ if (bit) GET_SIGNED_BITS(dq_y2_dc, 4);
+ GET_BITS(bit, 1);
+ if (bit) GET_SIGNED_BITS(dq_y2_ac, 4);
+ GET_BITS(bit, 1);
+ if (bit) GET_SIGNED_BITS(dq_uv_dc, 4);
+ GET_BITS(bit, 1);
+ if (bit) GET_SIGNED_BITS(dq_uv_ac, 4);
+ printf(" Base Q: %d\n", base_q);
+ printf(" DQ Y1 DC: %d\n", dq_y1_dc);
+ printf(" DQ Y2 DC: %d\n", dq_y2_dc);
+ printf(" DQ Y2 AC: %d\n", dq_y2_ac);
+ printf(" DQ UV DC: %d\n", dq_uv_dc);
+ printf(" DQ UV AC: %d\n", dq_uv_ac);
+ }
+ if ((*bit_pos >> 3) >= partition0_length) {
+ LOG_ERROR("Truncated lossy bitstream.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ return WEBP_INFO_OK;
+}
+
+// -----------------------------------------------------------------------------
+// Lossless bitstream analysis.
+
+static int LLGetBits(const uint8_t* const data, size_t data_size, size_t nb,
+ int* val, uint64_t* const bit_pos) {
+ uint32_t i = 0;
+ *val = 0;
+ while (i < nb) {
+ const uint64_t p = (*bit_pos)++;
+ if ((p >> 3) >= data_size) {
+ return 0;
+ } else {
+ const int bit = !!(data[p >> 3] & (1 << ((p & 7))));
+ *val = *val | (bit << i);
+ ++i;
+ }
+ }
+ return 1;
+}
+
+#define LL_GET_BITS(v, n) \
+ do { \
+ if (!LLGetBits(data, data_size, n, &(v), bit_pos)) { \
+ LOG_ERROR("Truncated lossless bitstream."); \
+ return WEBP_INFO_TRUNCATED_DATA; \
+ } \
+ } while (0)
+
+static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
+ const uint8_t* const data,
+ size_t data_size,
+ uint64_t* const bit_pos) {
+ int use_transform, block_size, n_colors;
+ LL_GET_BITS(use_transform, 1);
+ printf(" Use transform: %s\n", use_transform ? "Yes" : "No");
+ if (use_transform) {
+ int type;
+ LL_GET_BITS(type, 2);
+ printf(" 1st transform: %s (%d)\n", kLosslessTransforms[type], type);
+ switch (type) {
+ case PREDICTOR_TRANSFORM:
+ case CROSS_COLOR_TRANSFORM:
+ LL_GET_BITS(block_size, 3);
+ block_size = 1 << (block_size + 2);
+ printf(" Tran. block size: %d\n", block_size);
+ break;
+ case COLOR_INDEXING_TRANSFORM:
+ LL_GET_BITS(n_colors, 8);
+ n_colors += 1;
+ printf(" No. of colors: %d\n", n_colors);
+ break;
+ default: break;
+ }
+ }
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ const uint8_t* data = chunk_data->payload_;
+ size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+ uint64_t bit_position = 0;
+ uint64_t* const bit_pos = &bit_position;
+ WebPInfoStatus status;
+ printf(" Parsing lossless bitstream...\n");
+ if (data_size < VP8L_FRAME_HEADER_SIZE) {
+ LOG_ERROR("Truncated lossless bitstream.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ if (data[0] != VP8L_MAGIC_BYTE) {
+ LOG_ERROR("Invalid lossless bitstream signature.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ data += 1;
+ data_size -= 1;
+ {
+ int width, height, has_alpha, version;
+ LL_GET_BITS(width, 14);
+ LL_GET_BITS(height, 14);
+ LL_GET_BITS(has_alpha, 1);
+ LL_GET_BITS(version, 3);
+ width += 1;
+ height += 1;
+ printf(" Width: %d\n", width);
+ printf(" Height: %d\n", height);
+ printf(" Alpha: %d\n", has_alpha);
+ printf(" Version: %d\n", version);
+ }
+ status = ParseLosslessTransform(webp_info, data, data_size, bit_pos);
+ if (status != WEBP_INFO_OK) return status;
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ const uint8_t* data = chunk_data->payload_;
+ size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+ if (data_size <= ALPHA_HEADER_LEN) {
+ LOG_ERROR("Truncated ALPH chunk.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ printf(" Parsing ALPH chunk...\n");
+ {
+ const int compression_method = (data[0] >> 0) & 0x03;
+ const int filter = (data[0] >> 2) & 0x03;
+ const int pre_processing = (data[0] >> 4) & 0x03;
+ const int reserved_bits = (data[0] >> 6) & 0x03;
+ printf(" Compression: %d\n", compression_method);
+ printf(" Filter: %s (%d)\n",
+ kAlphaFilterMethods[filter], filter);
+ printf(" Pre-processing: %d\n", pre_processing);
+ if (compression_method > ALPHA_LOSSLESS_COMPRESSION) {
+ LOG_ERROR("Invalid Alpha compression method.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ if (pre_processing > ALPHA_PREPROCESSED_LEVELS) {
+ LOG_ERROR("Invalid Alpha pre-processing method.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ if (reserved_bits != 0) {
+ LOG_WARN("Reserved bits in ALPH chunk header are not all 0.");
+ }
+ data += ALPHA_HEADER_LEN;
+ data_size -= ALPHA_HEADER_LEN;
+ if (compression_method == ALPHA_LOSSLESS_COMPRESSION) {
+ uint64_t bit_pos = 0;
+ WebPInfoStatus status =
+ ParseLosslessTransform(webp_info, data, data_size, &bit_pos);
+ if (status != WEBP_INFO_OK) return status;
+ }
+ }
+ return WEBP_INFO_OK;
+}
+
+// -----------------------------------------------------------------------------
+// Chunk parsing.
+
+static WebPInfoStatus ParseRIFFHeader(const WebPInfo* const webp_info,
+ MemBuffer* const mem) {
+ const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
+ size_t riff_size;
+
+ if (MemDataSize(mem) < min_size) {
+ LOG_ERROR("Truncated data detected when parsing RIFF header.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
+ memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
+ LOG_ERROR("Corrupted RIFF header.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
+ if (riff_size < CHUNK_HEADER_SIZE) {
+ LOG_ERROR("RIFF size is too small.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (riff_size > MAX_CHUNK_PAYLOAD) {
+ LOG_ERROR("RIFF size is over limit.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ riff_size += CHUNK_HEADER_SIZE;
+ if (!webp_info->quiet_) {
+ printf("RIFF HEADER:\n");
+ printf(" File size: %6d\n", (int)riff_size);
+ }
+ if (riff_size < mem->end_) {
+ LOG_WARN("RIFF size is smaller than the file size.");
+ mem->end_ = riff_size;
+ } else if (riff_size > mem->end_) {
+ LOG_ERROR("Truncated data detected when parsing RIFF payload.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ Skip(mem, RIFF_HEADER_SIZE);
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
+ MemBuffer* const mem,
+ ChunkData* const chunk_data) {
+ memset(chunk_data, 0, sizeof(*chunk_data));
+ if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
+ LOG_ERROR("Truncated data detected when parsing chunk header.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ } else {
+ const size_t chunk_start_offset = mem->start_;
+ const uint32_t fourcc = ReadMemBufLE32(mem);
+ const uint32_t payload_size = ReadMemBufLE32(mem);
+ const uint32_t payload_size_padded = payload_size + (payload_size & 1);
+ const size_t chunk_size = CHUNK_HEADER_SIZE + payload_size_padded;
+ int i;
+ if (payload_size > MAX_CHUNK_PAYLOAD) {
+ LOG_ERROR("Size of chunk payload is over limit.");
+ return WEBP_INFO_INVALID_PARAM;
+ }
+ if (payload_size_padded > MemDataSize(mem)){
+ LOG_ERROR("Truncated data detected when parsing chunk payload.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ for (i = 0; i < CHUNK_TYPES; ++i) {
+ if (!memcmp(kWebPChunkTags[i], &fourcc, TAG_SIZE)) break;
+ }
+ chunk_data->offset_ = chunk_start_offset;
+ chunk_data->size_ = chunk_size;
+ chunk_data->id_ = (ChunkID)i;
+ chunk_data->payload_ = GetBuffer(mem);
+ if (chunk_data->id_ == CHUNK_ANMF) {
+ if (payload_size != payload_size_padded) {
+ LOG_ERROR("ANMF chunk size should always be even.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ // There are sub-chunks to be parsed in an ANMF chunk.
+ Skip(mem, ANMF_CHUNK_SIZE);
+ } else {
+ Skip(mem, payload_size_padded);
+ }
+ return WEBP_INFO_OK;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Chunk analysis.
+
+static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ const uint8_t* data = chunk_data->payload_;
+ if (webp_info->chunk_counts_[CHUNK_VP8] ||
+ webp_info->chunk_counts_[CHUNK_VP8L] ||
+ webp_info->chunk_counts_[CHUNK_VP8X]) {
+ LOG_ERROR("Already seen a VP8/VP8L/VP8X chunk when parsing VP8X chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (chunk_data->size_ != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
+ LOG_ERROR("Corrupted VP8X chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ ++webp_info->chunk_counts_[CHUNK_VP8X];
+ webp_info->feature_flags_ = *data;
+ data += 4;
+ webp_info->canvas_width_ = 1 + ReadLE24(&data);
+ webp_info->canvas_height_ = 1 + ReadLE24(&data);
+ if (!webp_info->quiet_) {
+ printf(" ICCP: %d\n Alpha: %d\n EXIF: %d\n XMP: %d\n Animation: %d\n",
+ (webp_info->feature_flags_ & ICCP_FLAG) != 0,
+ (webp_info->feature_flags_ & ALPHA_FLAG) != 0,
+ (webp_info->feature_flags_ & EXIF_FLAG) != 0,
+ (webp_info->feature_flags_ & XMP_FLAG) != 0,
+ (webp_info->feature_flags_ & ANIMATION_FLAG) != 0);
+ printf(" Canvas size %d x %d\n",
+ webp_info->canvas_width_, webp_info->canvas_height_);
+ }
+ if (webp_info->canvas_width_ > MAX_CANVAS_SIZE) {
+ LOG_WARN("Canvas width is out of range in VP8X chunk.");
+ }
+ if (webp_info->canvas_height_ > MAX_CANVAS_SIZE) {
+ LOG_WARN("Canvas height is out of range in VP8X chunk.");
+ }
+ if ((uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
+ MAX_IMAGE_AREA) {
+ LOG_WARN("Canvas area is out of range in VP8X chunk.");
+ }
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ const uint8_t* data = chunk_data->payload_;
+ if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+ LOG_ERROR("ANIM chunk detected before VP8X chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (chunk_data->size_ != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
+ LOG_ERROR("Corrupted ANIM chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ webp_info->bgcolor_ = ReadLE32(&data);
+ webp_info->loop_count_ = ReadLE16(&data);
+ ++webp_info->chunk_counts_[CHUNK_ANIM];
+ if (!webp_info->quiet_) {
+ printf(" Background color:(ARGB) %02x %02x %02x %02x\n",
+ (webp_info->bgcolor_ >> 24) & 0xff,
+ (webp_info->bgcolor_ >> 16) & 0xff,
+ (webp_info->bgcolor_ >> 8) & 0xff,
+ webp_info->bgcolor_ & 0xff);
+ printf(" Loop count : %d\n", webp_info->loop_count_);
+ }
+ if (webp_info->loop_count_ > MAX_LOOP_COUNT) {
+ LOG_WARN("Loop count is out of range in ANIM chunk.");
+ }
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ const uint8_t* data = chunk_data->payload_;
+ int offset_x, offset_y, width, height, duration, blend, dispose, temp;
+ if (webp_info->is_processing_anim_frame_) {
+ LOG_ERROR("ANMF chunk detected within another ANMF chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (!webp_info->chunk_counts_[CHUNK_ANIM]) {
+ LOG_ERROR("ANMF chunk detected before ANIM chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (chunk_data->size_ <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
+ LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ offset_x = 2 * ReadLE24(&data);
+ offset_y = 2 * ReadLE24(&data);
+ width = 1 + ReadLE24(&data);
+ height = 1 + ReadLE24(&data);
+ duration = ReadLE24(&data);
+ temp = *data;
+ dispose = temp & 1;
+ blend = (temp >> 1) & 1;
+ ++webp_info->chunk_counts_[CHUNK_ANMF];
+ if (!webp_info->quiet_) {
+ printf(" Offset_X: %d\n Offset_Y: %d\n Width: %d\n Height: %d\n"
+ " Duration: %d\n Dispose: %d\n Blend: %d\n",
+ offset_x, offset_y, width, height, duration, dispose, blend);
+ }
+ if (duration > MAX_DURATION) {
+ LOG_ERROR("Invalid duration parameter in ANMF chunk.");
+ return WEBP_INFO_INVALID_PARAM;
+ }
+ if (offset_x > MAX_POSITION_OFFSET || offset_y > MAX_POSITION_OFFSET) {
+ LOG_ERROR("Invalid offset parameters in ANMF chunk.");
+ return WEBP_INFO_INVALID_PARAM;
+ }
+ if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width_ ||
+ (uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height_) {
+ LOG_ERROR("Frame exceeds canvas in ANMF chunk.");
+ return WEBP_INFO_INVALID_PARAM;
+ }
+ webp_info->is_processing_anim_frame_ = 1;
+ webp_info->seen_alpha_subchunk_ = 0;
+ webp_info->seen_image_subchunk_ = 0;
+ webp_info->frame_width_ = width;
+ webp_info->frame_height_ = height;
+ webp_info->anim_frame_data_size_ =
+ chunk_data->size_ - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ const uint8_t* data = chunk_data->payload_ - CHUNK_HEADER_SIZE;
+ WebPBitstreamFeatures features;
+ const VP8StatusCode vp8_status =
+ WebPGetFeatures(data, chunk_data->size_, &features);
+ if (vp8_status != VP8_STATUS_OK) {
+ LOG_ERROR("VP8/VP8L bitstream error.");
+ return WEBP_INFO_BITSTREAM_ERROR;
+ }
+ if (!webp_info->quiet_) {
+ assert(features.format >= 0 && features.format <= 2);
+ printf(" Width: %d\n Height: %d\n Alpha: %d\n Animation: %d\n"
+ " Format: %s (%d)\n",
+ features.width, features.height, features.has_alpha,
+ features.has_animation, kFormats[features.format], features.format);
+ }
+ if (webp_info->is_processing_anim_frame_) {
+ ++webp_info->anmf_subchunk_counts_[chunk_data->id_ == CHUNK_VP8 ? 0 : 1];
+ if (chunk_data->id_ == CHUNK_VP8L && webp_info->seen_alpha_subchunk_) {
+ LOG_ERROR("Both VP8L and ALPH sub-chunks are present in an ANMF chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (webp_info->frame_width_ != features.width ||
+ webp_info->frame_height_ != features.height) {
+ LOG_ERROR("Frame size in VP8/VP8L sub-chunk differs from ANMF header.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (webp_info->seen_image_subchunk_) {
+ LOG_ERROR("Consecutive VP8/VP8L sub-chunks in an ANMF chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ webp_info->seen_image_subchunk_ = 1;
+ } else {
+ if (webp_info->chunk_counts_[CHUNK_VP8] ||
+ webp_info->chunk_counts_[CHUNK_VP8L]) {
+ LOG_ERROR("Multiple VP8/VP8L chunks detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (chunk_data->id_ == CHUNK_VP8L &&
+ webp_info->chunk_counts_[CHUNK_ALPHA]) {
+ LOG_WARN("Both VP8L and ALPH chunks are detected.");
+ }
+ if (webp_info->chunk_counts_[CHUNK_ANIM] ||
+ webp_info->chunk_counts_[CHUNK_ANMF]) {
+ LOG_ERROR("VP8/VP8L chunk and ANIM/ANMF chunk are both detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (webp_info->chunk_counts_[CHUNK_VP8X]) {
+ if (webp_info->canvas_width_ != features.width ||
+ webp_info->canvas_height_ != features.height) {
+ LOG_ERROR("Image size in VP8/VP8L chunk differs from VP8X chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ } else {
+ webp_info->canvas_width_ = features.width;
+ webp_info->canvas_height_ = features.height;
+ if (webp_info->canvas_width_ < 1 || webp_info->canvas_height_ < 1 ||
+ webp_info->canvas_width_ > MAX_CANVAS_SIZE ||
+ webp_info->canvas_height_ > MAX_CANVAS_SIZE ||
+ (uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
+ MAX_IMAGE_AREA) {
+ LOG_WARN("Invalid parameters in VP8/VP8L chunk.");
+ }
+ }
+ ++webp_info->chunk_counts_[chunk_data->id_];
+ }
+ ++webp_info->num_frames_;
+ webp_info->has_alpha_ |= features.has_alpha;
+ if (webp_info->parse_bitstream_) {
+ const int is_lossy = (chunk_data->id_ == CHUNK_VP8);
+ const WebPInfoStatus status =
+ is_lossy ? ParseLossyHeader(chunk_data, webp_info)
+ : ParseLosslessHeader(chunk_data, webp_info);
+ if (status != WEBP_INFO_OK) return status;
+ }
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ if (webp_info->is_processing_anim_frame_) {
+ ++webp_info->anmf_subchunk_counts_[2];
+ if (webp_info->seen_alpha_subchunk_) {
+ LOG_ERROR("Consecutive ALPH sub-chunks in an ANMF chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ webp_info->seen_alpha_subchunk_ = 1;
+
+ if (webp_info->seen_image_subchunk_) {
+ LOG_ERROR("ALPHA sub-chunk detected after VP8 sub-chunk "
+ "in an ANMF chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ } else {
+ if (webp_info->chunk_counts_[CHUNK_ANIM] ||
+ webp_info->chunk_counts_[CHUNK_ANMF]) {
+ LOG_ERROR("ALPHA chunk and ANIM/ANMF chunk are both detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+ LOG_ERROR("ALPHA chunk detected before VP8X chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (webp_info->chunk_counts_[CHUNK_VP8]) {
+ LOG_ERROR("ALPHA chunk detected after VP8 chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (webp_info->chunk_counts_[CHUNK_ALPHA]) {
+ LOG_ERROR("Multiple ALPHA chunks detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ ++webp_info->chunk_counts_[CHUNK_ALPHA];
+ }
+ webp_info->has_alpha_ = 1;
+ if (webp_info->parse_bitstream_) {
+ const WebPInfoStatus status = ParseAlphaHeader(chunk_data, webp_info);
+ if (status != WEBP_INFO_OK) return status;
+ }
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessICCPChunk(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ (void)chunk_data;
+ if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+ LOG_ERROR("ICCP chunk detected before VP8X chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (webp_info->chunk_counts_[CHUNK_VP8] ||
+ webp_info->chunk_counts_[CHUNK_VP8L] ||
+ webp_info->chunk_counts_[CHUNK_ANIM]) {
+ LOG_ERROR("ICCP chunk detected after image data.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ ++webp_info->chunk_counts_[CHUNK_ICCP];
+ return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
+ WebPInfo* const webp_info) {
+ WebPInfoStatus status = WEBP_INFO_OK;
+ ChunkID id = chunk_data->id_;
+ if (chunk_data->id_ == CHUNK_UNKNOWN) {
+ char error_message[50];
+ snprintf(error_message, 50, "Unknown chunk at offset %6d, length %6d",
+ (int)chunk_data->offset_, (int)chunk_data->size_);
+ LOG_WARN(error_message);
+ } else {
+ if (!webp_info->quiet_) {
+ const char* tag = kWebPChunkTags[chunk_data->id_];
+ printf("Chunk %c%c%c%c at offset %6d, length %6d\n",
+ tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_,
+ (int)chunk_data->size_);
+ }
+ }
+ switch (id) {
+ case CHUNK_VP8:
+ case CHUNK_VP8L:
+ status = ProcessImageChunk(chunk_data, webp_info);
+ break;
+ case CHUNK_VP8X:
+ status = ProcessVP8XChunk(chunk_data, webp_info);
+ break;
+ case CHUNK_ALPHA:
+ status = ProcessALPHChunk(chunk_data, webp_info);
+ break;
+ case CHUNK_ANIM:
+ status = ProcessANIMChunk(chunk_data, webp_info);
+ break;
+ case CHUNK_ANMF:
+ status = ProcessANMFChunk(chunk_data, webp_info);
+ break;
+ case CHUNK_ICCP:
+ status = ProcessICCPChunk(chunk_data, webp_info);
+ break;
+ case CHUNK_EXIF:
+ case CHUNK_XMP:
+ ++webp_info->chunk_counts_[id];
+ break;
+ case CHUNK_UNKNOWN:
+ default:
+ break;
+ }
+ if (webp_info->is_processing_anim_frame_ && id != CHUNK_ANMF) {
+ if (webp_info->anim_frame_data_size_ == chunk_data->size_) {
+ if (!webp_info->seen_image_subchunk_) {
+ LOG_ERROR("No VP8/VP8L chunk detected in an ANMF chunk.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ webp_info->is_processing_anim_frame_ = 0;
+ } else if (webp_info->anim_frame_data_size_ > chunk_data->size_) {
+ webp_info->anim_frame_data_size_ -= chunk_data->size_;
+ } else {
+ LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
+ return WEBP_INFO_TRUNCATED_DATA;
+ }
+ }
+ return status;
+}
+
+static WebPInfoStatus Validate(const WebPInfo* const webp_info) {
+ if (webp_info->num_frames_ < 1) {
+ LOG_ERROR("No image/frame detected.");
+ return WEBP_INFO_MISSING_DATA;
+ }
+ if (webp_info->chunk_counts_[CHUNK_VP8X]) {
+ const int iccp = !!(webp_info->feature_flags_ & ICCP_FLAG);
+ const int exif = !!(webp_info->feature_flags_ & EXIF_FLAG);
+ const int xmp = !!(webp_info->feature_flags_ & XMP_FLAG);
+ const int animation = !!(webp_info->feature_flags_ & ANIMATION_FLAG);
+ const int alpha = !!(webp_info->feature_flags_ & ALPHA_FLAG);
+ if (!alpha && webp_info->has_alpha_) {
+ LOG_ERROR("Unexpected alpha data detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (alpha && !webp_info->has_alpha_) {
+ LOG_WARN("Alpha flag is set with no alpha data present.");
+ }
+ if (iccp && !webp_info->chunk_counts_[CHUNK_ICCP]) {
+ LOG_ERROR("Missing ICCP chunk.");
+ return WEBP_INFO_MISSING_DATA;
+ }
+ if (exif && !webp_info->chunk_counts_[CHUNK_EXIF]) {
+ LOG_ERROR("Missing EXIF chunk.");
+ return WEBP_INFO_MISSING_DATA;
+ }
+ if (xmp && !webp_info->chunk_counts_[CHUNK_XMP]) {
+ LOG_ERROR("Missing XMP chunk.");
+ return WEBP_INFO_MISSING_DATA;
+ }
+ if (!iccp && webp_info->chunk_counts_[CHUNK_ICCP]) {
+ LOG_ERROR("Unexpected ICCP chunk detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (!exif && webp_info->chunk_counts_[CHUNK_EXIF]) {
+ LOG_ERROR("Unexpected EXIF chunk detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (!xmp && webp_info->chunk_counts_[CHUNK_XMP]) {
+ LOG_ERROR("Unexpected XMP chunk detected.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ // Incomplete animation frame.
+ if (webp_info->is_processing_anim_frame_) return WEBP_INFO_MISSING_DATA;
+ if (!animation && webp_info->num_frames_ > 1) {
+ LOG_ERROR("More than 1 frame detected in non-animation file.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ if (animation && (!webp_info->chunk_counts_[CHUNK_ANIM] ||
+ !webp_info->chunk_counts_[CHUNK_ANMF])) {
+ LOG_ERROR("No ANIM/ANMF chunk detected in animation file.");
+ return WEBP_INFO_PARSE_ERROR;
+ }
+ }
+ return WEBP_INFO_OK;
+}
+
+static void ShowSummary(const WebPInfo* const webp_info) {
+ int i;
+ printf("Summary:\n");
+ printf("Number of frames: %d\n", webp_info->num_frames_);
+ printf("Chunk type : VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
+ "EXIF XMP\n");
+ printf("Chunk counts: ");
+ for (i = 0; i < CHUNK_TYPES; ++i) {
+ printf("%4d ", webp_info->chunk_counts_[i]);
+ if (i == CHUNK_ANMF) {
+ printf("%4d %4d %4d ",
+ webp_info->anmf_subchunk_counts_[0],
+ webp_info->anmf_subchunk_counts_[1],
+ webp_info->anmf_subchunk_counts_[2]);
+ }
+ }
+ printf("\n");
+}
+
+static WebPInfoStatus AnalyzeWebP(WebPInfo* const webp_info,
+ const WebPData* webp_data) {
+ ChunkData chunk_data;
+ MemBuffer mem_buffer;
+ WebPInfoStatus webp_info_status = WEBP_INFO_OK;
+
+ InitMemBuffer(&mem_buffer, webp_data);
+ webp_info_status = ParseRIFFHeader(webp_info, &mem_buffer);
+ if (webp_info_status != WEBP_INFO_OK) goto Error;
+
+ // Loop through all the chunks. Terminate immediately in case of error.
+ while (webp_info_status == WEBP_INFO_OK && MemDataSize(&mem_buffer) > 0) {
+ webp_info_status = ParseChunk(webp_info, &mem_buffer, &chunk_data);
+ if (webp_info_status != WEBP_INFO_OK) goto Error;
+ webp_info_status = ProcessChunk(&chunk_data, webp_info);
+ }
+ if (webp_info_status != WEBP_INFO_OK) goto Error;
+ if (webp_info->show_summary_) ShowSummary(webp_info);
+
+ // Final check.
+ webp_info_status = Validate(webp_info);
+
+ Error:
+ if (!webp_info->quiet_) {
+ if (webp_info_status == WEBP_INFO_OK) {
+ printf("No error detected.\n");
+ } else {
+ printf("Errors detected.\n");
+ }
+ }
+ return webp_info_status;
+}
+
+static void HelpShort(void) {
+ printf("Usage: webpinfo [options] in_files\n"
+ "Try -longhelp for an exhaustive list of options.\n");
+}
+
+static void HelpLong(void) {
+ printf("Usage: webpinfo [options] in_files\n"
+ "Note: there could be multiple input files;\n"
+ " options must come before input files.\n"
+ "Options:\n"
+ " -version ........... Print version number and exit.\n"
+ " -quiet ............. Do not show chunk parsing information.\n"
+ " -diag .............. Show parsing error diagnosis.\n"
+ " -summary ........... Show chunk stats summary.\n"
+ " -bitstream_info .... Parse bitstream header.\n");
+}
+
+int main(int argc, const char* argv[]) {
+ int c, quiet = 0, show_diag = 0, show_summary = 0;
+ int parse_bitstream = 0;
+ WebPInfoStatus webp_info_status = WEBP_INFO_OK;
+ WebPInfo webp_info;
+
+ if (argc == 1) {
+ HelpShort();
+ return WEBP_INFO_OK;
+ }
+
+ // Parse command-line input.
+ for (c = 1; c < argc; ++c) {
+ if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+ HelpShort();
+ return WEBP_INFO_OK;
+ } else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
+ HelpLong();
+ return WEBP_INFO_OK;
+ } else if (!strcmp(argv[c], "-quiet")) {
+ quiet = 1;
+ } else if (!strcmp(argv[c], "-diag")) {
+ show_diag = 1;
+ } else if (!strcmp(argv[c], "-summary")) {
+ show_summary = 1;
+ } else if (!strcmp(argv[c], "-bitstream_info")) {
+ parse_bitstream = 1;
+ } else if (!strcmp(argv[c], "-version")) {
+ const int version = WebPGetDecoderVersion();
+ printf("WebP Decoder version: %d.%d.%d\n",
+ (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+ return 0;
+ } else { // Assume the remaining are all input files.
+ break;
+ }
+ }
+
+ if (c == argc) {
+ HelpShort();
+ return WEBP_INFO_INVALID_COMMAND;
+ }
+
+ // Process input files one by one.
+ for (; c < argc; ++c) {
+ WebPData webp_data;
+ const char* in_file = NULL;
+ WebPInfoInit(&webp_info);
+ webp_info.quiet_ = quiet;
+ webp_info.show_diagnosis_ = show_diag;
+ webp_info.show_summary_ = show_summary;
+ webp_info.parse_bitstream_ = parse_bitstream;
+ in_file = argv[c];
+ if (in_file == NULL || !ReadFileToWebPData(in_file, &webp_data)) {
+ webp_info_status = WEBP_INFO_INVALID_COMMAND;
+ fprintf(stderr, "Failed to open input file %s.\n", in_file);
+ continue;
+ }
+ if (!webp_info.quiet_) printf("File: %s\n", in_file);
+ webp_info_status = AnalyzeWebP(&webp_info, &webp_data);
+ WebPDataClear(&webp_data);
+ }
+ return webp_info_status;
+}
diff --git a/src/third_party/libwebp/examples/webpmux.c b/src/third_party/libwebp/examples/webpmux.c
new file mode 100644
index 0000000..55e8d21
--- /dev/null
+++ b/src/third_party/libwebp/examples/webpmux.c
@@ -0,0 +1,1148 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple command-line to create a WebP container file and to extract or strip
+// relevant data from the container file.
+//
+// Authors: Vikas (vikaas.arora@gmail.com),
+// Urvang (urvang@google.com)
+
+/* Usage examples:
+
+ Create container WebP file:
+ webpmux -frame anim_1.webp +100+10+10 \
+ -frame anim_2.webp +100+25+25+1 \
+ -frame anim_3.webp +100+50+50+1 \
+ -frame anim_4.webp +100 \
+ -loop 10 -bgcolor 128,255,255,255 \
+ -o out_animation_container.webp
+
+ webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
+ webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
+ webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
+
+ Extract relevant data from WebP container file:
+ webpmux -get frame n in.webp -o out_frame.webp
+ webpmux -get icc in.webp -o image_profile.icc
+ webpmux -get exif in.webp -o image_metadata.exif
+ webpmux -get xmp in.webp -o image_metadata.xmp
+
+ Strip data from WebP Container file:
+ webpmux -strip icc in.webp -o out.webp
+ webpmux -strip exif in.webp -o out.webp
+ webpmux -strip xmp in.webp -o out.webp
+
+ Change duration of frame intervals:
+ webpmux -duration 150 in.webp -o out.webp
+ webpmux -duration 33,2 in.webp -o out.webp
+ webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp
+
+ Misc:
+ webpmux -info in.webp
+ webpmux [ -h | -help ]
+ webpmux -version
+ webpmux argument_file_name
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "webp/decode.h"
+#include "webp/mux.h"
+#include "../examples/example_util.h"
+#include "../imageio/imageio_util.h"
+
+//------------------------------------------------------------------------------
+// Config object to parse command-line arguments.
+
+typedef enum {
+ NIL_ACTION = 0,
+ ACTION_GET,
+ ACTION_SET,
+ ACTION_STRIP,
+ ACTION_INFO,
+ ACTION_HELP,
+ ACTION_DURATION
+} ActionType;
+
+typedef enum {
+ NIL_SUBTYPE = 0,
+ SUBTYPE_ANMF,
+ SUBTYPE_LOOP,
+ SUBTYPE_BGCOLOR
+} FeatureSubType;
+
+typedef struct {
+ FeatureSubType subtype_;
+ const char* filename_;
+ const char* params_;
+} FeatureArg;
+
+typedef enum {
+ NIL_FEATURE = 0,
+ FEATURE_EXIF,
+ FEATURE_XMP,
+ FEATURE_ICCP,
+ FEATURE_ANMF,
+ FEATURE_DURATION,
+ LAST_FEATURE
+} FeatureType;
+
+static const char* const kFourccList[LAST_FEATURE] = {
+ NULL, "EXIF", "XMP ", "ICCP", "ANMF"
+};
+
+static const char* const kDescriptions[LAST_FEATURE] = {
+ NULL, "EXIF metadata", "XMP metadata", "ICC profile",
+ "Animation frame"
+};
+
+typedef struct {
+ CommandLineArguments cmd_args_;
+
+ ActionType action_type_;
+ const char* input_;
+ const char* output_;
+ FeatureType type_;
+ FeatureArg* args_;
+ int arg_count_;
+} Config;
+
+//------------------------------------------------------------------------------
+// Helper functions.
+
+static int CountOccurrences(const CommandLineArguments* const args,
+ const char* const arg) {
+ int i;
+ int num_occurences = 0;
+
+ for (i = 0; i < args->argc_; ++i) {
+ if (!strcmp(args->argv_[i], arg)) {
+ ++num_occurences;
+ }
+ }
+ return num_occurences;
+}
+
+static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
+ "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
+ "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
+};
+
+static const char* ErrorString(WebPMuxError err) {
+ assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
+ return kErrorMessages[-err];
+}
+
+#define RETURN_IF_ERROR(ERR_MSG) \
+ if (err != WEBP_MUX_OK) { \
+ fprintf(stderr, ERR_MSG); \
+ return err; \
+ }
+
+#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \
+ if (err != WEBP_MUX_OK) { \
+ fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
+ return err; \
+ }
+
+#define ERROR_GOTO1(ERR_MSG, LABEL) \
+ do { \
+ fprintf(stderr, ERR_MSG); \
+ ok = 0; \
+ goto LABEL; \
+ } while (0)
+
+#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
+ do { \
+ fprintf(stderr, ERR_MSG, FORMAT_STR); \
+ ok = 0; \
+ goto LABEL; \
+ } while (0)
+
+#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
+ do { \
+ fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
+ ok = 0; \
+ goto LABEL; \
+ } while (0)
+
+static WebPMuxError DisplayInfo(const WebPMux* mux) {
+ int width, height;
+ uint32_t flag;
+
+ WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
+ assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier.
+ printf("Canvas size: %d x %d\n", width, height);
+
+ err = WebPMuxGetFeatures(mux, &flag);
+ RETURN_IF_ERROR("Failed to retrieve features\n");
+
+ if (flag == 0) {
+ printf("No features present.\n");
+ return err;
+ }
+
+ // Print the features present.
+ printf("Features present:");
+ if (flag & ANIMATION_FLAG) printf(" animation");
+ if (flag & ICCP_FLAG) printf(" ICC profile");
+ if (flag & EXIF_FLAG) printf(" EXIF metadata");
+ if (flag & XMP_FLAG) printf(" XMP metadata");
+ if (flag & ALPHA_FLAG) printf(" transparency");
+ printf("\n");
+
+ if (flag & ANIMATION_FLAG) {
+ const WebPChunkId id = WEBP_CHUNK_ANMF;
+ const char* const type_str = "frame";
+ int nFrames;
+
+ WebPMuxAnimParams params;
+ err = WebPMuxGetAnimationParams(mux, ¶ms);
+ assert(err == WEBP_MUX_OK);
+ printf("Background color : 0x%.8X Loop Count : %d\n",
+ params.bgcolor, params.loop_count);
+
+ err = WebPMuxNumChunks(mux, id, &nFrames);
+ assert(err == WEBP_MUX_OK);
+
+ printf("Number of %ss: %d\n", type_str, nFrames);
+ if (nFrames > 0) {
+ int i;
+ printf("No.: width height alpha x_offset y_offset ");
+ printf("duration dispose blend ");
+ printf("image_size compression\n");
+ for (i = 1; i <= nFrames; i++) {
+ WebPMuxFrameInfo frame;
+ err = WebPMuxGetFrame(mux, i, &frame);
+ if (err == WEBP_MUX_OK) {
+ WebPBitstreamFeatures features;
+ const VP8StatusCode status = WebPGetFeatures(
+ frame.bitstream.bytes, frame.bitstream.size, &features);
+ assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate().
+ (void)status;
+ printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
+ features.height, features.has_alpha ? "yes" : "no",
+ frame.x_offset, frame.y_offset);
+ {
+ const char* const dispose =
+ (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
+ : "background";
+ const char* const blend =
+ (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
+ printf("%8d %10s %5s ", frame.duration, dispose, blend);
+ }
+ printf("%10d %11s\n", (int)frame.bitstream.size,
+ (features.format == 1) ? "lossy" :
+ (features.format == 2) ? "lossless" :
+ "undefined");
+ }
+ WebPDataClear(&frame.bitstream);
+ RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
+ }
+ }
+ }
+
+ if (flag & ICCP_FLAG) {
+ WebPData icc_profile;
+ err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
+ assert(err == WEBP_MUX_OK);
+ printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
+ }
+
+ if (flag & EXIF_FLAG) {
+ WebPData exif;
+ err = WebPMuxGetChunk(mux, "EXIF", &exif);
+ assert(err == WEBP_MUX_OK);
+ printf("Size of the EXIF metadata: %d\n", (int)exif.size);
+ }
+
+ if (flag & XMP_FLAG) {
+ WebPData xmp;
+ err = WebPMuxGetChunk(mux, "XMP ", &xmp);
+ assert(err == WEBP_MUX_OK);
+ printf("Size of the XMP metadata: %d\n", (int)xmp.size);
+ }
+
+ if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) {
+ WebPMuxFrameInfo image;
+ err = WebPMuxGetFrame(mux, 1, &image);
+ if (err == WEBP_MUX_OK) {
+ printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
+ }
+ WebPDataClear(&image.bitstream);
+ RETURN_IF_ERROR("Failed to retrieve the image\n");
+ }
+
+ return WEBP_MUX_OK;
+}
+
+static void PrintHelp(void) {
+ printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
+ printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
+ printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n");
+ printf(" INPUT -o OUTPUT\n");
+ printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
+ printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
+ "\n");
+ printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
+ printf(" webpmux -info INPUT\n");
+ printf(" webpmux [-h|-help]\n");
+ printf(" webpmux -version\n");
+ printf(" webpmux argument_file_name\n");
+
+ printf("\n");
+ printf("GET_OPTIONS:\n");
+ printf(" Extract relevant data:\n");
+ printf(" icc get ICC profile\n");
+ printf(" exif get EXIF metadata\n");
+ printf(" xmp get XMP metadata\n");
+ printf(" frame n get nth frame\n");
+
+ printf("\n");
+ printf("SET_OPTIONS:\n");
+ printf(" Set color profile/metadata:\n");
+ printf(" icc file.icc set ICC profile\n");
+ printf(" exif file.exif set EXIF metadata\n");
+ printf(" xmp file.xmp set XMP metadata\n");
+ printf(" where: 'file.icc' contains the ICC profile to be set,\n");
+ printf(" 'file.exif' contains the EXIF metadata to be set\n");
+ printf(" 'file.xmp' contains the XMP metadata to be set\n");
+
+ printf("\n");
+ printf("DURATION_OPTIONS:\n");
+ printf(" Set duration of selected frames:\n");
+ printf(" duration set duration for each frames\n");
+ printf(" duration,frame set duration of a particular frame\n");
+ printf(" duration,start,end set duration of frames in the\n");
+ printf(" interval [start,end])\n");
+ printf(" where: 'duration' is the duration in milliseconds\n");
+ printf(" 'start' is the start frame index\n");
+ printf(" 'end' is the inclusive end frame index\n");
+ printf(" The special 'end' value '0' means: last frame.\n");
+
+ printf("\n");
+ printf("STRIP_OPTIONS:\n");
+ printf(" Strip color profile/metadata:\n");
+ printf(" icc strip ICC profile\n");
+ printf(" exif strip EXIF metadata\n");
+ printf(" xmp strip XMP metadata\n");
+
+ printf("\n");
+ printf("FRAME_OPTIONS(i):\n");
+ printf(" Create animation:\n");
+ printf(" file_i +di+[xi+yi[+mi[bi]]]\n");
+ printf(" where: 'file_i' is the i'th animation frame (WebP format),\n");
+ printf(" 'di' is the pause duration before next frame,\n");
+ printf(" 'xi','yi' specify the image offset for this frame,\n");
+ printf(" 'mi' is the dispose method for this frame (0 or 1),\n");
+ printf(" 'bi' is the blending method for this frame (+b or -b)"
+ "\n");
+
+ printf("\n");
+ printf("LOOP_COUNT:\n");
+ printf(" Number of times to repeat the animation.\n");
+ printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
+
+ printf("\n");
+ printf("BACKGROUND_COLOR:\n");
+ printf(" Background color of the canvas.\n");
+ printf(" A,R,G,B\n");
+ printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
+ "specifying\n");
+ printf(" the Alpha, Red, Green and Blue component values "
+ "respectively\n");
+ printf(" [Default: 255,255,255,255]\n");
+
+ printf("\nINPUT & OUTPUT are in WebP format.\n");
+
+ printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
+ printf(" and is assumed to be\nvalid.\n");
+ printf("\nNote: if a single file name is passed as the argument, the "
+ "arguments will be\n");
+ printf("tokenized from this file. The file name must not start with "
+ "the character '-'.\n");
+}
+
+static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
+ if ((info->x_offset | info->y_offset) & 1) {
+ fprintf(stderr, "Warning: odd offsets will be snapped to even values"
+ " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
+ info->x_offset & ~1, info->y_offset & ~1);
+ }
+}
+
+static int CreateMux(const char* const filename, WebPMux** mux) {
+ WebPData bitstream;
+ assert(mux != NULL);
+ if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0;
+ *mux = WebPMuxCreate(&bitstream, 1);
+ WebPDataClear(&bitstream);
+ if (*mux != NULL) return 1;
+ fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
+ return 0;
+}
+
+static int WriteData(const char* filename, const WebPData* const webpdata) {
+ int ok = 0;
+ FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb")
+ : ImgIoUtilSetBinaryMode(stdout);
+ if (fout == NULL) {
+ fprintf(stderr, "Error opening output WebP file %s!\n", filename);
+ return 0;
+ }
+ if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
+ fprintf(stderr, "Error writing file %s!\n", filename);
+ } else {
+ fprintf(stderr, "Saved file %s (%d bytes)\n",
+ filename, (int)webpdata->size);
+ ok = 1;
+ }
+ if (fout != stdout) fclose(fout);
+ return ok;
+}
+
+static int WriteWebP(WebPMux* const mux, const char* filename) {
+ int ok;
+ WebPData webp_data;
+ const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
+ if (err != WEBP_MUX_OK) {
+ fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
+ return 0;
+ }
+ ok = WriteData(filename, &webp_data);
+ WebPDataClear(&webp_data);
+ return ok;
+}
+
+static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
+ WebPMux* new_mux = WebPMuxNew();
+ WebPMuxAnimParams p;
+ WebPMuxError err;
+ int i;
+ int ok = 1;
+
+ if (new_mux == NULL) return NULL;
+
+ err = WebPMuxGetAnimationParams(mux, &p);
+ if (err == WEBP_MUX_OK) {
+ err = WebPMuxSetAnimationParams(new_mux, &p);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO2("Error (%s) handling animation params.\n",
+ ErrorString(err), End);
+ }
+ } else {
+ /* it might not be an animation. Just keep moving. */
+ }
+
+ for (i = 1; i <= 3; ++i) {
+ WebPData metadata;
+ err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
+ if (err == WEBP_MUX_OK && metadata.size > 0) {
+ err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End);
+ }
+ }
+ }
+
+ End:
+ if (!ok) {
+ WebPMuxDelete(new_mux);
+ new_mux = NULL;
+ }
+ return new_mux;
+}
+
+static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
+ int dispose_method, dummy;
+ char plus_minus, blend_method;
+ const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
+ &info->x_offset, &info->y_offset, &dispose_method,
+ &plus_minus, &blend_method, &dummy);
+ switch (num_args) {
+ case 1:
+ info->x_offset = info->y_offset = 0; // fall through
+ case 3:
+ dispose_method = 0; // fall through
+ case 4:
+ plus_minus = '+';
+ blend_method = 'b'; // fall through
+ case 6:
+ break;
+ case 2:
+ case 5:
+ default:
+ return 0;
+ }
+
+ WarnAboutOddOffset(info);
+
+ // Note: The sanity of the following conversion is checked by
+ // WebPMuxPushFrame().
+ info->dispose_method = (WebPMuxAnimDispose)dispose_method;
+
+ if (blend_method != 'b') return 0;
+ if (plus_minus != '-' && plus_minus != '+') return 0;
+ info->blend_method =
+ (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
+ return 1;
+}
+
+static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
+ uint32_t a, r, g, b;
+ if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
+ if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
+ *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Clean-up.
+
+static void DeleteConfig(Config* const config) {
+ if (config != NULL) {
+ free(config->args_);
+ ExUtilDeleteCommandLineArguments(&config->cmd_args_);
+ memset(config, 0, sizeof(*config));
+ }
+}
+
+//------------------------------------------------------------------------------
+// Parsing.
+
+// Basic syntactic checks on the command-line arguments.
+// Returns 1 on valid, 0 otherwise.
+// Also fills up num_feature_args to be number of feature arguments given.
+// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
+static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
+ int* num_feature_args) {
+ int num_frame_args;
+ int num_loop_args;
+ int num_bgcolor_args;
+ int num_durations_args;
+ int ok = 1;
+
+ assert(num_feature_args != NULL);
+ *num_feature_args = 0;
+
+ // Simple checks.
+ if (CountOccurrences(cmd_args, "-get") > 1) {
+ ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
+ }
+ if (CountOccurrences(cmd_args, "-set") > 1) {
+ ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
+ }
+ if (CountOccurrences(cmd_args, "-strip") > 1) {
+ ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
+ }
+ if (CountOccurrences(cmd_args, "-info") > 1) {
+ ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
+ }
+ if (CountOccurrences(cmd_args, "-o") > 1) {
+ ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
+ }
+
+ // Compound checks.
+ num_frame_args = CountOccurrences(cmd_args, "-frame");
+ num_loop_args = CountOccurrences(cmd_args, "-loop");
+ num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor");
+ num_durations_args = CountOccurrences(cmd_args, "-duration");
+
+ if (num_loop_args > 1) {
+ ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
+ }
+ if (num_bgcolor_args > 1) {
+ ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
+ }
+
+ if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
+ ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
+ "case of animation.\n", ErrValidate);
+ }
+ if (num_durations_args > 0 && num_frame_args != 0) {
+ ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
+ ErrValidate);
+ }
+
+ assert(ok == 1);
+ if (num_durations_args > 0) {
+ *num_feature_args = num_durations_args;
+ } else if (num_frame_args == 0) {
+ // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
+ *num_feature_args = 1;
+ } else {
+ // Multiple arguments ('set' action for animation)
+ *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
+ }
+
+ ErrValidate:
+ return ok;
+}
+
+#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
+
+#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
+
+#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
+ if (argc < i + (NUM)) { \
+ fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
+ goto LABEL; \
+ }
+
+#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
+ if (argc != i + (NUM)) { \
+ fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
+ goto LABEL; \
+ }
+
+// Parses command-line arguments to fill up config object. Also performs some
+// semantic checks.
+static int ParseCommandLine(Config* config) {
+ int i = 0;
+ int feature_arg_index = 0;
+ int ok = 1;
+ int argc = config->cmd_args_.argc_;
+ const char* const* argv = config->cmd_args_.argv_;
+
+ while (i < argc) {
+ FeatureArg* const arg = &config->args_[feature_arg_index];
+ if (argv[i][0] == '-') { // One of the action types or output.
+ if (!strcmp(argv[i], "-set")) {
+ if (ACTION_IS_NIL) {
+ config->action_type_ = ACTION_SET;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+ }
+ ++i;
+ } else if (!strcmp(argv[i], "-duration")) {
+ CHECK_NUM_ARGS_LESS(2, ErrParse);
+ if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
+ config->action_type_ = ACTION_DURATION;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+ }
+ if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
+ config->type_ = FEATURE_DURATION;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+ }
+ arg->params_ = argv[i + 1];
+ ++feature_arg_index;
+ i += 2;
+ } else if (!strcmp(argv[i], "-get")) {
+ if (ACTION_IS_NIL) {
+ config->action_type_ = ACTION_GET;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+ }
+ ++i;
+ } else if (!strcmp(argv[i], "-strip")) {
+ if (ACTION_IS_NIL) {
+ config->action_type_ = ACTION_STRIP;
+ config->arg_count_ = 0;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+ }
+ ++i;
+ } else if (!strcmp(argv[i], "-frame")) {
+ CHECK_NUM_ARGS_LESS(3, ErrParse);
+ if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
+ config->action_type_ = ACTION_SET;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+ }
+ if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
+ config->type_ = FEATURE_ANMF;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+ }
+ arg->subtype_ = SUBTYPE_ANMF;
+ arg->filename_ = argv[i + 1];
+ arg->params_ = argv[i + 2];
+ ++feature_arg_index;
+ i += 3;
+ } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
+ CHECK_NUM_ARGS_LESS(2, ErrParse);
+ if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
+ config->action_type_ = ACTION_SET;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+ }
+ if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
+ config->type_ = FEATURE_ANMF;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+ }
+ arg->subtype_ =
+ !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
+ arg->params_ = argv[i + 1];
+ ++feature_arg_index;
+ i += 2;
+ } else if (!strcmp(argv[i], "-o")) {
+ CHECK_NUM_ARGS_LESS(2, ErrParse);
+ config->output_ = argv[i + 1];
+ i += 2;
+ } else if (!strcmp(argv[i], "-info")) {
+ CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
+ if (config->action_type_ != NIL_ACTION) {
+ ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+ } else {
+ config->action_type_ = ACTION_INFO;
+ config->arg_count_ = 0;
+ config->input_ = argv[i + 1];
+ }
+ i += 2;
+ } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
+ PrintHelp();
+ DeleteConfig(config);
+ exit(0);
+ } else if (!strcmp(argv[i], "-version")) {
+ const int version = WebPGetMuxVersion();
+ printf("%d.%d.%d\n",
+ (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+ DeleteConfig(config);
+ exit(0);
+ } else if (!strcmp(argv[i], "--")) {
+ if (i < argc - 1) {
+ ++i;
+ if (config->input_ == NULL) {
+ config->input_ = argv[i];
+ } else {
+ ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
+ argv[i], ErrParse);
+ }
+ }
+ break;
+ } else {
+ ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
+ }
+ } else { // One of the feature types or input.
+ if (ACTION_IS_NIL) {
+ ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
+ ErrParse);
+ }
+ if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
+ !strcmp(argv[i], "xmp")) {
+ if (FEATURETYPE_IS_NIL) {
+ config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
+ (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
+ } else {
+ ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+ }
+ if (config->action_type_ == ACTION_SET) {
+ CHECK_NUM_ARGS_LESS(2, ErrParse);
+ arg->filename_ = argv[i + 1];
+ ++feature_arg_index;
+ i += 2;
+ } else {
+ ++i;
+ }
+ } else if (!strcmp(argv[i], "frame") &&
+ (config->action_type_ == ACTION_GET)) {
+ CHECK_NUM_ARGS_LESS(2, ErrParse);
+ config->type_ = FEATURE_ANMF;
+ arg->params_ = argv[i + 1];
+ ++feature_arg_index;
+ i += 2;
+ } else { // Assume input file.
+ if (config->input_ == NULL) {
+ config->input_ = argv[i];
+ } else {
+ ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
+ argv[i], ErrParse);
+ }
+ ++i;
+ }
+ }
+ }
+ ErrParse:
+ return ok;
+}
+
+// Additional checks after config is filled.
+static int ValidateConfig(Config* const config) {
+ int ok = 1;
+
+ // Action.
+ if (ACTION_IS_NIL) {
+ ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
+ }
+
+ // Feature type.
+ if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
+ ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
+ }
+
+ // Input file.
+ if (config->input_ == NULL) {
+ if (config->action_type_ != ACTION_SET) {
+ ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
+ } else if (config->type_ != FEATURE_ANMF) {
+ ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
+ }
+ }
+
+ // Output file.
+ if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
+ ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
+ }
+
+ ErrValidate2:
+ return ok;
+}
+
+// Create config object from command-line arguments.
+static int InitializeConfig(int argc, const char* argv[],
+ Config* const config) {
+ int num_feature_args = 0;
+ int ok;
+
+ memset(config, 0, sizeof(*config));
+
+ ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_);
+ if (!ok) return 0;
+
+ // Validate command-line arguments.
+ if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) {
+ ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
+ }
+
+ config->arg_count_ = num_feature_args;
+ config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
+ if (config->args_ == NULL) {
+ ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
+ }
+
+ // Parse command-line.
+ if (!ParseCommandLine(config) || !ValidateConfig(config)) {
+ ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
+ }
+
+ Err1:
+ return ok;
+}
+
+#undef ACTION_IS_NIL
+#undef FEATURETYPE_IS_NIL
+#undef CHECK_NUM_ARGS_LESS
+#undef CHECK_NUM_ARGS_MORE
+
+//------------------------------------------------------------------------------
+// Processing.
+
+static int GetFrame(const WebPMux* mux, const Config* config) {
+ WebPMuxError err = WEBP_MUX_OK;
+ WebPMux* mux_single = NULL;
+ int num = 0;
+ int ok = 1;
+ int parse_error = 0;
+ const WebPChunkId id = WEBP_CHUNK_ANMF;
+ WebPMuxFrameInfo info;
+ WebPDataInit(&info.bitstream);
+
+ num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
+ if (num < 0) {
+ ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
+ }
+ if (parse_error) goto ErrGet;
+
+ err = WebPMuxGetFrame(mux, num, &info);
+ if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
+ ErrorString(err), num, ErrGet);
+ }
+
+ mux_single = WebPMuxNew();
+ if (mux_single == NULL) {
+ err = WEBP_MUX_MEMORY_ERROR;
+ ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
+ ErrorString(err), ErrGet);
+ }
+ err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
+ ErrorString(err), ErrGet);
+ }
+
+ ok = WriteWebP(mux_single, config->output_);
+
+ ErrGet:
+ WebPDataClear(&info.bitstream);
+ WebPMuxDelete(mux_single);
+ return ok && !parse_error;
+}
+
+// Read and process config.
+static int Process(const Config* config) {
+ WebPMux* mux = NULL;
+ WebPData chunk;
+ WebPMuxError err = WEBP_MUX_OK;
+ int ok = 1;
+
+ switch (config->action_type_) {
+ case ACTION_GET: {
+ ok = CreateMux(config->input_, &mux);
+ if (!ok) goto Err2;
+ switch (config->type_) {
+ case FEATURE_ANMF:
+ ok = GetFrame(mux, config);
+ break;
+
+ case FEATURE_ICCP:
+ case FEATURE_EXIF:
+ case FEATURE_XMP:
+ err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
+ ErrorString(err), kDescriptions[config->type_], Err2);
+ }
+ ok = WriteData(config->output_, &chunk);
+ break;
+
+ default:
+ ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
+ break;
+ }
+ break;
+ }
+ case ACTION_SET: {
+ switch (config->type_) {
+ case FEATURE_ANMF: {
+ int i;
+ WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
+ mux = WebPMuxNew();
+ if (mux == NULL) {
+ ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
+ ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
+ }
+ for (i = 0; i < config->arg_count_; ++i) {
+ switch (config->args_[i].subtype_) {
+ case SUBTYPE_BGCOLOR: {
+ uint32_t bgcolor;
+ ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
+ if (!ok) {
+ ERROR_GOTO1("ERROR: Could not parse the background color \n",
+ Err2);
+ }
+ params.bgcolor = bgcolor;
+ break;
+ }
+ case SUBTYPE_LOOP: {
+ int parse_error = 0;
+ const int loop_count =
+ ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
+ if (loop_count < 0 || loop_count > 65535) {
+ // Note: This is only a 'necessary' condition for loop_count
+ // to be valid. The 'sufficient' conditioned in checked in
+ // WebPMuxSetAnimationParams() method called later.
+ ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
+ "65535.\n", Err2);
+ }
+ ok = !parse_error;
+ if (!ok) goto Err2;
+ params.loop_count = loop_count;
+ break;
+ }
+ case SUBTYPE_ANMF: {
+ WebPMuxFrameInfo frame;
+ frame.id = WEBP_CHUNK_ANMF;
+ ok = ExUtilReadFileToWebPData(config->args_[i].filename_,
+ &frame.bitstream);
+ if (!ok) goto Err2;
+ ok = ParseFrameArgs(config->args_[i].params_, &frame);
+ if (!ok) {
+ WebPDataClear(&frame.bitstream);
+ ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
+ Err2);
+ }
+ err = WebPMuxPushFrame(mux, &frame, 1);
+ WebPDataClear(&frame.bitstream);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
+ "\n", ErrorString(err), i, Err2);
+ }
+ break;
+ }
+ default: {
+ ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
+ break;
+ }
+ }
+ }
+ err = WebPMuxSetAnimationParams(mux, ¶ms);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
+ ErrorString(err), Err2);
+ }
+ break;
+ }
+
+ case FEATURE_ICCP:
+ case FEATURE_EXIF:
+ case FEATURE_XMP: {
+ ok = CreateMux(config->input_, &mux);
+ if (!ok) goto Err2;
+ ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk);
+ if (!ok) goto Err2;
+ err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
+ free((void*)chunk.bytes);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
+ ErrorString(err), kDescriptions[config->type_], Err2);
+ }
+ break;
+ }
+ default: {
+ ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
+ break;
+ }
+ }
+ ok = WriteWebP(mux, config->output_);
+ break;
+ }
+ case ACTION_DURATION: {
+ int num_frames;
+ ok = CreateMux(config->input_, &mux);
+ if (!ok) goto Err2;
+ err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
+ ok = (err == WEBP_MUX_OK);
+ if (!ok) {
+ ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
+ }
+ if (num_frames == 0) {
+ fprintf(stderr, "Doesn't look like the source is animated. "
+ "Skipping duration setting.\n");
+ ok = WriteWebP(mux, config->output_);
+ if (!ok) goto Err2;
+ } else {
+ int i;
+ int* durations = NULL;
+ WebPMux* new_mux = DuplicateMuxHeader(mux);
+ if (new_mux == NULL) goto Err2;
+ durations = (int*)malloc((size_t)num_frames * sizeof(*durations));
+ if (durations == NULL) goto Err2;
+ for (i = 0; i < num_frames; ++i) durations[i] = -1;
+
+ // Parse intervals to process.
+ for (i = 0; i < config->arg_count_; ++i) {
+ int k;
+ int args[3];
+ int duration, start, end;
+ const int nb_args = ExUtilGetInts(config->args_[i].params_,
+ 10, 3, args);
+ ok = (nb_args >= 1);
+ if (!ok) goto Err3;
+ duration = args[0];
+ if (duration < 0) {
+ ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
+ }
+
+ if (nb_args == 1) { // only duration is present -> use full interval
+ start = 1;
+ end = num_frames;
+ } else {
+ start = args[1];
+ if (start <= 0) {
+ start = 1;
+ } else if (start > num_frames) {
+ start = num_frames;
+ }
+ end = (nb_args >= 3) ? args[2] : start;
+ if (end == 0 || end > num_frames) end = num_frames;
+ }
+
+ for (k = start; k <= end; ++k) {
+ assert(k >= 1 && k <= num_frames);
+ durations[k - 1] = duration;
+ }
+ }
+
+ // Apply non-negative durations to their destination frames.
+ for (i = 1; i <= num_frames; ++i) {
+ WebPMuxFrameInfo frame;
+ err = WebPMuxGetFrame(mux, i, &frame);
+ if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
+ ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
+ }
+ if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
+ err = WebPMuxPushFrame(new_mux, &frame, 1);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
+ }
+ WebPDataClear(&frame.bitstream);
+ }
+ WebPMuxDelete(mux);
+ ok = WriteWebP(new_mux, config->output_);
+ mux = new_mux; // transfer for the WebPMuxDelete() call
+ new_mux = NULL;
+
+ Err3:
+ free(durations);
+ WebPMuxDelete(new_mux);
+ if (!ok) goto Err2;
+ }
+ break;
+ }
+ case ACTION_STRIP: {
+ ok = CreateMux(config->input_, &mux);
+ if (!ok) goto Err2;
+ if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
+ config->type_ == FEATURE_XMP) {
+ err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
+ if (err != WEBP_MUX_OK) {
+ ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
+ ErrorString(err), kDescriptions[config->type_], Err2);
+ }
+ } else {
+ ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
+ break;
+ }
+ ok = WriteWebP(mux, config->output_);
+ break;
+ }
+ case ACTION_INFO: {
+ ok = CreateMux(config->input_, &mux);
+ if (!ok) goto Err2;
+ ok = (DisplayInfo(mux) == WEBP_MUX_OK);
+ break;
+ }
+ default: {
+ assert(0); // Invalid action.
+ break;
+ }
+ }
+
+ Err2:
+ WebPMuxDelete(mux);
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// Main.
+
+int main(int argc, const char* argv[]) {
+ Config config;
+ int ok = InitializeConfig(argc - 1, argv + 1, &config);
+ if (ok) {
+ ok = Process(&config);
+ } else {
+ PrintHelp();
+ }
+ DeleteConfig(&config);
+ return !ok;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/extras/Makefile.am b/src/third_party/libwebp/extras/Makefile.am
new file mode 100644
index 0000000..14d567a
--- /dev/null
+++ b/src/third_party/libwebp/extras/Makefile.am
@@ -0,0 +1,44 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+noinst_LTLIBRARIES = libwebpextras.la
+
+noinst_HEADERS =
+noinst_HEADERS += ../src/webp/types.h
+
+libwebpextras_la_SOURCES =
+libwebpextras_la_SOURCES += extras.c extras.h quality_estimate.c
+
+libwebpextras_la_CPPFLAGS = $(AM_CPPFLAGS)
+libwebpextras_la_LDFLAGS = -lm
+libwebpextras_la_LIBADD = ../src/libwebp.la
+
+noinst_PROGRAMS =
+noinst_PROGRAMS += webp_quality
+if WANT_DEMUX
+ noinst_PROGRAMS += get_disto
+endif
+if BUILD_VWEBP_SDL
+ noinst_PROGRAMS += vwebp_sdl
+endif
+
+get_disto_SOURCES = get_disto.c
+get_disto_CPPFLAGS = $(AM_CPPFLAGS)
+get_disto_LDADD =
+get_disto_LDADD += ../imageio/libimageio_util.la
+get_disto_LDADD += ../imageio/libimagedec.la
+get_disto_LDADD += ../src/libwebp.la
+get_disto_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
+
+webp_quality_SOURCES = webp_quality.c
+webp_quality_CPPFLAGS = $(AM_CPPFLAGS)
+webp_quality_LDADD =
+webp_quality_LDADD += ../imageio/libimageio_util.la
+webp_quality_LDADD += libwebpextras.la
+webp_quality_LDADD += ../src/libwebp.la
+
+vwebp_sdl_SOURCES = vwebp_sdl.c webp_to_sdl.c webp_to_sdl.h
+vwebp_sdl_CPPFLAGS = $(AM_CPPFLAGS) $(SDL_INCLUDES)
+vwebp_sdl_LDADD =
+vwebp_sdl_LDADD += ../imageio/libimageio_util.la
+vwebp_sdl_LDADD += ../src/libwebp.la
+vwebp_sdl_LDADD += $(SDL_LIBS)
diff --git a/src/third_party/libwebp/extras/extras.c b/src/third_party/libwebp/extras/extras.c
new file mode 100644
index 0000000..2feb595
--- /dev/null
+++ b/src/third_party/libwebp/extras/extras.c
@@ -0,0 +1,146 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Additional WebP utilities.
+//
+
+#include "extras/extras.h"
+#include "webp/format_constants.h"
+
+#include <assert.h>
+#include <string.h>
+
+#define XTRA_MAJ_VERSION 1
+#define XTRA_MIN_VERSION 0
+#define XTRA_REV_VERSION 0
+
+//------------------------------------------------------------------------------
+
+int WebPGetExtrasVersion(void) {
+ return (XTRA_MAJ_VERSION << 16) | (XTRA_MIN_VERSION << 8) | XTRA_REV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+
+int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) {
+ int y, width, uv_width;
+ if (pic == NULL || gray_data == NULL) return 0;
+ pic->colorspace = WEBP_YUV420;
+ if (!WebPPictureAlloc(pic)) return 0;
+ width = pic->width;
+ uv_width = (width + 1) >> 1;
+ for (y = 0; y < pic->height; ++y) {
+ memcpy(pic->y + y * pic->y_stride, gray_data, width);
+ gray_data += width; // <- we could use some 'data_stride' here if needed
+ if ((y & 1) == 0) {
+ memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width);
+ memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width);
+ }
+ }
+ return 1;
+}
+
+int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic) {
+ int x, y;
+ uint32_t* dst;
+ if (pic == NULL || rgb565 == NULL) return 0;
+ pic->colorspace = WEBP_YUV420;
+ pic->use_argb = 1;
+ if (!WebPPictureAlloc(pic)) return 0;
+ dst = pic->argb;
+ for (y = 0; y < pic->height; ++y) {
+ const int width = pic->width;
+ for (x = 0; x < width; ++x) {
+#ifdef WEBP_SWAP_16BIT_CSP
+ const uint32_t rg = rgb565[2 * x + 1];
+ const uint32_t gb = rgb565[2 * x + 0];
+#else
+ const uint32_t rg = rgb565[2 * x + 0];
+ const uint32_t gb = rgb565[2 * x + 1];
+#endif
+ uint32_t r = rg & 0xf8;
+ uint32_t g = ((rg << 5) | (gb >> 3)) & 0xfc;
+ uint32_t b = (gb << 5);
+ // dithering
+ r = r | (r >> 5);
+ g = g | (g >> 6);
+ b = b | (b >> 5);
+ dst[x] = (0xffu << 24) | (r << 16) | (g << 8) | b;
+ }
+ rgb565 += 2 * width;
+ dst += pic->argb_stride;
+ }
+ return 1;
+}
+
+int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic) {
+ int x, y;
+ uint32_t* dst;
+ if (pic == NULL || rgb4444 == NULL) return 0;
+ pic->colorspace = WEBP_YUV420;
+ pic->use_argb = 1;
+ if (!WebPPictureAlloc(pic)) return 0;
+ dst = pic->argb;
+ for (y = 0; y < pic->height; ++y) {
+ const int width = pic->width;
+ for (x = 0; x < width; ++x) {
+#ifdef WEBP_SWAP_16BIT_CSP
+ const uint32_t rg = rgb4444[2 * x + 1];
+ const uint32_t ba = rgb4444[2 * x + 0];
+#else
+ const uint32_t rg = rgb4444[2 * x + 0];
+ const uint32_t ba = rgb4444[2 * x + 1];
+#endif
+ uint32_t r = rg & 0xf0;
+ uint32_t g = (rg << 4);
+ uint32_t b = (ba & 0xf0);
+ uint32_t a = (ba << 4);
+ // dithering
+ r = r | (r >> 4);
+ g = g | (g >> 4);
+ b = b | (b >> 4);
+ a = a | (a >> 4);
+ dst[x] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ rgb4444 += 2 * width;
+ dst += pic->argb_stride;
+ }
+ return 1;
+}
+
+int WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
+ const uint32_t palette[], int palette_size,
+ WebPPicture* pic) {
+ int x, y;
+ uint32_t* dst;
+ // 256 as the input buffer is uint8_t.
+ assert(MAX_PALETTE_SIZE <= 256);
+ if (pic == NULL || indexed == NULL || indexed_stride < pic->width ||
+ palette == NULL || palette_size > MAX_PALETTE_SIZE || palette_size <= 0) {
+ return 0;
+ }
+ pic->use_argb = 1;
+ if (!WebPPictureAlloc(pic)) return 0;
+ dst = pic->argb;
+ for (y = 0; y < pic->height; ++y) {
+ for (x = 0; x < pic->width; ++x) {
+ // Make sure we are within the palette.
+ if (indexed[x] >= palette_size) {
+ WebPPictureFree(pic);
+ return 0;
+ }
+ dst[x] = palette[indexed[x]];
+ }
+ indexed += indexed_stride;
+ dst += pic->argb_stride;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/extras/extras.h b/src/third_party/libwebp/extras/extras.h
new file mode 100644
index 0000000..fd4437a
--- /dev/null
+++ b/src/third_party/libwebp/extras/extras.h
@@ -0,0 +1,70 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+
+#ifndef WEBP_EXTRAS_EXTRAS_H_
+#define WEBP_EXTRAS_EXTRAS_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "webp/encode.h"
+
+#define WEBP_EXTRAS_ABI_VERSION 0x0001 // MAJOR(8b) + MINOR(8b)
+
+//------------------------------------------------------------------------------
+
+// Returns the version number of the extras library, packed in hexadecimal using
+// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
+WEBP_EXTERN int WebPGetExtrasVersion(void);
+
+//------------------------------------------------------------------------------
+// Ad-hoc colorspace importers.
+
+// Import luma sample (gray scale image) into 'picture'. The 'picture'
+// width and height must be set prior to calling this function.
+WEBP_EXTERN int WebPImportGray(const uint8_t* gray, WebPPicture* picture);
+
+// Import rgb sample in RGB565 packed format into 'picture'. The 'picture'
+// width and height must be set prior to calling this function.
+WEBP_EXTERN int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic);
+
+// Import rgb sample in RGB4444 packed format into 'picture'. The 'picture'
+// width and height must be set prior to calling this function.
+WEBP_EXTERN int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic);
+
+// Import a color mapped image. The number of colors is less or equal to
+// MAX_PALETTE_SIZE. 'pic' must have been initialized. Its content, if any,
+// will be discarded. Returns 'false' in case of error, or if indexed[] contains
+// invalid indices.
+WEBP_EXTERN int
+WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
+ const uint32_t palette[], int palette_size,
+ WebPPicture* pic);
+
+//------------------------------------------------------------------------------
+
+// Parse a bitstream, search for VP8 (lossy) header and report a
+// rough estimation of the quality factor used for compressing the bitstream.
+// If the bitstream is in lossless format, the special value '101' is returned.
+// Otherwise (lossy bitstream), the returned value is in the range [0..100].
+// Any error (invalid bitstream, animated WebP, incomplete header, etc.)
+// will return a value of -1.
+WEBP_EXTERN int VP8EstimateQuality(const uint8_t* const data, size_t size);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_EXTRAS_EXTRAS_H_ */
diff --git a/src/third_party/libwebp/extras/get_disto.c b/src/third_party/libwebp/extras/get_disto.c
new file mode 100644
index 0000000..b406147
--- /dev/null
+++ b/src/third_party/libwebp/extras/get_disto.c
@@ -0,0 +1,351 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple tool to load two webp/png/jpg/tiff files and compute PSNR/SSIM.
+// This is mostly a wrapper around WebPPictureDistortion().
+//
+/*
+ gcc -o get_disto get_disto.c -O3 -I../ -L../examples -L../imageio \
+ -lexample_util -limageio_util -limagedec -lwebp -L/opt/local/lib \
+ -lpng -lz -ljpeg -ltiff -lm -lpthread
+*/
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "imageio/image_dec.h"
+#include "imageio/imageio_util.h"
+
+static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
+ int keep_alpha) {
+ const uint8_t* data = NULL;
+ size_t data_size = 0;
+ WebPImageReader reader = NULL;
+ int ok = ImgIoUtilReadFile(filename, &data, &data_size);
+ if (!ok) goto End;
+
+ pic->use_argb = 1; // force ARGB
+
+#ifdef HAVE_WINCODEC_H
+ // Try to decode the file using WIC falling back to the other readers for
+ // e.g., WebP.
+ ok = ReadPictureWithWIC(filename, pic, keep_alpha, NULL);
+ if (ok) goto End;
+#endif
+ reader = WebPGuessImageReader(data, data_size);
+ ok = reader(data, data_size, pic, keep_alpha, NULL);
+
+ End:
+ if (!ok) {
+ fprintf(stderr, "Error! Could not process file %s\n", filename);
+ }
+ free((void*)data);
+ return ok ? data_size : 0;
+}
+
+static void RescalePlane(uint8_t* plane, int width, int height,
+ int x_stride, int y_stride, int max) {
+ const uint32_t factor = (max > 0) ? (255u << 16) / max : 0;
+ int x, y;
+ for (y = 0; y < height; ++y) {
+ uint8_t* const ptr = plane + y * y_stride;
+ for (x = 0; x < width * x_stride; x += x_stride) {
+ const uint32_t diff = (ptr[x] * factor + (1 << 15)) >> 16;
+ ptr[x] = diff;
+ }
+ }
+}
+
+// Return the max absolute difference.
+static int DiffScaleChannel(uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int x_stride, int w, int h, int do_scaling) {
+ int x, y;
+ int max = 0;
+ for (y = 0; y < h; ++y) {
+ uint8_t* const ptr1 = src1 + y * stride1;
+ const uint8_t* const ptr2 = src2 + y * stride2;
+ for (x = 0; x < w * x_stride; x += x_stride) {
+ const int diff = abs(ptr1[x] - ptr2[x]);
+ if (diff > max) max = diff;
+ ptr1[x] = diff;
+ }
+ }
+
+ if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
+ return max;
+}
+
+//------------------------------------------------------------------------------
+// SSIM calculation. We re-implement these functions here, out of dsp/, to avoid
+// breaking the library's hidden visibility. This code duplication avoids the
+// bigger annoyance of having to open up internal details of libdsp...
+
+#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
+
+// struct for accumulating statistical moments
+typedef struct {
+ uint32_t w; // sum(w_i) : sum of weights
+ uint32_t xm, ym; // sum(w_i * x_i), sum(w_i * y_i)
+ uint32_t xxm, xym, yym; // sum(w_i * x_i * x_i), etc.
+} DistoStats;
+
+// hat-shaped filter. Sum of coefficients is equal to 16.
+static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = { 1, 2, 3, 4, 3, 2, 1 };
+
+static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
+ const uint32_t N = stats->w;
+ const uint32_t w2 = N * N;
+ const uint32_t C1 = 20 * w2;
+ const uint32_t C2 = 60 * w2;
+ const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
+ const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
+ const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
+ if (xmxm + ymym >= C3) {
+ const int64_t xmym = (int64_t)stats->xm * stats->ym;
+ const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
+ const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
+ const uint64_t syy = (uint64_t)stats->yym * N - ymym;
+ // we descale by 8 to prevent overflow during the fnum/fden multiply.
+ const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
+ const uint64_t den_S = (sxx + syy + C2) >> 8;
+ const uint64_t fnum = (2 * xmym + C1) * num_S;
+ const uint64_t fden = (xmxm + ymym + C1) * den_S;
+ const double r = (double)fnum / fden;
+ assert(r >= 0. && r <= 1.0);
+ return r;
+ }
+ return 1.; // area is too dark to contribute meaningfully
+}
+
+static double SSIMGetClipped(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int xo, int yo, int W, int H) {
+ DistoStats stats = { 0, 0, 0, 0, 0, 0 };
+ const int ymin = (yo - SSIM_KERNEL < 0) ? 0 : yo - SSIM_KERNEL;
+ const int ymax = (yo + SSIM_KERNEL > H - 1) ? H - 1 : yo + SSIM_KERNEL;
+ const int xmin = (xo - SSIM_KERNEL < 0) ? 0 : xo - SSIM_KERNEL;
+ const int xmax = (xo + SSIM_KERNEL > W - 1) ? W - 1 : xo + SSIM_KERNEL;
+ int x, y;
+ src1 += ymin * stride1;
+ src2 += ymin * stride2;
+ for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
+ for (x = xmin; x <= xmax; ++x) {
+ const uint32_t w = kWeight[SSIM_KERNEL + x - xo]
+ * kWeight[SSIM_KERNEL + y - yo];
+ const uint32_t s1 = src1[x];
+ const uint32_t s2 = src2[x];
+ stats.w += w;
+ stats.xm += w * s1;
+ stats.ym += w * s2;
+ stats.xxm += w * s1 * s1;
+ stats.xym += w * s1 * s2;
+ stats.yym += w * s2 * s2;
+ }
+ }
+ return SSIMCalculation(&stats);
+}
+
+// Compute SSIM-score map. Return -1 in case of error, max diff otherwise.
+static int SSIMScaleChannel(uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int x_stride, int w, int h, int do_scaling) {
+ int x, y;
+ int max = 0;
+ uint8_t* const plane1 = (uint8_t*)malloc(2 * w * h * sizeof(*plane1));
+ uint8_t* const plane2 = plane1 + w * h;
+ if (plane1 == NULL) return -1;
+
+ // extract plane
+ for (y = 0; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ plane1[x + y * w] = src1[x * x_stride + y * stride1];
+ plane2[x + y * w] = src2[x * x_stride + y * stride2];
+ }
+ }
+ for (y = 0; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ const double ssim = SSIMGetClipped(plane1, w, plane2, w, x, y, w, h);
+ int diff = (int)(255 * (1. - ssim));
+ if (diff < 0) {
+ diff = 0;
+ } else if (diff > max) {
+ max = diff;
+ }
+ src1[x * x_stride + y * stride1] = (diff > 255) ? 255u : (uint8_t)diff;
+ }
+ }
+ free(plane1);
+
+ if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
+ return max;
+}
+
+// Convert an argb picture to luminance.
+static void ConvertToGray(WebPPicture* const pic) {
+ int x, y;
+ assert(pic != NULL);
+ assert(pic->use_argb);
+ for (y = 0; y < pic->height; ++y) {
+ uint32_t* const row = &pic->argb[y * pic->argb_stride];
+ for (x = 0; x < pic->width; ++x) {
+ const uint32_t argb = row[x];
+ const uint32_t r = (argb >> 16) & 0xff;
+ const uint32_t g = (argb >> 8) & 0xff;
+ const uint32_t b = (argb >> 0) & 0xff;
+ // We use BT.709 for converting to luminance.
+ const uint32_t Y = (uint32_t)(0.2126 * r + 0.7152 * g + 0.0722 * b + .5);
+ row[x] = (argb & 0xff000000u) | (Y * 0x010101u);
+ }
+ }
+}
+
+static void Help(void) {
+ fprintf(stderr,
+ "Usage: get_disto [-ssim][-psnr][-alpha] compressed.webp orig.webp\n"
+ " -ssim ..... print SSIM distortion\n"
+ " -psnr ..... print PSNR distortion (default)\n"
+ " -alpha .... preserve alpha plane\n"
+ " -h ........ this message\n"
+ " -o <file> . save the diff map as a WebP lossless file\n"
+ " -scale .... scale the difference map to fit [0..255] range\n"
+ " -gray ..... use grayscale for difference map (-scale)\n"
+ " Also handles PNG, JPG and TIFF files, in addition to WebP.\n");
+}
+
+int main(int argc, const char *argv[]) {
+ WebPPicture pic1, pic2;
+ size_t size1 = 0, size2 = 0;
+ int ret = 1;
+ float disto[5];
+ int type = 0;
+ int c;
+ int help = 0;
+ int keep_alpha = 0;
+ int scale = 0;
+ int use_gray = 0;
+ const char* name1 = NULL;
+ const char* name2 = NULL;
+ const char* output = NULL;
+
+ if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
+ fprintf(stderr, "Can't init pictures\n");
+ return 1;
+ }
+
+ for (c = 1; c < argc; ++c) {
+ if (!strcmp(argv[c], "-ssim")) {
+ type = 1;
+ } else if (!strcmp(argv[c], "-psnr")) {
+ type = 0;
+ } else if (!strcmp(argv[c], "-alpha")) {
+ keep_alpha = 1;
+ } else if (!strcmp(argv[c], "-scale")) {
+ scale = 1;
+ } else if (!strcmp(argv[c], "-gray")) {
+ use_gray = 1;
+ } else if (!strcmp(argv[c], "-h")) {
+ help = 1;
+ ret = 0;
+ } else if (!strcmp(argv[c], "-o")) {
+ if (++c == argc) {
+ fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
+ goto End;
+ }
+ output = argv[c];
+ } else if (name1 == NULL) {
+ name1 = argv[c];
+ } else {
+ name2 = argv[c];
+ }
+ }
+ if (help || name1 == NULL || name2 == NULL) {
+ if (!help) {
+ fprintf(stderr, "Error: missing arguments.\n");
+ }
+ Help();
+ goto End;
+ }
+ size1 = ReadPicture(name1, &pic1, 1);
+ size2 = ReadPicture(name2, &pic2, 1);
+ if (size1 == 0 || size2 == 0) goto End;
+
+ if (!keep_alpha) {
+ WebPBlendAlpha(&pic1, 0x00000000);
+ WebPBlendAlpha(&pic2, 0x00000000);
+ }
+
+ if (!WebPPictureDistortion(&pic1, &pic2, type, disto)) {
+ fprintf(stderr, "Error while computing the distortion.\n");
+ goto End;
+ }
+ printf("%u %.2f %.2f %.2f %.2f %.2f [ %.2f bpp ]\n",
+ (unsigned int)size1,
+ disto[4], disto[0], disto[1], disto[2], disto[3],
+ 8.f * size1 / pic1.width / pic1.height);
+
+ if (output != NULL) {
+ uint8_t* data = NULL;
+ size_t data_size = 0;
+ if (pic1.use_argb != pic2.use_argb) {
+ fprintf(stderr, "Pictures are not in the same argb format. "
+ "Can't save the difference map.\n");
+ goto End;
+ }
+ if (pic1.use_argb) {
+ int n;
+ fprintf(stderr, "max differences per channel: ");
+ for (n = 0; n < 3; ++n) { // skip the alpha channel
+ const int range = (type == 1) ?
+ SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
+ (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
+ 4, pic1.width, pic1.height, scale) :
+ DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
+ (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
+ 4, pic1.width, pic1.height, scale);
+ if (range < 0) fprintf(stderr, "\nError computing diff map\n");
+ fprintf(stderr, "[%d]", range);
+ }
+ fprintf(stderr, "\n");
+ if (use_gray) ConvertToGray(&pic1);
+ } else {
+ fprintf(stderr, "Can only compute the difference map in ARGB format.\n");
+ goto End;
+ }
+#if !defined(WEBP_REDUCE_CSP)
+ data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb,
+ pic1.width, pic1.height,
+ pic1.argb_stride * 4,
+ &data);
+ if (data_size == 0) {
+ fprintf(stderr, "Error during lossless encoding.\n");
+ goto End;
+ }
+ ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1;
+ WebPFree(data);
+ if (ret) goto End;
+#else
+ (void)data;
+ (void)data_size;
+ fprintf(stderr, "Cannot save the difference map. Please recompile "
+ "without the WEBP_REDUCE_CSP flag.\n");
+#endif // WEBP_REDUCE_CSP
+ }
+ ret = 0;
+
+ End:
+ WebPPictureFree(&pic1);
+ WebPPictureFree(&pic2);
+ return ret;
+}
diff --git a/src/third_party/libwebp/extras/quality_estimate.c b/src/third_party/libwebp/extras/quality_estimate.c
new file mode 100644
index 0000000..17e98d9
--- /dev/null
+++ b/src/third_party/libwebp/extras/quality_estimate.c
@@ -0,0 +1,129 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// VP8EstimateQuality(): rough encoding quality estimate
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "extras/extras.h"
+#include "webp/decode.h"
+
+#include <math.h>
+
+//------------------------------------------------------------------------------
+
+#define INVALID_BIT_POS (1ull << 63)
+
+// In most cases, we don't need to use a full arithmetic decoder, since
+// all the header's bits are written using a uniform probability of 128.
+// We can just parse the header as if it was bits (works in 99.999% cases).
+static WEBP_INLINE uint32_t GetBit(const uint8_t* const data, size_t nb,
+ uint64_t max_size, uint64_t* const bit_pos) {
+ uint32_t val = 0;
+ if (*bit_pos + nb <= 8 * max_size) {
+ while (nb-- > 0) {
+ const uint64_t p = (*bit_pos)++;
+ const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
+ val = (val << 1) | bit;
+ }
+ } else {
+ *bit_pos = INVALID_BIT_POS;
+ }
+ return val;
+}
+
+#define GET_BIT(n) GetBit(data, (n), size, &bit_pos)
+#define CONDITIONAL_SKIP(n) (GET_BIT(1) ? GET_BIT((n)) : 0)
+
+int VP8EstimateQuality(const uint8_t* const data, size_t size) {
+ size_t pos = 0;
+ uint64_t bit_pos;
+ uint64_t sig = 0x00;
+ int ok = 0;
+ int Q = -1;
+ WebPBitstreamFeatures features;
+
+ if (data == NULL) return -1;
+
+ if (WebPGetFeatures(data, size, &features) != VP8_STATUS_OK) {
+ return -1; // invalid file
+ }
+ if (features.format == 2) return 101; // lossless
+ if (features.format == 0 || features.has_animation) return -1; // mixed
+
+ while (pos < size) {
+ sig = (sig >> 8) | ((uint64_t)data[pos++] << 40);
+ if ((sig >> 24) == 0x2a019dull) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) return -1;
+ if (pos + 4 > size) return -1;
+
+ // Skip main Header
+ // width = (data[pos + 0] | (data[pos + 1] << 8)) & 0x3fff;
+ // height = (data[pos + 2] | (data[pos + 3] << 8)) & 0x3fff;
+ pos += 4;
+ bit_pos = pos * 8;
+
+ GET_BIT(2); // colorspace + clamp type
+
+ // Segment header
+ if (GET_BIT(1)) { // use_segment_
+ int s;
+ const int update_map = GET_BIT(1);
+ if (GET_BIT(1)) { // update data
+ const int absolute_delta = GET_BIT(1);
+ int q[4] = { 0, 0, 0, 0 };
+ for (s = 0; s < 4; ++s) {
+ if (GET_BIT(1)) {
+ q[s] = GET_BIT(7);
+ if (GET_BIT(1)) q[s] = -q[s]; // sign
+ }
+ }
+ if (absolute_delta) Q = q[0]; // just use the first segment's quantizer
+ for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7); // filter strength
+ }
+ if (update_map) {
+ for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8);
+ }
+ }
+ // Filter header
+ GET_BIT(1 + 6 + 3); // simple + level + sharpness
+ if (GET_BIT(1)) { // use_lf_delta
+ if (GET_BIT(1)) { // update lf_delta?
+ int n;
+ for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6);
+ }
+ }
+ // num partitions
+ GET_BIT(2);
+
+ // ParseQuant
+ {
+ const int base_q = GET_BIT(7);
+ /* dqy1_dc = */ CONDITIONAL_SKIP(5);
+ /* dqy2_dc = */ CONDITIONAL_SKIP(5);
+ /* dqy2_ac = */ CONDITIONAL_SKIP(5);
+ /* dquv_dc = */ CONDITIONAL_SKIP(5);
+ /* dquv_ac = */ CONDITIONAL_SKIP(5);
+
+ if (Q < 0) Q = base_q;
+ }
+ if (bit_pos == INVALID_BIT_POS) return -1;
+
+ // base mapping
+ Q = (127 - Q) * 100 / 127;
+ // correction for power-law behavior in low range
+ if (Q < 80) {
+ Q = (int)(pow(Q / 80., 1. / 0.38) * 80);
+ }
+ return Q;
+}
diff --git a/src/third_party/libwebp/extras/vwebp_sdl.c b/src/third_party/libwebp/extras/vwebp_sdl.c
new file mode 100644
index 0000000..69171b9
--- /dev/null
+++ b/src/third_party/libwebp/extras/vwebp_sdl.c
@@ -0,0 +1,96 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple SDL-based WebP file viewer.
+// Does not support animation, just static images.
+//
+// Press 'q' to exit.
+//
+// Author: James Zern (jzern@google.com)
+
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#if defined(WEBP_HAVE_SDL)
+
+#include "webp_to_sdl.h"
+#include "webp/decode.h"
+#include "imageio/imageio_util.h"
+
+#if defined(WEBP_HAVE_JUST_SDL_H)
+#include <SDL.h>
+#else
+#include <SDL/SDL.h>
+#endif
+
+static void ProcessEvents(void) {
+ int done = 0;
+ SDL_Event event;
+ while (!done && SDL_WaitEvent(&event)) {
+ switch (event.type) {
+ case SDL_KEYUP:
+ switch (event.key.keysym.sym) {
+ case SDLK_q: done = 1; break;
+ default: break;
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+int main(int argc, char* argv[]) {
+ int c;
+ int ok = 0;
+ for (c = 1; c < argc; ++c) {
+ const char* file = NULL;
+ const uint8_t* webp = NULL;
+ size_t webp_size = 0;
+ if (!strcmp(argv[c], "-h")) {
+ printf("Usage: %s [-h] image.webp [more_files.webp...]\n", argv[0]);
+ return 0;
+ } else {
+ file = argv[c];
+ }
+ if (file == NULL) continue;
+ if (!ImgIoUtilReadFile(file, &webp, &webp_size)) {
+ fprintf(stderr, "Error opening file: %s\n", file);
+ goto Error;
+ }
+ if (webp_size != (size_t)(int)webp_size) {
+ fprintf(stderr, "File too large.\n");
+ goto Error;
+ }
+ ok = WebpToSDL((const char*)webp, (int)webp_size);
+ free((void*)webp);
+ if (!ok) {
+ fprintf(stderr, "Error decoding file %s\n", file);
+ goto Error;
+ }
+ ProcessEvents();
+ }
+ ok = 1;
+
+ Error:
+ SDL_Quit();
+ return ok ? 0 : 1;
+}
+
+#else // !WEBP_HAVE_SDL
+
+int main(int argc, const char *argv[]) {
+ fprintf(stderr, "SDL support not enabled in %s.\n", argv[0]);
+ (void)argc;
+ return 0;
+}
+
+#endif
diff --git a/src/third_party/libwebp/extras/webp_quality.c b/src/third_party/libwebp/extras/webp_quality.c
new file mode 100644
index 0000000..3f6ba20
--- /dev/null
+++ b/src/third_party/libwebp/extras/webp_quality.c
@@ -0,0 +1,50 @@
+// Simple tool to roughly evaluate the quality encoding of a webp bitstream
+//
+// Result is a *rough* estimation of the quality. You should just consider
+// the bucket it's in (q > 80? > 50? > 20?) and not take it for face value.
+/*
+ gcc -o webp_quality webp_quality.c -O3 -I../ -L. -L../imageio \
+ -limageio_util -lwebpextras -lwebp -lm -lpthread
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extras/extras.h"
+#include "imageio/imageio_util.h"
+
+int main(int argc, const char *argv[]) {
+ int c;
+ int quiet = 0;
+ int ok = 1;
+ for (c = 1; ok && c < argc; ++c) {
+ if (!strcmp(argv[c], "-quiet")) {
+ quiet = 1;
+ } else if (!strcmp(argv[c], "-help") || !strcmp(argv[c], "-h")) {
+ printf("webp_quality [-h][-quiet] webp_files...\n");
+ return 0;
+ } else {
+ const char* const filename = argv[c];
+ const uint8_t* data = NULL;
+ size_t data_size = 0;
+ int q;
+ ok = ImgIoUtilReadFile(filename, &data, &data_size);
+ if (!ok) break;
+ q = VP8EstimateQuality(data, data_size);
+ if (!quiet) printf("[%s] ", filename);
+ if (q < 0) {
+ fprintf(stderr, "Not a WebP file, or not a lossy WebP file.\n");
+ ok = 0;
+ } else {
+ if (!quiet) {
+ printf("Estimated quality factor: %d\n", q);
+ } else {
+ printf("%d\n", q); // just print the number
+ }
+ }
+ free((void*)data);
+ }
+ }
+ return ok ? 0 : 1;
+}
diff --git a/src/third_party/libwebp/extras/webp_to_sdl.c b/src/third_party/libwebp/extras/webp_to_sdl.c
new file mode 100644
index 0000000..2a74715
--- /dev/null
+++ b/src/third_party/libwebp/extras/webp_to_sdl.c
@@ -0,0 +1,110 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple WebP-to-SDL wrapper. Useful for emscripten.
+//
+// Author: James Zern (jzern@google.com)
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#if defined(WEBP_HAVE_SDL)
+
+#include "webp_to_sdl.h"
+
+#include <stdio.h>
+#include "src/webp/decode.h"
+
+#if defined(WEBP_HAVE_JUST_SDL_H)
+#include <SDL.h>
+#else
+#include <SDL/SDL.h>
+#endif
+
+static int init_ok = 0;
+int WebpToSDL(const char* data, unsigned int data_size) {
+ int ok = 0;
+ VP8StatusCode status;
+ WebPDecoderConfig config;
+ WebPBitstreamFeatures* const input = &config.input;
+ WebPDecBuffer* const output = &config.output;
+ SDL_Surface* screen = NULL;
+ SDL_Surface* surface = NULL;
+
+ if (!WebPInitDecoderConfig(&config)) {
+ fprintf(stderr, "Library version mismatch!\n");
+ return 1;
+ }
+
+ if (!init_ok) {
+ SDL_Init(SDL_INIT_VIDEO);
+ init_ok = 1;
+ }
+
+ status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input);
+ if (status != VP8_STATUS_OK) goto Error;
+
+ screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE);
+ if (screen == NULL) {
+ fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n",
+ input->width, input->height);
+ goto Error;
+ }
+
+ surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ input->width, input->height, 32,
+ 0x000000ffu, // R mask
+ 0x0000ff00u, // G mask
+ 0x00ff0000u, // B mask
+ 0xff000000u); // A mask
+
+ if (surface == NULL) {
+ fprintf(stderr, "Unable to create %dx%d RGBA surface!\n",
+ input->width, input->height);
+ goto Error;
+ }
+ if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ output->colorspace = MODE_BGRA;
+#else
+ output->colorspace = MODE_RGBA;
+#endif
+ output->width = surface->w;
+ output->height = surface->h;
+ output->u.RGBA.rgba = surface->pixels;
+ output->u.RGBA.stride = surface->pitch;
+ output->u.RGBA.size = surface->pitch * surface->h;
+ output->is_external_memory = 1;
+
+ status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
+ if (status != VP8_STATUS_OK) {
+ fprintf(stderr, "Error decoding image (%d)\n", status);
+ goto Error;
+ }
+
+ if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
+ if (SDL_BlitSurface(surface, NULL, screen, NULL) ||
+ SDL_Flip(screen)) {
+ goto Error;
+ }
+
+ ok = 1;
+
+ Error:
+ SDL_FreeSurface(surface);
+ SDL_FreeSurface(screen);
+ WebPFreeDecBuffer(output);
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+
+#endif // WEBP_HAVE_SDL
diff --git a/src/third_party/libwebp/extras/webp_to_sdl.h b/src/third_party/libwebp/extras/webp_to_sdl.h
new file mode 100644
index 0000000..1b5ea98
--- /dev/null
+++ b/src/third_party/libwebp/extras/webp_to_sdl.h
@@ -0,0 +1,22 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple WebP-to-SDL wrapper. Useful for emscripten.
+//
+// Author: James Zern (jzern@google.com)
+
+#ifndef WEBP_EXTRAS_WEBP_TO_SDL_H_
+#define WEBP_EXTRAS_WEBP_TO_SDL_H_
+
+// Exports the method WebpToSDL(const char* data, int data_size) which decodes
+// a WebP bitstream into an RGBA SDL surface.
+// Return false on failure.
+extern int WebpToSDL(const char* data, unsigned int data_size);
+
+#endif // WEBP_EXTRAS_WEBP_TO_SDL_H_
diff --git a/src/third_party/libwebp/gradle.properties b/src/third_party/libwebp/gradle.properties
new file mode 100644
index 0000000..28d39f5
--- /dev/null
+++ b/src/third_party/libwebp/gradle.properties
@@ -0,0 +1,14 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Versions for gradle
+BUILD_TOOLS_VERSION=23.0.3
+COMPILE_SDK_VERSION=23
+ANDROID_GRADLE_PLUGIN_VERSION=1.5.0
+GRADLE_DOWNLOAD_TASK_VERSION=2.1.0
diff --git a/src/third_party/libwebp/gradle/wrapper/gradle-wrapper.jar b/src/third_party/libwebp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..ca78035
--- /dev/null
+++ b/src/third_party/libwebp/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/src/third_party/libwebp/gradle/wrapper/gradle-wrapper.properties b/src/third_party/libwebp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..d556104
--- /dev/null
+++ b/src/third_party/libwebp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu May 12 17:06:25 CEST 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip
diff --git a/src/third_party/libwebp/gradlew b/src/third_party/libwebp/gradlew
new file mode 100755
index 0000000..27309d9
--- /dev/null
+++ b/src/third_party/libwebp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/src/third_party/libwebp/gradlew.bat b/src/third_party/libwebp/gradlew.bat
new file mode 100644
index 0000000..f6d5974
--- /dev/null
+++ b/src/third_party/libwebp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/third_party/libwebp/imageio/Android.mk b/src/third_party/libwebp/imageio/Android.mk
new file mode 100644
index 0000000..e779f71
--- /dev/null
+++ b/src/third_party/libwebp/imageio/Android.mk
@@ -0,0 +1,54 @@
+LOCAL_PATH := $(call my-dir)
+
+################################################################################
+# libimageio_util
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ imageio_util.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+
+LOCAL_MODULE := imageio_util
+
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+# libimagedec
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ image_dec.c \
+ jpegdec.c \
+ metadata.c \
+ pngdec.c \
+ pnmdec.c \
+ tiffdec.c \
+ webpdec.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := imageio_util
+
+LOCAL_MODULE := imagedec
+
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+# libimageenc
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ image_enc.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := imageio_util
+
+LOCAL_MODULE := imageenc
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/src/third_party/libwebp/imageio/Makefile.am b/src/third_party/libwebp/imageio/Makefile.am
new file mode 100644
index 0000000..b0e6d8e
--- /dev/null
+++ b/src/third_party/libwebp/imageio/Makefile.am
@@ -0,0 +1,32 @@
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+noinst_LTLIBRARIES =
+noinst_LTLIBRARIES += libimageio_util.la
+if WANT_DEMUX
+ noinst_LTLIBRARIES += libimagedec.la
+endif
+noinst_LTLIBRARIES += libimageenc.la
+
+noinst_HEADERS =
+noinst_HEADERS += ../src/webp/decode.h
+noinst_HEADERS += ../src/webp/types.h
+
+libimageio_util_la_SOURCES =
+libimageio_util_la_SOURCES += imageio_util.c imageio_util.h
+
+libimagedec_la_SOURCES =
+libimagedec_la_SOURCES += image_dec.c image_dec.h
+libimagedec_la_SOURCES += jpegdec.c jpegdec.h
+libimagedec_la_SOURCES += metadata.c metadata.h
+libimagedec_la_SOURCES += pngdec.c pngdec.h
+libimagedec_la_SOURCES += pnmdec.c pnmdec.h
+libimagedec_la_SOURCES += tiffdec.c tiffdec.h
+libimagedec_la_SOURCES += webpdec.c webpdec.h
+libimagedec_la_SOURCES += wicdec.c wicdec.h
+libimagedec_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
+libimagedec_la_CPPFLAGS += $(AM_CPPFLAGS)
+libimagedec_la_LIBADD = ../src/demux/libwebpdemux.la
+
+libimageenc_la_SOURCES =
+libimageenc_la_SOURCES += image_enc.c image_enc.h
+libimageenc_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
+libimageenc_la_CPPFLAGS += $(AM_CPPFLAGS)
diff --git a/src/third_party/libwebp/imageio/image_dec.c b/src/third_party/libwebp/imageio/image_dec.c
new file mode 100644
index 0000000..08a1b18
--- /dev/null
+++ b/src/third_party/libwebp/imageio/image_dec.c
@@ -0,0 +1,66 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Generic image-type guessing.
+
+#include "./image_dec.h"
+
+static WEBP_INLINE uint32_t GetBE32(const uint8_t buf[]) {
+ return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
+ size_t data_size) {
+ WebPInputFileFormat format = WEBP_UNSUPPORTED_FORMAT;
+ if (data != NULL && data_size >= 12) {
+ const uint32_t magic1 = GetBE32(data + 0);
+ const uint32_t magic2 = GetBE32(data + 8);
+ if (magic1 == 0x89504E47U) {
+ format = WEBP_PNG_FORMAT;
+ } else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) {
+ format = WEBP_JPEG_FORMAT;
+ } else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) {
+ format = WEBP_TIFF_FORMAT;
+ } else if (magic1 == 0x52494646 && magic2 == 0x57454250) {
+ format = WEBP_WEBP_FORMAT;
+ } else if (((magic1 >> 24) & 0xff) == 'P') {
+ const int type = (magic1 >> 16) & 0xff;
+ // we only support 'P5 -> P7' for now.
+ if (type >= '5' && type <= '7') format = WEBP_PNM_FORMAT;
+ }
+ }
+ return format;
+}
+
+static int FailReader(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic,
+ int keep_alpha, struct Metadata* const metadata) {
+ (void)data;
+ (void)data_size;
+ (void)pic;
+ (void)keep_alpha;
+ (void)metadata;
+ return 0;
+}
+
+WebPImageReader WebPGetImageReader(WebPInputFileFormat format) {
+ switch (format) {
+ case WEBP_PNG_FORMAT: return ReadPNG;
+ case WEBP_JPEG_FORMAT: return ReadJPEG;
+ case WEBP_TIFF_FORMAT: return ReadTIFF;
+ case WEBP_WEBP_FORMAT: return ReadWebP;
+ case WEBP_PNM_FORMAT: return ReadPNM;
+ default: return FailReader;
+ }
+}
+
+WebPImageReader WebPGuessImageReader(const uint8_t* const data,
+ size_t data_size) {
+ return WebPGetImageReader(WebPGuessImageType(data, data_size));
+}
diff --git a/src/third_party/libwebp/imageio/image_dec.h b/src/third_party/libwebp/imageio/image_dec.h
new file mode 100644
index 0000000..df411e1
--- /dev/null
+++ b/src/third_party/libwebp/imageio/image_dec.h
@@ -0,0 +1,67 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// All-in-one library to decode PNG/JPEG/WebP/TIFF/WIC input images.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
+#define WEBP_IMAGEIO_IMAGE_DEC_H_
+
+#include "webp/types.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "./metadata.h"
+#include "./jpegdec.h"
+#include "./pngdec.h"
+#include "./pnmdec.h"
+#include "./tiffdec.h"
+#include "./webpdec.h"
+#include "./wicdec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ WEBP_PNG_FORMAT = 0,
+ WEBP_JPEG_FORMAT,
+ WEBP_TIFF_FORMAT,
+ WEBP_WEBP_FORMAT,
+ WEBP_PNM_FORMAT,
+ WEBP_UNSUPPORTED_FORMAT
+} WebPInputFileFormat;
+
+// Try to infer the image format. 'data_size' should be larger than 12.
+// Returns WEBP_UNSUPPORTED_FORMAT if format can't be guess safely.
+WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
+ size_t data_size);
+
+// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...)
+typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic,
+ int keep_alpha, struct Metadata* const metadata);
+
+// Return the reader associated to a given file format.
+WebPImageReader WebPGetImageReader(WebPInputFileFormat format);
+
+// This function is similar to WebPGuessImageType(), but returns a
+// suitable reader function. The returned reader is never NULL, but
+// unknown formats will return an always-failing valid reader.
+WebPImageReader WebPGuessImageReader(const uint8_t* const data,
+ size_t data_size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_IMAGE_DEC_H_
diff --git a/src/third_party/libwebp/imageio/image_enc.c b/src/third_party/libwebp/imageio/image_enc.c
new file mode 100644
index 0000000..d413490
--- /dev/null
+++ b/src/third_party/libwebp/imageio/image_enc.c
@@ -0,0 +1,599 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Save image
+
+#include "./image_enc.h"
+
+#include <assert.h>
+#include <string.h>
+
+#ifdef WEBP_HAVE_PNG
+#include <png.h>
+#include <setjmp.h> // note: this must be included *after* png.h
+#endif
+
+#ifdef HAVE_WINCODEC_H
+#ifdef __MINGW32__
+#define INITGUID // Without this GUIDs are declared extern and fail to link
+#endif
+#define CINTERFACE
+#define COBJMACROS
+#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
+ // code with COBJMACROS.
+#include <ole2.h> // CreateStreamOnHGlobal()
+#include <shlwapi.h>
+#include <windows.h>
+#include <wincodec.h>
+#endif
+
+#include "./imageio_util.h"
+
+//------------------------------------------------------------------------------
+// PNG
+
+#ifdef HAVE_WINCODEC_H
+
+#define IFS(fn) \
+ do { \
+ if (SUCCEEDED(hr)) { \
+ hr = (fn); \
+ if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
+ } \
+ } while (0)
+
+#ifdef __cplusplus
+#define MAKE_REFGUID(x) (x)
+#else
+#define MAKE_REFGUID(x) &(x)
+#endif
+
+static HRESULT CreateOutputStream(const char* out_file_name,
+ int write_to_mem, IStream** stream) {
+ HRESULT hr = S_OK;
+ if (write_to_mem) {
+ // Output to a memory buffer. This is freed when 'stream' is released.
+ IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
+ } else {
+ IFS(SHCreateStreamOnFileA(out_file_name, STGM_WRITE | STGM_CREATE, stream));
+ }
+ if (FAILED(hr)) {
+ fprintf(stderr, "Error opening output file %s (%08lx)\n",
+ out_file_name, hr);
+ }
+ return hr;
+}
+
+static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
+ REFGUID container_guid,
+ uint8_t* rgb, int stride,
+ uint32_t width, uint32_t height, int has_alpha) {
+ HRESULT hr = S_OK;
+ IWICImagingFactory* factory = NULL;
+ IWICBitmapFrameEncode* frame = NULL;
+ IWICBitmapEncoder* encoder = NULL;
+ IStream* stream = NULL;
+ WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
+ : GUID_WICPixelFormat24bppBGR;
+
+ if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
+
+ IFS(CoInitialize(NULL));
+ IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
+ CLSCTX_INPROC_SERVER,
+ MAKE_REFGUID(IID_IWICImagingFactory),
+ (LPVOID*)&factory));
+ if (hr == REGDB_E_CLASSNOTREG) {
+ fprintf(stderr,
+ "Couldn't access Windows Imaging Component (are you running "
+ "Windows XP SP3 or newer?). PNG support not available. "
+ "Use -ppm or -pgm for available PPM and PGM formats.\n");
+ }
+ IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
+ IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
+ &encoder));
+ IFS(IWICBitmapEncoder_Initialize(encoder, stream,
+ WICBitmapEncoderNoCache));
+ IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
+ IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
+ IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
+ IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
+ IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
+ height * stride, rgb));
+ IFS(IWICBitmapFrameEncode_Commit(frame));
+ IFS(IWICBitmapEncoder_Commit(encoder));
+
+ if (SUCCEEDED(hr) && use_stdout) {
+ HGLOBAL image;
+ IFS(GetHGlobalFromStream(stream, &image));
+ if (SUCCEEDED(hr)) {
+ HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD mode;
+ const BOOL update_mode = GetConsoleMode(std_output, &mode);
+ const void* const image_mem = GlobalLock(image);
+ DWORD bytes_written = 0;
+
+ // Clear output processing if necessary, then output the image.
+ if (update_mode) SetConsoleMode(std_output, 0);
+ if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image),
+ &bytes_written, NULL) ||
+ bytes_written != GlobalSize(image)) {
+ hr = E_FAIL;
+ }
+ if (update_mode) SetConsoleMode(std_output, mode);
+ GlobalUnlock(image);
+ }
+ }
+
+ if (frame != NULL) IUnknown_Release(frame);
+ if (encoder != NULL) IUnknown_Release(encoder);
+ if (factory != NULL) IUnknown_Release(factory);
+ if (stream != NULL) IUnknown_Release(stream);
+ return hr;
+}
+
+int WebPWritePNG(const char* out_file_name, int use_stdout,
+ const WebPDecBuffer* const buffer) {
+ const uint32_t width = buffer->width;
+ const uint32_t height = buffer->height;
+ uint8_t* const rgb = buffer->u.RGBA.rgba;
+ const int stride = buffer->u.RGBA.stride;
+ const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+
+ return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
+ MAKE_REFGUID(GUID_ContainerFormatPng),
+ rgb, stride, width, height, has_alpha));
+}
+
+#elif defined(WEBP_HAVE_PNG) // !HAVE_WINCODEC_H
+static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp dummy) {
+ (void)dummy; // remove variable-unused warning
+ longjmp(png_jmpbuf(png), 1);
+}
+
+int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
+ volatile png_structp png;
+ volatile png_infop info;
+
+ if (out_file == NULL || buffer == NULL) return 0;
+
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, PNGErrorFunction, NULL);
+ if (png == NULL) {
+ return 0;
+ }
+ info = png_create_info_struct(png);
+ if (info == NULL) {
+ png_destroy_write_struct((png_structpp)&png, NULL);
+ return 0;
+ }
+ if (setjmp(png_jmpbuf(png))) {
+ png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
+ return 0;
+ }
+ png_init_io(png, out_file);
+ {
+ const uint32_t width = buffer->width;
+ const uint32_t height = buffer->height;
+ png_bytep row = buffer->u.RGBA.rgba;
+ const int stride = buffer->u.RGBA.stride;
+ const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+ uint32_t y;
+
+ png_set_IHDR(png, info, width, height, 8,
+ has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(png, info);
+ for (y = 0; y < height; ++y) {
+ png_write_rows(png, &row, 1);
+ row += stride;
+ }
+ }
+ png_write_end(png, info);
+ png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
+ return 1;
+}
+#else // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
+int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
+ if (fout == NULL || buffer == NULL) return 0;
+
+ fprintf(stderr, "PNG support not compiled. Please install the libpng "
+ "development package before building.\n");
+ fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
+ return 0;
+}
+#endif
+
+//------------------------------------------------------------------------------
+// PPM / PAM
+
+static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
+ int alpha) {
+ if (fout == NULL || buffer == NULL) {
+ return 0;
+ } else {
+ const uint32_t width = buffer->width;
+ const uint32_t height = buffer->height;
+ const uint8_t* row = buffer->u.RGBA.rgba;
+ const int stride = buffer->u.RGBA.stride;
+ const size_t bytes_per_px = alpha ? 4 : 3;
+ uint32_t y;
+
+ if (row == NULL) return 0;
+
+ if (alpha) {
+ fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
+ "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
+ } else {
+ fprintf(fout, "P6\n%u %u\n255\n", width, height);
+ }
+ for (y = 0; y < height; ++y) {
+ if (fwrite(row, width, bytes_per_px, fout) != bytes_per_px) {
+ return 0;
+ }
+ row += stride;
+ }
+ }
+ return 1;
+}
+
+int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) {
+ return WritePPMPAM(fout, buffer, 0);
+}
+
+int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
+ return WritePPMPAM(fout, buffer, 1);
+}
+
+//------------------------------------------------------------------------------
+// Raw PGM
+
+// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
+int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
+ const uint32_t width = buffer->width;
+ const uint32_t height = buffer->height;
+ const uint8_t* rgba = buffer->u.RGBA.rgba;
+ const int stride = buffer->u.RGBA.stride;
+ const uint32_t bytes_per_px = 2;
+ uint32_t y;
+
+ if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+
+ fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
+ for (y = 0; y < height; ++y) {
+ if (fwrite(rgba, width, bytes_per_px, fout) != bytes_per_px) {
+ return 0;
+ }
+ rgba += stride;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// BMP
+
+static void PutLE16(uint8_t* const dst, uint32_t value) {
+ dst[0] = (value >> 0) & 0xff;
+ dst[1] = (value >> 8) & 0xff;
+}
+
+static void PutLE32(uint8_t* const dst, uint32_t value) {
+ PutLE16(dst + 0, (value >> 0) & 0xffff);
+ PutLE16(dst + 2, (value >> 16) & 0xffff);
+}
+
+#define BMP_HEADER_SIZE 54
+int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
+ const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+ const uint32_t width = buffer->width;
+ const uint32_t height = buffer->height;
+ const uint8_t* rgba = buffer->u.RGBA.rgba;
+ const int stride = buffer->u.RGBA.stride;
+ const uint32_t bytes_per_px = has_alpha ? 4 : 3;
+ uint32_t y;
+ const uint32_t line_size = bytes_per_px * width;
+ const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
+ const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE;
+ uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 };
+
+ if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+
+ // bitmap file header
+ PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
+ PutLE32(bmp_header + 2, total_size); // size including header
+ PutLE32(bmp_header + 6, 0); // reserved
+ PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array
+ // bitmap info header
+ PutLE32(bmp_header + 14, 40); // DIB header size
+ PutLE32(bmp_header + 18, width); // dimensions
+ PutLE32(bmp_header + 22, -(int)height); // vertical flip!
+ PutLE16(bmp_header + 26, 1); // number of planes
+ PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
+ PutLE32(bmp_header + 30, 0); // no compression (BI_RGB)
+ PutLE32(bmp_header + 34, 0); // image size (dummy)
+ PutLE32(bmp_header + 38, 2400); // x pixels/meter
+ PutLE32(bmp_header + 42, 2400); // y pixels/meter
+ PutLE32(bmp_header + 46, 0); // number of palette colors
+ PutLE32(bmp_header + 50, 0); // important color count
+
+ // TODO(skal): color profile
+
+ // write header
+ if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) {
+ return 0;
+ }
+
+ // write pixel array
+ for (y = 0; y < height; ++y) {
+ if (fwrite(rgba, line_size, 1, fout) != 1) {
+ return 0;
+ }
+ // write padding zeroes
+ if (bmp_stride != line_size) {
+ const uint8_t zeroes[3] = { 0 };
+ if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
+ return 0;
+ }
+ }
+ rgba += stride;
+ }
+ return 1;
+}
+#undef BMP_HEADER_SIZE
+
+//------------------------------------------------------------------------------
+// TIFF
+
+#define NUM_IFD_ENTRIES 15
+#define EXTRA_DATA_SIZE 16
+// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
+#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
+#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
+
+int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
+ const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+ const uint32_t width = buffer->width;
+ const uint32_t height = buffer->height;
+ const uint8_t* rgba = buffer->u.RGBA.rgba;
+ const int stride = buffer->u.RGBA.stride;
+ const uint8_t bytes_per_px = has_alpha ? 4 : 3;
+ const uint8_t assoc_alpha =
+ WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
+ // For non-alpha case, we omit tag 0x152 (ExtraSamples).
+ const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
+ : NUM_IFD_ENTRIES - 1;
+ uint8_t tiff_header[TIFF_HEADER_SIZE] = {
+ 0x49, 0x49, 0x2a, 0x00, // little endian signature
+ 8, 0, 0, 0, // offset to the unique IFD that follows
+ // IFD (offset = 8). Entries must be written in increasing tag order.
+ num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
+ 0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
+ 0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
+ 0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
+ EXTRA_DATA_OFFSET + 0, 0, 0, 0,
+ 0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
+ 0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
+ 0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
+ TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
+ 0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
+ 0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
+ bytes_per_px, 0, 0, 0,
+ 0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
+ 0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
+ 0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
+ EXTRA_DATA_OFFSET + 8, 0, 0, 0,
+ 0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
+ EXTRA_DATA_OFFSET + 8, 0, 0, 0,
+ 0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
+ 0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
+ 0x52, 0x01, 3, 0, 1, 0, 0, 0,
+ assoc_alpha, 0, 0, 0, // 178: ExtraSamples: rgbA/RGBA
+ 0, 0, 0, 0, // 190: IFD terminator
+ // EXTRA_DATA_OFFSET:
+ 8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
+ 72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
+ };
+ uint32_t y;
+
+ if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+
+ // Fill placeholders in IFD:
+ PutLE32(tiff_header + 10 + 8, width);
+ PutLE32(tiff_header + 22 + 8, height);
+ PutLE32(tiff_header + 106 + 8, height);
+ PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
+ if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator
+
+ // write header
+ if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
+ return 0;
+ }
+ // write pixel values
+ for (y = 0; y < height; ++y) {
+ if (fwrite(rgba, bytes_per_px, width, fout) != width) {
+ return 0;
+ }
+ rgba += stride;
+ }
+
+ return 1;
+}
+
+#undef TIFF_HEADER_SIZE
+#undef EXTRA_DATA_OFFSET
+#undef EXTRA_DATA_SIZE
+#undef NUM_IFD_ENTRIES
+
+//------------------------------------------------------------------------------
+// Raw Alpha
+
+int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
+ if (fout == NULL || buffer == NULL) {
+ return 0;
+ } else {
+ const uint32_t width = buffer->width;
+ const uint32_t height = buffer->height;
+ const uint8_t* a = buffer->u.YUVA.a;
+ const int a_stride = buffer->u.YUVA.a_stride;
+ uint32_t y;
+
+ if (a == NULL) return 0;
+
+ fprintf(fout, "P5\n%u %u\n255\n", width, height);
+ for (y = 0; y < height; ++y) {
+ if (fwrite(a, width, 1, fout) != 1) return 0;
+ a += a_stride;
+ }
+ return 1;
+ }
+}
+
+//------------------------------------------------------------------------------
+// PGM with IMC4 layout
+
+int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
+ if (fout == NULL || buffer == NULL) {
+ return 0;
+ } else {
+ const int width = buffer->width;
+ const int height = buffer->height;
+ const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
+ const uint8_t* src_y = yuv->y;
+ const uint8_t* src_u = yuv->u;
+ const uint8_t* src_v = yuv->v;
+ const uint8_t* src_a = yuv->a;
+ const int uv_width = (width + 1) / 2;
+ const int uv_height = (height + 1) / 2;
+ const int a_height = (src_a != NULL) ? height : 0;
+ int ok = 1;
+ int y;
+
+ if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
+
+ fprintf(fout, "P5\n%d %d\n255\n",
+ (width + 1) & ~1, height + uv_height + a_height);
+ for (y = 0; ok && y < height; ++y) {
+ ok &= (fwrite(src_y, width, 1, fout) == 1);
+ if (width & 1) fputc(0, fout); // padding byte
+ src_y += yuv->y_stride;
+ }
+ for (y = 0; ok && y < uv_height; ++y) {
+ ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
+ ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
+ src_u += yuv->u_stride;
+ src_v += yuv->v_stride;
+ }
+ for (y = 0; ok && y < a_height; ++y) {
+ ok &= (fwrite(src_a, width, 1, fout) == 1);
+ if (width & 1) fputc(0, fout); // padding byte
+ src_a += yuv->a_stride;
+ }
+ return ok;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Raw YUV(A) planes
+
+int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) {
+ if (fout == NULL || buffer == NULL) {
+ return 0;
+ } else {
+ const int width = buffer->width;
+ const int height = buffer->height;
+ const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
+ const uint8_t* src_y = yuv->y;
+ const uint8_t* src_u = yuv->u;
+ const uint8_t* src_v = yuv->v;
+ const uint8_t* src_a = yuv->a;
+ const int uv_width = (width + 1) / 2;
+ const int uv_height = (height + 1) / 2;
+ const int a_height = (src_a != NULL) ? height : 0;
+ int ok = 1;
+ int y;
+
+ if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
+
+ for (y = 0; ok && y < height; ++y) {
+ ok &= (fwrite(src_y, width, 1, fout) == 1);
+ src_y += yuv->y_stride;
+ }
+ for (y = 0; ok && y < uv_height; ++y) {
+ ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
+ src_u += yuv->u_stride;
+ }
+ for (y = 0; ok && y < uv_height; ++y) {
+ ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
+ src_v += yuv->v_stride;
+ }
+ for (y = 0; ok && y < a_height; ++y) {
+ ok &= (fwrite(src_a, width, 1, fout) == 1);
+ src_a += yuv->a_stride;
+ }
+ return ok;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Generic top-level call
+
+int WebPSaveImage(const WebPDecBuffer* const buffer,
+ WebPOutputFileFormat format,
+ const char* const out_file_name) {
+ FILE* fout = NULL;
+ int needs_open_file = 1;
+ const int use_stdout = (out_file_name != NULL) && !strcmp(out_file_name, "-");
+ int ok = 1;
+
+ if (buffer == NULL || out_file_name == NULL) return 0;
+
+#ifdef HAVE_WINCODEC_H
+ needs_open_file = (format != PNG);
+#endif
+
+ if (needs_open_file) {
+ fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout)
+ : fopen(out_file_name, "wb");
+ if (fout == NULL) {
+ fprintf(stderr, "Error opening output file %s\n", out_file_name);
+ return 0;
+ }
+ }
+
+ if (format == PNG ||
+ format == RGBA || format == BGRA || format == ARGB ||
+ format == rgbA || format == bgrA || format == Argb) {
+#ifdef HAVE_WINCODEC_H
+ ok &= WebPWritePNG(out_file_name, use_stdout, buffer);
+#else
+ ok &= WebPWritePNG(fout, buffer);
+#endif
+ } else if (format == PAM) {
+ ok &= WebPWritePAM(fout, buffer);
+ } else if (format == PPM || format == RGB || format == BGR) {
+ ok &= WebPWritePPM(fout, buffer);
+ } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) {
+ ok &= WebPWrite16bAsPGM(fout, buffer);
+ } else if (format == BMP) {
+ ok &= WebPWriteBMP(fout, buffer);
+ } else if (format == TIFF) {
+ ok &= WebPWriteTIFF(fout, buffer);
+ } else if (format == RAW_YUV) {
+ ok &= WebPWriteYUV(fout, buffer);
+ } else if (format == PGM || format == YUV || format == YUVA) {
+ ok &= WebPWritePGM(fout, buffer);
+ } else if (format == ALPHA_PLANE_ONLY) {
+ ok &= WebPWriteAlphaPlane(fout, buffer);
+ }
+ if (fout != NULL && fout != stdout) {
+ fclose(fout);
+ }
+ return ok;
+}
diff --git a/src/third_party/libwebp/imageio/image_enc.h b/src/third_party/libwebp/imageio/image_enc.h
new file mode 100644
index 0000000..f8abdac
--- /dev/null
+++ b/src/third_party/libwebp/imageio/image_enc.h
@@ -0,0 +1,96 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// All-in-one library to save PNG/JPEG/WebP/TIFF/WIC images.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_IMAGEIO_IMAGE_ENC_H_
+#define WEBP_IMAGEIO_IMAGE_ENC_H_
+
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "webp/types.h"
+#include "webp/decode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Output types
+typedef enum {
+ PNG = 0,
+ PAM,
+ PPM,
+ PGM,
+ BMP,
+ TIFF,
+ RAW_YUV,
+ ALPHA_PLANE_ONLY, // this is for experimenting only
+ // forced colorspace output (for testing, mostly)
+ RGB, RGBA, BGR, BGRA, ARGB,
+ RGBA_4444, RGB_565,
+ rgbA, bgrA, Argb, rgbA_4444,
+ YUV, YUVA
+} WebPOutputFileFormat;
+
+// General all-purpose call.
+// Most formats expect a 'buffer' containing RGBA-like samples, except
+// RAW_YUV, YUV and YUVA formats.
+// If 'out_file_name' is "-", data is saved to stdout.
+// Returns false if an error occurred, true otherwise.
+int WebPSaveImage(const WebPDecBuffer* const buffer,
+ WebPOutputFileFormat format, const char* const out_file_name);
+
+// Save to PNG.
+#ifdef HAVE_WINCODEC_H
+int WebPWritePNG(const char* out_file_name, int use_stdout,
+ const struct WebPDecBuffer* const buffer);
+#else
+int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer);
+#endif
+
+// Save to PPM format (RGB, no alpha)
+int WebPWritePPM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save to PAM format (= PPM + alpha)
+int WebPWritePAM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save 16b mode (RGBA4444, RGB565, ...) for debugging purposes.
+int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save as BMP
+int WebPWriteBMP(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save as TIFF
+int WebPWriteTIFF(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save the ALPHA plane (only) as a PGM
+int WebPWriteAlphaPlane(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save as YUV samples as PGM format (using IMC4 layout).
+// See: http://www.fourcc.org/yuv.php#IMC4.
+// (very convenient format for viewing the samples, esp. for odd dimensions).
+int WebPWritePGM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save YUV(A) planes sequentially (raw dump)
+int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save 16b mode (RGBA4444, RGB565, ...) as PGM format, for debugging purposes.
+int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_IMAGE_ENC_H_
diff --git a/src/third_party/libwebp/imageio/imageio_util.c b/src/third_party/libwebp/imageio/imageio_util.c
new file mode 100644
index 0000000..3a4ade0
--- /dev/null
+++ b/src/third_party/libwebp/imageio/imageio_util.c
@@ -0,0 +1,158 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utility functions used by the image decoders.
+//
+
+#include "./imageio_util.h"
+
+#if defined(_WIN32)
+#include <fcntl.h> // for _O_BINARY
+#include <io.h> // for _setmode()
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+// -----------------------------------------------------------------------------
+// File I/O
+
+FILE* ImgIoUtilSetBinaryMode(FILE* file) {
+#if defined(_WIN32)
+ if (_setmode(_fileno(file), _O_BINARY) == -1) {
+ fprintf(stderr, "Failed to reopen file in O_BINARY mode.\n");
+ return NULL;
+ }
+#endif
+ return file;
+}
+
+int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
+ static const size_t kBlockSize = 16384; // default initial size
+ size_t max_size = 0;
+ size_t size = 0;
+ uint8_t* input = NULL;
+
+ if (data == NULL || data_size == NULL) return 0;
+ *data = NULL;
+ *data_size = 0;
+
+ if (!ImgIoUtilSetBinaryMode(stdin)) return 0;
+
+ while (!feof(stdin)) {
+ // We double the buffer size each time and read as much as possible.
+ const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
+ // we allocate one extra byte for the \0 terminator
+ void* const new_data = realloc(input, max_size + extra_size + 1);
+ if (new_data == NULL) goto Error;
+ input = (uint8_t*)new_data;
+ max_size += extra_size;
+ size += fread(input + size, 1, extra_size, stdin);
+ if (size < max_size) break;
+ }
+ if (ferror(stdin)) goto Error;
+ if (input != NULL) input[size] = '\0'; // convenient 0-terminator
+ *data = input;
+ *data_size = size;
+ return 1;
+
+ Error:
+ free(input);
+ fprintf(stderr, "Could not read from stdin\n");
+ return 0;
+}
+
+int ImgIoUtilReadFile(const char* const file_name,
+ const uint8_t** data, size_t* data_size) {
+ int ok;
+ uint8_t* file_data;
+ size_t file_size;
+ FILE* in;
+ const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
+
+ if (from_stdin) return ImgIoUtilReadFromStdin(data, data_size);
+
+ if (data == NULL || data_size == NULL) return 0;
+ *data = NULL;
+ *data_size = 0;
+
+ in = fopen(file_name, "rb");
+ if (in == NULL) {
+ fprintf(stderr, "cannot open input file '%s'\n", file_name);
+ return 0;
+ }
+ fseek(in, 0, SEEK_END);
+ file_size = ftell(in);
+ fseek(in, 0, SEEK_SET);
+ // we allocate one extra byte for the \0 terminator
+ file_data = (uint8_t*)malloc(file_size + 1);
+ if (file_data == NULL) {
+ fclose(in);
+ fprintf(stderr, "memory allocation failure when reading file %s\n",
+ file_name);
+ return 0;
+ }
+ ok = (fread(file_data, file_size, 1, in) == 1);
+ fclose(in);
+
+ if (!ok) {
+ fprintf(stderr, "Could not read %d bytes of data from file %s\n",
+ (int)file_size, file_name);
+ free(file_data);
+ return 0;
+ }
+ file_data[file_size] = '\0'; // convenient 0-terminator
+ *data = file_data;
+ *data_size = file_size;
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+int ImgIoUtilWriteFile(const char* const file_name,
+ const uint8_t* data, size_t data_size) {
+ int ok;
+ FILE* out;
+ const int to_stdout = (file_name == NULL) || !strcmp(file_name, "-");
+
+ if (data == NULL) {
+ return 0;
+ }
+ out = to_stdout ? ImgIoUtilSetBinaryMode(stdout) : fopen(file_name, "wb");
+ if (out == NULL) {
+ fprintf(stderr, "Error! Cannot open output file '%s'\n", file_name);
+ return 0;
+ }
+ ok = (fwrite(data, data_size, 1, out) == 1);
+ if (out != stdout) fclose(out);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+
+void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
+ uint8_t* dst, int dst_stride, int width, int height) {
+ while (height-- > 0) {
+ memcpy(dst, src, width * sizeof(*dst));
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
+ const uint64_t total_size = nmemb * size;
+ int ok = (total_size == (size_t)total_size);
+#if defined(WEBP_MAX_IMAGE_SIZE)
+ ok = ok && (total_size <= (uint64_t)WEBP_MAX_IMAGE_SIZE);
+#endif
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/imageio_util.h b/src/third_party/libwebp/imageio/imageio_util.h
new file mode 100644
index 0000000..72db159
--- /dev/null
+++ b/src/third_party/libwebp/imageio/imageio_util.h
@@ -0,0 +1,64 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utility functions used by the image decoders.
+//
+
+#ifndef WEBP_IMAGEIO_IMAGEIO_UTIL_H_
+#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
+
+#include <stdio.h>
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// File I/O
+
+// Reopen file in binary (O_BINARY) mode.
+// Returns 'file' on success, NULL otherwise.
+FILE* ImgIoUtilSetBinaryMode(FILE* file);
+
+// Allocates storage for entire file 'file_name' and returns contents and size
+// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
+// be deleted using free().
+// Note: for convenience, the data will be null-terminated with an extra byte
+// (not accounted for in *data_size), in case the file is text and intended
+// to be used as a C-string.
+// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
+// the function ImgIoUtilReadFromStdin().
+int ImgIoUtilReadFile(const char* const file_name,
+ const uint8_t** data, size_t* data_size);
+
+// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead.
+int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size);
+
+// Write a data segment into a file named 'file_name'. Returns true if ok.
+// If 'file_name' is NULL or equal to "-", output is written to stdout.
+int ImgIoUtilWriteFile(const char* const file_name,
+ const uint8_t* data, size_t data_size);
+
+//------------------------------------------------------------------------------
+
+// Copy width x height pixels from 'src' to 'dst' honoring the strides.
+void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
+ uint8_t* dst, int dst_stride, int width, int height);
+
+//------------------------------------------------------------------------------
+
+// Returns 0 in case of overflow of nmemb * size.
+int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t nmemb, size_t size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_IMAGEIO_UTIL_H_
diff --git a/src/third_party/libwebp/imageio/jpegdec.c b/src/third_party/libwebp/imageio/jpegdec.c
new file mode 100644
index 0000000..eefe92f
--- /dev/null
+++ b/src/third_party/libwebp/imageio/jpegdec.c
@@ -0,0 +1,359 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// JPEG decode.
+
+#include "./jpegdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef WEBP_HAVE_JPEG
+#include <jpeglib.h>
+#include <jerror.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+// -----------------------------------------------------------------------------
+// Metadata processing
+
+#ifndef JPEG_APP1
+# define JPEG_APP1 (JPEG_APP0 + 1)
+#endif
+#ifndef JPEG_APP2
+# define JPEG_APP2 (JPEG_APP0 + 2)
+#endif
+
+typedef struct {
+ const uint8_t* data;
+ size_t data_length;
+ int seq; // this segment's sequence number [1, 255] for use in reassembly.
+} ICCPSegment;
+
+static void SaveMetadataMarkers(j_decompress_ptr dinfo) {
+ const unsigned int max_marker_length = 0xffff;
+ jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length); // Exif/XMP
+ jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length); // ICC profile
+}
+
+static int CompareICCPSegments(const void* a, const void* b) {
+ const ICCPSegment* s1 = (const ICCPSegment*)a;
+ const ICCPSegment* s2 = (const ICCPSegment*)b;
+ return s1->seq - s2->seq;
+}
+
+// Extract ICC profile segments from the marker list in 'dinfo', reassembling
+// and storing them in 'iccp'.
+// Returns true on success and false for memory errors and corrupt profiles.
+static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
+ // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
+ static const char kICCPSignature[] = "ICC_PROFILE";
+ static const size_t kICCPSignatureLength = 12; // signature includes '\0'
+ static const size_t kICCPSkipLength = 14; // signature + seq & count
+ int expected_count = 0;
+ int actual_count = 0;
+ int seq_max = 0;
+ size_t total_size = 0;
+ ICCPSegment iccp_segments[255];
+ jpeg_saved_marker_ptr marker;
+
+ memset(iccp_segments, 0, sizeof(iccp_segments));
+ for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker->marker == JPEG_APP2 &&
+ marker->data_length > kICCPSkipLength &&
+ !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
+ // ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
+ const int seq = marker->data[kICCPSignatureLength];
+ const int count = marker->data[kICCPSignatureLength + 1];
+ const size_t segment_size = marker->data_length - kICCPSkipLength;
+ ICCPSegment* segment;
+
+ if (segment_size == 0 || count == 0 || seq == 0) {
+ fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
+ " cannot be 0!\n",
+ (int)segment_size, seq, count);
+ return 0;
+ }
+
+ if (expected_count == 0) {
+ expected_count = count;
+ } else if (expected_count != count) {
+ fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n",
+ expected_count, count);
+ return 0;
+ }
+
+ segment = iccp_segments + seq - 1;
+ if (segment->data_length != 0) {
+ fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
+ return 0;
+ }
+
+ segment->data = marker->data + kICCPSkipLength;
+ segment->data_length = segment_size;
+ segment->seq = seq;
+ total_size += segment_size;
+ if (seq > seq_max) seq_max = seq;
+ ++actual_count;
+ }
+ }
+
+ if (actual_count == 0) return 1;
+ if (seq_max != actual_count) {
+ fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n",
+ actual_count, seq_max);
+ return 0;
+ }
+ if (expected_count != actual_count) {
+ fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n",
+ actual_count, expected_count);
+ return 0;
+ }
+
+ // The segments may appear out of order in the file, sort them based on
+ // sequence number before assembling the payload.
+ qsort(iccp_segments, actual_count, sizeof(*iccp_segments),
+ CompareICCPSegments);
+
+ iccp->bytes = (uint8_t*)malloc(total_size);
+ if (iccp->bytes == NULL) return 0;
+ iccp->size = total_size;
+
+ {
+ int i;
+ size_t offset = 0;
+ for (i = 0; i < seq_max; ++i) {
+ memcpy(iccp->bytes + offset,
+ iccp_segments[i].data, iccp_segments[i].data_length);
+ offset += iccp_segments[i].data_length;
+ }
+ }
+ return 1;
+}
+
+// Returns true on success and false for memory errors and corrupt profiles.
+// The caller must use MetadataFree() on 'metadata' in all cases.
+static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
+ Metadata* const metadata) {
+ static const struct {
+ int marker;
+ const char* signature;
+ size_t signature_length;
+ size_t storage_offset;
+ } kJPEGMetadataMap[] = {
+ // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
+ { JPEG_APP1, "Exif\0", 6, METADATA_OFFSET(exif) },
+ // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
+ // TODO(jzern) Add support for 'ExtendedXMP'
+ { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
+ { 0, NULL, 0, 0 },
+ };
+ jpeg_saved_marker_ptr marker;
+ // Treat ICC profiles separately as they may be segmented and out of order.
+ if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
+
+ for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
+ int i;
+ for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) {
+ if (marker->marker == kJPEGMetadataMap[i].marker &&
+ marker->data_length > kJPEGMetadataMap[i].signature_length &&
+ !memcmp(marker->data, kJPEGMetadataMap[i].signature,
+ kJPEGMetadataMap[i].signature_length)) {
+ MetadataPayload* const payload =
+ (MetadataPayload*)((uint8_t*)metadata +
+ kJPEGMetadataMap[i].storage_offset);
+
+ if (payload->bytes == NULL) {
+ const char* marker_data = (const char*)marker->data +
+ kJPEGMetadataMap[i].signature_length;
+ const size_t marker_data_length =
+ marker->data_length - kJPEGMetadataMap[i].signature_length;
+ if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
+ } else {
+ fprintf(stderr, "Ignoring additional '%s' marker\n",
+ kJPEGMetadataMap[i].signature);
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+#undef JPEG_APP1
+#undef JPEG_APP2
+
+// -----------------------------------------------------------------------------
+// JPEG decoding
+
+struct my_error_mgr {
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+static void my_error_exit(j_common_ptr dinfo) {
+ struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
+ dinfo->err->output_message(dinfo);
+ longjmp(myerr->setjmp_buffer, 1);
+}
+
+typedef struct {
+ struct jpeg_source_mgr pub;
+ const uint8_t* data;
+ size_t data_size;
+} JPEGReadContext;
+
+static void ContextInit(j_decompress_ptr cinfo) {
+ JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
+ ctx->pub.next_input_byte = ctx->data;
+ ctx->pub.bytes_in_buffer = ctx->data_size;
+}
+
+static boolean ContextFill(j_decompress_ptr cinfo) {
+ // we shouldn't get here.
+ ERREXIT(cinfo, JERR_FILE_READ);
+ return FALSE;
+}
+
+static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
+ JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
+ size_t jump = (size_t)jump_size;
+ if (jump > ctx->pub.bytes_in_buffer) { // Don't overflow the buffer.
+ jump = ctx->pub.bytes_in_buffer;
+ }
+ ctx->pub.bytes_in_buffer -= jump;
+ ctx->pub.next_input_byte += jump;
+}
+
+static void ContextTerm(j_decompress_ptr cinfo) {
+ (void)cinfo;
+}
+
+static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
+ JPEGReadContext* const ctx) {
+ cinfo->src = (struct jpeg_source_mgr*)ctx;
+ ctx->pub.init_source = ContextInit;
+ ctx->pub.fill_input_buffer = ContextFill;
+ ctx->pub.skip_input_data = ContextSkip;
+ ctx->pub.resync_to_restart = jpeg_resync_to_restart;
+ ctx->pub.term_source = ContextTerm;
+ ctx->pub.bytes_in_buffer = 0;
+ ctx->pub.next_input_byte = NULL;
+}
+
+int ReadJPEG(const uint8_t* const data, size_t data_size,
+ WebPPicture* const pic, int keep_alpha,
+ Metadata* const metadata) {
+ volatile int ok = 0;
+ int width, height;
+ int64_t stride;
+ volatile struct jpeg_decompress_struct dinfo;
+ struct my_error_mgr jerr;
+ uint8_t* volatile rgb = NULL;
+ JSAMPROW buffer[1];
+ JPEGReadContext ctx;
+
+ if (data == NULL || data_size == 0 || pic == NULL) return 0;
+
+ (void)keep_alpha;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.data = data;
+ ctx.data_size = data_size;
+
+ memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo)); // for setjmp sanity
+ dinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+
+ if (setjmp(jerr.setjmp_buffer)) {
+ Error:
+ MetadataFree(metadata);
+ jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
+ goto End;
+ }
+
+ jpeg_create_decompress((j_decompress_ptr)&dinfo);
+ ContextSetup(&dinfo, &ctx);
+ if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
+ jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
+
+ dinfo.out_color_space = JCS_RGB;
+ dinfo.do_fancy_upsampling = TRUE;
+
+ jpeg_start_decompress((j_decompress_ptr)&dinfo);
+
+ if (dinfo.output_components != 3) {
+ goto Error;
+ }
+
+ width = dinfo.output_width;
+ height = dinfo.output_height;
+ stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
+
+ if (stride != (int)stride ||
+ !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
+ goto Error;
+ }
+
+ rgb = (uint8_t*)malloc((size_t)stride * height);
+ if (rgb == NULL) {
+ goto Error;
+ }
+ buffer[0] = (JSAMPLE*)rgb;
+
+ while (dinfo.output_scanline < dinfo.output_height) {
+ if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) {
+ goto Error;
+ }
+ buffer[0] += stride;
+ }
+
+ if (metadata != NULL) {
+ ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata);
+ if (!ok) {
+ fprintf(stderr, "Error extracting JPEG metadata!\n");
+ goto Error;
+ }
+ }
+
+ jpeg_finish_decompress((j_decompress_ptr)&dinfo);
+ jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
+
+ // WebP conversion.
+ pic->width = width;
+ pic->height = height;
+ ok = WebPPictureImportRGB(pic, rgb, (int)stride);
+ if (!ok) goto Error;
+
+ End:
+ free(rgb);
+ return ok;
+}
+#else // !WEBP_HAVE_JPEG
+int ReadJPEG(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata) {
+ (void)data;
+ (void)data_size;
+ (void)pic;
+ (void)keep_alpha;
+ (void)metadata;
+ fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
+ "development package before building.\n");
+ return 0;
+}
+#endif // WEBP_HAVE_JPEG
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/jpegdec.h b/src/third_party/libwebp/imageio/jpegdec.h
new file mode 100644
index 0000000..effc14f
--- /dev/null
+++ b/src/third_party/libwebp/imageio/jpegdec.h
@@ -0,0 +1,37 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// JPEG decode.
+
+#ifndef WEBP_IMAGEIO_JPEGDEC_H_
+#define WEBP_IMAGEIO_JPEGDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a JPEG from 'data', returning the decoded output in 'pic'.
+// The output is RGB or YUV depending on pic->use_argb value.
+// Returns true on success.
+// 'keep_alpha' has no effect, but is kept for coherence with other signatures
+// for image readers.
+int ReadJPEG(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_JPEGDEC_H_
diff --git a/src/third_party/libwebp/imageio/metadata.c b/src/third_party/libwebp/imageio/metadata.c
new file mode 100644
index 0000000..936f2f4
--- /dev/null
+++ b/src/third_party/libwebp/imageio/metadata.c
@@ -0,0 +1,49 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Metadata types and functions.
+//
+
+#include "./metadata.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/types.h"
+
+void MetadataInit(Metadata* const metadata) {
+ if (metadata == NULL) return;
+ memset(metadata, 0, sizeof(*metadata));
+}
+
+void MetadataPayloadDelete(MetadataPayload* const payload) {
+ if (payload == NULL) return;
+ free(payload->bytes);
+ payload->bytes = NULL;
+ payload->size = 0;
+}
+
+void MetadataFree(Metadata* const metadata) {
+ if (metadata == NULL) return;
+ MetadataPayloadDelete(&metadata->exif);
+ MetadataPayloadDelete(&metadata->iccp);
+ MetadataPayloadDelete(&metadata->xmp);
+}
+
+int MetadataCopy(const char* metadata, size_t metadata_len,
+ MetadataPayload* const payload) {
+ if (metadata == NULL || metadata_len == 0 || payload == NULL) return 0;
+ payload->bytes = (uint8_t*)malloc(metadata_len);
+ if (payload->bytes == NULL) return 0;
+ payload->size = metadata_len;
+ memcpy(payload->bytes, metadata, metadata_len);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/metadata.h b/src/third_party/libwebp/imageio/metadata.h
new file mode 100644
index 0000000..1d5be91
--- /dev/null
+++ b/src/third_party/libwebp/imageio/metadata.h
@@ -0,0 +1,47 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Metadata types and functions.
+//
+
+#ifndef WEBP_IMAGEIO_METADATA_H_
+#define WEBP_IMAGEIO_METADATA_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MetadataPayload {
+ uint8_t* bytes;
+ size_t size;
+} MetadataPayload;
+
+typedef struct Metadata {
+ MetadataPayload exif;
+ MetadataPayload iccp;
+ MetadataPayload xmp;
+} Metadata;
+
+#define METADATA_OFFSET(x) offsetof(Metadata, x)
+
+void MetadataInit(Metadata* const metadata);
+void MetadataPayloadDelete(MetadataPayload* const payload);
+void MetadataFree(Metadata* const metadata);
+
+// Stores 'metadata' to 'payload->bytes', returns false on allocation error.
+int MetadataCopy(const char* metadata, size_t metadata_len,
+ MetadataPayload* const payload);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_METADATA_H_
diff --git a/src/third_party/libwebp/imageio/pngdec.c b/src/third_party/libwebp/imageio/pngdec.c
new file mode 100644
index 0000000..4622353
--- /dev/null
+++ b/src/third_party/libwebp/imageio/pngdec.c
@@ -0,0 +1,340 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// PNG decode.
+
+#include "./pngdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef WEBP_HAVE_PNG
+#include <png.h>
+#include <setjmp.h> // note: this must be included *after* png.h
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+static void PNGAPI error_function(png_structp png, png_const_charp error) {
+ if (error != NULL) fprintf(stderr, "libpng error: %s\n", error);
+ longjmp(png_jmpbuf(png), 1);
+}
+
+// Converts the NULL terminated 'hexstring' which contains 2-byte character
+// representations of hex values to raw data.
+// 'hexstring' may contain values consisting of [A-F][a-f][0-9] in pairs,
+// e.g., 7af2..., separated by any number of newlines.
+// 'expected_length' is the anticipated processed size.
+// On success the raw buffer is returned with its length equivalent to
+// 'expected_length'. NULL is returned if the processed length is less than
+// 'expected_length' or any character aside from those above is encountered.
+// The returned buffer must be freed by the caller.
+static uint8_t* HexStringToBytes(const char* hexstring,
+ size_t expected_length) {
+ const char* src = hexstring;
+ size_t actual_length = 0;
+ uint8_t* const raw_data = (uint8_t*)malloc(expected_length);
+ uint8_t* dst;
+
+ if (raw_data == NULL) return NULL;
+
+ for (dst = raw_data; actual_length < expected_length && *src != '\0'; ++src) {
+ char* end;
+ char val[3];
+ if (*src == '\n') continue;
+ val[0] = *src++;
+ val[1] = *src;
+ val[2] = '\0';
+ *dst++ = (uint8_t)strtol(val, &end, 16);
+ if (end != val + 2) break;
+ ++actual_length;
+ }
+
+ if (actual_length != expected_length) {
+ free(raw_data);
+ return NULL;
+ }
+ return raw_data;
+}
+
+static int ProcessRawProfile(const char* profile, size_t profile_len,
+ MetadataPayload* const payload) {
+ const char* src = profile;
+ char* end;
+ int expected_length;
+
+ if (profile == NULL || profile_len == 0) return 0;
+
+ // ImageMagick formats 'raw profiles' as
+ // '\n<name>\n<length>(%8lu)\n<hex payload>\n'.
+ if (*src != '\n') {
+ fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
+ *src);
+ return 0;
+ }
+ ++src;
+ // skip the profile name and extract the length.
+ while (*src != '\0' && *src++ != '\n') {}
+ expected_length = (int)strtol(src, &end, 10);
+ if (*end != '\n') {
+ fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
+ *end);
+ return 0;
+ }
+ ++end;
+
+ // 'end' now points to the profile payload.
+ payload->bytes = HexStringToBytes(end, expected_length);
+ if (payload->bytes == NULL) return 0;
+ payload->size = expected_length;
+ return 1;
+}
+
+static const struct {
+ const char* name;
+ int (*process)(const char* profile, size_t profile_len,
+ MetadataPayload* const payload);
+ size_t storage_offset;
+} kPNGMetadataMap[] = {
+ // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html#TextualData
+ // See also: ExifTool on CPAN.
+ { "Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif) },
+ { "Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp) },
+ // Exiftool puts exif data in APP1 chunk, too.
+ { "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) },
+ // XMP Specification Part 3, Section 3 #PNG
+ { "XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp) },
+ { NULL, NULL, 0 },
+};
+
+// Looks for metadata at both the beginning and end of the PNG file, giving
+// preference to the head.
+// Returns true on success. The caller must use MetadataFree() on 'metadata' in
+// all cases.
+static int ExtractMetadataFromPNG(png_structp png,
+ png_infop const head_info,
+ png_infop const end_info,
+ Metadata* const metadata) {
+ int p;
+
+ for (p = 0; p < 2; ++p) {
+ png_infop const info = (p == 0) ? head_info : end_info;
+ png_textp text = NULL;
+ const png_uint_32 num = png_get_text(png, info, &text, NULL);
+ png_uint_32 i;
+ // Look for EXIF / XMP metadata.
+ for (i = 0; i < num; ++i, ++text) {
+ int j;
+ for (j = 0; kPNGMetadataMap[j].name != NULL; ++j) {
+ if (!strcmp(text->key, kPNGMetadataMap[j].name)) {
+ MetadataPayload* const payload =
+ (MetadataPayload*)((uint8_t*)metadata +
+ kPNGMetadataMap[j].storage_offset);
+ png_size_t text_length;
+ switch (text->compression) {
+#ifdef PNG_iTXt_SUPPORTED
+ case PNG_ITXT_COMPRESSION_NONE:
+ case PNG_ITXT_COMPRESSION_zTXt:
+ text_length = text->itxt_length;
+ break;
+#endif
+ case PNG_TEXT_COMPRESSION_NONE:
+ case PNG_TEXT_COMPRESSION_zTXt:
+ default:
+ text_length = text->text_length;
+ break;
+ }
+ if (payload->bytes != NULL) {
+ fprintf(stderr, "Ignoring additional '%s'\n", text->key);
+ } else if (!kPNGMetadataMap[j].process(text->text, text_length,
+ payload)) {
+ fprintf(stderr, "Failed to process: '%s'\n", text->key);
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ // Look for an ICC profile.
+ {
+ png_charp name;
+ int comp_type;
+#if ((PNG_LIBPNG_VER_MAJOR << 8) | PNG_LIBPNG_VER_MINOR << 0) < \
+ ((1 << 8) | (5 << 0))
+ png_charp profile;
+#else // >= libpng 1.5.0
+ png_bytep profile;
+#endif
+ png_uint_32 len;
+
+ if (png_get_iCCP(png, info,
+ &name, &comp_type, &profile, &len) == PNG_INFO_iCCP) {
+ if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+typedef struct {
+ const uint8_t* data;
+ size_t data_size;
+ png_size_t offset;
+} PNGReadContext;
+
+static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
+ PNGReadContext* const ctx = (PNGReadContext*)png_get_io_ptr(png_ptr);
+ if (ctx->data_size - ctx->offset < length) {
+ png_error(png_ptr, "ReadFunc: invalid read length (overflow)!");
+ }
+ memcpy(data, ctx->data + ctx->offset, length);
+ ctx->offset += length;
+}
+
+int ReadPNG(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic,
+ int keep_alpha, struct Metadata* const metadata) {
+ volatile png_structp png = NULL;
+ volatile png_infop info = NULL;
+ volatile png_infop end_info = NULL;
+ PNGReadContext context = { NULL, 0, 0 };
+ int color_type, bit_depth, interlaced;
+ int has_alpha;
+ int num_passes;
+ int p;
+ volatile int ok = 0;
+ png_uint_32 width, height, y;
+ int64_t stride;
+ uint8_t* volatile rgb = NULL;
+
+ if (data == NULL || data_size == 0 || pic == NULL) return 0;
+
+ context.data = data;
+ context.data_size = data_size;
+
+ png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (png == NULL) goto End;
+
+ png_set_error_fn(png, 0, error_function, NULL);
+ if (setjmp(png_jmpbuf(png))) {
+ Error:
+ MetadataFree(metadata);
+ goto End;
+ }
+
+ info = png_create_info_struct(png);
+ if (info == NULL) goto Error;
+ end_info = png_create_info_struct(png);
+ if (end_info == NULL) goto Error;
+
+ png_set_read_fn(png, &context, ReadFunc);
+ png_read_info(png, info);
+ if (!png_get_IHDR(png, info,
+ &width, &height, &bit_depth, &color_type, &interlaced,
+ NULL, NULL)) goto Error;
+
+ png_set_strip_16(png);
+ png_set_packing(png);
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(png);
+ }
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ if (bit_depth < 8) {
+ png_set_expand_gray_1_2_4_to_8(png);
+ }
+ png_set_gray_to_rgb(png);
+ }
+ if (png_get_valid(png, info, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(png);
+ has_alpha = 1;
+ } else {
+ has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA);
+ }
+
+ // Apply gamma correction if needed.
+ {
+ double image_gamma = 1 / 2.2, screen_gamma = 2.2;
+ int srgb_intent;
+ if (png_get_sRGB(png, info, &srgb_intent) ||
+ png_get_gAMA(png, info, &image_gamma)) {
+ png_set_gamma(png, screen_gamma, image_gamma);
+ }
+ }
+
+ if (!keep_alpha) {
+ png_set_strip_alpha(png);
+ has_alpha = 0;
+ }
+
+ num_passes = png_set_interlace_handling(png);
+ png_read_update_info(png, info);
+
+ stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb);
+ if (stride != (int)stride ||
+ !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
+ goto Error;
+ }
+
+ rgb = (uint8_t*)malloc((size_t)stride * height);
+ if (rgb == NULL) goto Error;
+ for (p = 0; p < num_passes; ++p) {
+ png_bytep row = rgb;
+ for (y = 0; y < height; ++y) {
+ png_read_rows(png, &row, NULL, 1);
+ row += stride;
+ }
+ }
+ png_read_end(png, end_info);
+
+ if (metadata != NULL &&
+ !ExtractMetadataFromPNG(png, info, end_info, metadata)) {
+ fprintf(stderr, "Error extracting PNG metadata!\n");
+ goto Error;
+ }
+
+ pic->width = (int)width;
+ pic->height = (int)height;
+ ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride)
+ : WebPPictureImportRGB(pic, rgb, (int)stride);
+
+ if (!ok) {
+ goto Error;
+ }
+
+ End:
+ if (png != NULL) {
+ png_destroy_read_struct((png_structpp)&png,
+ (png_infopp)&info, (png_infopp)&end_info);
+ }
+ free(rgb);
+ return ok;
+}
+#else // !WEBP_HAVE_PNG
+int ReadPNG(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic,
+ int keep_alpha, struct Metadata* const metadata) {
+ (void)data;
+ (void)data_size;
+ (void)pic;
+ (void)keep_alpha;
+ (void)metadata;
+ fprintf(stderr, "PNG support not compiled. Please install the libpng "
+ "development package before building.\n");
+ return 0;
+}
+#endif // WEBP_HAVE_PNG
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/pngdec.h b/src/third_party/libwebp/imageio/pngdec.h
new file mode 100644
index 0000000..e0a6122
--- /dev/null
+++ b/src/third_party/libwebp/imageio/pngdec.h
@@ -0,0 +1,37 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// PNG decode.
+
+#ifndef WEBP_IMAGEIO_PNGDEC_H_
+#define WEBP_IMAGEIO_PNGDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a PNG from 'data', returning the decoded output in 'pic'.
+// Output is RGBA or YUVA, depending on pic->use_argb value.
+// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
+// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
+// Returns true on success.
+int ReadPNG(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic,
+ int keep_alpha, struct Metadata* const metadata);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_PNGDEC_H_
diff --git a/src/third_party/libwebp/imageio/pnmdec.c b/src/third_party/libwebp/imageio/pnmdec.c
new file mode 100644
index 0000000..8f5a4a0
--- /dev/null
+++ b/src/third_party/libwebp/imageio/pnmdec.c
@@ -0,0 +1,257 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// (limited) PNM decoder
+
+#include "./pnmdec.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+
+typedef enum {
+ WIDTH_FLAG = 1 << 0,
+ HEIGHT_FLAG = 1 << 1,
+ DEPTH_FLAG = 1 << 2,
+ MAXVAL_FLAG = 1 << 3,
+ TUPLE_FLAG = 1 << 4,
+ ALL_NEEDED_FLAGS = 0x1f
+} PNMFlags;
+
+typedef struct {
+ const uint8_t* data;
+ size_t data_size;
+ int width, height;
+ int bytes_per_px; // 1, 3, 4
+ int depth;
+ int max_value;
+ int type; // 5, 6 or 7
+ int seen_flags;
+} PNMInfo;
+
+// -----------------------------------------------------------------------------
+// PNM decoding
+
+#define MAX_LINE_SIZE 1024
+static const size_t kMinPNMHeaderSize = 3;
+
+static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size,
+ char out[MAX_LINE_SIZE + 1], size_t* const out_size) {
+ size_t i = 0;
+ *out_size = 0;
+ redo:
+ for (i = 0; i < MAX_LINE_SIZE && off < data_size; ++i) {
+ out[i] = data[off++];
+ if (out[i] == '\n') break;
+ }
+ if (off < data_size) {
+ if (i == 0) goto redo; // empty line
+ if (out[0] == '#') goto redo; // skip comment
+ }
+ out[i] = 0; // safety sentinel
+ *out_size = i;
+ return off;
+}
+
+static size_t FlagError(const char flag[]) {
+ fprintf(stderr, "PAM header error: flags '%s' already seen.\n", flag);
+ return 0;
+}
+
+// inspired from http://netpbm.sourceforge.net/doc/pam.html
+static size_t ReadPAMFields(PNMInfo* const info, size_t off) {
+ char out[MAX_LINE_SIZE + 1];
+ size_t out_size;
+ int tmp;
+ assert(info != NULL);
+ while (1) {
+ off = ReadLine(info->data, off, info->data_size, out, &out_size);
+ if (off == 0) return 0;
+ if (sscanf(out, "WIDTH %d", &tmp) == 1) {
+ if (info->seen_flags & WIDTH_FLAG) return FlagError("WIDTH");
+ info->seen_flags |= WIDTH_FLAG;
+ info->width = tmp;
+ } else if (sscanf(out, "HEIGHT %d", &tmp) == 1) {
+ if (info->seen_flags & HEIGHT_FLAG) return FlagError("HEIGHT");
+ info->seen_flags |= HEIGHT_FLAG;
+ info->height = tmp;
+ } else if (sscanf(out, "DEPTH %d", &tmp) == 1) {
+ if (info->seen_flags & DEPTH_FLAG) return FlagError("DEPTH");
+ info->seen_flags |= DEPTH_FLAG;
+ info->depth = tmp;
+ } else if (sscanf(out, "MAXVAL %d", &tmp) == 1) {
+ if (info->seen_flags & MAXVAL_FLAG) return FlagError("MAXVAL");
+ info->seen_flags |= MAXVAL_FLAG;
+ info->max_value = tmp;
+ } else if (!strcmp(out, "TUPLTYPE RGB_ALPHA")) {
+ info->bytes_per_px = 4;
+ info->seen_flags |= TUPLE_FLAG;
+ } else if (!strcmp(out, "TUPLTYPE RGB")) {
+ info->bytes_per_px = 3;
+ info->seen_flags |= TUPLE_FLAG;
+ } else if (!strcmp(out, "TUPLTYPE GRAYSCALE")) {
+ info->bytes_per_px = 1;
+ info->seen_flags |= TUPLE_FLAG;
+ } else if (!strcmp(out, "ENDHDR")) {
+ break;
+ } else {
+ static const char kEllipsis[] = " ...";
+ int i;
+ if (out_size > 20) sprintf(out + 20 - strlen(kEllipsis), kEllipsis);
+ for (i = 0; i < (int)strlen(out); ++i) {
+ if (!isprint(out[i])) out[i] = ' ';
+ }
+ fprintf(stderr, "PAM header error: unrecognized entry [%s]\n", out);
+ return 0;
+ }
+ }
+ if (!(info->seen_flags & TUPLE_FLAG)) {
+ if (info->depth > 0 && info->depth <= 4 && info->depth != 2) {
+ info->seen_flags |= TUPLE_FLAG;
+ info->bytes_per_px = info->depth * (info->max_value > 255 ? 2 : 1);
+ } else {
+ fprintf(stderr, "PAM: invalid bitdepth (%d).\n", info->depth);
+ return 0;
+ }
+ }
+ if (info->seen_flags != ALL_NEEDED_FLAGS) {
+ fprintf(stderr, "PAM: incomplete header.\n");
+ return 0;
+ }
+ return off;
+}
+
+static size_t ReadHeader(PNMInfo* const info) {
+ size_t off = 0;
+ char out[MAX_LINE_SIZE + 1];
+ size_t out_size;
+ if (info == NULL) return 0;
+ if (info->data == NULL || info->data_size < kMinPNMHeaderSize) return 0;
+
+ info->width = info->height = 0;
+ info->type = -1;
+ info->seen_flags = 0;
+ info->bytes_per_px = 0;
+ info->depth = 0;
+ info->max_value = 0;
+
+ off = ReadLine(info->data, off, info->data_size, out, &out_size);
+ if (off == 0 || sscanf(out, "P%d", &info->type) != 1) return 0;
+ if (info->type == 7) {
+ off = ReadPAMFields(info, off);
+ } else {
+ off = ReadLine(info->data, off, info->data_size, out, &out_size);
+ if (off == 0 || sscanf(out, "%d %d", &info->width, &info->height) != 2) {
+ return 0;
+ }
+ off = ReadLine(info->data, off, info->data_size, out, &out_size);
+ if (off == 0 || sscanf(out, "%d", &info->max_value) != 1) return 0;
+
+ // finish initializing missing fields
+ info->depth = (info->type == 5) ? 1 : 3;
+ info->bytes_per_px = info->depth * (info->max_value > 255 ? 2 : 1);
+ }
+ // perform some basic numerical validation
+ if (info->width <= 0 || info->height <= 0 ||
+ info->type <= 0 || info->type >= 9 ||
+ info->depth <= 0 || info->depth == 2 || info->depth > 4 ||
+ info->bytes_per_px < info->depth ||
+ info->max_value <= 0 || info->max_value >= 65536) {
+ return 0;
+ }
+ return off;
+}
+
+int ReadPNM(const uint8_t* const data, size_t data_size,
+ WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata) {
+ int ok = 0;
+ int i, j;
+ uint64_t stride, pixel_bytes;
+ uint8_t* rgb = NULL, *tmp_rgb;
+ size_t offset;
+ PNMInfo info;
+
+ info.data = data;
+ info.data_size = data_size;
+ offset = ReadHeader(&info);
+ if (offset == 0) {
+ fprintf(stderr, "Error parsing PNM header.\n");
+ goto End;
+ }
+
+ if (info.type < 5 || info.type > 7) {
+ fprintf(stderr, "Unsupported P%d PNM format.\n", info.type);
+ goto End;
+ }
+
+ // Some basic validations.
+ if (pic == NULL) goto End;
+ if (info.width > WEBP_MAX_DIMENSION || info.height > WEBP_MAX_DIMENSION) {
+ fprintf(stderr, "Invalid %dx%d dimension for PNM\n",
+ info.width, info.height);
+ goto End;
+ }
+
+ pixel_bytes = (uint64_t)info.width * info.height * info.bytes_per_px;
+ if (data_size < offset + pixel_bytes) {
+ fprintf(stderr, "Truncated PNM file (P%d).\n", info.type);
+ goto End;
+ }
+ stride =
+ (uint64_t)(info.bytes_per_px < 3 ? 3 : info.bytes_per_px) * info.width;
+ if (stride != (size_t)stride ||
+ !ImgIoUtilCheckSizeArgumentsOverflow(stride, info.height)) {
+ goto End;
+ }
+
+ rgb = (uint8_t*)malloc((size_t)stride * info.height);
+ if (rgb == NULL) goto End;
+
+ // Convert input
+ tmp_rgb = rgb;
+ for (j = 0; j < info.height; ++j) {
+ assert(offset + info.bytes_per_px * info.width <= data_size);
+ if (info.depth == 1) {
+ // convert grayscale -> RGB
+ for (i = 0; i < info.width; ++i) {
+ const uint8_t v = data[offset + i];
+ tmp_rgb[3 * i + 0] = tmp_rgb[3 * i + 1] = tmp_rgb[3 * i + 2] = v;
+ }
+ } else if (info.depth == 3) { // RGB
+ memcpy(tmp_rgb, data + offset, 3 * info.width * sizeof(*data));
+ } else if (info.depth == 4) { // RGBA
+ memcpy(tmp_rgb, data + offset, 4 * info.width * sizeof(*data));
+ }
+ offset += info.bytes_per_px * info.width;
+ tmp_rgb += stride;
+ }
+
+ // WebP conversion.
+ pic->width = info.width;
+ pic->height = info.height;
+ ok = (info.depth == 4) ? WebPPictureImportRGBA(pic, rgb, (int)stride)
+ : WebPPictureImportRGB(pic, rgb, (int)stride);
+ if (!ok) goto End;
+
+ ok = 1;
+ End:
+ free((void*)rgb);
+
+ (void)metadata;
+ (void)keep_alpha;
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/pnmdec.h b/src/third_party/libwebp/imageio/pnmdec.h
new file mode 100644
index 0000000..c4d5823
--- /dev/null
+++ b/src/third_party/libwebp/imageio/pnmdec.h
@@ -0,0 +1,37 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// partial PNM format decoder (ppm/pgm)
+
+#ifndef WEBP_IMAGEIO_PNMDEC_H_
+#define WEBP_IMAGEIO_PNMDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a PNM file from 'data', returning the decoded output in 'pic'.
+// The output is RGB or YUV depending on pic->use_argb value.
+// Returns true on success.
+// 'metadata' has no effect, but is kept for coherence with other signatures
+// for image readers.
+int ReadPNM(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_PNMDEC_H_
diff --git a/src/third_party/libwebp/imageio/tiffdec.c b/src/third_party/libwebp/imageio/tiffdec.c
new file mode 100644
index 0000000..92eb682
--- /dev/null
+++ b/src/third_party/libwebp/imageio/tiffdec.c
@@ -0,0 +1,268 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// TIFF decode.
+
+#include "./tiffdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef WEBP_HAVE_TIFF
+#include <tiffio.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+static const struct {
+ ttag_t tag;
+ size_t storage_offset;
+} kTIFFMetadataMap[] = {
+ { TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) },
+ { TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp) },
+ { 0, 0 },
+};
+
+// Returns true on success. The caller must use MetadataFree() on 'metadata' in
+// all cases.
+static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) {
+ int i;
+ toff_t exif_ifd_offset;
+
+ for (i = 0; kTIFFMetadataMap[i].tag != 0; ++i) {
+ MetadataPayload* const payload =
+ (MetadataPayload*)((uint8_t*)metadata +
+ kTIFFMetadataMap[i].storage_offset);
+ void* tag_data;
+ uint32 tag_data_len;
+
+ if (TIFFGetField(tif, kTIFFMetadataMap[i].tag, &tag_data_len, &tag_data) &&
+ !MetadataCopy((const char*)tag_data, tag_data_len, payload)) {
+ return 0;
+ }
+ }
+
+ // TODO(jzern): To extract the raw EXIF directory some parsing of it would be
+ // necessary to determine the overall size. In addition, value offsets in
+ // individual directory entries may need to be updated as, depending on the
+ // type, they are file based.
+ // Exif 2.2 Section 4.6.2 Tag Structure
+ // TIFF Revision 6.0 Part 1 Section 2 TIFF Structure #Image File Directory
+ if (TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_ifd_offset)) {
+ fprintf(stderr, "Warning: EXIF extraction from TIFF is unsupported.\n");
+ }
+ return 1;
+}
+
+// Ad-hoc structure to supply read-from-memory functionalities.
+typedef struct {
+ const uint8_t* data;
+ toff_t size;
+ toff_t pos;
+} MyData;
+
+static int MyClose(thandle_t opaque) {
+ (void)opaque;
+ return 0;
+}
+
+static toff_t MySize(thandle_t opaque) {
+ const MyData* const my_data = (MyData*)opaque;
+ return my_data->size;
+}
+
+static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
+ MyData* const my_data = (MyData*)opaque;
+ offset += (whence == SEEK_CUR) ? my_data->pos
+ : (whence == SEEK_SET) ? 0
+ : my_data->size;
+ if (offset > my_data->size) return (toff_t)-1;
+ my_data->pos = offset;
+ return offset;
+}
+
+static int MyMapFile(thandle_t opaque, void** base, toff_t* size) {
+ (void)opaque;
+ (void)base;
+ (void)size;
+ return 0;
+}
+static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) {
+ (void)opaque;
+ (void)base;
+ (void)size;
+}
+
+static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
+ MyData* const my_data = (MyData*)opaque;
+ if (my_data->pos + size > my_data->size) {
+ size = (tsize_t)(my_data->size - my_data->pos);
+ }
+ if (size > 0) {
+ memcpy(dst, my_data->data + my_data->pos, size);
+ my_data->pos += size;
+ }
+ return size;
+}
+
+// Unmultiply Argb data. Taken from dsp/alpha_processing
+// (we don't want to force a dependency to a libdspdec library).
+#define MFIX 24 // 24bit fixed-point arithmetic
+#define HALF ((1u << MFIX) >> 1)
+#define KINV_255 ((1u << MFIX) / 255u)
+
+static uint32_t Unmult(uint8_t x, uint32_t mult) {
+ const uint32_t v = (x * mult + HALF) >> MFIX;
+ return (v > 255u) ? 255u : v;
+}
+
+static WEBP_INLINE uint32_t GetScale(uint32_t a) {
+ return (255u << MFIX) / a;
+}
+
+static void MultARGBRow(uint8_t* ptr, int width) {
+ int x;
+ for (x = 0; x < width; ++x, ptr += 4) {
+ const uint32_t alpha = ptr[3];
+ if (alpha < 255) {
+ if (alpha == 0) { // alpha == 0
+ ptr[0] = ptr[1] = ptr[2] = 0;
+ } else {
+ const uint32_t scale = GetScale(alpha);
+ ptr[0] = Unmult(ptr[0], scale);
+ ptr[1] = Unmult(ptr[1], scale);
+ ptr[2] = Unmult(ptr[2], scale);
+ }
+ }
+ }
+}
+
+int ReadTIFF(const uint8_t* const data, size_t data_size,
+ WebPPicture* const pic, int keep_alpha,
+ Metadata* const metadata) {
+ MyData my_data = { data, (toff_t)data_size, 0 };
+ TIFF* tif;
+ uint32_t width, height;
+ uint16_t samples_per_px = 0;
+ uint16_t extra_samples = 0;
+ uint16_t* extra_samples_ptr = NULL;
+ uint32_t* raster;
+ int64_t alloc_size;
+ int ok = 0;
+ tdir_t dircount;
+
+ if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) {
+ return 0;
+ }
+
+ tif = TIFFClientOpen("Memory", "r", &my_data,
+ MyRead, MyRead, MySeek, MyClose,
+ MySize, MyMapFile, MyUnmapFile);
+ if (tif == NULL) {
+ fprintf(stderr, "Error! Cannot parse TIFF file\n");
+ return 0;
+ }
+
+ dircount = TIFFNumberOfDirectories(tif);
+ if (dircount > 1) {
+ fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
+ "Only the first will be used, %d will be ignored.\n",
+ dircount - 1);
+ }
+ if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
+ fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
+ goto End;
+ }
+ if (samples_per_px < 3 || samples_per_px > 4) goto End; // not supported
+
+ if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
+ TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
+ fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
+ goto End;
+ }
+ if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height,
+ sizeof(*raster))) {
+ goto End;
+ }
+ if (samples_per_px > 3 && !TIFFGetField(tif, TIFFTAG_EXTRASAMPLES,
+ &extra_samples, &extra_samples_ptr)) {
+ fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n");
+ goto End;
+ }
+
+ // _Tiffmalloc uses a signed type for size.
+ alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster));
+ if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
+
+ raster = (uint32*)_TIFFmalloc((tsize_t)alloc_size);
+ if (raster != NULL) {
+ if (TIFFReadRGBAImageOriented(tif, width, height, raster,
+ ORIENTATION_TOPLEFT, 1)) {
+ const int stride = width * sizeof(*raster);
+ pic->width = width;
+ pic->height = height;
+ // TIFF data is ABGR
+#ifdef WORDS_BIGENDIAN
+ TIFFSwabArrayOfLong(raster, width * height);
+#endif
+ // if we have an alpha channel, we must un-multiply from rgbA to RGBA
+ if (extra_samples == 1 && extra_samples_ptr != NULL &&
+ extra_samples_ptr[0] == EXTRASAMPLE_ASSOCALPHA) {
+ uint32_t y;
+ uint8_t* tmp = (uint8_t*)raster;
+ for (y = 0; y < height; ++y) {
+ MultARGBRow(tmp, width);
+ tmp += stride;
+ }
+ }
+ ok = keep_alpha
+ ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
+ : WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
+ }
+ _TIFFfree(raster);
+ } else {
+ fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
+ }
+
+ if (ok) {
+ if (metadata != NULL) {
+ ok = ExtractMetadataFromTIFF(tif, metadata);
+ if (!ok) {
+ fprintf(stderr, "Error extracting TIFF metadata!\n");
+ MetadataFree(metadata);
+ WebPPictureFree(pic);
+ }
+ }
+ }
+ End:
+ TIFFClose(tif);
+ return ok;
+}
+#else // !WEBP_HAVE_TIFF
+int ReadTIFF(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata) {
+ (void)data;
+ (void)data_size;
+ (void)pic;
+ (void)keep_alpha;
+ (void)metadata;
+ fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
+ "development package before building.\n");
+ return 0;
+}
+#endif // WEBP_HAVE_TIFF
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/tiffdec.h b/src/third_party/libwebp/imageio/tiffdec.h
new file mode 100644
index 0000000..0c8becc
--- /dev/null
+++ b/src/third_party/libwebp/imageio/tiffdec.h
@@ -0,0 +1,37 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// TIFF decode.
+
+#ifndef WEBP_IMAGEIO_TIFFDEC_H_
+#define WEBP_IMAGEIO_TIFFDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a TIFF from 'data', returning the decoded output in 'pic'.
+// Output is RGBA or YUVA, depending on pic->use_argb value.
+// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
+// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
+// Returns true on success.
+int ReadTIFF(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_TIFFDEC_H_
diff --git a/src/third_party/libwebp/imageio/webpdec.c b/src/third_party/libwebp/imageio/webpdec.c
new file mode 100644
index 0000000..a9d0654
--- /dev/null
+++ b/src/third_party/libwebp/imageio/webpdec.c
@@ -0,0 +1,243 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP decode.
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "./webpdec.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "webp/decode.h"
+#include "webp/demux.h"
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+//------------------------------------------------------------------------------
+// WebP decoding
+
+static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
+ "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
+ "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
+};
+
+static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
+ if (config->input.has_animation) {
+ fprintf(stderr,
+ "Error! Decoding of an animated WebP file is not supported.\n"
+ " Use webpmux to extract the individual frames or\n"
+ " vwebp to view this image.\n");
+ }
+}
+
+void PrintWebPError(const char* const in_file, int status) {
+ fprintf(stderr, "Decoding of %s failed.\n", in_file);
+ fprintf(stderr, "Status: %d", status);
+ if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
+ fprintf(stderr, "(%s)", kStatusMessages[status]);
+ }
+ fprintf(stderr, "\n");
+}
+
+int LoadWebP(const char* const in_file,
+ const uint8_t** data, size_t* data_size,
+ WebPBitstreamFeatures* bitstream) {
+ VP8StatusCode status;
+ WebPBitstreamFeatures local_features;
+ if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0;
+
+ if (bitstream == NULL) {
+ bitstream = &local_features;
+ }
+
+ status = WebPGetFeatures(*data, *data_size, bitstream);
+ if (status != VP8_STATUS_OK) {
+ free((void*)*data);
+ *data = NULL;
+ *data_size = 0;
+ PrintWebPError(in_file, status);
+ return 0;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
+ WebPDecoderConfig* const config) {
+ if (config == NULL) return VP8_STATUS_INVALID_PARAM;
+ PrintAnimationWarning(config);
+ return WebPDecode(data, data_size, config);
+}
+
+VP8StatusCode DecodeWebPIncremental(
+ const uint8_t* const data, size_t data_size,
+ WebPDecoderConfig* const config) {
+ VP8StatusCode status = VP8_STATUS_OK;
+ if (config == NULL) return VP8_STATUS_INVALID_PARAM;
+
+ PrintAnimationWarning(config);
+
+ // Decoding call.
+ {
+ WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
+ if (idec == NULL) {
+ fprintf(stderr, "Failed during WebPINewDecoder().\n");
+ return VP8_STATUS_OUT_OF_MEMORY;
+ } else {
+ status = WebPIUpdate(idec, data, data_size);
+ WebPIDelete(idec);
+ }
+ }
+ return status;
+}
+
+// -----------------------------------------------------------------------------
+// Metadata
+
+static int ExtractMetadata(const uint8_t* const data, size_t data_size,
+ Metadata* const metadata) {
+ WebPData webp_data = { data, data_size };
+ WebPDemuxer* const demux = WebPDemux(&webp_data);
+ WebPChunkIterator chunk_iter;
+ uint32_t flags;
+
+ if (demux == NULL) return 0;
+ assert(metadata != NULL);
+
+ flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
+
+ if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) {
+ MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
+ &metadata->iccp);
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ }
+ if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) {
+ MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
+ &metadata->exif);
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ }
+ if ((flags & XMP_FLAG) && WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) {
+ MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
+ &metadata->xmp);
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ }
+ WebPDemuxDelete(demux);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+int ReadWebP(const uint8_t* const data, size_t data_size,
+ WebPPicture* const pic,
+ int keep_alpha, Metadata* const metadata) {
+ int ok = 0;
+ VP8StatusCode status = VP8_STATUS_OK;
+ WebPDecoderConfig config;
+ WebPDecBuffer* const output_buffer = &config.output;
+ WebPBitstreamFeatures* const bitstream = &config.input;
+
+ if (data == NULL || data_size == 0 || pic == NULL) return 0;
+
+ if (!WebPInitDecoderConfig(&config)) {
+ fprintf(stderr, "Library version mismatch!\n");
+ return 0;
+ }
+
+ status = WebPGetFeatures(data, data_size, bitstream);
+ if (status != VP8_STATUS_OK) {
+ PrintWebPError("input data", status);
+ return 0;
+ }
+
+ do {
+ const int has_alpha = keep_alpha && bitstream->has_alpha;
+ uint64_t stride;
+ pic->width = bitstream->width;
+ pic->height = bitstream->height;
+ if (pic->use_argb) {
+ stride = (uint64_t)bitstream->width * 4;
+ } else {
+ stride = (uint64_t)bitstream->width * (has_alpha ? 5 : 3) / 2;
+ pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
+ }
+
+ if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, bitstream->height)) {
+ status = VP8_STATUS_OUT_OF_MEMORY;
+ break;
+ }
+
+ ok = WebPPictureAlloc(pic);
+ if (!ok) {
+ status = VP8_STATUS_OUT_OF_MEMORY;
+ break;
+ }
+ if (pic->use_argb) {
+#ifdef WORDS_BIGENDIAN
+ output_buffer->colorspace = MODE_ARGB;
+#else
+ output_buffer->colorspace = MODE_BGRA;
+#endif
+ output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb;
+ output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t);
+ output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height;
+ } else {
+ output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
+ output_buffer->u.YUVA.y = pic->y;
+ output_buffer->u.YUVA.u = pic->u;
+ output_buffer->u.YUVA.v = pic->v;
+ output_buffer->u.YUVA.a = has_alpha ? pic->a : NULL;
+ output_buffer->u.YUVA.y_stride = pic->y_stride;
+ output_buffer->u.YUVA.u_stride = pic->uv_stride;
+ output_buffer->u.YUVA.v_stride = pic->uv_stride;
+ output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0;
+ output_buffer->u.YUVA.y_size = pic->height * pic->y_stride;
+ output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride;
+ output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride;
+ output_buffer->u.YUVA.a_size = pic->height * pic->a_stride;
+ }
+ output_buffer->is_external_memory = 1;
+
+ status = DecodeWebP(data, data_size, &config);
+ ok = (status == VP8_STATUS_OK);
+ if (ok && !keep_alpha && pic->use_argb) {
+ // Need to wipe out the alpha value, as requested.
+ int x, y;
+ uint32_t* argb = pic->argb;
+ for (y = 0; y < pic->height; ++y) {
+ for (x = 0; x < pic->width; ++x) argb[x] |= 0xff000000u;
+ argb += pic->argb_stride;
+ }
+ }
+ } while (0); // <- so we can 'break' out of the loop
+
+ if (status != VP8_STATUS_OK) {
+ PrintWebPError("input data", status);
+ ok = 0;
+ }
+
+ WebPFreeDecBuffer(output_buffer);
+
+ if (ok && metadata != NULL) {
+ ok = ExtractMetadata(data, data_size, metadata);
+ if (!ok) {
+ PrintWebPError("metadata", VP8_STATUS_BITSTREAM_ERROR);
+ }
+ }
+ if (!ok) WebPPictureFree(pic);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/webpdec.h b/src/third_party/libwebp/imageio/webpdec.h
new file mode 100644
index 0000000..d329d41
--- /dev/null
+++ b/src/third_party/libwebp/imageio/webpdec.h
@@ -0,0 +1,67 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP decode.
+
+#ifndef WEBP_IMAGEIO_WEBPDEC_H_
+#define WEBP_IMAGEIO_WEBPDEC_H_
+
+#include "webp/decode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+//------------------------------------------------------------------------------
+// WebP decoding
+
+// Prints an informative error message regarding decode failure of 'in_file'.
+// 'status' is treated as a VP8StatusCode and if valid will be printed as a
+// text string.
+void PrintWebPError(const char* const in_file, int status);
+
+// Reads a WebP from 'in_file', returning the contents and size in 'data' and
+// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
+// Returns true on success.
+int LoadWebP(const char* const in_file,
+ const uint8_t** data, size_t* data_size,
+ WebPBitstreamFeatures* bitstream);
+
+// Decodes the WebP contained in 'data'.
+// 'config' is a structure previously initialized by WebPInitDecoderConfig().
+// 'config->output' should have the desired colorspace selected.
+// Returns the decoder status. On success 'config->output' will contain the
+// decoded picture.
+VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
+ WebPDecoderConfig* const config);
+
+// Same as DecodeWebP(), but using the incremental decoder.
+VP8StatusCode DecodeWebPIncremental(
+ const uint8_t* const data, size_t data_size,
+ WebPDecoderConfig* const config);
+
+//------------------------------------------------------------------------------
+
+// Decodes a WebP contained in 'data', returning the decoded output in 'pic'.
+// Output is RGBA or YUVA, depending on pic->use_argb value.
+// If 'keep_alpha' is true and the WebP has an alpha channel, the output is RGBA
+// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
+// Returns true on success.
+int ReadWebP(const uint8_t* const data, size_t data_size,
+ struct WebPPicture* const pic,
+ int keep_alpha, struct Metadata* const metadata);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_WEBPDEC_H_
diff --git a/src/third_party/libwebp/imageio/wicdec.c b/src/third_party/libwebp/imageio/wicdec.c
new file mode 100644
index 0000000..3ee72a8
--- /dev/null
+++ b/src/third_party/libwebp/imageio/wicdec.c
@@ -0,0 +1,396 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Windows Imaging Component (WIC) decode.
+
+#include "./wicdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_WINCODEC_H
+#ifdef __MINGW32__
+#define INITGUID // Without this GUIDs are declared extern and fail to link
+#endif
+#define CINTERFACE
+#define COBJMACROS
+#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
+ // code with COBJMACROS.
+#include <ole2.h> // CreateStreamOnHGlobal()
+#include <shlwapi.h>
+#include <windows.h>
+#include <wincodec.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+#define IFS(fn) \
+ do { \
+ if (SUCCEEDED(hr)) { \
+ hr = (fn); \
+ if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
+ } \
+ } while (0)
+
+// modified version of DEFINE_GUID from guiddef.h.
+#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ static const GUID name = \
+ { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
+
+#ifdef __cplusplus
+#define MAKE_REFGUID(x) (x)
+#else
+#define MAKE_REFGUID(x) &(x)
+#endif
+
+typedef struct WICFormatImporter {
+ const GUID* pixel_format;
+ int bytes_per_pixel;
+ int (*import)(WebPPicture* const, const uint8_t* const, int);
+} WICFormatImporter;
+
+// From Microsoft SDK 7.0a -- wincodec.h
+// Create local copies for compatibility when building against earlier
+// versions of the SDK.
+WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_,
+ 0x6fddc324, 0x4e03, 0x4bfe,
+ 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
+ 0x6fddc324, 0x4e03, 0x4bfe,
+ 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
+ 0x6fddc324, 0x4e03, 0x4bfe,
+ 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
+ 0xf5c7ad2d, 0x6a8d, 0x43dd,
+ 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
+ 0x1562ff7c, 0xd352, 0x46f9,
+ 0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
+ 0x6fddc324, 0x4e03, 0x4bfe,
+ 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
+
+static HRESULT OpenInputStream(const char* filename, IStream** stream) {
+ HRESULT hr = S_OK;
+ if (!strcmp(filename, "-")) {
+ const uint8_t* data = NULL;
+ size_t data_size = 0;
+ const int ok = ImgIoUtilReadFile(filename, &data, &data_size);
+ if (ok) {
+ HGLOBAL image = GlobalAlloc(GMEM_MOVEABLE, data_size);
+ if (image != NULL) {
+ void* const image_mem = GlobalLock(image);
+ if (image_mem != NULL) {
+ memcpy(image_mem, data, data_size);
+ GlobalUnlock(image);
+ IFS(CreateStreamOnHGlobal(image, TRUE, stream));
+ } else {
+ hr = E_FAIL;
+ }
+ } else {
+ hr = E_OUTOFMEMORY;
+ }
+ free((void*)data);
+ } else {
+ hr = E_FAIL;
+ }
+ } else {
+ IFS(SHCreateStreamOnFileA(filename, STGM_READ, stream));
+ }
+
+ if (FAILED(hr)) {
+ fprintf(stderr, "Error opening input file %s (%08lx)\n", filename, hr);
+ }
+ return hr;
+}
+
+// -----------------------------------------------------------------------------
+// Metadata processing
+
+// Stores the first non-zero sized color profile from 'frame' to 'iccp'.
+// Returns an HRESULT to indicate success or failure. The caller is responsible
+// for freeing 'iccp->bytes' in either case.
+static HRESULT ExtractICCP(IWICImagingFactory* const factory,
+ IWICBitmapFrameDecode* const frame,
+ MetadataPayload* const iccp) {
+ HRESULT hr = S_OK;
+ UINT i, count;
+ IWICColorContext** color_contexts;
+
+ IFS(IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count));
+ if (FAILED(hr) || count == 0) return hr;
+
+ color_contexts = (IWICColorContext**)calloc(count, sizeof(*color_contexts));
+ if (color_contexts == NULL) return E_OUTOFMEMORY;
+ for (i = 0; SUCCEEDED(hr) && i < count; ++i) {
+ IFS(IWICImagingFactory_CreateColorContext(factory, &color_contexts[i]));
+ }
+
+ if (SUCCEEDED(hr)) {
+ UINT num_color_contexts;
+ IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
+ count, color_contexts,
+ &num_color_contexts));
+ assert(FAILED(hr) || num_color_contexts <= count);
+ for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
+ WICColorContextType type;
+ IFS(IWICColorContext_GetType(color_contexts[i], &type));
+ if (SUCCEEDED(hr) && type == WICColorContextProfile) {
+ UINT size;
+ IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
+ 0, NULL, &size));
+ if (SUCCEEDED(hr) && size > 0) {
+ iccp->bytes = (uint8_t*)malloc(size);
+ if (iccp->bytes == NULL) {
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+ iccp->size = size;
+ IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
+ (UINT)iccp->size, iccp->bytes,
+ &size));
+ if (SUCCEEDED(hr) && size != iccp->size) {
+ fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
+ size, (uint32_t)iccp->size);
+ iccp->size = size;
+ }
+ break;
+ }
+ }
+ }
+ }
+ for (i = 0; i < count; ++i) {
+ if (color_contexts[i] != NULL) IUnknown_Release(color_contexts[i]);
+ }
+ free(color_contexts);
+ return hr;
+}
+
+static HRESULT ExtractMetadata(IWICImagingFactory* const factory,
+ IWICBitmapFrameDecode* const frame,
+ Metadata* const metadata) {
+ // TODO(jzern): add XMP/EXIF extraction.
+ const HRESULT hr = ExtractICCP(factory, frame, &metadata->iccp);
+ if (FAILED(hr)) MetadataFree(metadata);
+ return hr;
+}
+
+// -----------------------------------------------------------------------------
+
+static int HasPalette(GUID pixel_format) {
+ return (IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat1bppIndexed)) ||
+ IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat2bppIndexed)) ||
+ IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat4bppIndexed)) ||
+ IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat8bppIndexed)));
+}
+
+static int HasAlpha(IWICImagingFactory* const factory,
+ IWICBitmapDecoder* const decoder,
+ IWICBitmapFrameDecode* const frame,
+ GUID pixel_format) {
+ int has_alpha;
+ if (HasPalette(pixel_format)) {
+ IWICPalette* frame_palette = NULL;
+ IWICPalette* global_palette = NULL;
+ BOOL frame_palette_has_alpha = FALSE;
+ BOOL global_palette_has_alpha = FALSE;
+
+ // A palette may exist at the frame or container level,
+ // check IWICPalette::HasAlpha() for both if present.
+ if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &frame_palette)) &&
+ SUCCEEDED(IWICBitmapFrameDecode_CopyPalette(frame, frame_palette))) {
+ IWICPalette_HasAlpha(frame_palette, &frame_palette_has_alpha);
+ }
+ if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &global_palette)) &&
+ SUCCEEDED(IWICBitmapDecoder_CopyPalette(decoder, global_palette))) {
+ IWICPalette_HasAlpha(global_palette, &global_palette_has_alpha);
+ }
+ has_alpha = frame_palette_has_alpha || global_palette_has_alpha;
+
+ if (frame_palette != NULL) IUnknown_Release(frame_palette);
+ if (global_palette != NULL) IUnknown_Release(global_palette);
+ } else {
+ has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
+ IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) ||
+ IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
+ IsEqualGUID(MAKE_REFGUID(pixel_format),
+ MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
+ }
+ return has_alpha;
+}
+
+int ReadPictureWithWIC(const char* const filename,
+ WebPPicture* const pic, int keep_alpha,
+ Metadata* const metadata) {
+ // From Microsoft SDK 6.0a -- ks.h
+ // Define a local copy to avoid link errors under mingw.
+ WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ static const WICFormatImporter kAlphaFormatImporters[] = {
+ { &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
+ { &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
+ { NULL, 0, NULL },
+ };
+ static const WICFormatImporter kNonAlphaFormatImporters[] = {
+ { &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
+ { &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
+ { NULL, 0, NULL },
+ };
+ HRESULT hr = S_OK;
+ IWICBitmapFrameDecode* frame = NULL;
+ IWICFormatConverter* converter = NULL;
+ IWICImagingFactory* factory = NULL;
+ IWICBitmapDecoder* decoder = NULL;
+ IStream* stream = NULL;
+ UINT frame_count = 0;
+ UINT width = 0, height = 0;
+ BYTE* rgb = NULL;
+ WICPixelFormatGUID src_pixel_format = GUID_WICPixelFormatUndefined;
+ const WICFormatImporter* importer = NULL;
+ GUID src_container_format = GUID_NULL_;
+ static const GUID* kAlphaContainers[] = {
+ &GUID_ContainerFormatBmp,
+ &GUID_ContainerFormatPng,
+ &GUID_ContainerFormatTiff,
+ NULL
+ };
+ int has_alpha = 0;
+ int64_t stride;
+
+ if (filename == NULL || pic == NULL) return 0;
+
+ IFS(CoInitialize(NULL));
+ IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
+ CLSCTX_INPROC_SERVER,
+ MAKE_REFGUID(IID_IWICImagingFactory),
+ (LPVOID*)&factory));
+ if (hr == REGDB_E_CLASSNOTREG) {
+ fprintf(stderr,
+ "Couldn't access Windows Imaging Component (are you running "
+ "Windows XP SP3 or newer?). Most formats not available. "
+ "Use -s for the available YUV input.\n");
+ }
+ // Prepare for image decoding.
+ IFS(OpenInputStream(filename, &stream));
+ IFS(IWICImagingFactory_CreateDecoderFromStream(
+ factory, stream, NULL,
+ WICDecodeMetadataCacheOnDemand, &decoder));
+ IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
+ if (SUCCEEDED(hr) && frame_count == 0) {
+ fprintf(stderr, "No frame found in input file.\n");
+ hr = E_FAIL;
+ }
+ IFS(IWICBitmapDecoder_GetFrame(decoder, 0, &frame));
+ IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
+ IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
+
+ if (SUCCEEDED(hr) && keep_alpha) {
+ const GUID** guid;
+ for (guid = kAlphaContainers; *guid != NULL; ++guid) {
+ if (IsEqualGUID(MAKE_REFGUID(src_container_format),
+ MAKE_REFGUID(**guid))) {
+ has_alpha = HasAlpha(factory, decoder, frame, src_pixel_format);
+ break;
+ }
+ }
+ }
+
+ // Prepare for pixel format conversion (if necessary).
+ IFS(IWICImagingFactory_CreateFormatConverter(factory, &converter));
+
+ for (importer = has_alpha ? kAlphaFormatImporters : kNonAlphaFormatImporters;
+ hr == S_OK && importer->import != NULL; ++importer) {
+ BOOL can_convert;
+ const HRESULT cchr = IWICFormatConverter_CanConvert(
+ converter,
+ MAKE_REFGUID(src_pixel_format),
+ MAKE_REFGUID(*importer->pixel_format),
+ &can_convert);
+ if (SUCCEEDED(cchr) && can_convert) break;
+ }
+ if (importer->import == NULL) hr = E_FAIL;
+
+ IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
+ importer->pixel_format,
+ WICBitmapDitherTypeNone,
+ NULL, 0.0, WICBitmapPaletteTypeCustom));
+
+ // Decode.
+ IFS(IWICFormatConverter_GetSize(converter, &width, &height));
+ stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb);
+ if (stride != (int)stride ||
+ !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
+ hr = E_FAIL;
+ }
+
+ if (SUCCEEDED(hr)) {
+ rgb = (BYTE*)malloc((size_t)stride * height);
+ if (rgb == NULL)
+ hr = E_OUTOFMEMORY;
+ }
+ IFS(IWICFormatConverter_CopyPixels(converter, NULL,
+ (UINT)stride, (UINT)stride * height, rgb));
+
+ // WebP conversion.
+ if (SUCCEEDED(hr)) {
+ int ok;
+ pic->width = width;
+ pic->height = height;
+ pic->use_argb = 1; // For WIC, we always force to argb
+ ok = importer->import(pic, rgb, (int)stride);
+ if (!ok) hr = E_FAIL;
+ }
+ if (SUCCEEDED(hr)) {
+ if (metadata != NULL) {
+ hr = ExtractMetadata(factory, frame, metadata);
+ if (FAILED(hr)) {
+ fprintf(stderr, "Error extracting image metadata using WIC!\n");
+ }
+ }
+ }
+
+ // Cleanup.
+ if (converter != NULL) IUnknown_Release(converter);
+ if (frame != NULL) IUnknown_Release(frame);
+ if (decoder != NULL) IUnknown_Release(decoder);
+ if (factory != NULL) IUnknown_Release(factory);
+ if (stream != NULL) IUnknown_Release(stream);
+ free(rgb);
+ return SUCCEEDED(hr);
+}
+#else // !HAVE_WINCODEC_H
+int ReadPictureWithWIC(const char* const filename,
+ struct WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata) {
+ (void)filename;
+ (void)pic;
+ (void)keep_alpha;
+ (void)metadata;
+ fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. "
+ "Visual Studio and mingw-w64 builds support WIC. Make sure "
+ "wincodec.h detection is working correctly if using autoconf "
+ "and HAVE_WINCODEC_H is defined before building.\n");
+ return 0;
+}
+#endif // HAVE_WINCODEC_H
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/imageio/wicdec.h b/src/third_party/libwebp/imageio/wicdec.h
new file mode 100644
index 0000000..d9eeca8
--- /dev/null
+++ b/src/third_party/libwebp/imageio/wicdec.h
@@ -0,0 +1,34 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Windows Imaging Component (WIC) decode.
+
+#ifndef WEBP_IMAGEIO_WICDEC_H_
+#define WEBP_IMAGEIO_WICDEC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads an image from 'filename', returning the decoded output in 'pic'.
+// If 'keep_alpha' is true and the image has an alpha channel, the output is
+// RGBA otherwise it will be RGB. pic->use_argb is always forced to true.
+// Returns true on success.
+int ReadPictureWithWIC(const char* const filename,
+ struct WebPPicture* const pic, int keep_alpha,
+ struct Metadata* const metadata);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_IMAGEIO_WICDEC_H_
diff --git a/src/third_party/libwebp/iosbuild.sh b/src/third_party/libwebp/iosbuild.sh
new file mode 100755
index 0000000..971efc8
--- /dev/null
+++ b/src/third_party/libwebp/iosbuild.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+#
+# This script generates 'WebP.framework' and 'WebPDecoder.framework'. An iOS
+# app can decode WebP images by including 'WebPDecoder.framework' and both
+# encode and decode WebP images by including 'WebP.framework'.
+#
+# Run ./iosbuild.sh to generate the frameworks under the current directory
+# (the previous build will be erased if it exists).
+#
+# This script is inspired by the build script written by Carson McDonald.
+# (http://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/).
+
+set -e
+
+# Extract the latest SDK version from the final field of the form: iphoneosX.Y
+readonly SDK=$(xcodebuild -showsdks \
+ | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
+)
+# Extract Xcode version.
+readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
+if [[ -z "${XCODE}" ]]; then
+ echo "Xcode not available"
+ exit 1
+fi
+
+readonly OLDPATH=${PATH}
+
+# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
+# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
+PLATFORMS="iPhoneSimulator iPhoneSimulator64"
+PLATFORMS+=" iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64"
+readonly PLATFORMS
+readonly SRCDIR=$(dirname $0)
+readonly TOPDIR=$(pwd)
+readonly BUILDDIR="${TOPDIR}/iosbuild"
+readonly TARGETDIR="${TOPDIR}/WebP.framework"
+readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.framework"
+readonly DEVELOPER=$(xcode-select --print-path)
+readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
+readonly LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
+LIBLIST=''
+DECLIBLIST=''
+
+if [[ -z "${SDK}" ]]; then
+ echo "iOS SDK not available"
+ exit 1
+elif [[ ${SDK%%.*} -gt 8 ]]; then
+ EXTRA_CFLAGS="-fembed-bitcode"
+elif [[ ${SDK} < 6.0 ]]; then
+ echo "You need iOS SDK version 6.0 or above"
+ exit 1
+else
+ echo "iOS SDK Version ${SDK}"
+fi
+
+rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR}
+mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/
+
+if [[ ! -e ${SRCDIR}/configure ]]; then
+ if ! (cd ${SRCDIR} && sh autogen.sh); then
+ cat <<EOT
+Error creating configure script!
+This script requires the autoconf/automake and libtool to build. MacPorts can
+be used to obtain these:
+http://www.macports.org/install.php
+EOT
+ exit 1
+ fi
+fi
+
+for PLATFORM in ${PLATFORMS}; do
+ ARCH2=""
+ if [[ "${PLATFORM}" == "iPhoneOS-V7-arm64" ]]; then
+ PLATFORM="iPhoneOS"
+ ARCH="aarch64"
+ ARCH2="arm64"
+ elif [[ "${PLATFORM}" == "iPhoneOS-V7s" ]]; then
+ PLATFORM="iPhoneOS"
+ ARCH="armv7s"
+ elif [[ "${PLATFORM}" == "iPhoneOS-V7" ]]; then
+ PLATFORM="iPhoneOS"
+ ARCH="armv7"
+ elif [[ "${PLATFORM}" == "iPhoneOS-V6" ]]; then
+ PLATFORM="iPhoneOS"
+ ARCH="armv6"
+ elif [[ "${PLATFORM}" == "iPhoneSimulator64" ]]; then
+ PLATFORM="iPhoneSimulator"
+ ARCH="x86_64"
+ else
+ ARCH="i386"
+ fi
+
+ ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}"
+ mkdir -p "${ROOTDIR}"
+
+ DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
+ SDKROOT="${PLATFORMSROOT}/"
+ SDKROOT+="${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/"
+ CFLAGS="-arch ${ARCH2:-${ARCH}} -pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
+ CFLAGS+=" -miphoneos-version-min=6.0 ${EXTRA_CFLAGS}"
+
+ set -x
+ export PATH="${DEVROOT}/usr/bin:${OLDPATH}"
+ ${SRCDIR}/configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} \
+ --build=$(${SRCDIR}/config.guess) \
+ --disable-shared --enable-static \
+ --enable-libwebpdecoder --enable-swap-16bit-csp \
+ CFLAGS="${CFLAGS}"
+ set +x
+
+ # run make only in the src/ directory to create libwebp.a/libwebpdecoder.a
+ cd src/
+ make V=0
+ make install
+
+ LIBLIST+=" ${ROOTDIR}/lib/libwebp.a"
+ DECLIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
+
+ make clean
+ cd ..
+
+ export PATH=${OLDPATH}
+done
+
+cp -a ${SRCDIR}/src/webp/{decode,encode,types}.h ${TARGETDIR}/Headers/
+${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP
+
+cp -a ${SRCDIR}/src/webp/{decode,types}.h ${DECTARGETDIR}/Headers/
+${LIPO} -create ${DECLIBLIST} -output ${DECTARGETDIR}/WebPDecoder
diff --git a/src/third_party/libwebp/libwebp.gyp b/src/third_party/libwebp/libwebp.gyp
index 6806f07..26b8d6d 100644
--- a/src/third_party/libwebp/libwebp.gyp
+++ b/src/third_party/libwebp/libwebp.gyp
@@ -1,30 +1,17 @@
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-
{
+ 'includes': [
+ 'libwebp.gypi'
+ ],
'targets': [
{
'target_name': 'libwebp_dec',
'type': 'static_library',
- 'dependencies' : [
- 'libwebp_dsp',
- 'libwebp_dsp_neon',
- 'libwebp_utils',
- ],
'include_dirs': ['.'],
'sources': [
- 'dec/alpha.c',
- 'dec/buffer.c',
- 'dec/frame.c',
- 'dec/idec.c',
- 'dec/io.c',
- 'dec/layer.c',
- 'dec/quant.c',
- 'dec/tree.c',
- 'dec/vp8.c',
- 'dec/vp8l.c',
- 'dec/webp.c',
+ '<@(libwebp_dec_sources)',
],
},
{
@@ -32,113 +19,124 @@
'type': 'static_library',
'include_dirs': ['.'],
'sources': [
- 'demux/demux.c',
+ '<@(libwebp_demux_sources)',
],
},
{
- 'target_name': 'libwebp_dsp',
+ 'target_name': 'libwebp_dsp_dec',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_dec_common_sources)',
+ ],
+ 'dependencies': [
+ 'libwebp_dsp_dec_msa',
+ 'libwebp_dsp_dec_neon',
+ 'libwebp_dsp_dec_sse2',
+ 'libwebp_dsp_dec_sse41',
+ 'libwebp_dsp_dec_mips32',
+ 'libwebp_dsp_dec_mips_dsp_r2',
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp_dec_msa',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_dec_msa_sources)',
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp_dec_neon',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_dec_neon_sources)',
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp_dec_sse2',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_dec_sse2_sources)',
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp_dec_sse41',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_dec_sse41_sources)',
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp_dec_mips32',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_dec_mips32_sources)',
+ ],
+ },
+ {
+ 'target_name': 'libwebp_dsp_dec_mips_dsp_r2',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_dec_mips_dsp_r2_sources)',
+ ],
+ },
+ {
+ 'target_name': 'libwebp_utils_dec',
'type': 'static_library',
'include_dirs': ['.'],
'sources': [
- 'dsp/cpu.c',
- 'dsp/dec.c',
- 'dsp/dec_sse2.c',
- 'dsp/enc.c',
- 'dsp/enc_sse2.c',
- 'dsp/lossless.c',
- 'dsp/upsampling.c',
- 'dsp/upsampling_sse2.c',
- 'dsp/yuv.c',
- ],
- 'conditions': [
- ['OS == "android"', {
- 'includes': [ '../../build/android/cpufeatures.gypi' ],
- }],
+ '<@(libwebp_utils_dec_sources)',
],
},
- {
- 'target_name': 'libwebp_dsp_neon',
- 'conditions': [
- ['((target_arch == "arm" and arm_version >= 7) or (target_arch == "arm64"))', {
- 'type': 'static_library',
- 'include_dirs': ['.'],
- 'sources': [
- 'dsp/dec_neon.c',
- 'dsp/enc_neon.c',
- 'dsp/upsampling_neon.c',
- ],
- # behavior similar to *.c.neon in an Android.mk
- 'cflags!': [ '-mfpu=vfpv3-d16' ],
- 'cflags': [ '-mfpu=neon' ],
- },{ # "target_arch != "arm" or arm_version < 7"
- 'type': 'none',
- }],
- ['(target_arch == "arm" and arm_version >= 8) or (target_arch == "arm64")', {
- # NEON is implicit on ARMv8, and both clang and gcc don't like the
- # redundant flag.
- 'cflags!': [ '-mfpu=neon' ],
- }],
- ],
- },
+
{
'target_name': 'libwebp_enc',
- 'type': 'static_library',
+ 'type' : 'static_library',
'include_dirs': ['.'],
- 'sources': [
- 'enc/alpha.c',
- 'enc/analysis.c',
- 'enc/backward_references.c',
- 'enc/config.c',
- 'enc/cost.c',
- 'enc/filter.c',
- 'enc/frame.c',
- 'enc/histogram.c',
- 'enc/iterator.c',
- 'enc/layer.c',
- 'enc/picture.c',
- 'enc/quant.c',
- 'enc/syntax.c',
- 'enc/token.c',
- 'enc/tree.c',
- 'enc/vp8l.c',
- 'enc/webpenc.c',
+ 'sources' : [
+ '<@(libwebp_enc_sources)',
],
},
{
- 'target_name': 'libwebp_utils',
+ 'target_name': 'libwebp_dsp_enc',
+ 'type' : 'static_library',
+ 'include_dirs': ['.'],
+ 'sources' : [
+ '<@(libwebp_dsp_enc_sources)',
+ ],
+ 'dependencies': [
+ ],
+ },
+ {
+ 'target_name': 'libwebp_utils_enc',
'type': 'static_library',
'include_dirs': ['.'],
'sources': [
- 'utils/bit_reader.c',
- 'utils/bit_writer.c',
- 'utils/color_cache.c',
- 'utils/filters.c',
- 'utils/huffman.c',
- #'utils/huffman_encode.c',
- 'utils/quant_levels.c',
- 'utils/quant_levels_dec.c',
- 'utils/rescaler.c',
- 'utils/thread.c',
- 'utils/utils.c',
+ '<@(libwebp_utils_enc_sources)',
],
},
+
{
'target_name': 'libwebp',
'type': 'none',
'dependencies' : [
'libwebp_dec',
'libwebp_demux',
- #'libwebp_dsp',
- #'libwebp_dsp_neon',
- #'libwebp_enc', # Not needed by Cobalt
- 'libwebp_utils',
+ 'libwebp_dsp_dec',
+ 'libwebp_utils_dec',
+ 'libwebp_enc',
+ 'libwebp_dsp_enc',
+ 'libwebp_utils_enc',
],
'direct_dependent_settings': {
'include_dirs': ['.'],
},
- 'conditions': [
- ['OS!="win"', {'product_name': 'webp'}],
- ],
},
],
}
diff --git a/src/third_party/libwebp/libwebp.gypi b/src/third_party/libwebp/libwebp.gypi
new file mode 100644
index 0000000..80a1bfd
--- /dev/null
+++ b/src/third_party/libwebp/libwebp.gypi
@@ -0,0 +1,168 @@
+{
+ 'variables': {
+ 'libwebp_dec_sources': [
+ 'src/dec/alpha_dec.c',
+ 'src/dec/buffer_dec.c',
+ 'src/dec/frame_dec.c',
+ 'src/dec/idec_dec.c',
+ 'src/dec/io_dec.c',
+ 'src/dec/quant_dec.c',
+ 'src/dec/tree_dec.c',
+ 'src/dec/vp8_dec.c',
+ 'src/dec/vp8l_dec.c',
+ 'src/dec/webp_dec.c',
+ ],
+ 'libwebp_demux_sources': [
+ 'src/demux/anim_decode.c',
+ 'src/demux/demux.c',
+ ],
+ 'libwebp_dsp_dec_common_sources': [
+ 'src/dsp/alpha_processing.c',
+ 'src/dsp/cpu.c',
+ 'src/dsp/dec.c',
+ 'src/dsp/dec_clip_tables.c',
+ 'src/dsp/filters.c',
+ 'src/dsp/lossless.c',
+ 'src/dsp/rescaler.c',
+ 'src/dsp/upsampling.c',
+ 'src/dsp/yuv.c',
+ ],
+ 'libwebp_dsp_dec_sse41_sources': [
+ 'src/dsp/alpha_processing_sse41.c',
+ 'src/dsp/dec_sse41.c',
+ 'src/dsp/upsampling_sse41.c',
+ 'src/dsp/yuv_sse41.c',
+ ],
+ 'libwebp_dsp_dec_sse2_sources': [
+ 'src/dsp/alpha_processing_sse2.c',
+ 'src/dsp/dec_sse2.c',
+ 'src/dsp/filters_sse2.c',
+ 'src/dsp/lossless_sse2.c',
+ 'src/dsp/rescaler_sse2.c',
+ 'src/dsp/upsampling_sse2.c',
+ 'src/dsp/yuv_sse2.c',
+ ],
+ 'libwebp_dsp_dec_neon_sources': [
+ 'src/dsp/alpha_processing_neon.c',
+ 'src/dsp/dec_neon.c',
+ 'src/dsp/filters_neon.c',
+ 'src/dsp/lossless_neon.c',
+ 'src/dsp/rescaler_neon.c',
+ 'src/dsp/upsampling_neon.c',
+ 'src/dsp/yuv_neon.c',
+ ],
+ 'libwebp_dsp_dec_mips_dsp_r2_sources': [
+ 'src/dsp/alpha_processing_mips_dsp_r2.c',
+ 'src/dsp/dec_mips_dsp_r2.c',
+ 'src/dsp/filters_mips_dsp_r2.c',
+ 'src/dsp/lossless_mips_dsp_r2.c',
+ 'src/dsp/rescaler_mips_dsp_r2.c',
+ 'src/dsp/upsampling_mips_dsp_r2.c',
+ 'src/dsp/yuv_mips_dsp_r2.c',
+ ],
+ 'libwebp_dsp_dec_mips32_sources': [
+ 'src/dsp/dec_mips32.c',
+ 'src/dsp/rescaler_mips32.c',
+ 'src/dsp/yuv_mips32.c',
+ ],
+ 'libwebp_dsp_dec_msa_sources': [
+ 'src/dsp/dec_msa.c',
+ 'src/dsp/filters_msa.c',
+ 'src/dsp/lossless_msa.c',
+ 'src/dsp/rescaler_msa.c',
+ 'src/dsp/upsampling_msa.c',
+ ],
+ 'libwebp_dsp_enc_sources' : [
+ 'src/dsp/cost.c',
+ 'src/dsp/cost_mips32.c',
+ 'src/dsp/cost_mips_dsp_r2.c',
+ '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',
+ 'src/dsp/enc_neon.c',
+ 'src/dsp/enc_sse2.c',
+ 'src/dsp/enc_sse41.c',
+ 'src/dsp/lossless_enc.c',
+ 'src/dsp/lossless_enc_mips32.c',
+ 'src/dsp/lossless_enc_mips_dsp_r2.c',
+ 'src/dsp/lossless_enc_msa.c',
+ 'src/dsp/lossless_enc_neon.c',
+ 'src/dsp/lossless_enc_sse2.c',
+ 'src/dsp/lossless_enc_sse41.c',
+ 'src/dsp/ssim.c',
+ 'src/dsp/ssim_sse2.c',
+ ],
+ 'libwebp_enc_sources': [
+ 'src/enc/alpha_enc.c',
+ 'src/enc/analysis_enc.c',
+ 'src/enc/backward_references_cost_enc.c',
+ 'src/enc/backward_references_enc.c',
+ 'src/enc/config_enc.c',
+ 'src/enc/cost_enc.c',
+ 'src/enc/filter_enc.c',
+ 'src/enc/frame_enc.c',
+ 'src/enc/histogram_enc.c',
+ 'src/enc/iterator_enc.c',
+ 'src/enc/near_lossless_enc.c',
+ 'src/enc/picture_enc.c',
+ 'src/enc/picture_csp_enc.c',
+ 'src/enc/picture_psnr_enc.c',
+ 'src/enc/picture_rescale_enc.c',
+ 'src/enc/picture_tools_enc.c',
+ 'src/enc/predictor_enc.c',
+ 'src/enc/quant_enc.c',
+ 'src/enc/syntax_enc.c',
+ 'src/enc/token_enc.c',
+ 'src/enc/tree_enc.c',
+ 'src/enc/vp8l_enc.c',
+ 'src/enc/webp_enc.c',
+ ],
+ 'libwebp_format_dec_sources': [
+ 'imageio/image_dec.c',
+ 'imageio/jpegdec.c',
+ 'imageio/metadata.c',
+ 'imageio/pngdec.c',
+ 'imageio/pnmdec.c',
+ 'imageio/tiffdec.c',
+ 'imageio/webpdec.c',
+ ],
+ 'libwebp_format_enc_sources': [
+ 'imageio/image_enc.c',
+ ],
+ 'libwebp_util_sources': [
+ 'examples/example_util.c',
+ ],
+ 'libwebp_image_util_sources': [
+ 'imageio/imageio_util.c',
+ ],
+ 'libwebp_mux_sources': [
+ 'src/mux/anim_encode.c',
+ 'src/mux/muxedit.c',
+ 'src/mux/muxinternal.c',
+ 'src/mux/muxread.c',
+ ],
+ 'libwebp_utils_dec_sources': [
+ 'src/utils/bit_reader_utils.c',
+ 'src/utils/color_cache_utils.c',
+ 'src/utils/filters_utils.c',
+ 'src/utils/huffman_utils.c',
+ 'src/utils/quant_levels_dec_utils.c',
+ 'src/utils/random_utils.c',
+ 'src/utils/rescaler_utils.c',
+ 'src/utils/thread_utils.c',
+ 'src/utils/utils.c',
+ ],
+ 'libwebp_utils_enc_sources': [
+ 'src/utils/bit_writer_utils.c',
+ 'src/utils/huffman_encode_utils.c',
+ 'src/utils/quant_levels_utils.c',
+ ],
+ 'libwebp_extra_sources': [
+ 'extras/extras.c',
+ 'extras/quality_estimate.c',
+ ],
+ },
+}
diff --git a/src/third_party/libwebp/m4/ax_pthread.m4 b/src/third_party/libwebp/m4/ax_pthread.m4
new file mode 100644
index 0000000..d383ad5
--- /dev/null
+++ b/src/third_party/libwebp/m4/ax_pthread.m4
@@ -0,0 +1,332 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+# This macro figures out how to build C programs using POSIX threads. It
+# sets the PTHREAD_LIBS output variable to the threads library and linker
+# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+# flags that are needed. (The user can also force certain compiler
+# flags/libs to be tested by setting these environment variables.)
+#
+# Also sets PTHREAD_CC to any special C compiler that is needed for
+# multi-threaded programs (defaults to the value of CC otherwise). (This
+# is necessary on AIX to use the special cc_r compiler alias.)
+#
+# NOTE: You are assumed to not only compile your program with these flags,
+# but also link it with them as well. e.g. you should link with
+# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+# If you are only building threads programs, you may wish to use these
+# variables in your default LIBS, CFLAGS, and CC:
+#
+# LIBS="$PTHREAD_LIBS $LIBS"
+# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+# CC="$PTHREAD_CC"
+#
+# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+# PTHREAD_CFLAGS.
+#
+# ACTION-IF-FOUND is a list of shell commands to run if a threads library
+# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+# is not found. If ACTION-IF-FOUND is not specified, the default action
+# will define HAVE_PTHREAD.
+#
+# Please let the authors know if this macro fails on any platform, or if
+# you have any other suggestions or comments. This macro was based on work
+# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+# Alejandro Forero Cuervo to the autoconf macro repository. We are also
+# grateful for the helpful feedback of numerous users.
+#
+# Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 21
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes])
+ AC_MSG_RESULT([$ax_pthread_ok])
+ if test x"$ax_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+ solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+ ;;
+
+ darwin*)
+ ax_pthread_flags="-pthread $ax_pthread_flags"
+ ;;
+esac
+
+# Clang doesn't consider unrecognized options an error unless we specify
+# -Werror. We throw in some extra Clang-specific options to ensure that
+# this doesn't happen for GCC, which also accepts -Werror.
+
+AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
+save_CFLAGS="$CFLAGS"
+ax_pthread_extra_flags="-Werror"
+CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
+ [AC_MSG_RESULT([yes])],
+ [ax_pthread_extra_flags=
+ AC_MSG_RESULT([no])])
+CFLAGS="$save_CFLAGS"
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+ if test x"$ax_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+ static void routine(void *a) { a = 0; }
+ static void *start_routine(void *a) { return a; }],
+ [pthread_t th; pthread_attr_t attr;
+ pthread_create(&th, 0, start_routine, 0);
+ pthread_join(th, 0);
+ pthread_attr_init(&attr);
+ pthread_cleanup_push(routine, 0);
+ pthread_cleanup_pop(0) /* ; */])],
+ [ax_pthread_ok=yes],
+ [])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT([$ax_pthread_ok])
+ if test "x$ax_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+ [int attr = $attr; return attr /* ; */])],
+ [attr_name=$attr; break],
+ [])
+ done
+ AC_MSG_RESULT([$attr_name])
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name],
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ flag=no
+ case ${host_os} in
+ aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+ osf* | hpux*) flag="-D_REENTRANT";;
+ solaris*)
+ if test "$GCC" = "yes"; then
+ flag="-D_REENTRANT"
+ else
+ # TODO: What about Clang on Solaris?
+ flag="-mt -D_REENTRANT"
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$flag])
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+ [ax_cv_PTHREAD_PRIO_INHERIT], [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+ [[int i = PTHREAD_PRIO_INHERIT;]])],
+ [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+ [ax_cv_PTHREAD_PRIO_INHERIT=no])
+ ])
+ AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+ [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: compile with *_r variant
+ if test "x$GCC" != xyes; then
+ case $host_os in
+ aix*)
+ AS_CASE(["x/$CC"],
+ [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+ [#handle absolute path differently from PATH based program lookup
+ AS_CASE(["x$CC"],
+ [x/*],
+ [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+ [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+ ;;
+ esac
+ fi
+fi
+
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST([PTHREAD_LIBS])
+AC_SUBST([PTHREAD_CFLAGS])
+AC_SUBST([PTHREAD_CC])
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+ ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
+ :
+else
+ ax_pthread_ok=no
+ $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
diff --git a/src/third_party/libwebp/makefile.unix b/src/third_party/libwebp/makefile.unix
new file mode 100644
index 0000000..cc0035d
--- /dev/null
+++ b/src/third_party/libwebp/makefile.unix
@@ -0,0 +1,502 @@
+# This makefile is a simpler alternative to the autoconf-based build
+# system, for simple local building of the libraries and tools.
+# It will not install the libraries system-wide, but just create the 'cwebp'
+# and 'dwebp' tools in the examples/ directory, along with the static
+# libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a',
+# 'src/demux/libwebpdemux.a' and 'extras/libwebpextras.a'.
+#
+# To build the library and examples, use:
+# make -f makefile.unix
+# from this top directory.
+
+#### Customizable part ####
+
+# These flags assume you have libpng, libjpeg, libtiff and libgif installed. If
+# not, either follow the install instructions below or just comment out the next
+# four lines.
+EXTRA_FLAGS= -DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG -DWEBP_HAVE_TIFF
+DWEBP_LIBS= -lpng -lz
+CWEBP_LIBS= $(DWEBP_LIBS) -ljpeg -ltiff
+GIF_LIBS = -lgif
+
+ifeq ($(strip $(shell uname)), Darwin)
+ # Work around a problem linking tables marked as common symbols,
+ # cf., src/enc/yuv.[hc]
+ # Failure observed with: gcc 4.2.1 and 4.0.1.
+ EXTRA_FLAGS += -fno-common
+ EXTRA_FLAGS += -DHAVE_GLUT_GLUT_H
+ EXTRA_FLAGS += -Wno-deprecated-declarations
+ EXTRA_FLAGS += -I/opt/local/include
+ EXTRA_LIBS += -L/opt/local/lib
+ GL_LIBS = -framework GLUT -framework OpenGL
+else
+ EXTRA_FLAGS += -I/usr/local/include
+ EXTRA_LIBS += -L/usr/local/lib
+ GL_LIBS = -lglut -lGL
+endif
+
+# SDL flags: use sdl-config if it exists
+SDL_CONFIG = $(shell sdl-config --version 2> /dev/null)
+ifneq ($(SDL_CONFIG),)
+ SDL_LIBS = $(shell sdl-config --libs)
+ SDL_FLAGS = $(shell sdl-config --cflags)
+else
+ # use best-guess
+ SDL_LIBS = -lSDL
+ SDL_FLAGS =
+endif
+
+# To install libraries on Mac OS X:
+# 1. Install MacPorts (http://www.macports.org/install.php)
+# 2. Run "sudo port install jpeg"
+# 3. Run "sudo port install libpng"
+# 4. Run "sudo port install tiff"
+# 5. Run "sudo port install giflib"
+
+# To install libraries on Linux:
+# 1. Run "sudo apt-get install libjpeg62-dev"
+# 2. Run "sudo apt-get install libpng12-dev"
+# 3. Run "sudo apt-get install libtiff4-dev"
+# 4. Run "sudo apt-get install libgif-dev"
+
+# Uncomment for build for 32bit platform
+# Alternatively, you can just use the command
+# 'make -f makefile.unix EXTRA_FLAGS=-m32' to that effect.
+# EXTRA_FLAGS += -m32
+
+# Extra flags to enable byte swap for 16 bit colorspaces.
+# EXTRA_FLAGS += -DWEBP_SWAP_16BIT_CSP=1
+
+# Extra flags to enable multi-threading
+EXTRA_FLAGS += -DWEBP_USE_THREAD
+EXTRA_LIBS += -lpthread
+
+# Control symbol visibility. Comment out if your compiler doesn't support it.
+EXTRA_FLAGS += -fvisibility=hidden
+
+# Extra flags to emulate C89 strictness with the full ANSI
+EXTRA_FLAGS += -Wextra -Wold-style-definition
+EXTRA_FLAGS += -Wmissing-prototypes
+EXTRA_FLAGS += -Wmissing-declarations
+EXTRA_FLAGS += -Wdeclaration-after-statement
+EXTRA_FLAGS += -Wshadow
+EXTRA_FLAGS += -Wformat-security -Wformat-nonliteral
+# EXTRA_FLAGS += -Wvla
+
+# SSE4.1-specific flags:
+ifeq ($(HAVE_SSE41), 1)
+EXTRA_FLAGS += -DWEBP_HAVE_SSE41
+src/dsp/%_sse41.o: EXTRA_FLAGS += -msse4.1
+endif
+
+# AVX2-specific flags:
+ifeq ($(HAVE_AVX2), 1)
+EXTRA_FLAGS += -DWEBP_HAVE_AVX2
+src/dsp/%_avx2.o: EXTRA_FLAGS += -mavx2
+endif
+
+# NEON-specific flags:
+# EXTRA_FLAGS += -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8
+# -> seems to make the overall lib slower: -fno-split-wide-types
+
+# MIPS (MSA) 32-bit build specific flags for mips32r5 (p5600):
+# EXTRA_FLAGS += -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64
+# EXTRA_FLAGS += -msched-weight -mload-store-pairs
+
+# MIPS (MSA) 64-bit build specific flags for mips64r6 (i6400):
+# EXTRA_FLAGS += -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64
+# EXTRA_FLAGS += -msched-weight -mload-store-pairs
+
+#### Nothing should normally be changed below this line ####
+
+AR = ar
+ARFLAGS = r
+CPPFLAGS = -I. -Isrc/ -Wall
+ifeq ($(DEBUG), 1)
+ CFLAGS = -g
+else
+ CFLAGS = -O3 -DNDEBUG
+endif
+CFLAGS += $(EXTRA_FLAGS)
+CC = gcc
+INSTALL = install
+GROFF = /usr/bin/groff
+COL = /usr/bin/col
+LDFLAGS = $(EXTRA_LIBS) $(EXTRA_FLAGS) -lm
+
+ANIM_UTIL_OBJS = \
+ examples/anim_util.o \
+
+DEC_OBJS = \
+ src/dec/alpha_dec.o \
+ src/dec/buffer_dec.o \
+ src/dec/frame_dec.o \
+ src/dec/idec_dec.o \
+ src/dec/io_dec.o \
+ src/dec/quant_dec.o \
+ src/dec/tree_dec.o \
+ src/dec/vp8_dec.o \
+ src/dec/vp8l_dec.o \
+ src/dec/webp_dec.o \
+
+DEMUX_OBJS = \
+ src/demux/anim_decode.o \
+ src/demux/demux.o \
+
+DSP_DEC_OBJS = \
+ src/dsp/alpha_processing.o \
+ src/dsp/alpha_processing_mips_dsp_r2.o \
+ src/dsp/alpha_processing_neon.o \
+ src/dsp/alpha_processing_sse2.o \
+ src/dsp/alpha_processing_sse41.o \
+ src/dsp/cpu.o \
+ src/dsp/dec.o \
+ src/dsp/dec_clip_tables.o \
+ src/dsp/dec_mips32.o \
+ src/dsp/dec_mips_dsp_r2.o \
+ src/dsp/dec_msa.o \
+ src/dsp/dec_neon.o \
+ src/dsp/dec_sse2.o \
+ src/dsp/dec_sse41.o \
+ src/dsp/filters.o \
+ src/dsp/filters_mips_dsp_r2.o \
+ src/dsp/filters_msa.o \
+ src/dsp/filters_neon.o \
+ src/dsp/filters_sse2.o \
+ src/dsp/lossless.o \
+ src/dsp/lossless_mips_dsp_r2.o \
+ src/dsp/lossless_msa.o \
+ src/dsp/lossless_neon.o \
+ src/dsp/lossless_sse2.o \
+ src/dsp/rescaler.o \
+ src/dsp/rescaler_mips32.o \
+ src/dsp/rescaler_mips_dsp_r2.o \
+ src/dsp/rescaler_msa.o \
+ src/dsp/rescaler_neon.o \
+ src/dsp/rescaler_sse2.o \
+ src/dsp/upsampling.o \
+ src/dsp/upsampling_mips_dsp_r2.o \
+ src/dsp/upsampling_msa.o \
+ src/dsp/upsampling_neon.o \
+ src/dsp/upsampling_sse2.o \
+ src/dsp/upsampling_sse41.o \
+ src/dsp/yuv.o \
+ src/dsp/yuv_mips32.o \
+ src/dsp/yuv_mips_dsp_r2.o \
+ src/dsp/yuv_neon.o \
+ src/dsp/yuv_sse2.o \
+ src/dsp/yuv_sse41.o \
+
+DSP_ENC_OBJS = \
+ src/dsp/cost.o \
+ src/dsp/cost_mips32.o \
+ src/dsp/cost_mips_dsp_r2.o \
+ src/dsp/cost_sse2.o \
+ src/dsp/enc.o \
+ src/dsp/enc_avx2.o \
+ src/dsp/enc_mips32.o \
+ src/dsp/enc_mips_dsp_r2.o \
+ src/dsp/enc_msa.o \
+ src/dsp/enc_neon.o \
+ src/dsp/enc_sse2.o \
+ src/dsp/enc_sse41.o \
+ src/dsp/lossless_enc.o \
+ src/dsp/lossless_enc_mips32.o \
+ src/dsp/lossless_enc_mips_dsp_r2.o \
+ src/dsp/lossless_enc_msa.o \
+ src/dsp/lossless_enc_neon.o \
+ src/dsp/lossless_enc_sse2.o \
+ src/dsp/lossless_enc_sse41.o \
+ src/dsp/ssim.o \
+ src/dsp/ssim_sse2.o \
+
+ENC_OBJS = \
+ src/enc/alpha_enc.o \
+ src/enc/analysis_enc.o \
+ src/enc/backward_references_cost_enc.o \
+ src/enc/backward_references_enc.o \
+ src/enc/config_enc.o \
+ src/enc/cost_enc.o \
+ src/enc/filter_enc.o \
+ src/enc/frame_enc.o \
+ src/enc/histogram_enc.o \
+ src/enc/iterator_enc.o \
+ src/enc/near_lossless_enc.o \
+ src/enc/picture_enc.o \
+ src/enc/picture_csp_enc.o \
+ src/enc/picture_psnr_enc.o \
+ src/enc/picture_rescale_enc.o \
+ src/enc/picture_tools_enc.o \
+ src/enc/predictor_enc.o \
+ src/enc/quant_enc.o \
+ src/enc/syntax_enc.o \
+ src/enc/token_enc.o \
+ src/enc/tree_enc.o \
+ src/enc/vp8l_enc.o \
+ src/enc/webp_enc.o \
+
+EX_FORMAT_DEC_OBJS = \
+ imageio/image_dec.o \
+ imageio/jpegdec.o \
+ imageio/metadata.o \
+ imageio/pngdec.o \
+ imageio/pnmdec.o \
+ imageio/tiffdec.o \
+ imageio/webpdec.o \
+
+EX_FORMAT_ENC_OBJS = \
+ imageio/image_enc.o \
+
+EX_UTIL_OBJS = \
+ examples/example_util.o \
+
+GIFDEC_OBJS = \
+ examples/gifdec.o \
+
+IMAGE_UTIL_OBJS = \
+ imageio/imageio_util.o \
+
+MUX_OBJS = \
+ src/mux/anim_encode.o \
+ src/mux/muxedit.o \
+ src/mux/muxinternal.o \
+ src/mux/muxread.o \
+
+UTILS_DEC_OBJS = \
+ src/utils/bit_reader_utils.o \
+ src/utils/color_cache_utils.o \
+ src/utils/filters_utils.o \
+ src/utils/huffman_utils.o \
+ src/utils/quant_levels_dec_utils.o \
+ src/utils/random_utils.o \
+ src/utils/rescaler_utils.o \
+ src/utils/thread_utils.o \
+ src/utils/utils.o \
+
+UTILS_ENC_OBJS = \
+ src/utils/bit_writer_utils.o \
+ src/utils/huffman_encode_utils.o \
+ src/utils/quant_levels_utils.o \
+
+EXTRA_OBJS = \
+ extras/extras.o \
+ extras/quality_estimate.o \
+
+LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
+LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) $(DSP_ENC_OBJS) \
+ $(UTILS_ENC_OBJS)
+LIBWEBPMUX_OBJS = $(MUX_OBJS)
+LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS)
+LIBWEBPEXTRA_OBJS = $(EXTRA_OBJS)
+
+HDRS_INSTALLED = \
+ src/webp/decode.h \
+ src/webp/demux.h \
+ src/webp/encode.h \
+ src/webp/mux.h \
+ src/webp/mux_types.h \
+ src/webp/types.h \
+
+HDRS = \
+ src/dec/alphai_dec.h \
+ src/dec/common_dec.h \
+ src/dec/vp8_dec.h \
+ src/dec/vp8i_dec.h \
+ src/dec/vp8li_dec.h \
+ src/dec/webpi_dec.h \
+ src/dsp/common_sse2.h \
+ src/dsp/dsp.h \
+ src/dsp/lossless.h \
+ src/dsp/lossless_common.h \
+ src/dsp/mips_macro.h \
+ src/dsp/msa_macro.h \
+ src/dsp/neon.h \
+ src/dsp/yuv.h \
+ src/enc/backward_references_enc.h \
+ src/enc/cost_enc.h \
+ src/enc/histogram_enc.h \
+ src/enc/vp8i_enc.h \
+ src/enc/vp8li_enc.h \
+ src/mux/animi.h \
+ src/mux/muxi.h \
+ src/utils/bit_reader_utils.h \
+ src/utils/bit_reader_inl_utils.h \
+ src/utils/bit_writer_utils.h \
+ src/utils/color_cache_utils.h \
+ src/utils/endian_inl_utils.h \
+ src/utils/filters_utils.h \
+ src/utils/huffman_utils.h \
+ src/utils/huffman_encode_utils.h \
+ src/utils/quant_levels_utils.h \
+ src/utils/quant_levels_dec_utils.h \
+ src/utils/random_utils.h \
+ src/utils/rescaler_utils.h \
+ src/utils/thread_utils.h \
+ src/utils/utils.h \
+ src/webp/format_constants.h \
+ $(HDRS_INSTALLED) \
+
+OUT_LIBS = examples/libexample_util.a
+OUT_LIBS += imageio/libimageio_util.a
+OUT_LIBS += imageio/libimagedec.a
+OUT_LIBS += imageio/libimageenc.a
+OUT_LIBS += src/libwebpdecoder.a
+OUT_LIBS += src/libwebp.a
+EXTRA_LIB = extras/libwebpextras.a
+OUT_EXAMPLES = examples/cwebp examples/dwebp
+EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \
+ examples/anim_diff examples/anim_dump \
+ examples/img2webp examples/webpinfo
+OTHER_EXAMPLES = extras/get_disto extras/webp_quality extras/vwebp_sdl
+
+OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES)
+ifeq ($(MAKECMDGOALS),clean)
+ OUTPUT += $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES)
+ OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a $(EXTRA_LIB)
+ OUTPUT += examples/libgifdec.a examples/libanim_util.a
+endif
+
+ex: $(OUT_EXAMPLES)
+all: ex $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES)
+extras: $(EXTRA_LIB)
+
+$(EX_FORMAT_DEC_OBJS): %.o: %.h
+
+# special dependencies:
+# tree_dec.c/vp8_dec.c/bit_reader_utils.c <->
+# bit_reader_inl_utils.h, endian_inl_utils.h
+# bit_writer_utils.c <-> endian_inl_utils.h
+src/dec/tree_dec.o: src/utils/bit_reader_inl_utils.h
+src/dec/tree_dec.o: src/utils/endian_inl_utils.h
+src/dec/vp8_dec.o: src/utils/bit_reader_inl_utils.h src/utils/endian_inl_utils.h
+src/utils/bit_reader_utils.o: src/utils/bit_reader_inl_utils.h
+src/utils/bit_reader_utils.o: src/utils/endian_inl_utils.h
+src/utils/bit_writer_utils.o: src/utils/endian_inl_utils.h
+
+%.o: %.c $(HDRS)
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+
+examples/libanim_util.a: $(ANIM_UTIL_OBJS)
+examples/libexample_util.a: $(EX_UTIL_OBJS)
+examples/libgifdec.a: $(GIFDEC_OBJS)
+extras/libwebpextras.a: $(LIBWEBPEXTRA_OBJS)
+imageio/libimagedec.a: $(EX_FORMAT_DEC_OBJS)
+imageio/libimageenc.a: $(EX_FORMAT_ENC_OBJS)
+imageio/libimageio_util.a: $(IMAGE_UTIL_OBJS)
+src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS)
+src/libwebp.a: $(LIBWEBP_OBJS)
+src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS)
+src/demux/libwebpdemux.a: $(LIBWEBPDEMUX_OBJS)
+
+%.a:
+ $(AR) $(ARFLAGS) $@ $^
+
+examples/anim_diff: examples/anim_diff.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS)
+examples/anim_dump: examples/anim_dump.o $(ANIM_UTIL_OBJS)
+examples/cwebp: examples/cwebp.o
+examples/dwebp: examples/dwebp.o
+examples/gif2webp: examples/gif2webp.o $(GIFDEC_OBJS)
+examples/vwebp: examples/vwebp.o
+examples/webpmux: examples/webpmux.o
+examples/img2webp: examples/img2webp.o
+examples/webpinfo: examples/webpinfo.o
+
+examples/anim_diff: examples/libanim_util.a examples/libgifdec.a
+examples/anim_diff: src/demux/libwebpdemux.a examples/libexample_util.a
+examples/anim_diff: imageio/libimageio_util.a src/libwebp.a
+examples/anim_diff: EXTRA_LIBS += $(GIF_LIBS)
+examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF
+examples/anim_dump: examples/libanim_util.a
+examples/anim_dump: src/demux/libwebpdemux.a
+examples/anim_dump: examples/libexample_util.a
+examples/anim_dump: imageio/libimageio_util.a
+examples/anim_dump: imageio/libimageenc.a
+examples/anim_dump: src/libwebp.a
+examples/anim_dump: EXTRA_LIBS += $(GIF_LIBS) $(DWEBP_LIBS)
+examples/cwebp: examples/libexample_util.a
+examples/cwebp: imageio/libimagedec.a
+examples/cwebp: src/demux/libwebpdemux.a
+examples/cwebp: imageio/libimageio_util.a
+examples/cwebp: src/libwebp.a
+examples/cwebp: EXTRA_LIBS += $(CWEBP_LIBS)
+examples/dwebp: examples/libexample_util.a
+examples/dwebp: imageio/libimagedec.a
+examples/dwebp: src/demux/libwebpdemux.a
+examples/dwebp: imageio/libimageenc.a
+examples/dwebp: imageio/libimageio_util.a
+examples/dwebp: src/libwebp.a
+examples/dwebp: EXTRA_LIBS += $(DWEBP_LIBS)
+examples/gif2webp: examples/libexample_util.a imageio/libimageio_util.a
+examples/gif2webp: examples/libgifdec.a src/mux/libwebpmux.a src/libwebp.a
+examples/gif2webp: EXTRA_LIBS += $(GIF_LIBS)
+examples/gif2webp: EXTRA_FLAGS += -DWEBP_HAVE_GIF
+examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a
+examples/vwebp: imageio/libimageio_util.a src/libwebp.a
+examples/vwebp: EXTRA_LIBS += $(GL_LIBS)
+examples/vwebp: EXTRA_FLAGS += -DWEBP_HAVE_GL
+examples/webpmux: examples/libexample_util.a imageio/libimageio_util.a
+examples/webpmux: src/mux/libwebpmux.a src/libwebpdecoder.a
+examples/img2webp: examples/libexample_util.a imageio/libimageio_util.a
+examples/img2webp: imageio/libimagedec.a
+examples/img2webp: src/demux/libwebpdemux.a
+examples/img2webp: src/mux/libwebpmux.a src/libwebp.a
+examples/img2webp: EXTRA_LIBS += $(CWEBP_LIBS)
+examples/webpinfo: examples/libexample_util.a imageio/libimageio_util.a
+examples/webpinfo: src/libwebpdecoder.a
+
+extras/get_disto: extras/get_disto.o
+extras/get_disto: imageio/libimagedec.a
+extras/get_disto: src/demux/libwebpdemux.a
+extras/get_disto: imageio/libimageio_util.a
+extras/get_disto: src/libwebp.a
+extras/get_disto: EXTRA_LIBS += $(CWEBP_LIBS)
+
+extras/webp_quality: extras/webp_quality.o
+extras/webp_quality: imageio/libimageio_util.a
+extras/webp_quality: $(EXTRA_LIB) src/libwebp.a
+
+extras/vwebp_sdl: extras/vwebp_sdl.o
+extras/vwebp_sdl: extras/webp_to_sdl.o
+extras/vwebp_sdl: imageio/libimageio_util.a
+extras/vwebp_sdl: src/libwebp.a
+extras/vwebp_sdl: EXTRA_FLAGS += -DWEBP_HAVE_SDL $(SDL_FLAGS)
+extras/vwebp_sdl: EXTRA_LIBS += $(SDL_LIBS)
+
+$(OUT_EXAMPLES) $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES):
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+dist: DESTDIR := dist
+dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES)
+dist: all
+ $(INSTALL) -m755 -d $(DESTDIR)/include/webp \
+ $(DESTDIR)/bin $(DESTDIR)/doc $(DESTDIR)/lib
+ $(INSTALL) -m755 -s $(OUT_EXAMPLES) $(DESTDIR)/bin
+ $(INSTALL) -m644 $(HDRS_INSTALLED) $(DESTDIR)/include/webp
+ $(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib
+ $(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib
+ $(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib
+ umask 022; \
+ for m in man/[cdv]webp.1 man/gif2webp.1 man/webpmux.1 \
+ man/img2webp.1 man/webpinfo.1; do \
+ basenam=$$(basename $$m .1); \
+ $(GROFF) -t -e -man -T ascii $$m \
+ | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \
+ $(GROFF) -t -e -man -T html $$m \
+ | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.html; \
+ done
+
+clean:
+ $(RM) $(OUTPUT) *~ \
+ examples/*.o examples/*~ \
+ extras/*.o extras/*~ \
+ imageio/*.o imageio/*~ \
+ src/dec/*.o src/dec/*~ \
+ src/demux/*.o src/demux/*~ \
+ src/dsp/*.o src/dsp/*~ \
+ src/enc/*.o src/enc/*~ \
+ src/mux/*.o src/mux/*~ \
+ src/utils/*.o src/utils/*~ \
+ src/webp/*~ man/*~ doc/*~ swig/*~ \
+
+.PHONY: all clean dist ex
+.SUFFIXES:
diff --git a/src/third_party/libwebp/man/Makefile.am b/src/third_party/libwebp/man/Makefile.am
new file mode 100644
index 0000000..a7ef77f
--- /dev/null
+++ b/src/third_party/libwebp/man/Makefile.am
@@ -0,0 +1,14 @@
+man_MANS = cwebp.1 dwebp.1
+if WANT_MUX
+ man_MANS += webpmux.1
+endif
+if BUILD_GIF2WEBP
+ man_MANS += gif2webp.1
+endif
+if BUILD_VWEBP
+ man_MANS += vwebp.1
+endif
+if BUILD_WEBPINFO
+ man_MANS += webpinfo.1
+endif
+EXTRA_DIST = $(man_MANS)
diff --git a/src/third_party/libwebp/man/cwebp.1 b/src/third_party/libwebp/man/cwebp.1
new file mode 100644
index 0000000..c943324
--- /dev/null
+++ b/src/third_party/libwebp/man/cwebp.1
@@ -0,0 +1,316 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH CWEBP 1 "January 20, 2017"
+.SH NAME
+cwebp \- compress an image file to a WebP file
+.SH SYNOPSIS
+.B cwebp
+.RI [ options ] " input_file \-o output_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B cwebp
+command.
+.PP
+\fBcwebp\fP compresses an image using the WebP format.
+Input format can be either PNG, JPEG, TIFF, WebP or raw Y'CbCr samples.
+.SH OPTIONS
+The basic options are:
+.TP
+.BI \-o " string
+Specify the name of the output WebP file. If omitted, \fBcwebp\fP will
+perform compression but only report statistics.
+Using "\-" as output name will direct output to 'stdout'.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with a '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored.
+.TP
+.B \-h, \-help
+A short usage summary.
+.TP
+.B \-H, \-longhelp
+A summary of all the possible options.
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.B \-lossless
+Encode the image without any loss. For images with fully transparent area,
+the invisible pixel values (R/G/B or Y/U/V) will be preserved only if the
+\-exact option is used.
+.TP
+.BI \-near_lossless " int
+Use near\-lossless image preprocessing. This option adjusts pixel values
+to help compressibility, but has minimal impact on the visual quality.
+It triggers lossless compression mode automatically.
+Range is 0 (maximum preprocessing) to 100 (no preprocessing, the default).
+.TP
+.BI \-q " float
+Specify the compression factor for RGB channels between 0 and 100. The default
+is 75.
+.br
+In case of lossy compression (default), a small factor produces a smaller file
+with lower quality. Best quality is achieved by using a value of 100.
+.br
+In case of lossless compression (specified by the \fB\-lossless\fP option), a
+small factor enables faster compression speed, but produces a larger file.
+Maximum compression is achieved by using a value of 100.
+.TP
+.BI \-z " int
+Switch on \fBlossless\fP compression mode with the specified level between 0
+and 9, with level 0 being the fastest, 9 being the slowest. Fast mode
+produces larger file size than slower ones. A good default is \fB\-z 6\fP.
+This option is actually a shortcut for some predefined settings for quality
+and method. If options \fB\-q\fP or \fB\-m\fP are subsequently used, they will
+invalidate the effect of this option.
+.TP
+.BI \-alpha_q " int
+Specify the compression factor for alpha compression between 0 and 100.
+Lossless compression of alpha is achieved using a value of 100, while the lower
+values result in a lossy compression. The default is 100.
+.TP
+.BI \-preset " string
+Specify a set of pre\-defined parameters to suit a particular type of
+source material. Possible values are: \fBdefault\fP, \fBphoto\fP,
+\fBpicture\fP, \fBdrawing\fP, \fBicon\fP, \fBtext\fP. Since
+\fB\-preset\fP overwrites the other parameters' values (except the
+\fB\-q\fP one), this option should preferably appear first in the
+order of the arguments.
+.TP
+.BI \-m " int
+Specify the compression method to use. This parameter controls the
+trade off between encoding speed and the compressed file size and quality.
+Possible values range from 0 to 6. Default value is 4.
+When higher values are used, the encoder will spend more time inspecting
+additional encoding possibilities and decide on the quality gain.
+Lower value can result in faster processing time at the expense of
+larger file size and lower compression quality.
+.TP
+.BI \-resize " width height
+Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP.
+If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
+the value will be calculated preserving the aspect\-ratio.
+.TP
+.BI \-crop " x_position y_position width height
+Crop the source to a rectangle with top\-left corner at coordinates
+(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
+This cropping area must be fully contained within the source rectangle.
+.TP
+.B \-mt
+Use multi\-threading for encoding, if possible.
+.TP
+.B \-low_memory
+Reduce memory usage of lossy encoding by saving four times the compressed
+size (typically). This will make the encoding slower and the output slightly
+different in size and distortion. This flag is only effective for methods
+3 and up, and is off by default. Note that leaving this flag off will have
+some side effects on the bitstream: it forces certain bitstream features
+like number of partitions (forced to 1). Note that a more detailed report
+of bitstream size is printed by \fBcwebp\fP when using this option.
+
+.SS LOSSY OPTIONS
+These options are only effective when doing lossy encoding (the default, with
+or without alpha).
+
+.TP
+.BI \-size " int
+Specify a target size (in bytes) to try and reach for the compressed output.
+The compressor will make several passes of partial encoding in order to get as
+close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
+are used, \fB\-size\fP value will prevail.
+.TP
+.BI \-psnr " float
+Specify a target PSNR (in dB) to try and reach for the compressed output.
+The compressor will make several passes of partial encoding in order to get as
+close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
+are used, \fB\-size\fP value will prevail.
+.TP
+.BI \-pass " int
+Set a maximum number of passes to use during the dichotomy used by
+options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10, default is 1.
+If options \fB\-size\fP or \fB\-psnr\fP were used, but \fB\-pass\fP wasn't
+specified, a default value of '6' passes will be used.
+.TP
+.B \-af
+Turns auto\-filter on. This algorithm will spend additional time optimizing
+the filtering strength to reach a well\-balanced quality.
+.TP
+.B \-jpeg_like
+Change the internal parameter mapping to better match the expected size
+of JPEG compression. This flag will generally produce an output file of
+similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
+with less visual distortion.
+
+.TP
+Advanced options:
+
+.TP
+.BI \-f " int
+Specify the strength of the deblocking filter, between 0 (no filtering)
+and 100 (maximum filtering). A value of 0 will turn off any filtering.
+Higher value will increase the strength of the filtering process applied
+after decoding the picture. The higher the value the smoother the picture will
+appear. Typical values are usually in the range of 20 to 50.
+.TP
+.BI \-sharpness " int
+Specify the sharpness of the filtering (if used).
+Range is 0 (sharpest) to 7 (least sharp). Default is 0.
+.TP
+.B \-strong
+Use strong filtering (if filtering is being used thanks to the
+\fB\-f\fP option). Strong filtering is on by default.
+.TP
+.B \-nostrong
+Disable strong filtering (if filtering is being used thanks to the
+\fB\-f\fP option) and use simple filtering instead.
+.TP
+.B \-sharp_yuv
+Use more accurate and sharper RGB->YUV conversion if needed. Note that this
+process is slower than the default 'fast' RGB->YUV conversion.
+.TP
+.BI \-sns " int
+Specify the amplitude of the spatial noise shaping. Spatial noise shaping
+(or \fBsns\fP for short) refers to a general collection of built\-in algorithms
+used to decide which area of the picture should use relatively less bits,
+and where else to better transfer these bits. The possible range goes from
+0 (algorithm is off) to 100 (the maximal effect). The default value is 50.
+.TP
+.BI \-segments " int
+Change the number of partitions to use during the segmentation of the
+sns algorithm. Segments should be in range 1 to 4. Default value is 4.
+This option has no effect for methods 3 and up, unless \fB\-low_memory\fP
+is used.
+.TP
+.BI \-partition_limit " int
+Degrade quality by limiting the number of bits used by some macroblocks.
+Range is 0 (no degradation, the default) to 100 (full degradation).
+Useful values are usually around 30\-70 for moderately large images.
+In the VP8 format, the so\-called control partition has a limit of 512k and
+is used to store the following information: whether the macroblock is skipped,
+which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16
+mode, and finally the prediction modes to use for each of the sub\-blocks.
+For a very large image, 512k only leaves room to few bits per 16x16 macroblock.
+The absolute minimum is 4 bits per macroblock. Skip, segment, and mode
+information can use up almost all these 4 bits (although the case is unlikely),
+which is problematic for very large images. The partition_limit factor controls
+how frequently the most bit\-costly mode (intra 4x4) will be used. This is
+useful in case the 512k limit is reached and the following message is displayed:
+\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP.
+If using \fB\-partition_limit\fP is not enough to meet the 512k constraint, one
+should use less segments in order to save more header bits per macroblock.
+See the \fB\-segments\fP option.
+
+.SS LOGGING OPTIONS
+These options control the level of output:
+.TP
+.B \-v
+Print extra information (encoding time in particular).
+.TP
+.B \-print_psnr
+Compute and report average PSNR (Peak\-Signal\-To\-Noise ratio).
+.TP
+.B \-print_ssim
+Compute and report average SSIM (structural similarity
+metric, see http://en.wikipedia.org/wiki/SSIM for additional details).
+.TP
+.B \-print_lsim
+Compute and report local similarity metric (sum of lowest error amongst the
+collocated pixel neighbors).
+.TP
+.B \-progress
+Report encoding progress in percent.
+.TP
+.B \-quiet
+Do not print anything.
+.TP
+.B \-short
+Only print brief information (output file size and PSNR) for testing purposes.
+.TP
+.BI \-map " int
+Output additional ASCII\-map of encoding information. Possible map values
+range from 1 to 6. This is only meant to help debugging.
+
+.SS ADDITIONAL OPTIONS
+More advanced options are:
+.TP
+.BI \-s " width height
+Specify that the input file actually consists of raw Y'CbCr samples following
+the ITU\-R BT.601 recommendation, in 4:2:0 linear format.
+The luma plane has size \fBwidth\fP x \fBheight\fP.
+.TP
+.BI \-pre " int
+Specify some preprocessing steps. Using a value of '2' will trigger
+quality\-dependent pseudo\-random dithering during RGBA\->YUVA conversion
+(lossy compression only).
+.TP
+.BI \-alpha_filter " string
+Specify the predictive filtering method for the alpha plane. One of 'none',
+\&'fast' or 'best', in increasing complexity and slowness order. Default is
+\&'fast'. Internally, alpha filtering is performed using four possible
+predictions (none, horizontal, vertical, gradient). The 'best' mode will try
+each mode in turn and pick the one which gives the smaller size. The 'fast'
+mode will just try to form an a priori guess without testing all modes.
+.TP
+.BI \-alpha_method " int
+Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
+no compression, 1 uses WebP lossless format for compression. The default is 1.
+.TP
+.B \-exact
+Preserve RGB values in transparent area. The default is off, to help
+compressibility.
+.TP
+.BI \-blend_alpha " int
+This option blends the alpha channel (if present) with the source using the
+background color specified in hexadecimal as 0xrrggbb. The alpha channel is
+afterward reset to the opaque value 255.
+.TP
+.B \-noalpha
+Using this option will discard the alpha channel.
+.TP
+.BI \-hint " string
+Specify the hint about input image type. Possible values are:
+\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
+.TP
+.BI \-metadata " string
+A comma separated list of metadata to copy from the input to the output if
+present.
+Valid values: \fBall\fP, \fBnone\fP, \fBexif\fP, \fBicc\fP, \fBxmp\fP.
+The default is \fBnone\fP.
+
+Note: each input format may not support all combinations.
+.TP
+.B \-noasm
+Disable all assembly optimizations.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+http://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
+.br
+cwebp \-q 70 picture_with_alpha.png \-o picture_with_alpha.webp
+.br
+cwebp \-sns 70 \-f 50 \-size 60000 picture.png \-o picture.webp
+.br
+cwebp \-o picture.webp \-\- \-\-\-picture.png
+
+.SH AUTHORS
+\fBcwebp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR dwebp (1),
+.BR gif2webp (1)
+.br
+Please refer to http://developers.google.com/speed/webp/ for additional
+information.
diff --git a/src/third_party/libwebp/man/dwebp.1 b/src/third_party/libwebp/man/dwebp.1
new file mode 100644
index 0000000..d4b60ed
--- /dev/null
+++ b/src/third_party/libwebp/man/dwebp.1
@@ -0,0 +1,149 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH DWEBP 1 "June 23, 2016"
+.SH NAME
+dwebp \- decompress a WebP file to an image file
+.SH SYNOPSIS
+.B dwebp
+.RI [ options ] " input_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B dwebp
+command.
+.PP
+\fBdwebp\fP decompresses WebP files into PNG, PAM, PPM or PGM images.
+.SH OPTIONS
+The basic options are:
+.TP
+.B \-h
+Print usage summary.
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.BI \-o " string
+Specify the name of the output file (as PNG format by default).
+Using "-" as output name will direct output to 'stdout'.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with an '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored. If the input file is "\-",
+the data will be read from \fIstdin\fP instead of a file.
+.TP
+.B \-bmp
+Change the output format to uncompressed BMP.
+.TP
+.B \-tiff
+Change the output format to uncompressed TIFF.
+.TP
+.B \-pam
+Change the output format to PAM (retains alpha).
+.TP
+.B \-ppm
+Change the output format to PPM (discards alpha).
+.TP
+.B \-pgm
+Change the output format to PGM. The output consists of luma/chroma
+samples instead of RGB, using the IMC4 layout. This option is mainly
+for verification and debugging purposes.
+.TP
+.B \-yuv
+Change the output format to raw YUV. The output consists of
+luma/chroma-U/chroma-V samples instead of RGB, saved sequentially as
+individual planes. This option is mainly for verification and debugging
+purposes.
+.TP
+.B \-nofancy
+Don't use the fancy upscaler for YUV420. This may lead to jaggy
+edges (especially the red ones), but should be faster.
+.TP
+.B \-nofilter
+Don't use the in-loop filtering process even if it is required by
+the bitstream. This may produce visible blocks on the non-compliant output,
+but it will make the decoding faster.
+.TP
+.BI \-dither " strength
+Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
+post-processing effect applied to chroma components in lossy compression.
+It helps by smoothing gradients and avoiding banding artifacts.
+.TP
+.BI \-alpha_dither
+If the compressed file contains a transparency plane that was quantized
+during compression, this flag will allow dithering the reconstructed plane
+in order to generate smoother transparency gradients.
+.TP
+.B \-nodither
+Disable all dithering (default).
+.TP
+.B \-mt
+Use multi-threading for decoding, if possible.
+.TP
+.BI \-crop " x_position y_position width height
+Crop the decoded picture to a rectangle with top-left corner at coordinates
+(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
+This cropping area must be fully contained within the source rectangle.
+The top-left corner will be snapped to even coordinates if needed.
+This option is meant to reduce the memory needed for cropping large images.
+Note: the cropping is applied \fIbefore\fP any scaling.
+.TP
+.B \-flip
+Flip decoded image vertically (can be useful for OpenGL textures for instance).
+.TP
+\fB\-resize\fR, \fB\-scale\fI width height\fR
+Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This
+option is mostly intended to reducing the memory needed to decode large images,
+when only a small version is needed (thumbnail, preview, etc.). Note: scaling
+is applied \fIafter\fP cropping.
+If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
+the value will be calculated preserving the aspect-ratio.
+.TP
+.B \-quiet
+Do not print anything.
+.TP
+.B \-v
+Print extra information (decoding time in particular).
+.TP
+.B \-noasm
+Disable all assembly optimizations.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+http://www.webmproject.org/code/contribute/submitting-patches/
+
+.SH EXAMPLES
+dwebp picture.webp \-o output.png
+.br
+dwebp picture.webp \-ppm \-o output.ppm
+.br
+dwebp \-o output.ppm \-\- \-\-\-picture.webp
+.br
+cat picture.webp | dwebp \-o \- \-\- \- > output.ppm
+
+.SH AUTHORS
+\fBdwebp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR cwebp (1),
+.BR gif2webp (1),
+.BR webpmux (1)
+.br
+Please refer to http://developers.google.com/speed/webp/ for additional
+information.
+.SS Output file format details
+PAM: http://netpbm.sourceforge.net/doc/pam.html
+.br
+PGM: http://netpbm.sourceforge.net/doc/pgm.html
+.br
+PPM: http://netpbm.sourceforge.net/doc/ppm.html
+.br
+PNG: http://www.libpng.org/pub/png/png-sitemap.html#info
diff --git a/src/third_party/libwebp/man/gif2webp.1 b/src/third_party/libwebp/man/gif2webp.1
new file mode 100644
index 0000000..a94f2ee
--- /dev/null
+++ b/src/third_party/libwebp/man/gif2webp.1
@@ -0,0 +1,164 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH GIF2WEBP 1 "January 25, 2018"
+.SH NAME
+gif2webp \- Convert a GIF image to WebP
+.SH SYNOPSIS
+.B gif2webp
+.RI [ options ] " input_file.gif \-o output_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B gif2webp
+command.
+.PP
+\fBgif2webp\fP converts a GIF image to a WebP image.
+.SH OPTIONS
+The basic options are:
+.TP
+.BI \-o " string
+Specify the name of the output WebP file. If omitted, \fBgif2webp\fP will
+perform conversion but only report statistics.
+Using "\-" as output name will direct output to 'stdout'.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with an '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored. If the input file is "\-",
+the data will be read from \fIstdin\fP instead of a file.
+.TP
+.B \-h, \-help
+Usage information.
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.B \-lossy
+Encode the image using lossy compression.
+.TP
+.B \-mixed
+Mixed compression mode: optimize compression of the image by picking either
+lossy or lossless compression for each frame heuristically.
+.TP
+.BI \-q " float
+Specify the compression factor for RGB channels between 0 and 100. The default
+is 75.
+.br
+In case of lossless compression (default), a small factor enables faster
+compression speed, but produces a larger file. Maximum compression is achieved
+by using a value of 100.
+.br
+In case of lossy compression (specified by the \-lossy option), a small factor
+produces a smaller file with lower quality. Best quality is achieved by using a
+value of 100.
+.TP
+.BI \-m " int
+Specify the compression method to use. This parameter controls the
+trade off between encoding speed and the compressed file size and quality.
+Possible values range from 0 to 6. Default value is 4.
+When higher values are used, the encoder will spend more time inspecting
+additional encoding possibilities and decide on the quality gain.
+Lower value can result is faster processing time at the expense of
+larger file size and lower compression quality.
+.TP
+.BI \-min_size
+Encode image to achieve smallest size. This disables key frame insertion and
+picks the dispose method resulting in smallest output for each frame. It uses
+lossless compression by default, but can be combined with \-q, \-m, \-lossy or
+\-mixed options.
+.TP
+.BI \-kmin " int
+.TP
+.BI \-kmax " int
+Specify the minimum and maximum distance between consecutive key frames
+(independently decodable frames) in the output animation. The tool will insert
+some key frames into the output animation as needed so that this criteria is
+satisfied.
+.br
+A 'kmax' value of 0 will turn off insertion of key frames. A 'kmax' value of 1
+will result in all frames being key frames. 'kmin' value is not taken into
+account in both these special cases.
+Typical values are in the range 3 to 30. Default values are kmin = 9,
+kmax = 17 for lossless compression and kmin = 3, kmax = 5 for lossy compression.
+.br
+These two options are relevant only for animated images with large number of
+frames (>50).
+.br
+When lower values are used, more frames will be converted to key frames. This
+may lead to smaller number of frames required to decode a frame on average,
+thereby improving the decoding performance. But this may lead to slightly bigger
+file sizes.
+Higher values may lead to worse decoding performance, but smaller file sizes.
+.br
+Some restrictions:
+.br
+(i) kmin < kmax,
+.br
+(ii) kmin >= kmax / 2 + 1 and
+.br
+(iii) kmax - kmin <= 30.
+.br
+If any of these restrictions are not met, they will be enforced automatically.
+.TP
+.BI \-metadata " string
+A comma separated list of metadata to copy from the input to the output if
+present.
+Valid values: \fBall\fP, \fBnone\fP, \fBicc\fP, \fBxmp\fP.
+The default is \fBxmp\fP.
+.TP
+.BI \-f " int
+For lossy encoding only (specified by the \-lossy option). Specify the strength
+of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).
+A value of 0 will turn off any filtering. Higher value will increase the
+strength of the filtering process applied after decoding the picture. The higher
+the value the smoother the picture will appear. Typical values are usually in
+the range of 20 to 50.
+.TP
+.B \-mt
+Use multi-threading for encoding, if possible.
+.TP
+.B \-loop_compatibility
+If enabled, handle the loop information in a compatible fashion for Chrome
+version prior to M62 (inclusive) and Firefox.
+.TP
+.B \-v
+Print extra information.
+.TP
+.B \-quiet
+Do not print anything.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+http://www.webmproject.org/code/contribute/submitting-patches/
+
+.SH EXAMPLES
+gif2webp picture.gif \-o picture.webp
+.br
+gif2webp \-q 70 picture.gif \-o picture.webp
+.br
+gif2webp \-lossy \-m 3 picture.gif \-o picture_lossy.webp
+.br
+gif2webp \-lossy \-f 50 picture.gif \-o picture.webp
+.br
+gif2webp \-q 70 \-o picture.webp \-\- \-\-\-picture.gif
+.br
+cat picture.gif | gif2webp \-o \- \-\- \- > output.webp
+
+.SH AUTHORS
+\fBgif2webp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Urvang Joshi <urvang@google.com>, for the
+Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR cwebp (1),
+.BR dwebp (1),
+.BR webpmux (1)
+.br
+Please refer to http://developers.google.com/speed/webp/ for additional
+information.
diff --git a/src/third_party/libwebp/man/img2webp.1 b/src/third_party/libwebp/man/img2webp.1
new file mode 100644
index 0000000..da1d91d
--- /dev/null
+++ b/src/third_party/libwebp/man/img2webp.1
@@ -0,0 +1,105 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH IMG2WEBP 1 "April 3, 2018"
+.SH NAME
+img2webp \- create animated WebP file from a sequence of input images.
+.SH SYNOPSIS
+.B img2webp
+[file_level_options] [files] [per_frame_options...]
+.br
+.B img2webp argument_file_name
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B img2webp
+command.
+.PP
+\fBimg2webp\fP compresses a sequence of images using the animated WebP format.
+Input images can either be PNG, JPEG, TIFF or WebP.
+If a single file name (not starting with the character '\-') is supplied as
+the argument, the command line argument are actually tokenized from this file.
+This allows for easy scripting or using large number of arguments.
+.SH FILE-LEVEL OPTIONS
+The file-level options are applied at the beginning of the compression process,
+before the input frames are read.
+.TP
+.BI \-o " string
+Specify the name of the output WebP file.
+.TP
+.BI \-min_size
+Encode images to achieve smallest size. This disables key frame insertion and
+picks the parameters resulting in smallest output for each frame. It uses
+lossless compression by default, but can be combined with \-q, \-m, \-lossy or
+\-mixed options.
+.TP
+.BI \-kmin " int
+.TP
+.BI \-kmax " int
+Specify the minimum and maximum distance between consecutive key frames
+(independently decodable frames) in the output animation. The tool will insert
+some key frames into the output animation as needed so that this criteria is
+satisfied.
+.br
+.B \-mixed
+Mixed compression mode: optimize compression of the image by picking either
+lossy or lossless compression for each frame heuristically. This global
+option disables the local option \fB-lossy\fP and \fB-lossless\fP .
+.TP
+.BI \-loop " int
+Specifies the number of times the animation should loop. Using '0'
+means 'loop indefinitely'.
+.TP
+.BI \-v
+Be more verbose.
+.TP
+.B \-h, \-help
+A short usage summary.
+.TP
+.B \-version
+Print the version numbers of the relevant libraries used.
+
+.SH PER-FRAME OPTIONS
+The per-frame options are applied for the images following as arguments in the
+command line. They can be modified any number of times preceding each particular
+input image.
+.TP
+.BI \-d " int
+Specify the image duration in milliseconds.
+.TP
+.B \-lossless, \-lossy
+Compress the next image(s) using lossless or lossy compression mode. The
+default mode is lossless.
+.TP
+.BI \-q " float
+Specify the compression factor between 0 and 100. The default is 75.
+.TP
+.BI \-m " int
+Specify the compression method to use. This parameter controls the
+trade off between encoding speed and the compressed file size and quality.
+Possible values range from 0 to 6. Default value is 4.
+
+.SH EXAMPLE
+img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp
+.br
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+http://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH AUTHORS
+\fBimg2webp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR webpmux (1),
+.BR gif2webp (1)
+.br
+Please refer to http://developers.google.com/speed/webp/ for additional
+information.
diff --git a/src/third_party/libwebp/man/vwebp.1 b/src/third_party/libwebp/man/vwebp.1
new file mode 100644
index 0000000..4ec346a
--- /dev/null
+++ b/src/third_party/libwebp/man/vwebp.1
@@ -0,0 +1,94 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH VWEBP 1 "November 25, 2016"
+.SH NAME
+vwebp \- decompress a WebP file and display it in a window
+.SH SYNOPSIS
+.B vwebp
+.RI [ options ] " input_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B vwebp
+command.
+.PP
+\fBvwebp\fP decompresses a WebP file and displays it in a window using OpenGL.
+.SH OPTIONS
+.TP
+.B \-h
+Print usage summary.
+.TP
+.B \-version
+Print version number and exit.
+.TP
+.B \-noicc
+Don't use the ICC profile if present.
+.TP
+.B \-nofancy
+Don't use the fancy YUV420 upscaler.
+.TP
+.B \-nofilter
+Disable in-loop filtering.
+.TP
+.BI \-dither " strength
+Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
+post-processing effect applied to chroma components in lossy compression.
+It helps by smoothing gradients and avoiding banding artifacts. Default: 50.
+.TP
+.BI \-noalphadither
+By default, quantized transparency planes are dithered during decompression,
+to smooth the gradients. This flag will prevent this dithering.
+.TP
+.B \-mt
+Use multi-threading for decoding, if possible.
+.TP
+.B \-info
+Display image information on top of the decoded image.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with an '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored. If the input file is "\-",
+the data will be read from \fIstdin\fP instead of a file.
+.TP
+
+.SH KEYBOARD SHORTCUTS
+.TP
+.B 'c'
+Toggle use of color profile.
+.TP
+.B 'i'
+Overlay file information.
+.TP
+.B 'd'
+Disable blending and disposal process, for debugging purposes.
+.TP
+.B 'q' / 'Q' / ESC
+Quit.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+http://www.webmproject.org/code/contribute/submitting-patches/
+
+.SH EXAMPLES
+vwebp picture.webp
+.br
+vwebp picture.webp -mt -dither 0
+.br
+vwebp \-\- \-\-\-picture.webp
+
+.SH AUTHORS
+\fBvwebp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR dwebp (1)
+.br
+Please refer to http://developers.google.com/speed/webp/ for additional
+information.
diff --git a/src/third_party/libwebp/man/webpinfo.1 b/src/third_party/libwebp/man/webpinfo.1
new file mode 100644
index 0000000..902ba9e
--- /dev/null
+++ b/src/third_party/libwebp/man/webpinfo.1
@@ -0,0 +1,80 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH WEBPINFO 1 "November 24, 2017"
+.SH NAME
+webpinfo \- print out the chunk level structure of WebP files
+along with basic integrity checks.
+.SH SYNOPSIS
+.B webpinfo
+.I OPTIONS
+.I INPUT
+.br
+.B webpinfo [\-h|\-help|\-H|\-longhelp]
+.br
+
+.SH DESCRIPTION
+This manual page documents the
+.B webpinfo
+command.
+.PP
+\fBwebpinfo\fP 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.
+
+.SH OPTIONS
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.B \-quiet
+Do not show chunk parsing information.
+.TP
+.B \-diag
+Show parsing error diagnosis.
+.TP
+.B \-summary
+Show chunk stats summary.
+.TP
+.BI \-bitstream_info
+Parse bitstream header.
+.TP
+.B \-h, \-help
+A short usage summary.
+.TP
+.B \-H, \-longhelp
+Detailed usage instructions.
+
+.SH INPUT
+Input files in WebP format. Input files must come last, following
+options (if any). There can be multiple input files.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+http://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+.br
+webpinfo \-h
+.br
+webpinfo \-diag \-summary input_file.webp
+.br
+webpinfo \-bitstream_info input_file_1.webp input_file_2.webp
+.br
+webpinfo *.webp
+
+.SH AUTHORS
+\fBwebpinfo\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Hui Su <huisu@google.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR webpmux (1)
+.br
+Please refer to http://developers.google.com/speed/webp/ for additional
+information.
diff --git a/src/third_party/libwebp/man/webpmux.1 b/src/third_party/libwebp/man/webpmux.1
new file mode 100644
index 0000000..eb41a61
--- /dev/null
+++ b/src/third_party/libwebp/man/webpmux.1
@@ -0,0 +1,260 @@
+.\" Hey, EMACS: -*- nroff -*-
+.TH WEBPMUX 1 "December 1, 2017"
+.SH NAME
+webpmux \- create animated WebP files from non\-animated WebP images, extract
+frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
+.SH SYNOPSIS
+.B webpmux \-get
+.I GET_OPTIONS
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-set
+.I SET_OPTIONS
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-strip
+.I STRIP_OPTIONS
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-frame
+.I FRAME_OPTIONS
+.B [ \-frame ... ] [ \-loop
+.I LOOP_COUNT
+.B ]
+.br
+.RS 8
+.B [ \-bgcolor
+.I BACKGROUND_COLOR
+.B ] \-o
+.I OUTPUT
+.RE
+.br
+.B webpmux \-duration
+.I DURATION OPTIONS
+.B [ \-duration ... ]
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-info
+.I INPUT
+.br
+.B webpmux [\-h|\-help]
+.br
+.B webpmux \-version
+.br
+.B webpmux argument_file_name
+.SH DESCRIPTION
+This manual page documents the
+.B webpmux
+command.
+.PP
+\fBwebpmux\fP can be used to create/extract from animated WebP files, as well as
+to add/extract/strip XMP/EXIF metadata and ICC profile.
+If a single file name (not starting with the character '\-') is supplied as
+the argument, the command line argument are actually tokenized from this file.
+This allows for easy scripting or using large number of arguments.
+.SH OPTIONS
+.SS GET_OPTIONS (\-get):
+.TP
+.B icc
+Get ICC profile.
+.TP
+.B exif
+Get EXIF metadata.
+.TP
+.B xmp
+Get XMP metadata.
+.TP
+.BI frame " n
+Get nth frame from an animated image. (n = 0 has a special meaning: last frame).
+
+.SS SET_OPTIONS (\-set)
+.TP
+.BI icc " file.icc
+Set ICC profile.
+.P
+Where: 'file.icc' contains the ICC profile to be set.
+.TP
+.BI exif " file.exif
+Set EXIF metadata.
+.P
+Where: 'file.exif' contains the EXIF metadata to be set.
+.TP
+.BI xmp " file.xmp
+Set XMP metadata.
+.P
+Where: 'file.xmp' contains the XMP metadata to be set.
+
+.SS STRIP_OPTIONS (\-strip)
+.TP
+.B icc
+Strip ICC profile.
+.TP
+.B exif
+Strip EXIF metadata.
+.TP
+.B xmp
+Strip XMP metadata.
+
+.SS DURATION_OPTIONS (\-duration)
+Amend the duration of a specific interval of frames. This option is only
+effective on animated WebP and has no effect on a single-frame file.
+.TP
+.I duration[,start[,end]]
+Where:
+.br
+.B duration
+is the duration for the interval in milliseconds (mandatory).
+Must be non-negative.
+.br
+.B start
+is the starting frame index of the interval (optional).
+.br
+.B end
+is the ending frame index (inclusive) of the interval (optional).
+.TP
+The three typical usages of this option are:
+.br
+.B -duration d
+ set the duration to 'd' for the whole animation.
+.br
+.B -duration d,f
+ set the duration of frame 'f' to 'd'.
+.br
+.B -duration d,start,end
+ set the duration to 'd' for the whole [start,end] interval.
+.TP
+.P
+Note that the frames outside of the [start, end] interval will remain untouched.
+The 'end' value '0' has the special meaning 'last frame of the animation'.
+.TP
+.I Reminder:
+frame indexing starts at '1'.
+.br
+
+.SS FRAME_OPTIONS (\-frame)
+Create an animated WebP file from multiple (non\-animated) WebP images.
+.TP
+.I file_i +di[+xi+yi[+mi[bi]]]
+Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image
+offset for this frame, 'di' is the pause duration before next frame, 'mi' is
+the dispose method for this frame (0 for NONE or 1 for BACKGROUND) and 'bi' is
+the blending method for this frame (+b for BLEND or \-b for NO_BLEND).
+Argument 'bi' can be omitted and will default to +b (BLEND).
+Also, 'mi' can be omitted if 'bi' is omitted and will default to 0 (NONE).
+Finally, if 'mi' and 'bi' are omitted then 'xi' and 'yi' can be omitted and will
+default to +0+0.
+.TP
+.BI \-loop " n
+Loop the frames n number of times. 0 indicates the frames should loop forever.
+Valid range is 0 to 65535 [Default: 0 (infinite)].
+.TP
+.BI \-bgcolor " A,R,G,B
+Background color of the canvas.
+.br
+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].
+
+.SS INPUT
+.TP
+Input file in WebP format.
+
+.SS OUTPUT (\-o)
+.TP
+Output file in WebP format.
+
+.SS Note:
+.TP
+The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+http://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+.P
+Add ICC profile:
+.br
+webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp
+.P
+Extract ICC profile:
+.br
+webpmux \-get icc icc_container.webp \-o image_profile.icc
+.P
+Strip ICC profile:
+.br
+webpmux \-strip icc icc_container.webp \-o without_icc.webp
+.P
+Add XMP metadata:
+.br
+webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp
+.P
+Extract XMP metadata:
+.br
+webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp
+.P
+Strip XMP metadata:
+.br
+webpmux \-strip xmp xmp_container.webp \-o without_xmp.webp
+.P
+Add EXIF metadata:
+.br
+webpmux \-set exif image_metadata.exif in.webp \-o exif_container.webp
+.P
+Extract EXIF metadata:
+.br
+webpmux \-get exif exif_container.webp \-o image_metadata.exif
+.P
+Strip EXIF metadata:
+.br
+webpmux \-strip exif exif_container.webp \-o without_exif.webp
+.P
+Create an animated WebP file from 3 (non\-animated) WebP images:
+.br
+webpmux \-frame 1.webp +100 \-frame 2.webp +100+50+50
+.br
+.RS 8
+\-frame 3.webp +100+50+50+1+b \-loop 10 \-bgcolor 255,255,255,255
+.br
+\-o anim_container.webp
+.RE
+.P
+Get the 2nd frame from an animated WebP file:
+.br
+webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
+.P
+Using \-get/\-set/\-strip with input file name starting with '\-':
+.br
+webpmux \-set icc image_profile.icc \-o icc_container.webp \-\- \-\-\-in.webp
+.br
+webpmux \-get icc \-o image_profile.icc \-\- \-\-\-icc_container.webp
+.br
+webpmux \-strip icc \-o without_icc.webp \-\- \-\-\-icc_container.webp
+
+.SH AUTHORS
+\fBwebpmux\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Vikas Arora <vikaas.arora@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR cwebp (1),
+.BR dwebp (1),
+.BR gif2webp (1)
+.br
+Please refer to http://developers.google.com/speed/webp/ for additional
+information.
diff --git a/src/third_party/libwebp/src/Makefile.am b/src/third_party/libwebp/src/Makefile.am
new file mode 100644
index 0000000..32cfa38
--- /dev/null
+++ b/src/third_party/libwebp/src/Makefile.am
@@ -0,0 +1,55 @@
+# The mux and demux libraries depend on libwebp, thus the '.' to force
+# the build order so it's available to them.
+SUBDIRS = dec enc dsp utils .
+if WANT_MUX
+ SUBDIRS += mux
+endif
+if WANT_DEMUX
+ SUBDIRS += demux
+endif
+
+lib_LTLIBRARIES = libwebp.la
+
+if BUILD_LIBWEBPDECODER
+ lib_LTLIBRARIES += libwebpdecoder.la
+endif
+
+common_HEADERS =
+common_HEADERS += webp/decode.h
+common_HEADERS += webp/types.h
+commondir = $(includedir)/webp
+
+libwebp_la_SOURCES =
+libwebpinclude_HEADERS =
+libwebpinclude_HEADERS += webp/encode.h
+
+noinst_HEADERS =
+noinst_HEADERS += webp/format_constants.h
+
+libwebp_la_LIBADD =
+libwebp_la_LIBADD += dec/libwebpdecode.la
+libwebp_la_LIBADD += dsp/libwebpdsp.la
+libwebp_la_LIBADD += enc/libwebpencode.la
+libwebp_la_LIBADD += utils/libwebputils.la
+
+# Use '-no-undefined' to declare that libwebp does not depend on any libraries
+# other than the ones listed on the command line, i.e., after linking, it will
+# not have unresolved symbols. Some platforms (Windows among them) require all
+# symbols in shared libraries to be resolved at library creation.
+libwebp_la_LDFLAGS = -no-undefined -version-info 7:2:0
+libwebpincludedir = $(includedir)/webp
+pkgconfig_DATA = libwebp.pc
+
+if BUILD_LIBWEBPDECODER
+ libwebpdecoder_la_SOURCES =
+
+ libwebpdecoder_la_LIBADD =
+ libwebpdecoder_la_LIBADD += dec/libwebpdecode.la
+ libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
+ libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
+
+ libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 3:2:0
+ pkgconfig_DATA += libwebpdecoder.pc
+endif
+
+${pkgconfig_DATA}: ${top_builddir}/config.status
diff --git a/src/third_party/libwebp/src/dec/Makefile.am b/src/third_party/libwebp/src/dec/Makefile.am
new file mode 100644
index 0000000..f8c6398
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/Makefile.am
@@ -0,0 +1,29 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES = libwebpdecode.la
+
+libwebpdecode_la_SOURCES =
+libwebpdecode_la_SOURCES += alpha_dec.c
+libwebpdecode_la_SOURCES += alphai_dec.h
+libwebpdecode_la_SOURCES += buffer_dec.c
+libwebpdecode_la_SOURCES += common_dec.h
+libwebpdecode_la_SOURCES += vp8_dec.h
+libwebpdecode_la_SOURCES += frame_dec.c
+libwebpdecode_la_SOURCES += idec_dec.c
+libwebpdecode_la_SOURCES += io_dec.c
+libwebpdecode_la_SOURCES += quant_dec.c
+libwebpdecode_la_SOURCES += tree_dec.c
+libwebpdecode_la_SOURCES += vp8_dec.c
+libwebpdecode_la_SOURCES += vp8i_dec.h
+libwebpdecode_la_SOURCES += vp8l_dec.c
+libwebpdecode_la_SOURCES += vp8li_dec.h
+libwebpdecode_la_SOURCES += webp_dec.c
+libwebpdecode_la_SOURCES += webpi_dec.h
+
+libwebpdecodeinclude_HEADERS =
+libwebpdecodeinclude_HEADERS += ../webp/decode.h
+libwebpdecodeinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpdecode_la_CPPFLAGS = $(AM_CPPFLAGS)
+libwebpdecodeincludedir = $(includedir)/webp
diff --git a/src/third_party/libwebp/src/dec/alpha_dec.c b/src/third_party/libwebp/src/dec/alpha_dec.c
new file mode 100644
index 0000000..4a6c8a7
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/alpha_dec.c
@@ -0,0 +1,238 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Alpha-plane decompression.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <stdlib.h>
+#endif
+#include "src/dec/alphai_dec.h"
+#include "src/dec/vp8i_dec.h"
+#include "src/dec/vp8li_dec.h"
+#include "src/dsp/dsp.h"
+#include "src/utils/quant_levels_dec_utils.h"
+#include "src/utils/utils.h"
+#include "src/webp/format_constants.h"
+
+//------------------------------------------------------------------------------
+// ALPHDecoder object.
+
+// Allocates a new alpha decoder instance.
+static ALPHDecoder* ALPHNew(void) {
+ ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
+ return dec;
+}
+
+// Clears and deallocates an alpha decoder instance.
+static void ALPHDelete(ALPHDecoder* const dec) {
+ if (dec != NULL) {
+ VP8LDelete(dec->vp8l_dec_);
+ dec->vp8l_dec_ = NULL;
+ WebPSafeFree(dec);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Decoding.
+
+// Initialize alpha decoding by parsing the alpha header and decoding the image
+// header for alpha data stored using lossless compression.
+// Returns false in case of error in alpha header (data too short, invalid
+// compression method or filter, error in lossless header data etc).
+static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
+ size_t data_size, const VP8Io* const src_io,
+ uint8_t* output) {
+ int ok = 0;
+ const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
+ const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
+ int rsrv;
+ VP8Io* const io = &dec->io_;
+
+ assert(data != NULL && output != NULL && src_io != NULL);
+
+ VP8FiltersInit();
+ dec->output_ = output;
+ dec->width_ = src_io->width;
+ dec->height_ = src_io->height;
+ assert(dec->width_ > 0 && dec->height_ > 0);
+
+ if (data_size <= ALPHA_HEADER_LEN) {
+ return 0;
+ }
+
+ dec->method_ = (data[0] >> 0) & 0x03;
+ dec->filter_ = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03);
+ dec->pre_processing_ = (data[0] >> 4) & 0x03;
+ rsrv = (data[0] >> 6) & 0x03;
+ if (dec->method_ < ALPHA_NO_COMPRESSION ||
+ dec->method_ > ALPHA_LOSSLESS_COMPRESSION ||
+ dec->filter_ >= WEBP_FILTER_LAST ||
+ dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS ||
+ rsrv != 0) {
+ return 0;
+ }
+
+ // Copy the necessary parameters from src_io to io
+ VP8InitIo(io);
+ WebPInitCustomIo(NULL, io);
+ io->opaque = dec;
+ io->width = src_io->width;
+ io->height = src_io->height;
+
+ io->use_cropping = src_io->use_cropping;
+ io->crop_left = src_io->crop_left;
+ io->crop_right = src_io->crop_right;
+ io->crop_top = src_io->crop_top;
+ io->crop_bottom = src_io->crop_bottom;
+ // No need to copy the scaling parameters.
+
+ if (dec->method_ == ALPHA_NO_COMPRESSION) {
+ const size_t alpha_decoded_size = dec->width_ * dec->height_;
+ ok = (alpha_data_size >= alpha_decoded_size);
+ } else {
+ assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
+ ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size);
+ }
+
+ return ok;
+}
+
+// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha
+// starting from row number 'row'. It assumes that rows up to (row - 1) have
+// already been decoded.
+// Returns false in case of bitstream error.
+static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
+ ALPHDecoder* const alph_dec = dec->alph_dec_;
+ const int width = alph_dec->width_;
+ const int height = alph_dec->io_.crop_bottom;
+ if (alph_dec->method_ == ALPHA_NO_COMPRESSION) {
+ int y;
+ const uint8_t* prev_line = dec->alpha_prev_line_;
+ const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width;
+ uint8_t* dst = dec->alpha_plane_ + row * width;
+ assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]);
+ if (alph_dec->filter_ != WEBP_FILTER_NONE) {
+ assert(WebPUnfilters[alph_dec->filter_] != NULL);
+ for (y = 0; y < num_rows; ++y) {
+ WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width);
+ prev_line = dst;
+ dst += width;
+ deltas += width;
+ }
+ } else {
+ for (y = 0; y < num_rows; ++y) {
+ memcpy(dst, deltas, width * sizeof(*dst));
+ prev_line = dst;
+ dst += width;
+ deltas += width;
+ }
+ }
+ dec->alpha_prev_line_ = prev_line;
+ } else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION
+ assert(alph_dec->vp8l_dec_ != NULL);
+ if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
+ return 0;
+ }
+ }
+
+ if (row + num_rows >= height) {
+ dec->is_alpha_decoded_ = 1;
+ }
+ return 1;
+}
+
+static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) {
+ const int stride = io->width;
+ const int height = io->crop_bottom;
+ const uint64_t alpha_size = (uint64_t)stride * height;
+ assert(dec->alpha_plane_mem_ == NULL);
+ dec->alpha_plane_mem_ =
+ (uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_));
+ if (dec->alpha_plane_mem_ == NULL) {
+ return 0;
+ }
+ dec->alpha_plane_ = dec->alpha_plane_mem_;
+ dec->alpha_prev_line_ = NULL;
+ return 1;
+}
+
+void WebPDeallocateAlphaMemory(VP8Decoder* const dec) {
+ assert(dec != NULL);
+ WebPSafeFree(dec->alpha_plane_mem_);
+ dec->alpha_plane_mem_ = NULL;
+ dec->alpha_plane_ = NULL;
+ ALPHDelete(dec->alph_dec_);
+ dec->alph_dec_ = NULL;
+}
+
+//------------------------------------------------------------------------------
+// Main entry point.
+
+const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
+ const VP8Io* const io,
+ int row, int num_rows) {
+ const int width = io->width;
+ const int height = io->crop_bottom;
+
+ assert(dec != NULL && io != NULL);
+
+ if (row < 0 || num_rows <= 0 || row + num_rows > height) {
+ return NULL; // sanity check.
+ }
+
+ if (!dec->is_alpha_decoded_) {
+ if (dec->alph_dec_ == NULL) { // Initialize decoder.
+ dec->alph_dec_ = ALPHNew();
+ if (dec->alph_dec_ == NULL) return NULL;
+ if (!AllocateAlphaPlane(dec, io)) goto Error;
+ if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
+ io, dec->alpha_plane_)) {
+ goto Error;
+ }
+ // if we allowed use of alpha dithering, check whether it's needed at all
+ if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) {
+ dec->alpha_dithering_ = 0; // disable dithering
+ } else {
+ num_rows = height - row; // decode everything in one pass
+ }
+ }
+
+ assert(dec->alph_dec_ != NULL);
+ assert(row + num_rows <= height);
+ if (!ALPHDecode(dec, row, num_rows)) goto Error;
+
+ if (dec->is_alpha_decoded_) { // finished?
+ ALPHDelete(dec->alph_dec_);
+ dec->alph_dec_ = NULL;
+ if (dec->alpha_dithering_ > 0) {
+ uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width
+ + io->crop_left;
+ if (!WebPDequantizeLevels(alpha,
+ io->crop_right - io->crop_left,
+ io->crop_bottom - io->crop_top,
+ width, dec->alpha_dithering_)) {
+ goto Error;
+ }
+ }
+ }
+ }
+
+ // Return a pointer to the current decoded row.
+ return dec->alpha_plane_ + row * width;
+
+ Error:
+ WebPDeallocateAlphaMemory(dec);
+ return NULL;
+}
diff --git a/src/third_party/libwebp/src/dec/alphai_dec.h b/src/third_party/libwebp/src/dec/alphai_dec.h
new file mode 100644
index 0000000..e0fa281
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/alphai_dec.h
@@ -0,0 +1,54 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Alpha decoder: internal header.
+//
+// Author: Urvang (urvang@google.com)
+
+#ifndef WEBP_DEC_ALPHAI_DEC_H_
+#define WEBP_DEC_ALPHAI_DEC_H_
+
+#include "src/dec/webpi_dec.h"
+#include "src/utils/filters_utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct VP8LDecoder; // Defined in dec/vp8li.h.
+
+typedef struct ALPHDecoder ALPHDecoder;
+struct ALPHDecoder {
+ int width_;
+ int height_;
+ int method_;
+ WEBP_FILTER_TYPE filter_;
+ int pre_processing_;
+ struct VP8LDecoder* vp8l_dec_;
+ VP8Io io_;
+ int use_8b_decode_; // Although alpha channel requires only 1 byte per
+ // pixel, sometimes VP8LDecoder may need to allocate
+ // 4 bytes per pixel internally during decode.
+ uint8_t* output_;
+ const uint8_t* prev_line_; // last output row (or NULL)
+};
+
+//------------------------------------------------------------------------------
+// internal functions. Not public.
+
+// Deallocate memory associated to dec->alpha_plane_ decoding
+void WebPDeallocateAlphaMemory(VP8Decoder* const dec);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_DEC_ALPHAI_DEC_H_ */
diff --git a/src/third_party/libwebp/src/dec/buffer_dec.c b/src/third_party/libwebp/src/dec/buffer_dec.c
new file mode 100644
index 0000000..e9e3d0f
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/buffer_dec.c
@@ -0,0 +1,317 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Everything about WebPDecBuffer
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <stdlib.h>
+#endif
+
+#include "src/dec/vp8i_dec.h"
+#include "src/dec/webpi_dec.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// WebPDecBuffer
+
+// Number of bytes per pixel for the different color-spaces.
+static const uint8_t kModeBpp[MODE_LAST] = {
+ 3, 4, 3, 4, 4, 2, 2,
+ 4, 4, 4, 2, // pre-multiplied modes
+ 1, 1 };
+
+// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
+// Convert to an integer to handle both the unsigned/signed enum cases
+// without the need for casting to remove type limit warnings.
+static int IsValidColorspace(int webp_csp_mode) {
+ return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
+}
+
+// strictly speaking, the very last (or first, if flipped) row
+// doesn't require padding.
+#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
+ ((uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH))
+
+static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
+ int ok = 1;
+ const WEBP_CSP_MODE mode = buffer->colorspace;
+ const int width = buffer->width;
+ const int height = buffer->height;
+ if (!IsValidColorspace(mode)) {
+ ok = 0;
+ } else if (!WebPIsRGBMode(mode)) { // YUV checks
+ const WebPYUVABuffer* const buf = &buffer->u.YUVA;
+ const int uv_width = (width + 1) / 2;
+ const int uv_height = (height + 1) / 2;
+ const int y_stride = abs(buf->y_stride);
+ const int u_stride = abs(buf->u_stride);
+ const int v_stride = abs(buf->v_stride);
+ const int a_stride = abs(buf->a_stride);
+ const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride);
+ const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride);
+ const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride);
+ const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride);
+ ok &= (y_size <= buf->y_size);
+ ok &= (u_size <= buf->u_size);
+ ok &= (v_size <= buf->v_size);
+ ok &= (y_stride >= width);
+ ok &= (u_stride >= uv_width);
+ ok &= (v_stride >= uv_width);
+ ok &= (buf->y != NULL);
+ ok &= (buf->u != NULL);
+ ok &= (buf->v != NULL);
+ if (mode == MODE_YUVA) {
+ ok &= (a_stride >= width);
+ ok &= (a_size <= buf->a_size);
+ ok &= (buf->a != NULL);
+ }
+ } else { // RGB checks
+ const WebPRGBABuffer* const buf = &buffer->u.RGBA;
+ const int stride = abs(buf->stride);
+ const uint64_t size = MIN_BUFFER_SIZE(width, height, stride);
+ ok &= (size <= buf->size);
+ ok &= (stride >= width * kModeBpp[mode]);
+ ok &= (buf->rgba != NULL);
+ }
+ return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
+}
+#undef MIN_BUFFER_SIZE
+
+static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
+ const int w = buffer->width;
+ const int h = buffer->height;
+ const WEBP_CSP_MODE mode = buffer->colorspace;
+
+ if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+
+ if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) {
+ uint8_t* output;
+ int uv_stride = 0, a_stride = 0;
+ uint64_t uv_size = 0, a_size = 0, total_size;
+ // We need memory and it hasn't been allocated yet.
+ // => initialize output buffer, now that dimensions are known.
+ int stride;
+ uint64_t size;
+
+ if ((uint64_t)w * kModeBpp[mode] >= (1ull << 32)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ stride = w * kModeBpp[mode];
+ size = (uint64_t)stride * h;
+ if (!WebPIsRGBMode(mode)) {
+ uv_stride = (w + 1) / 2;
+ uv_size = (uint64_t)uv_stride * ((h + 1) / 2);
+ if (mode == MODE_YUVA) {
+ a_stride = w;
+ a_size = (uint64_t)a_stride * h;
+ }
+ }
+ total_size = size + 2 * uv_size + a_size;
+
+ // Security/sanity checks
+ output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output));
+ if (output == NULL) {
+ return VP8_STATUS_OUT_OF_MEMORY;
+ }
+ buffer->private_memory = output;
+
+ if (!WebPIsRGBMode(mode)) { // YUVA initialization
+ WebPYUVABuffer* const buf = &buffer->u.YUVA;
+ buf->y = output;
+ buf->y_stride = stride;
+ buf->y_size = (size_t)size;
+ buf->u = output + size;
+ buf->u_stride = uv_stride;
+ buf->u_size = (size_t)uv_size;
+ buf->v = output + size + uv_size;
+ buf->v_stride = uv_stride;
+ buf->v_size = (size_t)uv_size;
+ if (mode == MODE_YUVA) {
+ buf->a = output + size + 2 * uv_size;
+ }
+ buf->a_size = (size_t)a_size;
+ buf->a_stride = a_stride;
+ } else { // RGBA initialization
+ WebPRGBABuffer* const buf = &buffer->u.RGBA;
+ buf->rgba = output;
+ buf->stride = stride;
+ buf->size = (size_t)size;
+ }
+ }
+ return CheckDecBuffer(buffer);
+}
+
+VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) {
+ if (buffer == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ if (WebPIsRGBMode(buffer->colorspace)) {
+ WebPRGBABuffer* const buf = &buffer->u.RGBA;
+ buf->rgba += (buffer->height - 1) * buf->stride;
+ buf->stride = -buf->stride;
+ } else {
+ WebPYUVABuffer* const buf = &buffer->u.YUVA;
+ const int H = buffer->height;
+ buf->y += (H - 1) * buf->y_stride;
+ buf->y_stride = -buf->y_stride;
+ buf->u += ((H - 1) >> 1) * buf->u_stride;
+ buf->u_stride = -buf->u_stride;
+ buf->v += ((H - 1) >> 1) * buf->v_stride;
+ buf->v_stride = -buf->v_stride;
+ if (buf->a != NULL) {
+ buf->a += (H - 1) * buf->a_stride;
+ buf->a_stride = -buf->a_stride;
+ }
+ }
+ return VP8_STATUS_OK;
+}
+
+VP8StatusCode WebPAllocateDecBuffer(int width, int height,
+ const WebPDecoderOptions* const options,
+ WebPDecBuffer* const buffer) {
+ VP8StatusCode status;
+ if (buffer == NULL || width <= 0 || height <= 0) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ if (options != NULL) { // First, apply options if there is any.
+ if (options->use_cropping) {
+ const int cw = options->crop_width;
+ const int ch = options->crop_height;
+ const int x = options->crop_left & ~1;
+ const int y = options->crop_top & ~1;
+ if (x < 0 || y < 0 || cw <= 0 || ch <= 0 ||
+ x + cw > width || y + ch > height) {
+ return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
+ }
+ width = cw;
+ height = ch;
+ }
+
+ if (options->use_scaling) {
+#if !defined(WEBP_REDUCE_SIZE)
+ int scaled_width = options->scaled_width;
+ int scaled_height = options->scaled_height;
+ if (!WebPRescalerGetScaledDimensions(
+ width, height, &scaled_width, &scaled_height)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ width = scaled_width;
+ height = scaled_height;
+#else
+ return VP8_STATUS_INVALID_PARAM; // rescaling not supported
+#endif
+ }
+ }
+ buffer->width = width;
+ buffer->height = height;
+
+ // Then, allocate buffer for real.
+ status = AllocateBuffer(buffer);
+ if (status != VP8_STATUS_OK) return status;
+
+ // Use the stride trick if vertical flip is needed.
+ if (options != NULL && options->flip) {
+ status = WebPFlipBuffer(buffer);
+ }
+ return status;
+}
+
+//------------------------------------------------------------------------------
+// constructors / destructors
+
+int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
+ return 0; // version mismatch
+ }
+ if (buffer == NULL) return 0;
+ memset(buffer, 0, sizeof(*buffer));
+ return 1;
+}
+
+void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
+ if (buffer != NULL) {
+ if (buffer->is_external_memory <= 0) {
+ WebPSafeFree(buffer->private_memory);
+ }
+ buffer->private_memory = NULL;
+ }
+}
+
+void WebPCopyDecBuffer(const WebPDecBuffer* const src,
+ WebPDecBuffer* const dst) {
+ if (src != NULL && dst != NULL) {
+ *dst = *src;
+ if (src->private_memory != NULL) {
+ dst->is_external_memory = 1; // dst buffer doesn't own the memory.
+ dst->private_memory = NULL;
+ }
+ }
+}
+
+// Copy and transfer ownership from src to dst (beware of parameter order!)
+void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
+ if (src != NULL && dst != NULL) {
+ *dst = *src;
+ if (src->private_memory != NULL) {
+ src->is_external_memory = 1; // src relinquishes ownership
+ src->private_memory = NULL;
+ }
+ }
+}
+
+VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf,
+ WebPDecBuffer* const dst_buf) {
+ assert(src_buf != NULL && dst_buf != NULL);
+ assert(src_buf->colorspace == dst_buf->colorspace);
+
+ dst_buf->width = src_buf->width;
+ dst_buf->height = src_buf->height;
+ if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ if (WebPIsRGBMode(src_buf->colorspace)) {
+ const WebPRGBABuffer* const src = &src_buf->u.RGBA;
+ const WebPRGBABuffer* const dst = &dst_buf->u.RGBA;
+ WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride,
+ src_buf->width * kModeBpp[src_buf->colorspace],
+ src_buf->height);
+ } else {
+ const WebPYUVABuffer* const src = &src_buf->u.YUVA;
+ const WebPYUVABuffer* const dst = &dst_buf->u.YUVA;
+ WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride,
+ src_buf->width, src_buf->height);
+ WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride,
+ (src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
+ WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride,
+ (src_buf->width + 1) / 2, (src_buf->height + 1) / 2);
+ if (WebPIsAlphaMode(src_buf->colorspace)) {
+ WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride,
+ src_buf->width, src_buf->height);
+ }
+ }
+ return VP8_STATUS_OK;
+}
+
+int WebPAvoidSlowMemory(const WebPDecBuffer* const output,
+ const WebPBitstreamFeatures* const features) {
+ assert(output != NULL);
+ return (output->is_external_memory >= 2) &&
+ WebPIsPremultipliedMode(output->colorspace) &&
+ (features != NULL && features->has_alpha);
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dec/common_dec.h b/src/third_party/libwebp/src/dec/common_dec.h
new file mode 100644
index 0000000..9995f1a
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/common_dec.h
@@ -0,0 +1,54 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Definitions and macros common to encoding and decoding
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_DEC_COMMON_DEC_H_
+#define WEBP_DEC_COMMON_DEC_H_
+
+// intra prediction modes
+enum { B_DC_PRED = 0, // 4x4 modes
+ B_TM_PRED = 1,
+ B_VE_PRED = 2,
+ B_HE_PRED = 3,
+ B_RD_PRED = 4,
+ B_VR_PRED = 5,
+ B_LD_PRED = 6,
+ B_VL_PRED = 7,
+ B_HD_PRED = 8,
+ B_HU_PRED = 9,
+ NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
+
+ // Luma16 or UV modes
+ DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
+ H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
+ B_PRED = NUM_BMODES, // refined I4x4 mode
+ NUM_PRED_MODES = 4,
+
+ // special modes
+ B_DC_PRED_NOTOP = 4,
+ B_DC_PRED_NOLEFT = 5,
+ B_DC_PRED_NOTOPLEFT = 6,
+ NUM_B_DC_MODES = 7 };
+
+enum { MB_FEATURE_TREE_PROBS = 3,
+ NUM_MB_SEGMENTS = 4,
+ NUM_REF_LF_DELTAS = 4,
+ NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
+ MAX_NUM_PARTITIONS = 8,
+ // Probabilities
+ NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
+ NUM_BANDS = 8,
+ NUM_CTX = 3,
+ NUM_PROBAS = 11
+ };
+
+#endif // WEBP_DEC_COMMON_DEC_H_
diff --git a/src/third_party/libwebp/src/dec/frame_dec.c b/src/third_party/libwebp/src/dec/frame_dec.c
new file mode 100644
index 0000000..c75f31e
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/frame_dec.c
@@ -0,0 +1,816 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Frame-reconstruction function. Memory allocation.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <stdlib.h>
+#endif
+#include "src/dec/vp8i_dec.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// Main reconstruction function.
+
+static const uint16_t kScan[16] = {
+ 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
+ 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
+ 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
+ 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
+};
+
+static int CheckMode(int mb_x, int mb_y, int mode) {
+ if (mode == B_DC_PRED) {
+ if (mb_x == 0) {
+ return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
+ } else {
+ return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
+ }
+ }
+ return mode;
+}
+
+static void Copy32b(uint8_t* const dst, const uint8_t* const src) {
+ memcpy(dst, src, 4);
+}
+
+static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
+ uint8_t* const dst) {
+ switch (bits >> 30) {
+ case 3:
+ VP8Transform(src, dst, 0);
+ break;
+ case 2:
+ VP8TransformAC3(src, dst);
+ break;
+ case 1:
+ VP8TransformDC(src, dst);
+ break;
+ default:
+ break;
+ }
+}
+
+static void DoUVTransform(uint32_t bits, const int16_t* const src,
+ uint8_t* const dst) {
+ if (bits & 0xff) { // any non-zero coeff at all?
+ if (bits & 0xaa) { // any non-zero AC coefficient?
+ VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
+ } else {
+ VP8TransformDCUV(src, dst);
+ }
+ }
+}
+
+static void ReconstructRow(const VP8Decoder* const dec,
+ const VP8ThreadContext* ctx) {
+ int j;
+ int mb_x;
+ const int mb_y = ctx->mb_y_;
+ const int cache_id = ctx->id_;
+ uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
+ uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
+ uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
+
+ // Initialize left-most block.
+ for (j = 0; j < 16; ++j) {
+ y_dst[j * BPS - 1] = 129;
+ }
+ for (j = 0; j < 8; ++j) {
+ u_dst[j * BPS - 1] = 129;
+ v_dst[j * BPS - 1] = 129;
+ }
+
+ // Init top-left sample on left column too.
+ if (mb_y > 0) {
+ y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
+ } else {
+ // we only need to do this init once at block (0,0).
+ // Afterward, it remains valid for the whole topmost row.
+ memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
+ memset(u_dst - BPS - 1, 127, 8 + 1);
+ memset(v_dst - BPS - 1, 127, 8 + 1);
+ }
+
+ // Reconstruct one row.
+ for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
+ const VP8MBData* const block = ctx->mb_data_ + mb_x;
+
+ // Rotate in the left samples from previously decoded block. We move four
+ // pixels at a time for alignment reason, and because of in-loop filter.
+ if (mb_x > 0) {
+ for (j = -1; j < 16; ++j) {
+ Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
+ }
+ for (j = -1; j < 8; ++j) {
+ Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
+ Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
+ }
+ }
+ {
+ // bring top samples into the cache
+ VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x;
+ const int16_t* const coeffs = block->coeffs_;
+ uint32_t bits = block->non_zero_y_;
+ int n;
+
+ if (mb_y > 0) {
+ memcpy(y_dst - BPS, top_yuv[0].y, 16);
+ memcpy(u_dst - BPS, top_yuv[0].u, 8);
+ memcpy(v_dst - BPS, top_yuv[0].v, 8);
+ }
+
+ // predict and add residuals
+ if (block->is_i4x4_) { // 4x4
+ uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
+
+ if (mb_y > 0) {
+ if (mb_x >= dec->mb_w_ - 1) { // on rightmost border
+ memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
+ } else {
+ memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
+ }
+ }
+ // replicate the top-right pixels below
+ top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
+
+ // predict and add residuals for all 4x4 blocks in turn.
+ for (n = 0; n < 16; ++n, bits <<= 2) {
+ uint8_t* const dst = y_dst + kScan[n];
+ VP8PredLuma4[block->imodes_[n]](dst);
+ DoTransform(bits, coeffs + n * 16, dst);
+ }
+ } else { // 16x16
+ const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]);
+ VP8PredLuma16[pred_func](y_dst);
+ if (bits != 0) {
+ for (n = 0; n < 16; ++n, bits <<= 2) {
+ DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]);
+ }
+ }
+ }
+ {
+ // Chroma
+ const uint32_t bits_uv = block->non_zero_uv_;
+ const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_);
+ VP8PredChroma8[pred_func](u_dst);
+ VP8PredChroma8[pred_func](v_dst);
+ DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst);
+ DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst);
+ }
+
+ // stash away top samples for next block
+ if (mb_y < dec->mb_h_ - 1) {
+ memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16);
+ memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8);
+ memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8);
+ }
+ }
+ // Transfer reconstructed samples from yuv_b_ cache to final destination.
+ {
+ const int y_offset = cache_id * 16 * dec->cache_y_stride_;
+ const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
+ uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset;
+ uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset;
+ uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset;
+ for (j = 0; j < 16; ++j) {
+ memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
+ }
+ for (j = 0; j < 8; ++j) {
+ memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8);
+ memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Filtering
+
+// kFilterExtraRows[] = How many extra lines are needed on the MB boundary
+// for caching, given a filtering level.
+// Simple filter: up to 2 luma samples are read and 1 is written.
+// Complex filter: up to 4 luma samples are read and 3 are written. Same for
+// U/V, so it's 8 samples total (because of the 2x upsampling).
+static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
+
+static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
+ const VP8ThreadContext* const ctx = &dec->thread_ctx_;
+ const int cache_id = ctx->id_;
+ const int y_bps = dec->cache_y_stride_;
+ const VP8FInfo* const f_info = ctx->f_info_ + mb_x;
+ uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16;
+ const int ilevel = f_info->f_ilevel_;
+ const int limit = f_info->f_limit_;
+ if (limit == 0) {
+ return;
+ }
+ assert(limit >= 3);
+ if (dec->filter_type_ == 1) { // simple
+ if (mb_x > 0) {
+ VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
+ }
+ if (f_info->f_inner_) {
+ VP8SimpleHFilter16i(y_dst, y_bps, limit);
+ }
+ if (mb_y > 0) {
+ VP8SimpleVFilter16(y_dst, y_bps, limit + 4);
+ }
+ if (f_info->f_inner_) {
+ VP8SimpleVFilter16i(y_dst, y_bps, limit);
+ }
+ } else { // complex
+ const int uv_bps = dec->cache_uv_stride_;
+ uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
+ uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
+ const int hev_thresh = f_info->hev_thresh_;
+ if (mb_x > 0) {
+ VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
+ VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
+ }
+ if (f_info->f_inner_) {
+ VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
+ VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
+ }
+ if (mb_y > 0) {
+ VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
+ VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
+ }
+ if (f_info->f_inner_) {
+ VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
+ VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
+ }
+ }
+}
+
+// Filter the decoded macroblock row (if needed)
+static void FilterRow(const VP8Decoder* const dec) {
+ int mb_x;
+ const int mb_y = dec->thread_ctx_.mb_y_;
+ assert(dec->thread_ctx_.filter_row_);
+ for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
+ DoFilter(dec, mb_x, mb_y);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Precompute the filtering strength for each segment and each i4x4/i16x16 mode.
+
+static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
+ if (dec->filter_type_ > 0) {
+ int s;
+ const VP8FilterHeader* const hdr = &dec->filter_hdr_;
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ int i4x4;
+ // First, compute the initial level
+ int base_level;
+ if (dec->segment_hdr_.use_segment_) {
+ base_level = dec->segment_hdr_.filter_strength_[s];
+ if (!dec->segment_hdr_.absolute_delta_) {
+ base_level += hdr->level_;
+ }
+ } else {
+ base_level = hdr->level_;
+ }
+ for (i4x4 = 0; i4x4 <= 1; ++i4x4) {
+ VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
+ int level = base_level;
+ if (hdr->use_lf_delta_) {
+ level += hdr->ref_lf_delta_[0];
+ if (i4x4) {
+ level += hdr->mode_lf_delta_[0];
+ }
+ }
+ level = (level < 0) ? 0 : (level > 63) ? 63 : level;
+ if (level > 0) {
+ int ilevel = level;
+ if (hdr->sharpness_ > 0) {
+ if (hdr->sharpness_ > 4) {
+ ilevel >>= 2;
+ } else {
+ ilevel >>= 1;
+ }
+ if (ilevel > 9 - hdr->sharpness_) {
+ ilevel = 9 - hdr->sharpness_;
+ }
+ }
+ if (ilevel < 1) ilevel = 1;
+ info->f_ilevel_ = ilevel;
+ info->f_limit_ = 2 * level + ilevel;
+ info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
+ } else {
+ info->f_limit_ = 0; // no filtering
+ }
+ info->f_inner_ = i4x4;
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Dithering
+
+// minimal amp that will provide a non-zero dithering effect
+#define MIN_DITHER_AMP 4
+
+#define DITHER_AMP_TAB_SIZE 12
+static const uint8_t kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
+ // roughly, it's dqm->uv_mat_[1]
+ 8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
+};
+
+void VP8InitDithering(const WebPDecoderOptions* const options,
+ VP8Decoder* const dec) {
+ assert(dec != NULL);
+ if (options != NULL) {
+ const int d = options->dithering_strength;
+ const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1;
+ const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100);
+ if (f > 0) {
+ int s;
+ int all_amp = 0;
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ VP8QuantMatrix* const dqm = &dec->dqm_[s];
+ if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) {
+ // TODO(skal): should we specially dither more for uv_quant_ < 0?
+ const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_;
+ dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3;
+ }
+ all_amp |= dqm->dither_;
+ }
+ if (all_amp != 0) {
+ VP8InitRandom(&dec->dithering_rg_, 1.0f);
+ dec->dither_ = 1;
+ }
+ }
+ // potentially allow alpha dithering
+ dec->alpha_dithering_ = options->alpha_dithering_strength;
+ if (dec->alpha_dithering_ > 100) {
+ dec->alpha_dithering_ = 100;
+ } else if (dec->alpha_dithering_ < 0) {
+ dec->alpha_dithering_ = 0;
+ }
+ }
+}
+
+// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
+static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
+ uint8_t dither[64];
+ int i;
+ for (i = 0; i < 8 * 8; ++i) {
+ dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp);
+ }
+ VP8DitherCombine8x8(dither, dst, bps);
+}
+
+static void DitherRow(VP8Decoder* const dec) {
+ int mb_x;
+ assert(dec->dither_);
+ for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
+ const VP8ThreadContext* const ctx = &dec->thread_ctx_;
+ const VP8MBData* const data = ctx->mb_data_ + mb_x;
+ const int cache_id = ctx->id_;
+ const int uv_bps = dec->cache_uv_stride_;
+ if (data->dither_ >= MIN_DITHER_AMP) {
+ uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
+ uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
+ Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_);
+ Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// This function is called after a row of macroblocks is finished decoding.
+// It also takes into account the following restrictions:
+// * In case of in-loop filtering, we must hold off sending some of the bottom
+// pixels as they are yet unfiltered. They will be when the next macroblock
+// row is decoded. Meanwhile, we must preserve them by rotating them in the
+// cache area. This doesn't hold for the very bottom row of the uncropped
+// picture of course.
+// * we must clip the remaining pixels against the cropping area. The VP8Io
+// struct must have the following fields set correctly before calling put():
+
+#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
+
+// Finalize and transmit a complete row. Return false in case of user-abort.
+static int FinishRow(void* arg1, void* arg2) {
+ VP8Decoder* const dec = (VP8Decoder*)arg1;
+ VP8Io* const io = (VP8Io*)arg2;
+ int ok = 1;
+ const VP8ThreadContext* const ctx = &dec->thread_ctx_;
+ const int cache_id = ctx->id_;
+ const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
+ const int ysize = extra_y_rows * dec->cache_y_stride_;
+ const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
+ const int y_offset = cache_id * 16 * dec->cache_y_stride_;
+ const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
+ uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
+ uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
+ uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
+ const int mb_y = ctx->mb_y_;
+ const int is_first_row = (mb_y == 0);
+ const int is_last_row = (mb_y >= dec->br_mb_y_ - 1);
+
+ if (dec->mt_method_ == 2) {
+ ReconstructRow(dec, ctx);
+ }
+
+ if (ctx->filter_row_) {
+ FilterRow(dec);
+ }
+
+ if (dec->dither_) {
+ DitherRow(dec);
+ }
+
+ if (io->put != NULL) {
+ int y_start = MACROBLOCK_VPOS(mb_y);
+ int y_end = MACROBLOCK_VPOS(mb_y + 1);
+ if (!is_first_row) {
+ y_start -= extra_y_rows;
+ io->y = ydst;
+ io->u = udst;
+ io->v = vdst;
+ } else {
+ io->y = dec->cache_y_ + y_offset;
+ io->u = dec->cache_u_ + uv_offset;
+ io->v = dec->cache_v_ + uv_offset;
+ }
+
+ if (!is_last_row) {
+ y_end -= extra_y_rows;
+ }
+ if (y_end > io->crop_bottom) {
+ y_end = io->crop_bottom; // make sure we don't overflow on last row.
+ }
+ // If dec->alpha_data_ is not NULL, we have some alpha plane present.
+ io->a = NULL;
+ if (dec->alpha_data_ != NULL && y_start < y_end) {
+ io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start);
+ if (io->a == NULL) {
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "Could not decode alpha data.");
+ }
+ }
+ if (y_start < io->crop_top) {
+ const int delta_y = io->crop_top - y_start;
+ y_start = io->crop_top;
+ assert(!(delta_y & 1));
+ io->y += dec->cache_y_stride_ * delta_y;
+ io->u += dec->cache_uv_stride_ * (delta_y >> 1);
+ io->v += dec->cache_uv_stride_ * (delta_y >> 1);
+ if (io->a != NULL) {
+ io->a += io->width * delta_y;
+ }
+ }
+ if (y_start < y_end) {
+ io->y += io->crop_left;
+ io->u += io->crop_left >> 1;
+ io->v += io->crop_left >> 1;
+ if (io->a != NULL) {
+ io->a += io->crop_left;
+ }
+ io->mb_y = y_start - io->crop_top;
+ io->mb_w = io->crop_right - io->crop_left;
+ io->mb_h = y_end - y_start;
+ ok = io->put(io);
+ }
+ }
+ // rotate top samples if needed
+ if (cache_id + 1 == dec->num_caches_) {
+ if (!is_last_row) {
+ memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
+ memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
+ memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
+ }
+ }
+
+ return ok;
+}
+
+#undef MACROBLOCK_VPOS
+
+//------------------------------------------------------------------------------
+
+int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
+ int ok = 1;
+ VP8ThreadContext* const ctx = &dec->thread_ctx_;
+ const int filter_row =
+ (dec->filter_type_ > 0) &&
+ (dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
+ if (dec->mt_method_ == 0) {
+ // ctx->id_ and ctx->f_info_ are already set
+ ctx->mb_y_ = dec->mb_y_;
+ ctx->filter_row_ = filter_row;
+ ReconstructRow(dec, ctx);
+ ok = FinishRow(dec, io);
+ } else {
+ WebPWorker* const worker = &dec->worker_;
+ // Finish previous job *before* updating context
+ ok &= WebPGetWorkerInterface()->Sync(worker);
+ assert(worker->status_ == OK);
+ if (ok) { // spawn a new deblocking/output job
+ ctx->io_ = *io;
+ ctx->id_ = dec->cache_id_;
+ ctx->mb_y_ = dec->mb_y_;
+ ctx->filter_row_ = filter_row;
+ if (dec->mt_method_ == 2) { // swap macroblock data
+ VP8MBData* const tmp = ctx->mb_data_;
+ ctx->mb_data_ = dec->mb_data_;
+ dec->mb_data_ = tmp;
+ } else {
+ // perform reconstruction directly in main thread
+ ReconstructRow(dec, ctx);
+ }
+ if (filter_row) { // swap filter info
+ VP8FInfo* const tmp = ctx->f_info_;
+ ctx->f_info_ = dec->f_info_;
+ dec->f_info_ = tmp;
+ }
+ // (reconstruct)+filter in parallel
+ WebPGetWorkerInterface()->Launch(worker);
+ if (++dec->cache_id_ == dec->num_caches_) {
+ dec->cache_id_ = 0;
+ }
+ }
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// Finish setting up the decoding parameter once user's setup() is called.
+
+VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
+ // Call setup() first. This may trigger additional decoding features on 'io'.
+ // Note: Afterward, we must call teardown() no matter what.
+ if (io->setup != NULL && !io->setup(io)) {
+ VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
+ return dec->status_;
+ }
+
+ // Disable filtering per user request
+ if (io->bypass_filtering) {
+ dec->filter_type_ = 0;
+ }
+
+ // Define the area where we can skip in-loop filtering, in case of cropping.
+ //
+ // 'Simple' filter reads two luma samples outside of the macroblock
+ // and filters one. It doesn't filter the chroma samples. Hence, we can
+ // avoid doing the in-loop filtering before crop_top/crop_left position.
+ // For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
+ // Means: there's a dependency chain that goes all the way up to the
+ // top-left corner of the picture (MB #0). We must filter all the previous
+ // macroblocks.
+ {
+ const int extra_pixels = kFilterExtraRows[dec->filter_type_];
+ if (dec->filter_type_ == 2) {
+ // For complex filter, we need to preserve the dependency chain.
+ dec->tl_mb_x_ = 0;
+ dec->tl_mb_y_ = 0;
+ } else {
+ // For simple filter, we can filter only the cropped region.
+ // We include 'extra_pixels' on the other side of the boundary, since
+ // vertical or horizontal filtering of the previous macroblock can
+ // modify some abutting pixels.
+ dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4;
+ dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4;
+ if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0;
+ if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0;
+ }
+ // We need some 'extra' pixels on the right/bottom.
+ dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4;
+ dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4;
+ if (dec->br_mb_x_ > dec->mb_w_) {
+ dec->br_mb_x_ = dec->mb_w_;
+ }
+ if (dec->br_mb_y_ > dec->mb_h_) {
+ dec->br_mb_y_ = dec->mb_h_;
+ }
+ }
+ PrecomputeFilterStrengths(dec);
+ return VP8_STATUS_OK;
+}
+
+int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
+ int ok = 1;
+ if (dec->mt_method_ > 0) {
+ ok = WebPGetWorkerInterface()->Sync(&dec->worker_);
+ }
+
+ if (io->teardown != NULL) {
+ io->teardown(io);
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
+//
+// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
+// immediately, and needs to wait for first few rows of the next macroblock to
+// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
+// on strength).
+// With two threads, the vertical positions of the rows being decoded are:
+// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
+// Deblock: [ 0..11][12..27][28..43][44..59][...
+// If we use two threads and two caches of 16 pixels, the sequence would be:
+// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
+// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
+// The problem occurs during row [12..15!!] that both the decoding and
+// deblocking threads are writing simultaneously.
+// With 3 cache lines, one get a safe write pattern:
+// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
+// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
+// Note that multi-threaded output _without_ deblocking can make use of two
+// cache lines of 16 pixels only, since there's no lagging behind. The decoding
+// and output process have non-concurrent writing:
+// Decode: [ 0..15][16..31][ 0..15][16..31][...
+// io->put: [ 0..15][16..31][ 0..15][...
+
+#define MT_CACHE_LINES 3
+#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
+
+// Initialize multi/single-thread worker
+static int InitThreadContext(VP8Decoder* const dec) {
+ dec->cache_id_ = 0;
+ if (dec->mt_method_ > 0) {
+ WebPWorker* const worker = &dec->worker_;
+ if (!WebPGetWorkerInterface()->Reset(worker)) {
+ return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
+ "thread initialization failed.");
+ }
+ worker->data1 = dec;
+ worker->data2 = (void*)&dec->thread_ctx_.io_;
+ worker->hook = FinishRow;
+ dec->num_caches_ =
+ (dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
+ } else {
+ dec->num_caches_ = ST_CACHE_LINES;
+ }
+ return 1;
+}
+
+int VP8GetThreadMethod(const WebPDecoderOptions* const options,
+ const WebPHeaderStructure* const headers,
+ int width, int height) {
+ if (options == NULL || options->use_threads == 0) {
+ return 0;
+ }
+ (void)headers;
+ (void)width;
+ (void)height;
+ assert(headers == NULL || !headers->is_lossless);
+#if defined(WEBP_USE_THREAD)
+ if (width < MIN_WIDTH_FOR_THREADS) return 0;
+ // TODO(skal): tune the heuristic further
+#if 0
+ if (height < 2 * width) return 2;
+#endif
+ return 2;
+#else // !WEBP_USE_THREAD
+ return 0;
+#endif
+}
+
+#undef MT_CACHE_LINES
+#undef ST_CACHE_LINES
+
+//------------------------------------------------------------------------------
+// Memory setup
+
+static int AllocateMemory(VP8Decoder* const dec) {
+ const int num_caches = dec->num_caches_;
+ const int mb_w = dec->mb_w_;
+ // Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
+ const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
+ const size_t top_size = sizeof(VP8TopSamples) * mb_w;
+ const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
+ const size_t f_info_size =
+ (dec->filter_type_ > 0) ?
+ mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo)
+ : 0;
+ const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
+ const size_t mb_data_size =
+ (dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
+ const size_t cache_height = (16 * num_caches
+ + kFilterExtraRows[dec->filter_type_]) * 3 / 2;
+ const size_t cache_size = top_size * cache_height;
+ // alpha_size is the only one that scales as width x height.
+ const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
+ (uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
+ const uint64_t needed = (uint64_t)intra_pred_mode_size
+ + top_size + mb_info_size + f_info_size
+ + yuv_size + mb_data_size
+ + cache_size + alpha_size + WEBP_ALIGN_CST;
+ uint8_t* mem;
+
+ if (needed != (size_t)needed) return 0; // check for overflow
+ if (needed > dec->mem_size_) {
+ WebPSafeFree(dec->mem_);
+ dec->mem_size_ = 0;
+ dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
+ if (dec->mem_ == NULL) {
+ return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
+ "no memory during frame initialization.");
+ }
+ // down-cast is ok, thanks to WebPSafeMalloc() above.
+ dec->mem_size_ = (size_t)needed;
+ }
+
+ mem = (uint8_t*)dec->mem_;
+ dec->intra_t_ = mem;
+ mem += intra_pred_mode_size;
+
+ dec->yuv_t_ = (VP8TopSamples*)mem;
+ mem += top_size;
+
+ dec->mb_info_ = ((VP8MB*)mem) + 1;
+ mem += mb_info_size;
+
+ dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
+ mem += f_info_size;
+ dec->thread_ctx_.id_ = 0;
+ dec->thread_ctx_.f_info_ = dec->f_info_;
+ if (dec->mt_method_ > 0) {
+ // secondary cache line. The deblocking process need to make use of the
+ // filtering strength from previous macroblock row, while the new ones
+ // are being decoded in parallel. We'll just swap the pointers.
+ dec->thread_ctx_.f_info_ += mb_w;
+ }
+
+ mem = (uint8_t*)WEBP_ALIGN(mem);
+ assert((yuv_size & WEBP_ALIGN_CST) == 0);
+ dec->yuv_b_ = mem;
+ mem += yuv_size;
+
+ dec->mb_data_ = (VP8MBData*)mem;
+ dec->thread_ctx_.mb_data_ = (VP8MBData*)mem;
+ if (dec->mt_method_ == 2) {
+ dec->thread_ctx_.mb_data_ += mb_w;
+ }
+ mem += mb_data_size;
+
+ dec->cache_y_stride_ = 16 * mb_w;
+ dec->cache_uv_stride_ = 8 * mb_w;
+ {
+ const int extra_rows = kFilterExtraRows[dec->filter_type_];
+ const int extra_y = extra_rows * dec->cache_y_stride_;
+ const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
+ dec->cache_y_ = mem + extra_y;
+ dec->cache_u_ = dec->cache_y_
+ + 16 * num_caches * dec->cache_y_stride_ + extra_uv;
+ dec->cache_v_ = dec->cache_u_
+ + 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
+ dec->cache_id_ = 0;
+ }
+ mem += cache_size;
+
+ // alpha plane
+ dec->alpha_plane_ = alpha_size ? mem : NULL;
+ mem += alpha_size;
+ assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
+
+ // note: left/top-info is initialized once for all.
+ memset(dec->mb_info_ - 1, 0, mb_info_size);
+ VP8InitScanline(dec); // initialize left too.
+
+ // initialize top
+ memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
+
+ return 1;
+}
+
+static void InitIo(VP8Decoder* const dec, VP8Io* io) {
+ // prepare 'io'
+ io->mb_y = 0;
+ io->y = dec->cache_y_;
+ io->u = dec->cache_u_;
+ io->v = dec->cache_v_;
+ io->y_stride = dec->cache_y_stride_;
+ io->uv_stride = dec->cache_uv_stride_;
+ io->a = NULL;
+}
+
+int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) {
+ if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
+ if (!AllocateMemory(dec)) return 0;
+ InitIo(dec, io);
+ VP8DspInit(); // Init critical function pointers and look-up tables.
+ return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dec/idec_dec.c b/src/third_party/libwebp/src/dec/idec_dec.c
new file mode 100644
index 0000000..5b09b03
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/idec_dec.c
@@ -0,0 +1,898 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Incremental decoding
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+#include "src/dec/alphai_dec.h"
+#include "src/dec/webpi_dec.h"
+#include "src/dec/vp8i_dec.h"
+#include "src/utils/utils.h"
+
+// In append mode, buffer allocations increase as multiples of this value.
+// Needs to be a power of 2.
+#define CHUNK_SIZE 4096
+#define MAX_MB_SIZE 4096
+
+//------------------------------------------------------------------------------
+// Data structures for memory and states
+
+// Decoding states. State normally flows as:
+// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
+// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
+// If there is any error the decoder goes into state ERROR.
+typedef enum {
+ STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.
+ STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).
+ STATE_VP8_PARTS0,
+ STATE_VP8_DATA,
+ STATE_VP8L_HEADER,
+ STATE_VP8L_DATA,
+ STATE_DONE,
+ STATE_ERROR
+} DecState;
+
+// Operating state for the MemBuffer
+typedef enum {
+ MEM_MODE_NONE = 0,
+ MEM_MODE_APPEND,
+ MEM_MODE_MAP
+} MemBufferMode;
+
+// storage for partition #0 and partial data (in a rolling fashion)
+typedef struct {
+ MemBufferMode mode_; // Operation mode
+ size_t start_; // start location of the data to be decoded
+ size_t end_; // end location
+ size_t buf_size_; // size of the allocated buffer
+ uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()
+
+ size_t part0_size_; // size of partition #0
+ const uint8_t* part0_buf_; // buffer to store partition #0
+} MemBuffer;
+
+struct WebPIDecoder {
+ DecState state_; // current decoding state
+ WebPDecParams params_; // Params to store output info
+ int is_lossless_; // for down-casting 'dec_'.
+ void* dec_; // either a VP8Decoder or a VP8LDecoder instance
+ VP8Io io_;
+
+ MemBuffer mem_; // input memory buffer.
+ WebPDecBuffer output_; // output buffer (when no external one is supplied,
+ // or if the external one has slow-memory)
+ WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually.
+ size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
+
+ int last_mb_y_; // last row reached for intra-mode decoding
+};
+
+// MB context to restore in case VP8DecodeMB() fails
+typedef struct {
+ VP8MB left_;
+ VP8MB info_;
+ VP8BitReader token_br_;
+} MBContext;
+
+//------------------------------------------------------------------------------
+// MemBuffer: incoming data handling
+
+static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
+ return (mem->end_ - mem->start_);
+}
+
+// Check if we need to preserve the compressed alpha data, as it may not have
+// been decoded yet.
+static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
+ if (idec->state_ == STATE_WEBP_HEADER) {
+ // We haven't parsed the headers yet, so we don't know whether the image is
+ // lossy or lossless. This also means that we haven't parsed the ALPH chunk.
+ return 0;
+ }
+ if (idec->is_lossless_) {
+ return 0; // ALPH chunk is not present for lossless images.
+ } else {
+ const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
+ assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER.
+ return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;
+ }
+}
+
+static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
+ MemBuffer* const mem = &idec->mem_;
+ const uint8_t* const new_base = mem->buf_ + mem->start_;
+ // note: for VP8, setting up idec->io_ is only really needed at the beginning
+ // of the decoding, till partition #0 is complete.
+ idec->io_.data = new_base;
+ idec->io_.data_size = MemDataSize(mem);
+
+ if (idec->dec_ != NULL) {
+ if (!idec->is_lossless_) {
+ VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
+ const uint32_t last_part = dec->num_parts_minus_one_;
+ if (offset != 0) {
+ uint32_t p;
+ for (p = 0; p <= last_part; ++p) {
+ VP8RemapBitReader(dec->parts_ + p, offset);
+ }
+ // Remap partition #0 data pointer to new offset, but only in MAP
+ // mode (in APPEND mode, partition #0 is copied into a fixed memory).
+ if (mem->mode_ == MEM_MODE_MAP) {
+ VP8RemapBitReader(&dec->br_, offset);
+ }
+ }
+ {
+ const uint8_t* const last_start = dec->parts_[last_part].buf_;
+ VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start,
+ mem->buf_ + mem->end_ - last_start);
+ }
+ if (NeedCompressedAlpha(idec)) {
+ ALPHDecoder* const alph_dec = dec->alph_dec_;
+ dec->alpha_data_ += offset;
+ if (alph_dec != NULL) {
+ if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
+ VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
+ assert(alph_vp8l_dec != NULL);
+ assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
+ VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
+ dec->alpha_data_ + ALPHA_HEADER_LEN,
+ dec->alpha_data_size_ - ALPHA_HEADER_LEN);
+ } else { // alph_dec->method_ == ALPHA_NO_COMPRESSION
+ // Nothing special to do in this case.
+ }
+ }
+ }
+ } else { // Resize lossless bitreader
+ VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
+ VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
+ }
+ }
+}
+
+// Appends data to the end of MemBuffer->buf_. It expands the allocated memory
+// size if required and also updates VP8BitReader's if new memory is allocated.
+static int AppendToMemBuffer(WebPIDecoder* const idec,
+ const uint8_t* const data, size_t data_size) {
+ VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
+ MemBuffer* const mem = &idec->mem_;
+ const int need_compressed_alpha = NeedCompressedAlpha(idec);
+ const uint8_t* const old_start = mem->buf_ + mem->start_;
+ const uint8_t* const old_base =
+ need_compressed_alpha ? dec->alpha_data_ : old_start;
+ assert(mem->mode_ == MEM_MODE_APPEND);
+ if (data_size > MAX_CHUNK_PAYLOAD) {
+ // security safeguard: trying to allocate more than what the format
+ // allows for a chunk should be considered a smoke smell.
+ return 0;
+ }
+
+ if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
+ const size_t new_mem_start = old_start - old_base;
+ const size_t current_size = MemDataSize(mem) + new_mem_start;
+ const uint64_t new_size = (uint64_t)current_size + data_size;
+ const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
+ uint8_t* const new_buf =
+ (uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
+ if (new_buf == NULL) return 0;
+ memcpy(new_buf, old_base, current_size);
+ WebPSafeFree(mem->buf_);
+ mem->buf_ = new_buf;
+ mem->buf_size_ = (size_t)extra_size;
+ mem->start_ = new_mem_start;
+ mem->end_ = current_size;
+ }
+
+ memcpy(mem->buf_ + mem->end_, data, data_size);
+ mem->end_ += data_size;
+ assert(mem->end_ <= mem->buf_size_);
+
+ DoRemap(idec, mem->buf_ + mem->start_ - old_start);
+ return 1;
+}
+
+static int RemapMemBuffer(WebPIDecoder* const idec,
+ const uint8_t* const data, size_t data_size) {
+ MemBuffer* const mem = &idec->mem_;
+ const uint8_t* const old_buf = mem->buf_;
+ const uint8_t* const old_start = old_buf + mem->start_;
+ assert(mem->mode_ == MEM_MODE_MAP);
+
+ if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
+
+ mem->buf_ = (uint8_t*)data;
+ mem->end_ = mem->buf_size_ = data_size;
+
+ DoRemap(idec, mem->buf_ + mem->start_ - old_start);
+ return 1;
+}
+
+static void InitMemBuffer(MemBuffer* const mem) {
+ mem->mode_ = MEM_MODE_NONE;
+ mem->buf_ = NULL;
+ mem->buf_size_ = 0;
+ mem->part0_buf_ = NULL;
+ mem->part0_size_ = 0;
+}
+
+static void ClearMemBuffer(MemBuffer* const mem) {
+ assert(mem);
+ if (mem->mode_ == MEM_MODE_APPEND) {
+ WebPSafeFree(mem->buf_);
+ WebPSafeFree((void*)mem->part0_buf_);
+ }
+}
+
+static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
+ if (mem->mode_ == MEM_MODE_NONE) {
+ mem->mode_ = expected; // switch to the expected mode
+ } else if (mem->mode_ != expected) {
+ return 0; // we mixed the modes => error
+ }
+ assert(mem->mode_ == expected); // mode is ok
+ return 1;
+}
+
+// To be called last.
+static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
+ const WebPDecoderOptions* const options = idec->params_.options;
+ WebPDecBuffer* const output = idec->params_.output;
+
+ idec->state_ = STATE_DONE;
+ if (options != NULL && options->flip) {
+ const VP8StatusCode status = WebPFlipBuffer(output);
+ if (status != VP8_STATUS_OK) return status;
+ }
+ if (idec->final_output_ != NULL) {
+ WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy
+ WebPFreeDecBuffer(&idec->output_);
+ *output = *idec->final_output_;
+ idec->final_output_ = NULL;
+ }
+ return VP8_STATUS_OK;
+}
+
+//------------------------------------------------------------------------------
+// Macroblock-decoding contexts
+
+static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
+ MBContext* const context) {
+ context->left_ = dec->mb_info_[-1];
+ context->info_ = dec->mb_info_[dec->mb_x_];
+ context->token_br_ = *token_br;
+}
+
+static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
+ VP8BitReader* const token_br) {
+ dec->mb_info_[-1] = context->left_;
+ dec->mb_info_[dec->mb_x_] = context->info_;
+ *token_br = context->token_br_;
+}
+
+//------------------------------------------------------------------------------
+
+static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
+ if (idec->state_ == STATE_VP8_DATA) {
+ VP8Io* const io = &idec->io_;
+ if (io->teardown != NULL) {
+ io->teardown(io);
+ }
+ }
+ idec->state_ = STATE_ERROR;
+ return error;
+}
+
+static void ChangeState(WebPIDecoder* const idec, DecState new_state,
+ size_t consumed_bytes) {
+ MemBuffer* const mem = &idec->mem_;
+ idec->state_ = new_state;
+ mem->start_ += consumed_bytes;
+ assert(mem->start_ <= mem->end_);
+ idec->io_.data = mem->buf_ + mem->start_;
+ idec->io_.data_size = MemDataSize(mem);
+}
+
+// Headers
+static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
+ MemBuffer* const mem = &idec->mem_;
+ const uint8_t* data = mem->buf_ + mem->start_;
+ size_t curr_size = MemDataSize(mem);
+ VP8StatusCode status;
+ WebPHeaderStructure headers;
+
+ headers.data = data;
+ headers.data_size = curr_size;
+ headers.have_all_data = 0;
+ status = WebPParseHeaders(&headers);
+ if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
+ return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
+ } else if (status != VP8_STATUS_OK) {
+ return IDecError(idec, status);
+ }
+
+ idec->chunk_size_ = headers.compressed_size;
+ idec->is_lossless_ = headers.is_lossless;
+ if (!idec->is_lossless_) {
+ VP8Decoder* const dec = VP8New();
+ if (dec == NULL) {
+ return VP8_STATUS_OUT_OF_MEMORY;
+ }
+ idec->dec_ = dec;
+ dec->alpha_data_ = headers.alpha_data;
+ dec->alpha_data_size_ = headers.alpha_data_size;
+ ChangeState(idec, STATE_VP8_HEADER, headers.offset);
+ } else {
+ VP8LDecoder* const dec = VP8LNew();
+ if (dec == NULL) {
+ return VP8_STATUS_OUT_OF_MEMORY;
+ }
+ idec->dec_ = dec;
+ ChangeState(idec, STATE_VP8L_HEADER, headers.offset);
+ }
+ return VP8_STATUS_OK;
+}
+
+static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
+ const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
+ const size_t curr_size = MemDataSize(&idec->mem_);
+ int width, height;
+ uint32_t bits;
+
+ if (curr_size < VP8_FRAME_HEADER_SIZE) {
+ // Not enough data bytes to extract VP8 Frame Header.
+ return VP8_STATUS_SUSPENDED;
+ }
+ if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) {
+ return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
+ }
+
+ bits = data[0] | (data[1] << 8) | (data[2] << 16);
+ idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE;
+
+ idec->io_.data = data;
+ idec->io_.data_size = curr_size;
+ idec->state_ = STATE_VP8_PARTS0;
+ return VP8_STATUS_OK;
+}
+
+// Partition #0
+static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {
+ VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
+ VP8BitReader* const br = &dec->br_;
+ const size_t part_size = br->buf_end_ - br->buf_;
+ MemBuffer* const mem = &idec->mem_;
+ assert(!idec->is_lossless_);
+ assert(mem->part0_buf_ == NULL);
+ // the following is a format limitation, no need for runtime check:
+ assert(part_size <= mem->part0_size_);
+ if (part_size == 0) { // can't have zero-size partition #0
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ if (mem->mode_ == MEM_MODE_APPEND) {
+ // We copy and grab ownership of the partition #0 data.
+ uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size);
+ if (part0_buf == NULL) {
+ return VP8_STATUS_OUT_OF_MEMORY;
+ }
+ memcpy(part0_buf, br->buf_, part_size);
+ mem->part0_buf_ = part0_buf;
+ VP8BitReaderSetBuffer(br, part0_buf, part_size);
+ } else {
+ // Else: just keep pointers to the partition #0's data in dec_->br_.
+ }
+ mem->start_ += part_size;
+ return VP8_STATUS_OK;
+}
+
+static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
+ VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
+ VP8Io* const io = &idec->io_;
+ const WebPDecParams* const params = &idec->params_;
+ WebPDecBuffer* const output = params->output;
+
+ // Wait till we have enough data for the whole partition #0
+ if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) {
+ return VP8_STATUS_SUSPENDED;
+ }
+
+ if (!VP8GetHeaders(dec, io)) {
+ const VP8StatusCode status = dec->status_;
+ if (status == VP8_STATUS_SUSPENDED ||
+ status == VP8_STATUS_NOT_ENOUGH_DATA) {
+ // treating NOT_ENOUGH_DATA as SUSPENDED state
+ return VP8_STATUS_SUSPENDED;
+ }
+ return IDecError(idec, status);
+ }
+
+ // Allocate/Verify output buffer now
+ dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
+ output);
+ if (dec->status_ != VP8_STATUS_OK) {
+ return IDecError(idec, dec->status_);
+ }
+ // This change must be done before calling VP8InitFrame()
+ dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,
+ io->width, io->height);
+ VP8InitDithering(params->options, dec);
+
+ dec->status_ = CopyParts0Data(idec);
+ if (dec->status_ != VP8_STATUS_OK) {
+ return IDecError(idec, dec->status_);
+ }
+
+ // Finish setting up the decoding parameters. Will call io->setup().
+ if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) {
+ return IDecError(idec, dec->status_);
+ }
+
+ // Note: past this point, teardown() must always be called
+ // in case of error.
+ idec->state_ = STATE_VP8_DATA;
+ // Allocate memory and prepare everything.
+ if (!VP8InitFrame(dec, io)) {
+ return IDecError(idec, dec->status_);
+ }
+ return VP8_STATUS_OK;
+}
+
+// Remaining partitions
+static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
+ VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
+ VP8Io* const io = &idec->io_;
+
+ assert(dec->ready_);
+ for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
+ if (idec->last_mb_y_ != dec->mb_y_) {
+ if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
+ // note: normally, error shouldn't occur since we already have the whole
+ // partition0 available here in DecodeRemaining(). Reaching EOF while
+ // reading intra modes really means a BITSTREAM_ERROR.
+ return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
+ }
+ idec->last_mb_y_ = dec->mb_y_;
+ }
+ for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
+ VP8BitReader* const token_br =
+ &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
+ MBContext context;
+ SaveContext(dec, token_br, &context);
+ if (!VP8DecodeMB(dec, token_br)) {
+ // We shouldn't fail when MAX_MB data was available
+ if (dec->num_parts_minus_one_ == 0 &&
+ MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
+ return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
+ }
+ RestoreContext(&context, dec, token_br);
+ return VP8_STATUS_SUSPENDED;
+ }
+ // Release buffer only if there is only one partition
+ if (dec->num_parts_minus_one_ == 0) {
+ idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
+ assert(idec->mem_.start_ <= idec->mem_.end_);
+ }
+ }
+ VP8InitScanline(dec); // Prepare for next scanline
+
+ // Reconstruct, filter and emit the row.
+ if (!VP8ProcessRow(dec, io)) {
+ return IDecError(idec, VP8_STATUS_USER_ABORT);
+ }
+ }
+ // Synchronize the thread and check for errors.
+ if (!VP8ExitCritical(dec, io)) {
+ return IDecError(idec, VP8_STATUS_USER_ABORT);
+ }
+ dec->ready_ = 0;
+ return FinishDecoding(idec);
+}
+
+static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec,
+ VP8StatusCode status) {
+ if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
+ return VP8_STATUS_SUSPENDED;
+ }
+ return IDecError(idec, status);
+}
+
+static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
+ VP8Io* const io = &idec->io_;
+ VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
+ const WebPDecParams* const params = &idec->params_;
+ WebPDecBuffer* const output = params->output;
+ size_t curr_size = MemDataSize(&idec->mem_);
+ assert(idec->is_lossless_);
+
+ // Wait until there's enough data for decoding header.
+ if (curr_size < (idec->chunk_size_ >> 3)) {
+ dec->status_ = VP8_STATUS_SUSPENDED;
+ return ErrorStatusLossless(idec, dec->status_);
+ }
+
+ if (!VP8LDecodeHeader(dec, io)) {
+ if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR &&
+ curr_size < idec->chunk_size_) {
+ dec->status_ = VP8_STATUS_SUSPENDED;
+ }
+ return ErrorStatusLossless(idec, dec->status_);
+ }
+ // Allocate/verify output buffer now.
+ dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
+ output);
+ if (dec->status_ != VP8_STATUS_OK) {
+ return IDecError(idec, dec->status_);
+ }
+
+ idec->state_ = STATE_VP8L_DATA;
+ return VP8_STATUS_OK;
+}
+
+static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
+ VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
+ const size_t curr_size = MemDataSize(&idec->mem_);
+ assert(idec->is_lossless_);
+
+ // Switch to incremental decoding if we don't have all the bytes available.
+ dec->incremental_ = (curr_size < idec->chunk_size_);
+
+ if (!VP8LDecodeImage(dec)) {
+ return ErrorStatusLossless(idec, dec->status_);
+ }
+ assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);
+ return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_
+ : FinishDecoding(idec);
+}
+
+ // Main decoding loop
+static VP8StatusCode IDecode(WebPIDecoder* idec) {
+ VP8StatusCode status = VP8_STATUS_SUSPENDED;
+
+ if (idec->state_ == STATE_WEBP_HEADER) {
+ status = DecodeWebPHeaders(idec);
+ } else {
+ if (idec->dec_ == NULL) {
+ return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
+ }
+ }
+ if (idec->state_ == STATE_VP8_HEADER) {
+ status = DecodeVP8FrameHeader(idec);
+ }
+ if (idec->state_ == STATE_VP8_PARTS0) {
+ status = DecodePartition0(idec);
+ }
+ if (idec->state_ == STATE_VP8_DATA) {
+ status = DecodeRemaining(idec);
+ }
+ if (idec->state_ == STATE_VP8L_HEADER) {
+ status = DecodeVP8LHeader(idec);
+ }
+ if (idec->state_ == STATE_VP8L_DATA) {
+ status = DecodeVP8LData(idec);
+ }
+ return status;
+}
+
+//------------------------------------------------------------------------------
+// Internal constructor
+
+static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer,
+ const WebPBitstreamFeatures* const features) {
+ WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
+ if (idec == NULL) {
+ return NULL;
+ }
+
+ idec->state_ = STATE_WEBP_HEADER;
+ idec->chunk_size_ = 0;
+
+ idec->last_mb_y_ = -1;
+
+ InitMemBuffer(&idec->mem_);
+ WebPInitDecBuffer(&idec->output_);
+ VP8InitIo(&idec->io_);
+
+ WebPResetDecParams(&idec->params_);
+ if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) {
+ idec->params_.output = &idec->output_;
+ idec->final_output_ = output_buffer;
+ if (output_buffer != NULL) {
+ idec->params_.output->colorspace = output_buffer->colorspace;
+ }
+ } else {
+ idec->params_.output = output_buffer;
+ idec->final_output_ = NULL;
+ }
+ WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
+
+ return idec;
+}
+
+//------------------------------------------------------------------------------
+// Public functions
+
+WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
+ return NewDecoder(output_buffer, NULL);
+}
+
+WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
+ WebPDecoderConfig* config) {
+ WebPIDecoder* idec;
+ WebPBitstreamFeatures tmp_features;
+ WebPBitstreamFeatures* const features =
+ (config == NULL) ? &tmp_features : &config->input;
+ memset(&tmp_features, 0, sizeof(tmp_features));
+
+ // Parse the bitstream's features, if requested:
+ if (data != NULL && data_size > 0) {
+ if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) {
+ return NULL;
+ }
+ }
+
+ // Create an instance of the incremental decoder
+ idec = (config != NULL) ? NewDecoder(&config->output, features)
+ : NewDecoder(NULL, features);
+ if (idec == NULL) {
+ return NULL;
+ }
+ // Finish initialization
+ if (config != NULL) {
+ idec->params_.options = &config->options;
+ }
+ return idec;
+}
+
+void WebPIDelete(WebPIDecoder* idec) {
+ if (idec == NULL) return;
+ if (idec->dec_ != NULL) {
+ if (!idec->is_lossless_) {
+ if (idec->state_ == STATE_VP8_DATA) {
+ // Synchronize the thread, clean-up and check for errors.
+ VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
+ }
+ VP8Delete((VP8Decoder*)idec->dec_);
+ } else {
+ VP8LDelete((VP8LDecoder*)idec->dec_);
+ }
+ }
+ ClearMemBuffer(&idec->mem_);
+ WebPFreeDecBuffer(&idec->output_);
+ WebPSafeFree(idec);
+}
+
+//------------------------------------------------------------------------------
+// Wrapper toward WebPINewDecoder
+
+WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE csp, uint8_t* output_buffer,
+ size_t output_buffer_size, int output_stride) {
+ const int is_external_memory = (output_buffer != NULL) ? 1 : 0;
+ WebPIDecoder* idec;
+
+ if (csp >= MODE_YUV) return NULL;
+ if (is_external_memory == 0) { // Overwrite parameters to sane values.
+ output_buffer_size = 0;
+ output_stride = 0;
+ } else { // A buffer was passed. Validate the other params.
+ if (output_stride == 0 || output_buffer_size == 0) {
+ return NULL; // invalid parameter.
+ }
+ }
+ idec = WebPINewDecoder(NULL);
+ if (idec == NULL) return NULL;
+ idec->output_.colorspace = csp;
+ idec->output_.is_external_memory = is_external_memory;
+ idec->output_.u.RGBA.rgba = output_buffer;
+ idec->output_.u.RGBA.stride = output_stride;
+ idec->output_.u.RGBA.size = output_buffer_size;
+ return idec;
+}
+
+WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
+ uint8_t* u, size_t u_size, int u_stride,
+ uint8_t* v, size_t v_size, int v_stride,
+ uint8_t* a, size_t a_size, int a_stride) {
+ const int is_external_memory = (luma != NULL) ? 1 : 0;
+ WebPIDecoder* idec;
+ WEBP_CSP_MODE colorspace;
+
+ if (is_external_memory == 0) { // Overwrite parameters to sane values.
+ luma_size = u_size = v_size = a_size = 0;
+ luma_stride = u_stride = v_stride = a_stride = 0;
+ u = v = a = NULL;
+ colorspace = MODE_YUVA;
+ } else { // A luma buffer was passed. Validate the other parameters.
+ if (u == NULL || v == NULL) return NULL;
+ if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL;
+ if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL;
+ if (a != NULL) {
+ if (a_size == 0 || a_stride == 0) return NULL;
+ }
+ colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
+ }
+
+ idec = WebPINewDecoder(NULL);
+ if (idec == NULL) return NULL;
+
+ idec->output_.colorspace = colorspace;
+ idec->output_.is_external_memory = is_external_memory;
+ idec->output_.u.YUVA.y = luma;
+ idec->output_.u.YUVA.y_stride = luma_stride;
+ idec->output_.u.YUVA.y_size = luma_size;
+ idec->output_.u.YUVA.u = u;
+ idec->output_.u.YUVA.u_stride = u_stride;
+ idec->output_.u.YUVA.u_size = u_size;
+ idec->output_.u.YUVA.v = v;
+ idec->output_.u.YUVA.v_stride = v_stride;
+ idec->output_.u.YUVA.v_size = v_size;
+ idec->output_.u.YUVA.a = a;
+ idec->output_.u.YUVA.a_stride = a_stride;
+ idec->output_.u.YUVA.a_size = a_size;
+ return idec;
+}
+
+WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
+ uint8_t* u, size_t u_size, int u_stride,
+ uint8_t* v, size_t v_size, int v_stride) {
+ return WebPINewYUVA(luma, luma_size, luma_stride,
+ u, u_size, u_stride,
+ v, v_size, v_stride,
+ NULL, 0, 0);
+}
+
+//------------------------------------------------------------------------------
+
+static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
+ assert(idec);
+ if (idec->state_ == STATE_ERROR) {
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ if (idec->state_ == STATE_DONE) {
+ return VP8_STATUS_OK;
+ }
+ return VP8_STATUS_SUSPENDED;
+}
+
+VP8StatusCode WebPIAppend(WebPIDecoder* idec,
+ const uint8_t* data, size_t data_size) {
+ VP8StatusCode status;
+ if (idec == NULL || data == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ status = IDecCheckStatus(idec);
+ if (status != VP8_STATUS_SUSPENDED) {
+ return status;
+ }
+ // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
+ if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ // Append data to memory buffer
+ if (!AppendToMemBuffer(idec, data, data_size)) {
+ return VP8_STATUS_OUT_OF_MEMORY;
+ }
+ return IDecode(idec);
+}
+
+VP8StatusCode WebPIUpdate(WebPIDecoder* idec,
+ const uint8_t* data, size_t data_size) {
+ VP8StatusCode status;
+ if (idec == NULL || data == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ status = IDecCheckStatus(idec);
+ if (status != VP8_STATUS_SUSPENDED) {
+ return status;
+ }
+ // Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
+ if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ // Make the memory buffer point to the new buffer
+ if (!RemapMemBuffer(idec, data, data_size)) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ return IDecode(idec);
+}
+
+//------------------------------------------------------------------------------
+
+static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
+ if (idec == NULL || idec->dec_ == NULL) {
+ return NULL;
+ }
+ if (idec->state_ <= STATE_VP8_PARTS0) {
+ return NULL;
+ }
+ if (idec->final_output_ != NULL) {
+ return NULL; // not yet slow-copied
+ }
+ return idec->params_.output;
+}
+
+const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
+ int* left, int* top,
+ int* width, int* height) {
+ const WebPDecBuffer* const src = GetOutputBuffer(idec);
+ if (left != NULL) *left = 0;
+ if (top != NULL) *top = 0;
+ if (src != NULL) {
+ if (width != NULL) *width = src->width;
+ if (height != NULL) *height = idec->params_.last_y;
+ } else {
+ if (width != NULL) *width = 0;
+ if (height != NULL) *height = 0;
+ }
+ return src;
+}
+
+uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
+ int* width, int* height, int* stride) {
+ const WebPDecBuffer* const src = GetOutputBuffer(idec);
+ if (src == NULL) return NULL;
+ if (src->colorspace >= MODE_YUV) {
+ return NULL;
+ }
+
+ if (last_y != NULL) *last_y = idec->params_.last_y;
+ if (width != NULL) *width = src->width;
+ if (height != NULL) *height = src->height;
+ if (stride != NULL) *stride = src->u.RGBA.stride;
+
+ return src->u.RGBA.rgba;
+}
+
+uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
+ uint8_t** u, uint8_t** v, uint8_t** a,
+ int* width, int* height,
+ int* stride, int* uv_stride, int* a_stride) {
+ const WebPDecBuffer* const src = GetOutputBuffer(idec);
+ if (src == NULL) return NULL;
+ if (src->colorspace < MODE_YUV) {
+ return NULL;
+ }
+
+ if (last_y != NULL) *last_y = idec->params_.last_y;
+ if (u != NULL) *u = src->u.YUVA.u;
+ if (v != NULL) *v = src->u.YUVA.v;
+ if (a != NULL) *a = src->u.YUVA.a;
+ if (width != NULL) *width = src->width;
+ if (height != NULL) *height = src->height;
+ if (stride != NULL) *stride = src->u.YUVA.y_stride;
+ if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride;
+ if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride;
+
+ return src->u.YUVA.y;
+}
+
+int WebPISetIOHooks(WebPIDecoder* const idec,
+ VP8IoPutHook put,
+ VP8IoSetupHook setup,
+ VP8IoTeardownHook teardown,
+ void* user_data) {
+ if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) {
+ return 0;
+ }
+
+ idec->io_.put = put;
+ idec->io_.setup = setup;
+ idec->io_.teardown = teardown;
+ idec->io_.opaque = user_data;
+
+ return 1;
+}
diff --git a/src/third_party/libwebp/src/dec/io_dec.c b/src/third_party/libwebp/src/dec/io_dec.c
new file mode 100644
index 0000000..da9e55d
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/io_dec.c
@@ -0,0 +1,655 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// functions for sample output.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#endif
+#include "src/dec/vp8i_dec.h"
+#include "src/dec/webpi_dec.h"
+#include "src/dsp/dsp.h"
+#include "src/dsp/yuv.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// Main YUV<->RGB conversion functions
+
+static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
+ WebPDecBuffer* output = p->output;
+ const WebPYUVABuffer* const buf = &output->u.YUVA;
+ uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride;
+ uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride;
+ uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride;
+ const int mb_w = io->mb_w;
+ const int mb_h = io->mb_h;
+ const int uv_w = (mb_w + 1) / 2;
+ const int uv_h = (mb_h + 1) / 2;
+ int j;
+ for (j = 0; j < mb_h; ++j) {
+ memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
+ }
+ for (j = 0; j < uv_h; ++j) {
+ memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
+ memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
+ }
+ return io->mb_h;
+}
+
+// Point-sampling U/V sampler.
+static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
+ WebPDecBuffer* const output = p->output;
+ WebPRGBABuffer* const buf = &output->u.RGBA;
+ uint8_t* const dst = buf->rgba + io->mb_y * buf->stride;
+ WebPSamplerProcessPlane(io->y, io->y_stride,
+ io->u, io->v, io->uv_stride,
+ dst, buf->stride, io->mb_w, io->mb_h,
+ WebPSamplers[output->colorspace]);
+ return io->mb_h;
+}
+
+//------------------------------------------------------------------------------
+// Fancy upsampling
+
+#ifdef FANCY_UPSAMPLING
+static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
+ int num_lines_out = io->mb_h; // a priori guess
+ const WebPRGBABuffer* const buf = &p->output->u.RGBA;
+ uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
+ WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
+ const uint8_t* cur_y = io->y;
+ const uint8_t* cur_u = io->u;
+ const uint8_t* cur_v = io->v;
+ const uint8_t* top_u = p->tmp_u;
+ const uint8_t* top_v = p->tmp_v;
+ int y = io->mb_y;
+ const int y_end = io->mb_y + io->mb_h;
+ const int mb_w = io->mb_w;
+ const int uv_w = (mb_w + 1) / 2;
+
+ if (y == 0) {
+ // First line is special cased. We mirror the u/v samples at boundary.
+ upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w);
+ } else {
+ // We can finish the left-over line from previous call.
+ upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
+ dst - buf->stride, dst, mb_w);
+ ++num_lines_out;
+ }
+ // Loop over each output pairs of row.
+ for (; y + 2 < y_end; y += 2) {
+ top_u = cur_u;
+ top_v = cur_v;
+ cur_u += io->uv_stride;
+ cur_v += io->uv_stride;
+ dst += 2 * buf->stride;
+ cur_y += 2 * io->y_stride;
+ upsample(cur_y - io->y_stride, cur_y,
+ top_u, top_v, cur_u, cur_v,
+ dst - buf->stride, dst, mb_w);
+ }
+ // move to last row
+ cur_y += io->y_stride;
+ if (io->crop_top + y_end < io->crop_bottom) {
+ // Save the unfinished samples for next call (as we're not done yet).
+ memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
+ memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
+ memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
+ // The fancy upsampler leaves a row unfinished behind
+ // (except for the very last row)
+ num_lines_out--;
+ } else {
+ // Process the very last row of even-sized picture
+ if (!(y_end & 1)) {
+ upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
+ dst + buf->stride, NULL, mb_w);
+ }
+ }
+ return num_lines_out;
+}
+
+#endif /* FANCY_UPSAMPLING */
+
+//------------------------------------------------------------------------------
+
+static void FillAlphaPlane(uint8_t* dst, int w, int h, int stride) {
+ int j;
+ for (j = 0; j < h; ++j) {
+ memset(dst, 0xff, w * sizeof(*dst));
+ dst += stride;
+ }
+}
+
+static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
+ int expected_num_lines_out) {
+ const uint8_t* alpha = io->a;
+ const WebPYUVABuffer* const buf = &p->output->u.YUVA;
+ const int mb_w = io->mb_w;
+ const int mb_h = io->mb_h;
+ uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
+ int j;
+ (void)expected_num_lines_out;
+ assert(expected_num_lines_out == mb_h);
+ if (alpha != NULL) {
+ for (j = 0; j < mb_h; ++j) {
+ memcpy(dst, alpha, mb_w * sizeof(*dst));
+ alpha += io->width;
+ dst += buf->a_stride;
+ }
+ } else if (buf->a != NULL) {
+ // the user requested alpha, but there is none, set it to opaque.
+ FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride);
+ }
+ return 0;
+}
+
+static int GetAlphaSourceRow(const VP8Io* const io,
+ const uint8_t** alpha, int* const num_rows) {
+ int start_y = io->mb_y;
+ *num_rows = io->mb_h;
+
+ // Compensate for the 1-line delay of the fancy upscaler.
+ // This is similar to EmitFancyRGB().
+ if (io->fancy_upsampling) {
+ if (start_y == 0) {
+ // We don't process the last row yet. It'll be done during the next call.
+ --*num_rows;
+ } else {
+ --start_y;
+ // Fortunately, *alpha data is persistent, so we can go back
+ // one row and finish alpha blending, now that the fancy upscaler
+ // completed the YUV->RGB interpolation.
+ *alpha -= io->width;
+ }
+ if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
+ // If it's the very last call, we process all the remaining rows!
+ *num_rows = io->crop_bottom - io->crop_top - start_y;
+ }
+ }
+ return start_y;
+}
+
+static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
+ int expected_num_lines_out) {
+ const uint8_t* alpha = io->a;
+ if (alpha != NULL) {
+ const int mb_w = io->mb_w;
+ const WEBP_CSP_MODE colorspace = p->output->colorspace;
+ const int alpha_first =
+ (colorspace == MODE_ARGB || colorspace == MODE_Argb);
+ const WebPRGBABuffer* const buf = &p->output->u.RGBA;
+ int num_rows;
+ const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
+ uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
+ uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3);
+ const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w,
+ num_rows, dst, buf->stride);
+ (void)expected_num_lines_out;
+ assert(expected_num_lines_out == num_rows);
+ // has_alpha is true if there's non-trivial alpha to premultiply with.
+ if (has_alpha && WebPIsPremultipliedMode(colorspace)) {
+ WebPApplyAlphaMultiply(base_rgba, alpha_first,
+ mb_w, num_rows, buf->stride);
+ }
+ }
+ return 0;
+}
+
+static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
+ int expected_num_lines_out) {
+ const uint8_t* alpha = io->a;
+ if (alpha != NULL) {
+ const int mb_w = io->mb_w;
+ const WEBP_CSP_MODE colorspace = p->output->colorspace;
+ const WebPRGBABuffer* const buf = &p->output->u.RGBA;
+ int num_rows;
+ const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
+ uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ uint8_t* alpha_dst = base_rgba;
+#else
+ uint8_t* alpha_dst = base_rgba + 1;
+#endif
+ uint32_t alpha_mask = 0x0f;
+ int i, j;
+ for (j = 0; j < num_rows; ++j) {
+ for (i = 0; i < mb_w; ++i) {
+ // Fill in the alpha value (converted to 4 bits).
+ const uint32_t alpha_value = alpha[i] >> 4;
+ alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
+ alpha_mask &= alpha_value;
+ }
+ alpha += io->width;
+ alpha_dst += buf->stride;
+ }
+ (void)expected_num_lines_out;
+ assert(expected_num_lines_out == num_rows);
+ if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
+ WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
+ }
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// YUV rescaling (no final RGB conversion needed)
+
+#if !defined(WEBP_REDUCE_SIZE)
+static int Rescale(const uint8_t* src, int src_stride,
+ int new_lines, WebPRescaler* const wrk) {
+ int num_lines_out = 0;
+ while (new_lines > 0) { // import new contributions of source rows.
+ const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
+ src += lines_in * src_stride;
+ new_lines -= lines_in;
+ num_lines_out += WebPRescalerExport(wrk); // emit output row(s)
+ }
+ return num_lines_out;
+}
+
+static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
+ const int mb_h = io->mb_h;
+ const int uv_mb_h = (mb_h + 1) >> 1;
+ WebPRescaler* const scaler = p->scaler_y;
+ int num_lines_out = 0;
+ if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) {
+ // Before rescaling, we premultiply the luma directly into the io->y
+ // internal buffer. This is OK since these samples are not used for
+ // intra-prediction (the top samples are saved in cache_y_/u_/v_).
+ // But we need to cast the const away, though.
+ WebPMultRows((uint8_t*)io->y, io->y_stride,
+ io->a, io->width, io->mb_w, mb_h, 0);
+ }
+ num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler);
+ Rescale(io->u, io->uv_stride, uv_mb_h, p->scaler_u);
+ Rescale(io->v, io->uv_stride, uv_mb_h, p->scaler_v);
+ return num_lines_out;
+}
+
+static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
+ int expected_num_lines_out) {
+ const WebPYUVABuffer* const buf = &p->output->u.YUVA;
+ uint8_t* const dst_a = buf->a + p->last_y * buf->a_stride;
+ if (io->a != NULL) {
+ uint8_t* const dst_y = buf->y + p->last_y * buf->y_stride;
+ const int num_lines_out = Rescale(io->a, io->width, io->mb_h, p->scaler_a);
+ assert(expected_num_lines_out == num_lines_out);
+ if (num_lines_out > 0) { // unmultiply the Y
+ WebPMultRows(dst_y, buf->y_stride, dst_a, buf->a_stride,
+ p->scaler_a->dst_width, num_lines_out, 1);
+ }
+ } else if (buf->a != NULL) {
+ // the user requested alpha, but there is none, set it to opaque.
+ assert(p->last_y + expected_num_lines_out <= io->scaled_height);
+ FillAlphaPlane(dst_a, io->scaled_width, expected_num_lines_out,
+ buf->a_stride);
+ }
+ return 0;
+}
+
+static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
+ const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
+ const WebPYUVABuffer* const buf = &p->output->u.YUVA;
+ const int out_width = io->scaled_width;
+ const int out_height = io->scaled_height;
+ const int uv_out_width = (out_width + 1) >> 1;
+ const int uv_out_height = (out_height + 1) >> 1;
+ const int uv_in_width = (io->mb_w + 1) >> 1;
+ const int uv_in_height = (io->mb_h + 1) >> 1;
+ const size_t work_size = 2 * out_width; // scratch memory for luma rescaler
+ const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones
+ size_t tmp_size, rescaler_size;
+ rescaler_t* work;
+ WebPRescaler* scalers;
+ const int num_rescalers = has_alpha ? 4 : 3;
+
+ tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work);
+ if (has_alpha) {
+ tmp_size += work_size * sizeof(*work);
+ }
+ rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
+
+ p->memory = WebPSafeMalloc(1ULL, tmp_size + rescaler_size);
+ if (p->memory == NULL) {
+ return 0; // memory error
+ }
+ work = (rescaler_t*)p->memory;
+
+ scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + tmp_size);
+ p->scaler_y = &scalers[0];
+ p->scaler_u = &scalers[1];
+ p->scaler_v = &scalers[2];
+ p->scaler_a = has_alpha ? &scalers[3] : NULL;
+
+ WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
+ buf->y, out_width, out_height, buf->y_stride, 1,
+ work);
+ WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
+ buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
+ work + work_size);
+ WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
+ buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
+ work + work_size + uv_work_size);
+ p->emit = EmitRescaledYUV;
+
+ if (has_alpha) {
+ WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
+ buf->a, out_width, out_height, buf->a_stride, 1,
+ work + work_size + 2 * uv_work_size);
+ p->emit_alpha = EmitRescaledAlphaYUV;
+ WebPInitAlphaProcessing();
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// RGBA rescaling
+
+static int ExportRGB(WebPDecParams* const p, int y_pos) {
+ const WebPYUV444Converter convert =
+ WebPYUV444Converters[p->output->colorspace];
+ const WebPRGBABuffer* const buf = &p->output->u.RGBA;
+ uint8_t* dst = buf->rgba + y_pos * buf->stride;
+ int num_lines_out = 0;
+ // For RGB rescaling, because of the YUV420, current scan position
+ // U/V can be +1/-1 line from the Y one. Hence the double test.
+ while (WebPRescalerHasPendingOutput(p->scaler_y) &&
+ WebPRescalerHasPendingOutput(p->scaler_u)) {
+ assert(y_pos + num_lines_out < p->output->height);
+ assert(p->scaler_u->y_accum == p->scaler_v->y_accum);
+ WebPRescalerExportRow(p->scaler_y);
+ WebPRescalerExportRow(p->scaler_u);
+ WebPRescalerExportRow(p->scaler_v);
+ convert(p->scaler_y->dst, p->scaler_u->dst, p->scaler_v->dst,
+ dst, p->scaler_y->dst_width);
+ dst += buf->stride;
+ ++num_lines_out;
+ }
+ return num_lines_out;
+}
+
+static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
+ const int mb_h = io->mb_h;
+ const int uv_mb_h = (mb_h + 1) >> 1;
+ int j = 0, uv_j = 0;
+ int num_lines_out = 0;
+ while (j < mb_h) {
+ const int y_lines_in =
+ WebPRescalerImport(p->scaler_y, mb_h - j,
+ io->y + j * io->y_stride, io->y_stride);
+ j += y_lines_in;
+ if (WebPRescaleNeededLines(p->scaler_u, uv_mb_h - uv_j)) {
+ const int u_lines_in =
+ WebPRescalerImport(p->scaler_u, uv_mb_h - uv_j,
+ io->u + uv_j * io->uv_stride, io->uv_stride);
+ const int v_lines_in =
+ WebPRescalerImport(p->scaler_v, uv_mb_h - uv_j,
+ io->v + uv_j * io->uv_stride, io->uv_stride);
+ (void)v_lines_in; // remove a gcc warning
+ assert(u_lines_in == v_lines_in);
+ uv_j += u_lines_in;
+ }
+ num_lines_out += ExportRGB(p, p->last_y + num_lines_out);
+ }
+ return num_lines_out;
+}
+
+static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
+ const WebPRGBABuffer* const buf = &p->output->u.RGBA;
+ uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
+ const WEBP_CSP_MODE colorspace = p->output->colorspace;
+ const int alpha_first =
+ (colorspace == MODE_ARGB || colorspace == MODE_Argb);
+ uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
+ int num_lines_out = 0;
+ const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
+ uint32_t non_opaque = 0;
+ const int width = p->scaler_a->dst_width;
+
+ while (WebPRescalerHasPendingOutput(p->scaler_a) &&
+ num_lines_out < max_lines_out) {
+ assert(y_pos + num_lines_out < p->output->height);
+ WebPRescalerExportRow(p->scaler_a);
+ non_opaque |= WebPDispatchAlpha(p->scaler_a->dst, 0, width, 1, dst, 0);
+ dst += buf->stride;
+ ++num_lines_out;
+ }
+ if (is_premult_alpha && non_opaque) {
+ WebPApplyAlphaMultiply(base_rgba, alpha_first,
+ width, num_lines_out, buf->stride);
+ }
+ return num_lines_out;
+}
+
+static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
+ int max_lines_out) {
+ const WebPRGBABuffer* const buf = &p->output->u.RGBA;
+ uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ uint8_t* alpha_dst = base_rgba;
+#else
+ uint8_t* alpha_dst = base_rgba + 1;
+#endif
+ int num_lines_out = 0;
+ const WEBP_CSP_MODE colorspace = p->output->colorspace;
+ const int width = p->scaler_a->dst_width;
+ const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
+ uint32_t alpha_mask = 0x0f;
+
+ while (WebPRescalerHasPendingOutput(p->scaler_a) &&
+ num_lines_out < max_lines_out) {
+ int i;
+ assert(y_pos + num_lines_out < p->output->height);
+ WebPRescalerExportRow(p->scaler_a);
+ for (i = 0; i < width; ++i) {
+ // Fill in the alpha value (converted to 4 bits).
+ const uint32_t alpha_value = p->scaler_a->dst[i] >> 4;
+ alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
+ alpha_mask &= alpha_value;
+ }
+ alpha_dst += buf->stride;
+ ++num_lines_out;
+ }
+ if (is_premult_alpha && alpha_mask != 0x0f) {
+ WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
+ }
+ return num_lines_out;
+}
+
+static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
+ int expected_num_out_lines) {
+ if (io->a != NULL) {
+ WebPRescaler* const scaler = p->scaler_a;
+ int lines_left = expected_num_out_lines;
+ const int y_end = p->last_y + lines_left;
+ while (lines_left > 0) {
+ const int row_offset = scaler->src_y - io->mb_y;
+ WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y,
+ io->a + row_offset * io->width, io->width);
+ lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
+ }
+ }
+ return 0;
+}
+
+static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
+ const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
+ const int out_width = io->scaled_width;
+ const int out_height = io->scaled_height;
+ const int uv_in_width = (io->mb_w + 1) >> 1;
+ const int uv_in_height = (io->mb_h + 1) >> 1;
+ const size_t work_size = 2 * out_width; // scratch memory for one rescaler
+ rescaler_t* work; // rescalers work area
+ uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
+ size_t tmp_size1, tmp_size2, total_size, rescaler_size;
+ WebPRescaler* scalers;
+ const int num_rescalers = has_alpha ? 4 : 3;
+
+ tmp_size1 = 3 * work_size;
+ tmp_size2 = 3 * out_width;
+ if (has_alpha) {
+ tmp_size1 += work_size;
+ tmp_size2 += out_width;
+ }
+ total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp);
+ rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST;
+
+ p->memory = WebPSafeMalloc(1ULL, total_size + rescaler_size);
+ if (p->memory == NULL) {
+ return 0; // memory error
+ }
+ work = (rescaler_t*)p->memory;
+ tmp = (uint8_t*)(work + tmp_size1);
+
+ scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size);
+ p->scaler_y = &scalers[0];
+ p->scaler_u = &scalers[1];
+ p->scaler_v = &scalers[2];
+ p->scaler_a = has_alpha ? &scalers[3] : NULL;
+
+ WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h,
+ tmp + 0 * out_width, out_width, out_height, 0, 1,
+ work + 0 * work_size);
+ WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height,
+ tmp + 1 * out_width, out_width, out_height, 0, 1,
+ work + 1 * work_size);
+ WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height,
+ tmp + 2 * out_width, out_width, out_height, 0, 1,
+ work + 2 * work_size);
+ p->emit = EmitRescaledRGB;
+ WebPInitYUV444Converters();
+
+ if (has_alpha) {
+ WebPRescalerInit(p->scaler_a, io->mb_w, io->mb_h,
+ tmp + 3 * out_width, out_width, out_height, 0, 1,
+ work + 3 * work_size);
+ p->emit_alpha = EmitRescaledAlphaRGB;
+ if (p->output->colorspace == MODE_RGBA_4444 ||
+ p->output->colorspace == MODE_rgbA_4444) {
+ p->emit_alpha_row = ExportAlphaRGBA4444;
+ } else {
+ p->emit_alpha_row = ExportAlpha;
+ }
+ WebPInitAlphaProcessing();
+ }
+ return 1;
+}
+
+#endif // WEBP_REDUCE_SIZE
+
+//------------------------------------------------------------------------------
+// Default custom functions
+
+static int CustomSetup(VP8Io* io) {
+ WebPDecParams* const p = (WebPDecParams*)io->opaque;
+ const WEBP_CSP_MODE colorspace = p->output->colorspace;
+ const int is_rgb = WebPIsRGBMode(colorspace);
+ const int is_alpha = WebPIsAlphaMode(colorspace);
+
+ p->memory = NULL;
+ p->emit = NULL;
+ p->emit_alpha = NULL;
+ p->emit_alpha_row = NULL;
+ if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
+ return 0;
+ }
+ if (is_alpha && WebPIsPremultipliedMode(colorspace)) {
+ WebPInitUpsamplers();
+ }
+ if (io->use_scaling) {
+#if !defined(WEBP_REDUCE_SIZE)
+ const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
+ if (!ok) {
+ return 0; // memory error
+ }
+#else
+ return 0; // rescaling support not compiled
+#endif
+ } else {
+ if (is_rgb) {
+ WebPInitSamplers();
+ p->emit = EmitSampledRGB; // default
+ if (io->fancy_upsampling) {
+#ifdef FANCY_UPSAMPLING
+ const int uv_width = (io->mb_w + 1) >> 1;
+ p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width));
+ if (p->memory == NULL) {
+ return 0; // memory error.
+ }
+ p->tmp_y = (uint8_t*)p->memory;
+ p->tmp_u = p->tmp_y + io->mb_w;
+ p->tmp_v = p->tmp_u + uv_width;
+ p->emit = EmitFancyRGB;
+ WebPInitUpsamplers();
+#endif
+ }
+ } else {
+ p->emit = EmitYUV;
+ }
+ if (is_alpha) { // need transparency output
+ p->emit_alpha =
+ (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
+ EmitAlphaRGBA4444
+ : is_rgb ? EmitAlphaRGB
+ : EmitAlphaYUV;
+ if (is_rgb) {
+ WebPInitAlphaProcessing();
+ }
+ }
+ }
+
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+static int CustomPut(const VP8Io* io) {
+ WebPDecParams* const p = (WebPDecParams*)io->opaque;
+ const int mb_w = io->mb_w;
+ const int mb_h = io->mb_h;
+ int num_lines_out;
+ assert(!(io->mb_y & 1));
+
+ if (mb_w <= 0 || mb_h <= 0) {
+ return 0;
+ }
+ num_lines_out = p->emit(io, p);
+ if (p->emit_alpha != NULL) {
+ p->emit_alpha(io, p, num_lines_out);
+ }
+ p->last_y += num_lines_out;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+static void CustomTeardown(const VP8Io* io) {
+ WebPDecParams* const p = (WebPDecParams*)io->opaque;
+ WebPSafeFree(p->memory);
+ p->memory = NULL;
+}
+
+//------------------------------------------------------------------------------
+// Main entry point
+
+void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
+ io->put = CustomPut;
+ io->setup = CustomSetup;
+ io->teardown = CustomTeardown;
+ io->opaque = params;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dec/quant_dec.c b/src/third_party/libwebp/src/dec/quant_dec.c
new file mode 100644
index 0000000..f07212a
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/quant_dec.c
@@ -0,0 +1,110 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Quantizer initialization
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dec/vp8i_dec.h"
+
+static WEBP_INLINE int clip(int v, int M) {
+ return v < 0 ? 0 : v > M ? M : v;
+}
+
+// Paragraph 14.1
+static const uint8_t kDcTable[128] = {
+ 4, 5, 6, 7, 8, 9, 10, 10,
+ 11, 12, 13, 14, 15, 16, 17, 17,
+ 18, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 25, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89,
+ 91, 93, 95, 96, 98, 100, 101, 102,
+ 104, 106, 108, 110, 112, 114, 116, 118,
+ 122, 124, 126, 128, 130, 132, 134, 136,
+ 138, 140, 143, 145, 148, 151, 154, 157
+};
+
+static const uint16_t kAcTable[128] = {
+ 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 60,
+ 62, 64, 66, 68, 70, 72, 74, 76,
+ 78, 80, 82, 84, 86, 88, 90, 92,
+ 94, 96, 98, 100, 102, 104, 106, 108,
+ 110, 112, 114, 116, 119, 122, 125, 128,
+ 131, 134, 137, 140, 143, 146, 149, 152,
+ 155, 158, 161, 164, 167, 170, 173, 177,
+ 181, 185, 189, 193, 197, 201, 205, 209,
+ 213, 217, 221, 225, 229, 234, 239, 245,
+ 249, 254, 259, 264, 269, 274, 279, 284
+};
+
+//------------------------------------------------------------------------------
+// Paragraph 9.6
+
+void VP8ParseQuant(VP8Decoder* const dec) {
+ VP8BitReader* const br = &dec->br_;
+ const int base_q0 = VP8GetValue(br, 7);
+ const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
+ const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
+ const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
+ const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
+ const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
+
+ const VP8SegmentHeader* const hdr = &dec->segment_hdr_;
+ int i;
+
+ for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ int q;
+ if (hdr->use_segment_) {
+ q = hdr->quantizer_[i];
+ if (!hdr->absolute_delta_) {
+ q += base_q0;
+ }
+ } else {
+ if (i > 0) {
+ dec->dqm_[i] = dec->dqm_[0];
+ continue;
+ } else {
+ q = base_q0;
+ }
+ }
+ {
+ VP8QuantMatrix* const m = &dec->dqm_[i];
+ m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)];
+ m->y1_mat_[1] = kAcTable[clip(q + 0, 127)];
+
+ m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2;
+ // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
+ // The smallest precision for that is '(x*6349) >> 12' but 16 is a good
+ // word size.
+ m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16;
+ if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8;
+
+ m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
+ m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
+
+ m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/src/third_party/libwebp/src/dec/tree_dec.c b/src/third_party/libwebp/src/dec/tree_dec.c
new file mode 100644
index 0000000..1bd4dfc
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/tree_dec.c
@@ -0,0 +1,536 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Coding trees and probas
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/string_poem.h"
+#endif
+
+#include "src/dec/vp8i_dec.h"
+#include "src/utils/bit_reader_inl_utils.h"
+
+#if !defined(USE_GENERIC_TREE)
+#if !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__)
+// using a table is ~1-2% slower on ARM. Prefer the coded-tree approach then.
+#define USE_GENERIC_TREE 1 // ALTERNATE_CODE
+#else
+#define USE_GENERIC_TREE 0
+#endif
+#endif // USE_GENERIC_TREE
+
+#if (USE_GENERIC_TREE == 1)
+static const int8_t kYModesIntra4[18] = {
+ -B_DC_PRED, 1,
+ -B_TM_PRED, 2,
+ -B_VE_PRED, 3,
+ 4, 6,
+ -B_HE_PRED, 5,
+ -B_RD_PRED, -B_VR_PRED,
+ -B_LD_PRED, 7,
+ -B_VL_PRED, 8,
+ -B_HD_PRED, -B_HU_PRED
+};
+#endif
+
+//------------------------------------------------------------------------------
+// Default probabilities
+
+// Paragraph 13.5
+static const uint8_t
+ CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
+ { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
+ { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
+ { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
+ { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
+ { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
+ },
+ { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
+ { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
+ { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
+ },
+ { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
+ { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
+ { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
+ { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
+ { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
+ { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
+ { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
+ { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
+ { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
+ },
+ { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
+ { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
+ { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
+ },
+ { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
+ { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
+ { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
+ },
+ { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
+ { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
+ { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
+ { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
+ { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
+ },
+ { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
+ { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
+ { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
+ },
+ { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
+ { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
+ { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
+ },
+ { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
+ { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
+ { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
+ { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
+ },
+ { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
+ { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
+ { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
+ { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
+ { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
+ },
+ { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
+ { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
+ { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
+ { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
+ { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
+ },
+ { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
+ { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
+ { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
+ },
+ { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
+ { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
+ { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
+ },
+ { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
+ { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
+ { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
+ },
+ { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
+ { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
+ { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
+ },
+ { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
+ { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
+ { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
+ { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
+ { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
+ },
+ { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ }
+};
+
+// Paragraph 11.5
+static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
+ { { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
+ { 152, 179, 64, 126, 170, 118, 46, 70, 95 },
+ { 175, 69, 143, 80, 85, 82, 72, 155, 103 },
+ { 56, 58, 10, 171, 218, 189, 17, 13, 152 },
+ { 114, 26, 17, 163, 44, 195, 21, 10, 173 },
+ { 121, 24, 80, 195, 26, 62, 44, 64, 85 },
+ { 144, 71, 10, 38, 171, 213, 144, 34, 26 },
+ { 170, 46, 55, 19, 136, 160, 33, 206, 71 },
+ { 63, 20, 8, 114, 114, 208, 12, 9, 226 },
+ { 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
+ { { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
+ { 72, 187, 100, 130, 157, 111, 32, 75, 80 },
+ { 66, 102, 167, 99, 74, 62, 40, 234, 128 },
+ { 41, 53, 9, 178, 241, 141, 26, 8, 107 },
+ { 74, 43, 26, 146, 73, 166, 49, 23, 157 },
+ { 65, 38, 105, 160, 51, 52, 31, 115, 128 },
+ { 104, 79, 12, 27, 217, 255, 87, 17, 7 },
+ { 87, 68, 71, 44, 114, 51, 15, 186, 23 },
+ { 47, 41, 14, 110, 182, 183, 21, 17, 194 },
+ { 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
+ { { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
+ { 43, 97, 183, 117, 85, 38, 35, 179, 61 },
+ { 39, 53, 200, 87, 26, 21, 43, 232, 171 },
+ { 56, 34, 51, 104, 114, 102, 29, 93, 77 },
+ { 39, 28, 85, 171, 58, 165, 90, 98, 64 },
+ { 34, 22, 116, 206, 23, 34, 43, 166, 73 },
+ { 107, 54, 32, 26, 51, 1, 81, 43, 31 },
+ { 68, 25, 106, 22, 64, 171, 36, 225, 114 },
+ { 34, 19, 21, 102, 132, 188, 16, 76, 124 },
+ { 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
+ { { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
+ { 60, 148, 31, 172, 219, 228, 21, 18, 111 },
+ { 112, 113, 77, 85, 179, 255, 38, 120, 114 },
+ { 40, 42, 1, 196, 245, 209, 10, 25, 109 },
+ { 88, 43, 29, 140, 166, 213, 37, 43, 154 },
+ { 61, 63, 30, 155, 67, 45, 68, 1, 209 },
+ { 100, 80, 8, 43, 154, 1, 51, 26, 71 },
+ { 142, 78, 78, 16, 255, 128, 34, 197, 171 },
+ { 41, 40, 5, 102, 211, 183, 4, 1, 221 },
+ { 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
+ { { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
+ { 67, 87, 58, 169, 82, 115, 26, 59, 179 },
+ { 63, 59, 90, 180, 59, 166, 93, 73, 154 },
+ { 40, 40, 21, 116, 143, 209, 34, 39, 175 },
+ { 47, 15, 16, 183, 34, 223, 49, 45, 183 },
+ { 46, 17, 33, 183, 6, 98, 15, 32, 183 },
+ { 57, 46, 22, 24, 128, 1, 54, 17, 37 },
+ { 65, 32, 73, 115, 28, 128, 23, 128, 205 },
+ { 40, 3, 9, 115, 51, 192, 18, 6, 223 },
+ { 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
+ { { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
+ { 64, 90, 70, 205, 40, 41, 23, 26, 57 },
+ { 54, 57, 112, 184, 5, 41, 38, 166, 213 },
+ { 30, 34, 26, 133, 152, 116, 10, 32, 134 },
+ { 39, 19, 53, 221, 26, 114, 32, 73, 255 },
+ { 31, 9, 65, 234, 2, 15, 1, 118, 73 },
+ { 75, 32, 12, 51, 192, 255, 160, 43, 51 },
+ { 88, 31, 35, 67, 102, 85, 55, 186, 85 },
+ { 56, 21, 23, 111, 59, 205, 45, 37, 192 },
+ { 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
+ { { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
+ { 95, 84, 53, 89, 128, 100, 113, 101, 45 },
+ { 75, 79, 123, 47, 51, 128, 81, 171, 1 },
+ { 57, 17, 5, 71, 102, 57, 53, 41, 49 },
+ { 38, 33, 13, 121, 57, 73, 26, 1, 85 },
+ { 41, 10, 67, 138, 77, 110, 90, 47, 114 },
+ { 115, 21, 2, 10, 102, 255, 166, 23, 6 },
+ { 101, 29, 16, 10, 85, 128, 101, 196, 26 },
+ { 57, 18, 10, 102, 102, 213, 34, 20, 43 },
+ { 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
+ { { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
+ { 69, 60, 71, 38, 73, 119, 28, 222, 37 },
+ { 68, 45, 128, 34, 1, 47, 11, 245, 171 },
+ { 62, 17, 19, 70, 146, 85, 55, 62, 70 },
+ { 37, 43, 37, 154, 100, 163, 85, 160, 1 },
+ { 63, 9, 92, 136, 28, 64, 32, 201, 85 },
+ { 75, 15, 9, 9, 64, 255, 184, 119, 16 },
+ { 86, 6, 28, 5, 64, 255, 25, 248, 1 },
+ { 56, 8, 17, 132, 137, 255, 55, 116, 128 },
+ { 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
+ { { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
+ { 51, 103, 44, 131, 131, 123, 31, 6, 158 },
+ { 86, 40, 64, 135, 148, 224, 45, 183, 128 },
+ { 22, 26, 17, 131, 240, 154, 14, 1, 209 },
+ { 45, 16, 21, 91, 64, 222, 7, 1, 197 },
+ { 56, 21, 39, 155, 60, 138, 23, 102, 213 },
+ { 83, 12, 13, 54, 192, 255, 68, 47, 28 },
+ { 85, 26, 85, 85, 128, 128, 32, 146, 171 },
+ { 18, 11, 7, 63, 144, 171, 4, 4, 246 },
+ { 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
+ { { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
+ { 85, 126, 47, 87, 176, 51, 41, 20, 32 },
+ { 101, 75, 128, 139, 118, 146, 116, 128, 85 },
+ { 56, 41, 15, 176, 236, 85, 37, 9, 62 },
+ { 71, 30, 17, 119, 118, 255, 17, 18, 138 },
+ { 101, 38, 60, 138, 55, 70, 43, 26, 142 },
+ { 146, 36, 19, 30, 171, 255, 97, 27, 20 },
+ { 138, 45, 61, 62, 219, 1, 81, 188, 64 },
+ { 32, 41, 20, 117, 151, 142, 20, 21, 163 },
+ { 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
+};
+
+void VP8ResetProba(VP8Proba* const proba) {
+ memset(proba->segments_, 255u, sizeof(proba->segments_));
+ // proba->bands_[][] is initialized later
+}
+
+static void ParseIntraMode(VP8BitReader* const br,
+ VP8Decoder* const dec, int mb_x) {
+ uint8_t* const top = dec->intra_t_ + 4 * mb_x;
+ uint8_t* const left = dec->intra_l_;
+ VP8MBData* const block = dec->mb_data_ + mb_x;
+
+ // Note: we don't save segment map (yet), as we don't expect
+ // to decode more than 1 keyframe.
+ if (dec->segment_hdr_.update_map_) {
+ // Hardcoded tree parsing
+ block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0])
+ ? VP8GetBit(br, dec->proba_.segments_[1])
+ : 2 + VP8GetBit(br, dec->proba_.segments_[2]);
+ } else {
+ block->segment_ = 0; // default for intra
+ }
+ if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_);
+
+ block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
+ if (!block->is_i4x4_) {
+ // Hardcoded 16x16 intra-mode decision tree.
+ const int ymode =
+ VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED)
+ : (VP8GetBit(br, 163) ? V_PRED : DC_PRED);
+ block->imodes_[0] = ymode;
+ memset(top, ymode, 4 * sizeof(*top));
+ memset(left, ymode, 4 * sizeof(*left));
+ } else {
+ uint8_t* modes = block->imodes_;
+ int y;
+ for (y = 0; y < 4; ++y) {
+ int ymode = left[y];
+ int x;
+ for (x = 0; x < 4; ++x) {
+ const uint8_t* const prob = kBModesProba[top[x]][ymode];
+#if (USE_GENERIC_TREE == 1)
+ // Generic tree-parsing
+ int i = kYModesIntra4[VP8GetBit(br, prob[0])];
+ while (i > 0) {
+ i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])];
+ }
+ ymode = -i;
+#else
+ // Hardcoded tree parsing
+ ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED :
+ !VP8GetBit(br, prob[1]) ? B_TM_PRED :
+ !VP8GetBit(br, prob[2]) ? B_VE_PRED :
+ !VP8GetBit(br, prob[3]) ?
+ (!VP8GetBit(br, prob[4]) ? B_HE_PRED :
+ (!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) :
+ (!VP8GetBit(br, prob[6]) ? B_LD_PRED :
+ (!VP8GetBit(br, prob[7]) ? B_VL_PRED :
+ (!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED)));
+#endif // USE_GENERIC_TREE
+ top[x] = ymode;
+ }
+ memcpy(modes, top, 4 * sizeof(*top));
+ modes += 4;
+ left[y] = ymode;
+ }
+ }
+ // Hardcoded UVMode decision tree
+ block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
+ : !VP8GetBit(br, 114) ? V_PRED
+ : VP8GetBit(br, 183) ? TM_PRED : H_PRED;
+}
+
+int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
+ int mb_x;
+ for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
+ ParseIntraMode(br, dec, mb_x);
+ }
+ return !dec->br_.eof_;
+}
+
+//------------------------------------------------------------------------------
+// Paragraph 13
+
+static const uint8_t
+ CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
+ { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
+ { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
+ },
+ { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
+ { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ }
+};
+
+// Paragraph 9.9
+
+static const uint8_t kBands[16 + 1] = {
+ 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 0 // extra entry as sentinel
+};
+
+void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
+ VP8Proba* const proba = &dec->proba_;
+ int t, b, c, p;
+ for (t = 0; t < NUM_TYPES; ++t) {
+ for (b = 0; b < NUM_BANDS; ++b) {
+ for (c = 0; c < NUM_CTX; ++c) {
+ for (p = 0; p < NUM_PROBAS; ++p) {
+ const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ?
+ VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p];
+ proba->bands_[t][b].probas_[c][p] = v;
+ }
+ }
+ }
+ for (b = 0; b < 16 + 1; ++b) {
+ proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
+ }
+ }
+ dec->use_skip_proba_ = VP8Get(br);
+ if (dec->use_skip_proba_) {
+ dec->skip_p_ = VP8GetValue(br, 8);
+ }
+}
+
diff --git a/src/third_party/libwebp/src/dec/vp8_dec.c b/src/third_party/libwebp/src/dec/vp8_dec.c
new file mode 100644
index 0000000..e6476fa
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/vp8_dec.c
@@ -0,0 +1,727 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// main entry for the decoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <stdlib.h>
+#endif
+
+#include "src/dec/alphai_dec.h"
+#include "src/dec/vp8i_dec.h"
+#include "src/dec/vp8li_dec.h"
+#include "src/dec/webpi_dec.h"
+#include "src/utils/bit_reader_inl_utils.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+
+int WebPGetDecoderVersion(void) {
+ return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+// Signature and pointer-to-function for GetCoeffs() variants below.
+
+typedef int (*GetCoeffsFunc)(VP8BitReader* const br,
+ const VP8BandProbas* const prob[],
+ int ctx, const quant_t dq, int n, int16_t* out);
+static volatile GetCoeffsFunc GetCoeffs = NULL;
+
+static void InitGetCoeffs(void);
+
+//------------------------------------------------------------------------------
+// VP8Decoder
+
+static void SetOk(VP8Decoder* const dec) {
+ dec->status_ = VP8_STATUS_OK;
+ dec->error_msg_ = "OK";
+}
+
+int VP8InitIoInternal(VP8Io* const io, int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
+ return 0; // mismatch error
+ }
+ if (io != NULL) {
+ memset(io, 0, sizeof(*io));
+ }
+ return 1;
+}
+
+VP8Decoder* VP8New(void) {
+ VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
+ if (dec != NULL) {
+ SetOk(dec);
+ WebPGetWorkerInterface()->Init(&dec->worker_);
+ dec->ready_ = 0;
+ dec->num_parts_minus_one_ = 0;
+ InitGetCoeffs();
+ }
+ return dec;
+}
+
+VP8StatusCode VP8Status(VP8Decoder* const dec) {
+ if (!dec) return VP8_STATUS_INVALID_PARAM;
+ return dec->status_;
+}
+
+const char* VP8StatusMessage(VP8Decoder* const dec) {
+ if (dec == NULL) return "no object";
+ if (!dec->error_msg_) return "OK";
+ return dec->error_msg_;
+}
+
+void VP8Delete(VP8Decoder* const dec) {
+ if (dec != NULL) {
+ VP8Clear(dec);
+ WebPSafeFree(dec);
+ }
+}
+
+int VP8SetError(VP8Decoder* const dec,
+ VP8StatusCode error, const char* const msg) {
+ // The oldest error reported takes precedence over the new one.
+ if (dec->status_ == VP8_STATUS_OK) {
+ dec->status_ = error;
+ dec->error_msg_ = msg;
+ dec->ready_ = 0;
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+
+int VP8CheckSignature(const uint8_t* const data, size_t data_size) {
+ return (data_size >= 3 &&
+ data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a);
+}
+
+int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
+ int* const width, int* const height) {
+ if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) {
+ return 0; // not enough data
+ }
+ // check signature
+ if (!VP8CheckSignature(data + 3, data_size - 3)) {
+ return 0; // Wrong signature.
+ } else {
+ const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
+ const int key_frame = !(bits & 1);
+ const int w = ((data[7] << 8) | data[6]) & 0x3fff;
+ const int h = ((data[9] << 8) | data[8]) & 0x3fff;
+
+ if (!key_frame) { // Not a keyframe.
+ return 0;
+ }
+
+ if (((bits >> 1) & 7) > 3) {
+ return 0; // unknown profile
+ }
+ if (!((bits >> 4) & 1)) {
+ return 0; // first frame is invisible!
+ }
+ if (((bits >> 5)) >= chunk_size) { // partition_length
+ return 0; // inconsistent size information.
+ }
+ if (w == 0 || h == 0) {
+ return 0; // We don't support both width and height to be zero.
+ }
+
+ if (width) {
+ *width = w;
+ }
+ if (height) {
+ *height = h;
+ }
+
+ return 1;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Header parsing
+
+static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
+ assert(hdr != NULL);
+ hdr->use_segment_ = 0;
+ hdr->update_map_ = 0;
+ hdr->absolute_delta_ = 1;
+ memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_));
+ memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_));
+}
+
+// Paragraph 9.3
+static int ParseSegmentHeader(VP8BitReader* br,
+ VP8SegmentHeader* hdr, VP8Proba* proba) {
+ assert(br != NULL);
+ assert(hdr != NULL);
+ hdr->use_segment_ = VP8Get(br);
+ if (hdr->use_segment_) {
+ hdr->update_map_ = VP8Get(br);
+ if (VP8Get(br)) { // update data
+ int s;
+ hdr->absolute_delta_ = VP8Get(br);
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0;
+ }
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0;
+ }
+ }
+ if (hdr->update_map_) {
+ int s;
+ for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
+ proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u;
+ }
+ }
+ } else {
+ hdr->update_map_ = 0;
+ }
+ return !br->eof_;
+}
+
+// Paragraph 9.5
+// This function returns VP8_STATUS_SUSPENDED if we don't have all the
+// necessary data in 'buf'.
+// This case is not necessarily an error (for incremental decoding).
+// Still, no bitreader is ever initialized to make it possible to read
+// unavailable memory.
+// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
+// is returned, and this is an unrecoverable error.
+// If the partitions were positioned ok, VP8_STATUS_OK is returned.
+static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
+ const uint8_t* buf, size_t size) {
+ VP8BitReader* const br = &dec->br_;
+ const uint8_t* sz = buf;
+ const uint8_t* buf_end = buf + size;
+ const uint8_t* part_start;
+ size_t size_left = size;
+ size_t last_part;
+ size_t p;
+
+ dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1;
+ last_part = dec->num_parts_minus_one_;
+ if (size < 3 * last_part) {
+ // we can't even read the sizes with sz[]! That's a failure.
+ return VP8_STATUS_NOT_ENOUGH_DATA;
+ }
+ part_start = buf + last_part * 3;
+ size_left -= last_part * 3;
+ for (p = 0; p < last_part; ++p) {
+ size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
+ if (psize > size_left) psize = size_left;
+ VP8InitBitReader(dec->parts_ + p, part_start, psize);
+ part_start += psize;
+ size_left -= psize;
+ sz += 3;
+ }
+ VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
+ return (part_start < buf_end) ? VP8_STATUS_OK :
+ VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
+}
+
+// Paragraph 9.4
+static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
+ VP8FilterHeader* const hdr = &dec->filter_hdr_;
+ hdr->simple_ = VP8Get(br);
+ hdr->level_ = VP8GetValue(br, 6);
+ hdr->sharpness_ = VP8GetValue(br, 3);
+ hdr->use_lf_delta_ = VP8Get(br);
+ if (hdr->use_lf_delta_) {
+ if (VP8Get(br)) { // update lf-delta?
+ int i;
+ for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
+ if (VP8Get(br)) {
+ hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6);
+ }
+ }
+ for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
+ if (VP8Get(br)) {
+ hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6);
+ }
+ }
+ }
+ }
+ dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
+ return !br->eof_;
+}
+
+// Topmost call
+int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
+ const uint8_t* buf;
+ size_t buf_size;
+ VP8FrameHeader* frm_hdr;
+ VP8PictureHeader* pic_hdr;
+ VP8BitReader* br;
+ VP8StatusCode status;
+
+ if (dec == NULL) {
+ return 0;
+ }
+ SetOk(dec);
+ if (io == NULL) {
+ return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
+ "null VP8Io passed to VP8GetHeaders()");
+ }
+ buf = io->data;
+ buf_size = io->data_size;
+ if (buf_size < 4) {
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "Truncated header.");
+ }
+
+ // Paragraph 9.1
+ {
+ const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
+ frm_hdr = &dec->frm_hdr_;
+ frm_hdr->key_frame_ = !(bits & 1);
+ frm_hdr->profile_ = (bits >> 1) & 7;
+ frm_hdr->show_ = (bits >> 4) & 1;
+ frm_hdr->partition_length_ = (bits >> 5);
+ if (frm_hdr->profile_ > 3) {
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "Incorrect keyframe parameters.");
+ }
+ if (!frm_hdr->show_) {
+ return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
+ "Frame not displayable.");
+ }
+ buf += 3;
+ buf_size -= 3;
+ }
+
+ pic_hdr = &dec->pic_hdr_;
+ if (frm_hdr->key_frame_) {
+ // Paragraph 9.2
+ if (buf_size < 7) {
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "cannot parse picture header");
+ }
+ if (!VP8CheckSignature(buf, buf_size)) {
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "Bad code word");
+ }
+ pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff;
+ pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
+ pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff;
+ pic_hdr->yscale_ = buf[6] >> 6;
+ buf += 7;
+ buf_size -= 7;
+
+ dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
+ dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
+
+ // Setup default output area (can be later modified during io->setup())
+ io->width = pic_hdr->width_;
+ io->height = pic_hdr->height_;
+ // IMPORTANT! use some sane dimensions in crop_* and scaled_* fields.
+ // So they can be used interchangeably without always testing for
+ // 'use_cropping'.
+ io->use_cropping = 0;
+ io->crop_top = 0;
+ io->crop_left = 0;
+ io->crop_right = io->width;
+ io->crop_bottom = io->height;
+ io->use_scaling = 0;
+ io->scaled_width = io->width;
+ io->scaled_height = io->height;
+
+ io->mb_w = io->width; // sanity check
+ io->mb_h = io->height; // ditto
+
+ VP8ResetProba(&dec->proba_);
+ ResetSegmentHeader(&dec->segment_hdr_);
+ }
+
+ // Check if we have all the partition #0 available, and initialize dec->br_
+ // to read this partition (and this partition only).
+ if (frm_hdr->partition_length_ > buf_size) {
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "bad partition length");
+ }
+
+ br = &dec->br_;
+ VP8InitBitReader(br, buf, frm_hdr->partition_length_);
+ buf += frm_hdr->partition_length_;
+ buf_size -= frm_hdr->partition_length_;
+
+ if (frm_hdr->key_frame_) {
+ pic_hdr->colorspace_ = VP8Get(br);
+ pic_hdr->clamp_type_ = VP8Get(br);
+ }
+ if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "cannot parse segment header");
+ }
+ // Filter specs
+ if (!ParseFilterHeader(br, dec)) {
+ return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
+ "cannot parse filter header");
+ }
+ status = ParsePartitions(dec, buf, buf_size);
+ if (status != VP8_STATUS_OK) {
+ return VP8SetError(dec, status, "cannot parse partitions");
+ }
+
+ // quantizer change
+ VP8ParseQuant(dec);
+
+ // Frame buffer marking
+ if (!frm_hdr->key_frame_) {
+ return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
+ "Not a key frame.");
+ }
+
+ VP8Get(br); // ignore the value of update_proba_
+
+ VP8ParseProba(br, dec);
+
+ // sanitized state
+ dec->ready_ = 1;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Residual decoding (Paragraph 13.2 / 13.3)
+
+static const uint8_t kCat3[] = { 173, 148, 140, 0 };
+static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 };
+static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
+static const uint8_t kCat6[] =
+ { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 };
+static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
+static const uint8_t kZigzag[16] = {
+ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
+};
+
+// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
+static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
+ int v;
+ if (!VP8GetBit(br, p[3])) {
+ if (!VP8GetBit(br, p[4])) {
+ v = 2;
+ } else {
+ v = 3 + VP8GetBit(br, p[5]);
+ }
+ } else {
+ if (!VP8GetBit(br, p[6])) {
+ if (!VP8GetBit(br, p[7])) {
+ v = 5 + VP8GetBit(br, 159);
+ } else {
+ v = 7 + 2 * VP8GetBit(br, 165);
+ v += VP8GetBit(br, 145);
+ }
+ } else {
+ const uint8_t* tab;
+ const int bit1 = VP8GetBit(br, p[8]);
+ const int bit0 = VP8GetBit(br, p[9 + bit1]);
+ const int cat = 2 * bit1 + bit0;
+ v = 0;
+ for (tab = kCat3456[cat]; *tab; ++tab) {
+ v += v + VP8GetBit(br, *tab);
+ }
+ v += 3 + (8 << cat);
+ }
+ }
+ return v;
+}
+
+// Returns the position of the last non-zero coeff plus one
+static int GetCoeffsFast(VP8BitReader* const br,
+ const VP8BandProbas* const prob[],
+ int ctx, const quant_t dq, int n, int16_t* out) {
+ const uint8_t* p = prob[n]->probas_[ctx];
+ for (; n < 16; ++n) {
+ if (!VP8GetBit(br, p[0])) {
+ return n; // previous coeff was last non-zero coeff
+ }
+ while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs
+ p = prob[++n]->probas_[0];
+ if (n == 16) return 16;
+ }
+ { // non zero coeff
+ const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
+ int v;
+ if (!VP8GetBit(br, p[2])) {
+ v = 1;
+ p = p_ctx[1];
+ } else {
+ v = GetLargeValue(br, p);
+ p = p_ctx[2];
+ }
+ out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
+ }
+ }
+ return 16;
+}
+
+// This version of GetCoeffs() uses VP8GetBitAlt() which is an alternate version
+// of VP8GetBitAlt() targeting specific platforms.
+static int GetCoeffsAlt(VP8BitReader* const br,
+ const VP8BandProbas* const prob[],
+ int ctx, const quant_t dq, int n, int16_t* out) {
+ const uint8_t* p = prob[n]->probas_[ctx];
+ for (; n < 16; ++n) {
+ if (!VP8GetBitAlt(br, p[0])) {
+ return n; // previous coeff was last non-zero coeff
+ }
+ while (!VP8GetBitAlt(br, p[1])) { // sequence of zero coeffs
+ p = prob[++n]->probas_[0];
+ if (n == 16) return 16;
+ }
+ { // non zero coeff
+ const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
+ int v;
+ if (!VP8GetBitAlt(br, p[2])) {
+ v = 1;
+ p = p_ctx[1];
+ } else {
+ v = GetLargeValue(br, p);
+ p = p_ctx[2];
+ }
+ out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
+ }
+ }
+ return 16;
+}
+
+static WEBP_TSAN_IGNORE_FUNCTION void InitGetCoeffs(void) {
+ if (GetCoeffs == NULL) {
+ if (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kSlowSSSE3)) {
+ GetCoeffs = GetCoeffsAlt;
+ } else {
+ GetCoeffs = GetCoeffsFast;
+ }
+ }
+}
+
+static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
+ nz_coeffs <<= 2;
+ nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
+ return nz_coeffs;
+}
+
+static int ParseResiduals(VP8Decoder* const dec,
+ VP8MB* const mb, VP8BitReader* const token_br) {
+ const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_;
+ const VP8BandProbas* const * ac_proba;
+ VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
+ const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
+ int16_t* dst = block->coeffs_;
+ VP8MB* const left_mb = dec->mb_info_ - 1;
+ uint8_t tnz, lnz;
+ uint32_t non_zero_y = 0;
+ uint32_t non_zero_uv = 0;
+ int x, y, ch;
+ uint32_t out_t_nz, out_l_nz;
+ int first;
+
+ memset(dst, 0, 384 * sizeof(*dst));
+ if (!block->is_i4x4_) { // parse DC
+ int16_t dc[16] = { 0 };
+ const int ctx = mb->nz_dc_ + left_mb->nz_dc_;
+ const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc);
+ mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0);
+ if (nz > 1) { // more than just the DC -> perform the full transform
+ VP8TransformWHT(dc, dst);
+ } else { // only DC is non-zero -> inlined simplified transform
+ int i;
+ const int dc0 = (dc[0] + 3) >> 3;
+ for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0;
+ }
+ first = 1;
+ ac_proba = bands[0];
+ } else {
+ first = 0;
+ ac_proba = bands[3];
+ }
+
+ tnz = mb->nz_ & 0x0f;
+ lnz = left_mb->nz_ & 0x0f;
+ for (y = 0; y < 4; ++y) {
+ int l = lnz & 1;
+ uint32_t nz_coeffs = 0;
+ for (x = 0; x < 4; ++x) {
+ const int ctx = l + (tnz & 1);
+ const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst);
+ l = (nz > first);
+ tnz = (tnz >> 1) | (l << 7);
+ nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
+ dst += 16;
+ }
+ tnz >>= 4;
+ lnz = (lnz >> 1) | (l << 7);
+ non_zero_y = (non_zero_y << 8) | nz_coeffs;
+ }
+ out_t_nz = tnz;
+ out_l_nz = lnz >> 4;
+
+ for (ch = 0; ch < 4; ch += 2) {
+ uint32_t nz_coeffs = 0;
+ tnz = mb->nz_ >> (4 + ch);
+ lnz = left_mb->nz_ >> (4 + ch);
+ for (y = 0; y < 2; ++y) {
+ int l = lnz & 1;
+ for (x = 0; x < 2; ++x) {
+ const int ctx = l + (tnz & 1);
+ const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst);
+ l = (nz > 0);
+ tnz = (tnz >> 1) | (l << 3);
+ nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
+ dst += 16;
+ }
+ tnz >>= 2;
+ lnz = (lnz >> 1) | (l << 5);
+ }
+ // Note: we don't really need the per-4x4 details for U/V blocks.
+ non_zero_uv |= nz_coeffs << (4 * ch);
+ out_t_nz |= (tnz << 4) << ch;
+ out_l_nz |= (lnz & 0xf0) << ch;
+ }
+ mb->nz_ = out_t_nz;
+ left_mb->nz_ = out_l_nz;
+
+ block->non_zero_y_ = non_zero_y;
+ block->non_zero_uv_ = non_zero_uv;
+
+ // We look at the mode-code of each block and check if some blocks have less
+ // than three non-zero coeffs (code < 2). This is to avoid dithering flat and
+ // empty blocks.
+ block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_;
+
+ return !(non_zero_y | non_zero_uv); // will be used for further optimization
+}
+
+//------------------------------------------------------------------------------
+// Main loop
+
+int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
+ VP8MB* const left = dec->mb_info_ - 1;
+ VP8MB* const mb = dec->mb_info_ + dec->mb_x_;
+ VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
+ int skip = dec->use_skip_proba_ ? block->skip_ : 0;
+
+ if (!skip) {
+ skip = ParseResiduals(dec, mb, token_br);
+ } else {
+ left->nz_ = mb->nz_ = 0;
+ if (!block->is_i4x4_) {
+ left->nz_dc_ = mb->nz_dc_ = 0;
+ }
+ block->non_zero_y_ = 0;
+ block->non_zero_uv_ = 0;
+ block->dither_ = 0;
+ }
+
+ if (dec->filter_type_ > 0) { // store filter info
+ VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
+ *finfo = dec->fstrengths_[block->segment_][block->is_i4x4_];
+ finfo->f_inner_ |= !skip;
+ }
+
+ return !token_br->eof_;
+}
+
+void VP8InitScanline(VP8Decoder* const dec) {
+ VP8MB* const left = dec->mb_info_ - 1;
+ left->nz_ = 0;
+ left->nz_dc_ = 0;
+ memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
+ dec->mb_x_ = 0;
+}
+
+static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
+ for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
+ // Parse bitstream for this row.
+ VP8BitReader* const token_br =
+ &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
+ if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "Premature end-of-partition0 encountered.");
+ }
+ for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
+ if (!VP8DecodeMB(dec, token_br)) {
+ return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
+ "Premature end-of-file encountered.");
+ }
+ }
+ VP8InitScanline(dec); // Prepare for next scanline
+
+ // Reconstruct, filter and emit the row.
+ if (!VP8ProcessRow(dec, io)) {
+ return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
+ }
+ }
+ if (dec->mt_method_ > 0) {
+ if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0;
+ }
+
+ return 1;
+}
+
+// Main entry point
+int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
+ int ok = 0;
+ if (dec == NULL) {
+ return 0;
+ }
+ if (io == NULL) {
+ return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
+ "NULL VP8Io parameter in VP8Decode().");
+ }
+
+ if (!dec->ready_) {
+ if (!VP8GetHeaders(dec, io)) {
+ return 0;
+ }
+ }
+ assert(dec->ready_);
+
+ // Finish setting up the decoding parameter. Will call io->setup().
+ ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK);
+ if (ok) { // good to go.
+ // Will allocate memory and prepare everything.
+ if (ok) ok = VP8InitFrame(dec, io);
+
+ // Main decoding loop
+ if (ok) ok = ParseFrame(dec, io);
+
+ // Exit.
+ ok &= VP8ExitCritical(dec, io);
+ }
+
+ if (!ok) {
+ VP8Clear(dec);
+ return 0;
+ }
+
+ dec->ready_ = 0;
+ return ok;
+}
+
+void VP8Clear(VP8Decoder* const dec) {
+ if (dec == NULL) {
+ return;
+ }
+ WebPGetWorkerInterface()->End(&dec->worker_);
+ WebPDeallocateAlphaMemory(dec);
+ WebPSafeFree(dec->mem_);
+ dec->mem_ = NULL;
+ dec->mem_size_ = 0;
+ memset(&dec->br_, 0, sizeof(dec->br_));
+ dec->ready_ = 0;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dec/vp8_dec.h b/src/third_party/libwebp/src/dec/vp8_dec.h
new file mode 100644
index 0000000..ca85b34
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/vp8_dec.h
@@ -0,0 +1,185 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Low-level API for VP8 decoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_DEC_VP8_DEC_H_
+#define WEBP_DEC_VP8_DEC_H_
+
+#include "src/webp/decode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Lower-level API
+//
+// These functions provide fine-grained control of the decoding process.
+// The call flow should resemble:
+//
+// VP8Io io;
+// VP8InitIo(&io);
+// io.data = data;
+// io.data_size = size;
+// /* customize io's functions (setup()/put()/teardown()) if needed. */
+//
+// VP8Decoder* dec = VP8New();
+// int ok = VP8Decode(dec, &io);
+// if (!ok) printf("Error: %s\n", VP8StatusMessage(dec));
+// VP8Delete(dec);
+// return ok;
+
+// Input / Output
+typedef struct VP8Io VP8Io;
+typedef int (*VP8IoPutHook)(const VP8Io* io);
+typedef int (*VP8IoSetupHook)(VP8Io* io);
+typedef void (*VP8IoTeardownHook)(const VP8Io* io);
+
+struct VP8Io {
+ // set by VP8GetHeaders()
+ int width, height; // picture dimensions, in pixels (invariable).
+ // These are the original, uncropped dimensions.
+ // The actual area passed to put() is stored
+ // in mb_w / mb_h fields.
+
+ // set before calling put()
+ int mb_y; // position of the current rows (in pixels)
+ int mb_w; // number of columns in the sample
+ int mb_h; // number of rows in the sample
+ const uint8_t* y, *u, *v; // rows to copy (in yuv420 format)
+ int y_stride; // row stride for luma
+ int uv_stride; // row stride for chroma
+
+ void* opaque; // user data
+
+ // called when fresh samples are available. Currently, samples are in
+ // YUV420 format, and can be up to width x 24 in size (depending on the
+ // in-loop filtering level, e.g.). Should return false in case of error
+ // or abort request. The actual size of the area to update is mb_w x mb_h
+ // in size, taking cropping into account.
+ VP8IoPutHook put;
+
+ // called just before starting to decode the blocks.
+ // Must return false in case of setup error, true otherwise. If false is
+ // returned, teardown() will NOT be called. But if the setup succeeded
+ // and true is returned, then teardown() will always be called afterward.
+ VP8IoSetupHook setup;
+
+ // Called just after block decoding is finished (or when an error occurred
+ // during put()). Is NOT called if setup() failed.
+ VP8IoTeardownHook teardown;
+
+ // this is a recommendation for the user-side yuv->rgb converter. This flag
+ // is set when calling setup() hook and can be overwritten by it. It then
+ // can be taken into consideration during the put() method.
+ int fancy_upsampling;
+
+ // Input buffer.
+ size_t data_size;
+ const uint8_t* data;
+
+ // If true, in-loop filtering will not be performed even if present in the
+ // bitstream. Switching off filtering may speed up decoding at the expense
+ // of more visible blocking. Note that output will also be non-compliant
+ // with the VP8 specifications.
+ int bypass_filtering;
+
+ // Cropping parameters.
+ int use_cropping;
+ int crop_left, crop_right, crop_top, crop_bottom;
+
+ // Scaling parameters.
+ int use_scaling;
+ int scaled_width, scaled_height;
+
+ // If non NULL, pointer to the alpha data (if present) corresponding to the
+ // start of the current row (That is: it is pre-offset by mb_y and takes
+ // cropping into account).
+ const uint8_t* a;
+};
+
+// Internal, version-checked, entry point
+int VP8InitIoInternal(VP8Io* const, int);
+
+// Set the custom IO function pointers and user-data. The setter for IO hooks
+// should be called before initiating incremental decoding. Returns true if
+// WebPIDecoder object is successfully modified, false otherwise.
+int WebPISetIOHooks(WebPIDecoder* const idec,
+ VP8IoPutHook put,
+ VP8IoSetupHook setup,
+ VP8IoTeardownHook teardown,
+ void* user_data);
+
+// Main decoding object. This is an opaque structure.
+typedef struct VP8Decoder VP8Decoder;
+
+// Create a new decoder object.
+VP8Decoder* VP8New(void);
+
+// Must be called to make sure 'io' is initialized properly.
+// Returns false in case of version mismatch. Upon such failure, no other
+// decoding function should be called (VP8Decode, VP8GetHeaders, ...)
+static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
+ return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
+}
+
+// Decode the VP8 frame header. Returns true if ok.
+// Note: 'io->data' must be pointing to the start of the VP8 frame header.
+int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
+
+// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
+// Returns false in case of error.
+int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
+
+// Return current status of the decoder:
+VP8StatusCode VP8Status(VP8Decoder* const dec);
+
+// return readable string corresponding to the last status.
+const char* VP8StatusMessage(VP8Decoder* const dec);
+
+// Resets the decoder in its initial state, reclaiming memory.
+// Not a mandatory call between calls to VP8Decode().
+void VP8Clear(VP8Decoder* const dec);
+
+// Destroy the decoder object.
+void VP8Delete(VP8Decoder* const dec);
+
+//------------------------------------------------------------------------------
+// Miscellaneous VP8/VP8L bitstream probing functions.
+
+// Returns true if the next 3 bytes in data contain the VP8 signature.
+WEBP_EXTERN int VP8CheckSignature(const uint8_t* const data, size_t data_size);
+
+// Validates the VP8 data-header and retrieves basic header information viz
+// width and height. Returns 0 in case of formatting error. *width/*height
+// can be passed NULL.
+WEBP_EXTERN int VP8GetInfo(
+ const uint8_t* data,
+ size_t data_size, // data available so far
+ size_t chunk_size, // total data size expected in the chunk
+ int* const width, int* const height);
+
+// Returns true if the next byte(s) in data is a VP8L signature.
+WEBP_EXTERN int VP8LCheckSignature(const uint8_t* const data, size_t size);
+
+// Validates the VP8L data-header and retrieves basic header information viz
+// width, height and alpha. Returns 0 in case of formatting error.
+// width/height/has_alpha can be passed NULL.
+WEBP_EXTERN int VP8LGetInfo(
+ const uint8_t* data, size_t data_size, // data available so far
+ int* const width, int* const height, int* const has_alpha);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_DEC_VP8_DEC_H_ */
diff --git a/src/third_party/libwebp/src/dec/vp8i_dec.h b/src/third_party/libwebp/src/dec/vp8i_dec.h
new file mode 100644
index 0000000..33b0dbd
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/vp8i_dec.h
@@ -0,0 +1,322 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// VP8 decoder: internal header.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_DEC_VP8I_DEC_H_
+#define WEBP_DEC_VP8I_DEC_H_
+
+#if !defined(STARBOARD)
+#include <string.h> // for memcpy()
+#endif
+
+#include "src/dec/common_dec.h"
+#include "src/dec/vp8li_dec.h"
+#include "src/utils/bit_reader_utils.h"
+#include "src/utils/random_utils.h"
+#include "src/utils/thread_utils.h"
+#include "src/dsp/dsp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Various defines and enums
+
+// version numbers
+#define DEC_MAJ_VERSION 1
+#define DEC_MIN_VERSION 0
+#define DEC_REV_VERSION 0
+
+// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
+// Constraints are: We need to store one 16x16 block of luma samples (y),
+// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
+// in order to be SIMD-friendly. We also need to store the top, left and
+// top-left samples (from previously decoded blocks), along with four
+// extra top-right samples for luma (intra4x4 prediction only).
+// One possible layout is, using 32 * (17 + 9) bytes:
+//
+// .+------ <- only 1 pixel high
+// .|yyyyt.
+// .|yyyyt.
+// .|yyyyt.
+// .|yyyy..
+// .+--.+-- <- only 1 pixel high
+// .|uu.|vv
+// .|uu.|vv
+//
+// Every character is a 4x4 block, with legend:
+// '.' = unused
+// 'y' = y-samples 'u' = u-samples 'v' = u-samples
+// '|' = left sample, '-' = top sample, '+' = top-left sample
+// 't' = extra top-right sample for 4x4 modes
+#define YUV_SIZE (BPS * 17 + BPS * 9)
+#define Y_OFF (BPS * 1 + 8)
+#define U_OFF (Y_OFF + BPS * 16 + BPS)
+#define V_OFF (U_OFF + 16)
+
+// minimal width under which lossy multi-threading is always disabled
+#define MIN_WIDTH_FOR_THREADS 512
+
+//------------------------------------------------------------------------------
+// Headers
+
+typedef struct {
+ uint8_t key_frame_;
+ uint8_t profile_;
+ uint8_t show_;
+ uint32_t partition_length_;
+} VP8FrameHeader;
+
+typedef struct {
+ uint16_t width_;
+ uint16_t height_;
+ uint8_t xscale_;
+ uint8_t yscale_;
+ uint8_t colorspace_; // 0 = YCbCr
+ uint8_t clamp_type_;
+} VP8PictureHeader;
+
+// segment features
+typedef struct {
+ int use_segment_;
+ int update_map_; // whether to update the segment map or not
+ int absolute_delta_; // absolute or delta values for quantizer and filter
+ int8_t quantizer_[NUM_MB_SEGMENTS]; // quantization changes
+ int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
+} VP8SegmentHeader;
+
+// probas associated to one of the contexts
+typedef uint8_t VP8ProbaArray[NUM_PROBAS];
+
+typedef struct { // all the probas associated to one band
+ VP8ProbaArray probas_[NUM_CTX];
+} VP8BandProbas;
+
+// Struct collecting all frame-persistent probabilities.
+typedef struct {
+ uint8_t segments_[MB_FEATURE_TREE_PROBS];
+ // Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
+ VP8BandProbas bands_[NUM_TYPES][NUM_BANDS];
+ const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1];
+} VP8Proba;
+
+// Filter parameters
+typedef struct {
+ int simple_; // 0=complex, 1=simple
+ int level_; // [0..63]
+ int sharpness_; // [0..7]
+ int use_lf_delta_;
+ int ref_lf_delta_[NUM_REF_LF_DELTAS];
+ int mode_lf_delta_[NUM_MODE_LF_DELTAS];
+} VP8FilterHeader;
+
+//------------------------------------------------------------------------------
+// Informations about the macroblocks.
+
+typedef struct { // filter specs
+ uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering
+ uint8_t f_ilevel_; // inner limit in [1..63]
+ uint8_t f_inner_; // do inner filtering?
+ uint8_t hev_thresh_; // high edge variance threshold in [0..2]
+} VP8FInfo;
+
+typedef struct { // Top/Left Contexts used for syntax-parsing
+ uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma)
+ uint8_t nz_dc_; // non-zero DC coeff (1bit)
+} VP8MB;
+
+// Dequantization matrices
+typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
+typedef struct {
+ quant_t y1_mat_, y2_mat_, uv_mat_;
+
+ int uv_quant_; // U/V quantizer value
+ int dither_; // dithering amplitude (0 = off, max=255)
+} VP8QuantMatrix;
+
+// Data needed to reconstruct a macroblock
+typedef struct {
+ int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4
+ uint8_t is_i4x4_; // true if intra4x4
+ uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
+ uint8_t uvmode_; // chroma prediction mode
+ // bit-wise info about the content of each sub-4x4 blocks (in decoding order).
+ // Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
+ // code=0 -> no coefficient
+ // code=1 -> only DC
+ // code=2 -> first three coefficients are non-zero
+ // code=3 -> more than three coefficients are non-zero
+ // This allows to call specialized transform functions.
+ uint32_t non_zero_y_;
+ uint32_t non_zero_uv_;
+ uint8_t dither_; // local dithering strength (deduced from non_zero_*)
+ uint8_t skip_;
+ uint8_t segment_;
+} VP8MBData;
+
+// Persistent information needed by the parallel processing
+typedef struct {
+ int id_; // cache row to process (in [0..2])
+ int mb_y_; // macroblock position of the row
+ int filter_row_; // true if row-filtering is needed
+ VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_)
+ VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_)
+ VP8Io io_; // copy of the VP8Io to pass to put()
+} VP8ThreadContext;
+
+// Saved top samples, per macroblock. Fits into a cache-line.
+typedef struct {
+ uint8_t y[16], u[8], v[8];
+} VP8TopSamples;
+
+//------------------------------------------------------------------------------
+// VP8Decoder: the main opaque structure handed over to user
+
+struct VP8Decoder {
+ VP8StatusCode status_;
+ int ready_; // true if ready to decode a picture with VP8Decode()
+ const char* error_msg_; // set when status_ is not OK.
+
+ // Main data source
+ VP8BitReader br_;
+
+ // headers
+ VP8FrameHeader frm_hdr_;
+ VP8PictureHeader pic_hdr_;
+ VP8FilterHeader filter_hdr_;
+ VP8SegmentHeader segment_hdr_;
+
+ // Worker
+ WebPWorker worker_;
+ int mt_method_; // multi-thread method: 0=off, 1=[parse+recon][filter]
+ // 2=[parse][recon+filter]
+ int cache_id_; // current cache row
+ int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3)
+ VP8ThreadContext thread_ctx_; // Thread context
+
+ // dimension, in macroblock units.
+ int mb_w_, mb_h_;
+
+ // Macroblock to process/filter, depending on cropping and filter_type.
+ int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered
+ int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded
+
+ // number of partitions minus one.
+ uint32_t num_parts_minus_one_;
+ // per-partition boolean decoders.
+ VP8BitReader parts_[MAX_NUM_PARTITIONS];
+
+ // Dithering strength, deduced from decoding options
+ int dither_; // whether to use dithering or not
+ VP8Random dithering_rg_; // random generator for dithering
+
+ // dequantization (one set of DC/AC dequant factor per segment)
+ VP8QuantMatrix dqm_[NUM_MB_SEGMENTS];
+
+ // probabilities
+ VP8Proba proba_;
+ int use_skip_proba_;
+ uint8_t skip_p_;
+
+ // Boundary data cache and persistent buffers.
+ uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
+ uint8_t intra_l_[4]; // left intra modes values
+
+ VP8TopSamples* yuv_t_; // top y/u/v samples
+
+ VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
+ VP8FInfo* f_info_; // filter strength info
+ uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
+
+ uint8_t* cache_y_; // macroblock row for storing unfiltered samples
+ uint8_t* cache_u_;
+ uint8_t* cache_v_;
+ int cache_y_stride_;
+ int cache_uv_stride_;
+
+ // main memory chunk for the above data. Persistent.
+ void* mem_;
+ size_t mem_size_;
+
+ // Per macroblock non-persistent infos.
+ int mb_x_, mb_y_; // current position, in macroblock units
+ VP8MBData* mb_data_; // parsed reconstruction data
+
+ // Filtering side-info
+ int filter_type_; // 0=off, 1=simple, 2=complex
+ VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type
+
+ // Alpha
+ struct ALPHDecoder* alph_dec_; // alpha-plane decoder object
+ const uint8_t* alpha_data_; // compressed alpha data (if present)
+ size_t alpha_data_size_;
+ int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
+ uint8_t* alpha_plane_mem_; // memory allocated for alpha_plane_
+ uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
+ const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL)
+ int alpha_dithering_; // derived from decoding options (0=off, 100=full)
+};
+
+//------------------------------------------------------------------------------
+// internal functions. Not public.
+
+// in vp8.c
+int VP8SetError(VP8Decoder* const dec,
+ VP8StatusCode error, const char* const msg);
+
+// in tree.c
+void VP8ResetProba(VP8Proba* const proba);
+void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec);
+// parses one row of intra mode data in partition 0, returns !eof
+int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec);
+
+// in quant.c
+void VP8ParseQuant(VP8Decoder* const dec);
+
+// in frame.c
+int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
+// Call io->setup() and finish setting up scan parameters.
+// After this call returns, one must always call VP8ExitCritical() with the
+// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
+// if ok, otherwise sets and returns the error status on *dec.
+VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
+// Must always be called in pair with VP8EnterCritical().
+// Returns false in case of error.
+int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
+// Return the multi-threading method to use (0=off), depending
+// on options and bitstream size. Only for lossy decoding.
+int VP8GetThreadMethod(const WebPDecoderOptions* const options,
+ const WebPHeaderStructure* const headers,
+ int width, int height);
+// Initialize dithering post-process if needed.
+void VP8InitDithering(const WebPDecoderOptions* const options,
+ VP8Decoder* const dec);
+// Process the last decoded row (filtering + output).
+int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
+// To be called at the start of a new scanline, to initialize predictors.
+void VP8InitScanline(VP8Decoder* const dec);
+// Decode one macroblock. Returns false if there is not enough data.
+int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
+
+// in alpha.c
+const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
+ const VP8Io* const io,
+ int row, int num_rows);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_DEC_VP8I_DEC_H_ */
diff --git a/src/third_party/libwebp/src/dec/vp8l_dec.c b/src/third_party/libwebp/src/dec/vp8l_dec.c
new file mode 100644
index 0000000..6e724b7
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/vp8l_dec.c
@@ -0,0 +1,1694 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// main entry for the decoder
+//
+// Authors: Vikas Arora (vikaas.arora@gmail.com)
+// Jyrki Alakuijala (jyrki@google.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#else
+#include <stdlib.h>
+#endif
+
+#include "src/dec/alphai_dec.h"
+#include "src/dec/vp8li_dec.h"
+#include "src/dsp/dsp.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+#include "src/dsp/yuv.h"
+#include "src/utils/endian_inl_utils.h"
+#include "src/utils/huffman_utils.h"
+#include "src/utils/utils.h"
+
+#define NUM_ARGB_CACHE_ROWS 16
+
+static const int kCodeLengthLiterals = 16;
+static const int kCodeLengthRepeatCode = 16;
+static const uint8_t kCodeLengthExtraBits[3] = { 2, 3, 7 };
+static const uint8_t kCodeLengthRepeatOffsets[3] = { 3, 3, 11 };
+
+// -----------------------------------------------------------------------------
+// Five Huffman codes are used at each meta code:
+// 1. green + length prefix codes + color cache codes,
+// 2. alpha,
+// 3. red,
+// 4. blue, and,
+// 5. distance prefix codes.
+typedef enum {
+ GREEN = 0,
+ RED = 1,
+ BLUE = 2,
+ ALPHA = 3,
+ DIST = 4
+} HuffIndex;
+
+static const uint16_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = {
+ NUM_LITERAL_CODES + NUM_LENGTH_CODES,
+ NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES,
+ NUM_DISTANCE_CODES
+};
+
+static const uint8_t kLiteralMap[HUFFMAN_CODES_PER_META_CODE] = {
+ 0, 1, 1, 1, 0
+};
+
+#define NUM_CODE_LENGTH_CODES 19
+static const uint8_t kCodeLengthCodeOrder[NUM_CODE_LENGTH_CODES] = {
+ 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+#define CODE_TO_PLANE_CODES 120
+static const uint8_t kCodeToPlane[CODE_TO_PLANE_CODES] = {
+ 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a,
+ 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a,
+ 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b,
+ 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03,
+ 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c,
+ 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e,
+ 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b,
+ 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f,
+ 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b,
+ 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41,
+ 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f,
+ 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70
+};
+
+// Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha
+// and distance alphabets are constant (256 for red, blue and alpha, 40 for
+// distance) and lookup table sizes for them in worst case are 630 and 410
+// respectively. Size of green alphabet depends on color cache size and is equal
+// to 256 (green component values) + 24 (length prefix values)
+// + color_cache_size (between 0 and 2048).
+// All values computed for 8-bit first level lookup with Mark Adler's tool:
+// http://www.hdfgroup.org/ftp/lib-external/zlib/zlib-1.2.5/examples/enough.c
+#define FIXED_TABLE_SIZE (630 * 3 + 410)
+static const uint16_t kTableSize[12] = {
+ FIXED_TABLE_SIZE + 654,
+ FIXED_TABLE_SIZE + 656,
+ FIXED_TABLE_SIZE + 658,
+ FIXED_TABLE_SIZE + 662,
+ FIXED_TABLE_SIZE + 670,
+ FIXED_TABLE_SIZE + 686,
+ FIXED_TABLE_SIZE + 718,
+ FIXED_TABLE_SIZE + 782,
+ FIXED_TABLE_SIZE + 912,
+ FIXED_TABLE_SIZE + 1168,
+ FIXED_TABLE_SIZE + 1680,
+ FIXED_TABLE_SIZE + 2704
+};
+
+static int DecodeImageStream(int xsize, int ysize,
+ int is_level0,
+ VP8LDecoder* const dec,
+ uint32_t** const decoded_data);
+
+//------------------------------------------------------------------------------
+
+int VP8LCheckSignature(const uint8_t* const data, size_t size) {
+ return (size >= VP8L_FRAME_HEADER_SIZE &&
+ data[0] == VP8L_MAGIC_BYTE &&
+ (data[4] >> 5) == 0); // version
+}
+
+static int ReadImageInfo(VP8LBitReader* const br,
+ int* const width, int* const height,
+ int* const has_alpha) {
+ if (VP8LReadBits(br, 8) != VP8L_MAGIC_BYTE) return 0;
+ *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1;
+ *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1;
+ *has_alpha = VP8LReadBits(br, 1);
+ if (VP8LReadBits(br, VP8L_VERSION_BITS) != 0) return 0;
+ return !br->eos_;
+}
+
+int VP8LGetInfo(const uint8_t* data, size_t data_size,
+ int* const width, int* const height, int* const has_alpha) {
+ if (data == NULL || data_size < VP8L_FRAME_HEADER_SIZE) {
+ return 0; // not enough data
+ } else if (!VP8LCheckSignature(data, data_size)) {
+ return 0; // bad signature
+ } else {
+ int w, h, a;
+ VP8LBitReader br;
+ VP8LInitBitReader(&br, data, data_size);
+ if (!ReadImageInfo(&br, &w, &h, &a)) {
+ return 0;
+ }
+ if (width != NULL) *width = w;
+ if (height != NULL) *height = h;
+ if (has_alpha != NULL) *has_alpha = a;
+ return 1;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE int GetCopyDistance(int distance_symbol,
+ VP8LBitReader* const br) {
+ int extra_bits, offset;
+ if (distance_symbol < 4) {
+ return distance_symbol + 1;
+ }
+ extra_bits = (distance_symbol - 2) >> 1;
+ offset = (2 + (distance_symbol & 1)) << extra_bits;
+ return offset + VP8LReadBits(br, extra_bits) + 1;
+}
+
+static WEBP_INLINE int GetCopyLength(int length_symbol,
+ VP8LBitReader* const br) {
+ // Length and distance prefixes are encoded the same way.
+ return GetCopyDistance(length_symbol, br);
+}
+
+static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) {
+ if (plane_code > CODE_TO_PLANE_CODES) {
+ return plane_code - CODE_TO_PLANE_CODES;
+ } else {
+ const int dist_code = kCodeToPlane[plane_code - 1];
+ const int yoffset = dist_code >> 4;
+ const int xoffset = 8 - (dist_code & 0xf);
+ const int dist = yoffset * xsize + xoffset;
+ return (dist >= 1) ? dist : 1; // dist<1 can happen if xsize is very small
+ }
+}
+
+//------------------------------------------------------------------------------
+// Decodes the next Huffman code from bit-stream.
+// FillBitWindow(br) needs to be called at minimum every second call
+// to ReadSymbol, in order to pre-fetch enough bits.
+static WEBP_INLINE int ReadSymbol(const HuffmanCode* table,
+ VP8LBitReader* const br) {
+ int nbits;
+ uint32_t val = VP8LPrefetchBits(br);
+ table += val & HUFFMAN_TABLE_MASK;
+ nbits = table->bits - HUFFMAN_TABLE_BITS;
+ if (nbits > 0) {
+ VP8LSetBitPos(br, br->bit_pos_ + HUFFMAN_TABLE_BITS);
+ val = VP8LPrefetchBits(br);
+ table += table->value;
+ table += val & ((1 << nbits) - 1);
+ }
+ VP8LSetBitPos(br, br->bit_pos_ + table->bits);
+ return table->value;
+}
+
+// Reads packed symbol depending on GREEN channel
+#define BITS_SPECIAL_MARKER 0x100 // something large enough (and a bit-mask)
+#define PACKED_NON_LITERAL_CODE 0 // must be < NUM_LITERAL_CODES
+static WEBP_INLINE int ReadPackedSymbols(const HTreeGroup* group,
+ VP8LBitReader* const br,
+ uint32_t* const dst) {
+ const uint32_t val = VP8LPrefetchBits(br) & (HUFFMAN_PACKED_TABLE_SIZE - 1);
+ const HuffmanCode32 code = group->packed_table[val];
+ assert(group->use_packed_table);
+ if (code.bits < BITS_SPECIAL_MARKER) {
+ VP8LSetBitPos(br, br->bit_pos_ + code.bits);
+ *dst = code.value;
+ return PACKED_NON_LITERAL_CODE;
+ } else {
+ VP8LSetBitPos(br, br->bit_pos_ + code.bits - BITS_SPECIAL_MARKER);
+ assert(code.value >= NUM_LITERAL_CODES);
+ return code.value;
+ }
+}
+
+static int AccumulateHCode(HuffmanCode hcode, int shift,
+ HuffmanCode32* const huff) {
+ huff->bits += hcode.bits;
+ huff->value |= (uint32_t)hcode.value << shift;
+ assert(huff->bits <= HUFFMAN_TABLE_BITS);
+ return hcode.bits;
+}
+
+static void BuildPackedTable(HTreeGroup* const htree_group) {
+ uint32_t code;
+ for (code = 0; code < HUFFMAN_PACKED_TABLE_SIZE; ++code) {
+ uint32_t bits = code;
+ HuffmanCode32* const huff = &htree_group->packed_table[bits];
+ HuffmanCode hcode = htree_group->htrees[GREEN][bits];
+ if (hcode.value >= NUM_LITERAL_CODES) {
+ huff->bits = hcode.bits + BITS_SPECIAL_MARKER;
+ huff->value = hcode.value;
+ } else {
+ huff->bits = 0;
+ huff->value = 0;
+ bits >>= AccumulateHCode(hcode, 8, huff);
+ bits >>= AccumulateHCode(htree_group->htrees[RED][bits], 16, huff);
+ bits >>= AccumulateHCode(htree_group->htrees[BLUE][bits], 0, huff);
+ bits >>= AccumulateHCode(htree_group->htrees[ALPHA][bits], 24, huff);
+ (void)bits;
+ }
+ }
+}
+
+static int ReadHuffmanCodeLengths(
+ VP8LDecoder* const dec, const int* const code_length_code_lengths,
+ int num_symbols, int* const code_lengths) {
+ int ok = 0;
+ VP8LBitReader* const br = &dec->br_;
+ int symbol;
+ int max_symbol;
+ int prev_code_len = DEFAULT_CODE_LENGTH;
+ HuffmanCode table[1 << LENGTHS_TABLE_BITS];
+
+ if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
+ code_length_code_lengths,
+ NUM_CODE_LENGTH_CODES)) {
+ goto End;
+ }
+
+ if (VP8LReadBits(br, 1)) { // use length
+ const int length_nbits = 2 + 2 * VP8LReadBits(br, 3);
+ max_symbol = 2 + VP8LReadBits(br, length_nbits);
+ if (max_symbol > num_symbols) {
+ goto End;
+ }
+ } else {
+ max_symbol = num_symbols;
+ }
+
+ symbol = 0;
+ while (symbol < num_symbols) {
+ const HuffmanCode* p;
+ int code_len;
+ if (max_symbol-- == 0) break;
+ VP8LFillBitWindow(br);
+ p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
+ VP8LSetBitPos(br, br->bit_pos_ + p->bits);
+ code_len = p->value;
+ if (code_len < kCodeLengthLiterals) {
+ code_lengths[symbol++] = code_len;
+ if (code_len != 0) prev_code_len = code_len;
+ } else {
+ const int use_prev = (code_len == kCodeLengthRepeatCode);
+ const int slot = code_len - kCodeLengthLiterals;
+ const int extra_bits = kCodeLengthExtraBits[slot];
+ const int repeat_offset = kCodeLengthRepeatOffsets[slot];
+ int repeat = VP8LReadBits(br, extra_bits) + repeat_offset;
+ if (symbol + repeat > num_symbols) {
+ goto End;
+ } else {
+ const int length = use_prev ? prev_code_len : 0;
+ while (repeat-- > 0) code_lengths[symbol++] = length;
+ }
+ }
+ }
+ ok = 1;
+
+ End:
+ if (!ok) dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ return ok;
+}
+
+// 'code_lengths' is pre-allocated temporary buffer, used for creating Huffman
+// tree.
+static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
+ int* const code_lengths, HuffmanCode* const table) {
+ int ok = 0;
+ int size = 0;
+ VP8LBitReader* const br = &dec->br_;
+ const int simple_code = VP8LReadBits(br, 1);
+
+ memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths));
+
+ if (simple_code) { // Read symbols, codes & code lengths directly.
+ const int num_symbols = VP8LReadBits(br, 1) + 1;
+ const int first_symbol_len_code = VP8LReadBits(br, 1);
+ // The first code is either 1 bit or 8 bit code.
+ int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8);
+ code_lengths[symbol] = 1;
+ // The second code (if present), is always 8 bit long.
+ if (num_symbols == 2) {
+ symbol = VP8LReadBits(br, 8);
+ code_lengths[symbol] = 1;
+ }
+ ok = 1;
+ } else { // Decode Huffman-coded code lengths.
+ int i;
+ int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 };
+ const int num_codes = VP8LReadBits(br, 4) + 4;
+ if (num_codes > NUM_CODE_LENGTH_CODES) {
+ dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ return 0;
+ }
+
+ for (i = 0; i < num_codes; ++i) {
+ code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3);
+ }
+ ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size,
+ code_lengths);
+ }
+
+ ok = ok && !br->eos_;
+ if (ok) {
+ size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS,
+ code_lengths, alphabet_size);
+ }
+ if (!ok || size == 0) {
+ dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ return 0;
+ }
+ return size;
+}
+
+static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
+ int color_cache_bits, int allow_recursion) {
+ int i, j;
+ VP8LBitReader* const br = &dec->br_;
+ VP8LMetadata* const hdr = &dec->hdr_;
+ uint32_t* huffman_image = NULL;
+ HTreeGroup* htree_groups = NULL;
+ HuffmanCode* huffman_tables = NULL;
+ HuffmanCode* next = NULL;
+ int num_htree_groups = 1;
+ int max_alphabet_size = 0;
+ int* code_lengths = NULL;
+ const int table_size = kTableSize[color_cache_bits];
+
+ if (allow_recursion && VP8LReadBits(br, 1)) {
+ // use meta Huffman codes.
+ const int huffman_precision = VP8LReadBits(br, 3) + 2;
+ const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision);
+ const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision);
+ const int huffman_pixs = huffman_xsize * huffman_ysize;
+ if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec,
+ &huffman_image)) {
+ goto Error;
+ }
+ hdr->huffman_subsample_bits_ = huffman_precision;
+ for (i = 0; i < huffman_pixs; ++i) {
+ // The huffman data is stored in red and green bytes.
+ const int group = (huffman_image[i] >> 8) & 0xffff;
+ huffman_image[i] = group;
+ if (group >= num_htree_groups) {
+ num_htree_groups = group + 1;
+ }
+ }
+ }
+
+ if (br->eos_) goto Error;
+
+ // Find maximum alphabet size for the htree group.
+ for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
+ int alphabet_size = kAlphabetSize[j];
+ if (j == 0 && color_cache_bits > 0) {
+ alphabet_size += 1 << color_cache_bits;
+ }
+ if (max_alphabet_size < alphabet_size) {
+ max_alphabet_size = alphabet_size;
+ }
+ }
+
+ huffman_tables = (HuffmanCode*)WebPSafeMalloc(num_htree_groups * table_size,
+ sizeof(*huffman_tables));
+ htree_groups = VP8LHtreeGroupsNew(num_htree_groups);
+ code_lengths = (int*)WebPSafeCalloc((uint64_t)max_alphabet_size,
+ sizeof(*code_lengths));
+
+ if (htree_groups == NULL || code_lengths == NULL || huffman_tables == NULL) {
+ dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ next = huffman_tables;
+ for (i = 0; i < num_htree_groups; ++i) {
+ HTreeGroup* const htree_group = &htree_groups[i];
+ HuffmanCode** const htrees = htree_group->htrees;
+ int size;
+ int total_size = 0;
+ int is_trivial_literal = 1;
+ int max_bits = 0;
+ for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
+ int alphabet_size = kAlphabetSize[j];
+ htrees[j] = next;
+ if (j == 0 && color_cache_bits > 0) {
+ alphabet_size += 1 << color_cache_bits;
+ }
+ size = ReadHuffmanCode(alphabet_size, dec, code_lengths, next);
+ if (size == 0) {
+ goto Error;
+ }
+ if (is_trivial_literal && kLiteralMap[j] == 1) {
+ is_trivial_literal = (next->bits == 0);
+ }
+ total_size += next->bits;
+ next += size;
+ if (j <= ALPHA) {
+ int local_max_bits = code_lengths[0];
+ int k;
+ for (k = 1; k < alphabet_size; ++k) {
+ if (code_lengths[k] > local_max_bits) {
+ local_max_bits = code_lengths[k];
+ }
+ }
+ max_bits += local_max_bits;
+ }
+ }
+ htree_group->is_trivial_literal = is_trivial_literal;
+ htree_group->is_trivial_code = 0;
+ if (is_trivial_literal) {
+ const int red = htrees[RED][0].value;
+ const int blue = htrees[BLUE][0].value;
+ const int alpha = htrees[ALPHA][0].value;
+ htree_group->literal_arb =
+ ((uint32_t)alpha << 24) | (red << 16) | blue;
+ if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) {
+ htree_group->is_trivial_code = 1;
+ htree_group->literal_arb |= htrees[GREEN][0].value << 8;
+ }
+ }
+ htree_group->use_packed_table = !htree_group->is_trivial_code &&
+ (max_bits < HUFFMAN_PACKED_BITS);
+ if (htree_group->use_packed_table) BuildPackedTable(htree_group);
+ }
+ WebPSafeFree(code_lengths);
+
+ // All OK. Finalize pointers and return.
+ hdr->huffman_image_ = huffman_image;
+ hdr->num_htree_groups_ = num_htree_groups;
+ hdr->htree_groups_ = htree_groups;
+ hdr->huffman_tables_ = huffman_tables;
+ return 1;
+
+ Error:
+ WebPSafeFree(code_lengths);
+ WebPSafeFree(huffman_image);
+ WebPSafeFree(huffman_tables);
+ VP8LHtreeGroupsFree(htree_groups);
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// Scaling.
+
+#if !defined(WEBP_REDUCE_SIZE)
+static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) {
+ const int num_channels = 4;
+ const int in_width = io->mb_w;
+ const int out_width = io->scaled_width;
+ const int in_height = io->mb_h;
+ const int out_height = io->scaled_height;
+ const uint64_t work_size = 2 * num_channels * (uint64_t)out_width;
+ rescaler_t* work; // Rescaler work area.
+ const uint64_t scaled_data_size = (uint64_t)out_width;
+ uint32_t* scaled_data; // Temporary storage for scaled BGRA data.
+ const uint64_t memory_size = sizeof(*dec->rescaler) +
+ work_size * sizeof(*work) +
+ scaled_data_size * sizeof(*scaled_data);
+ uint8_t* memory = (uint8_t*)WebPSafeMalloc(memory_size, sizeof(*memory));
+ if (memory == NULL) {
+ dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ return 0;
+ }
+ assert(dec->rescaler_memory == NULL);
+ dec->rescaler_memory = memory;
+
+ dec->rescaler = (WebPRescaler*)memory;
+ memory += sizeof(*dec->rescaler);
+ work = (rescaler_t*)memory;
+ memory += work_size * sizeof(*work);
+ scaled_data = (uint32_t*)memory;
+
+ WebPRescalerInit(dec->rescaler, in_width, in_height, (uint8_t*)scaled_data,
+ out_width, out_height, 0, num_channels, work);
+ return 1;
+}
+#endif // WEBP_REDUCE_SIZE
+
+//------------------------------------------------------------------------------
+// Export to ARGB
+
+#if !defined(WEBP_REDUCE_SIZE)
+
+// We have special "export" function since we need to convert from BGRA
+static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace,
+ int rgba_stride, uint8_t* const rgba) {
+ uint32_t* const src = (uint32_t*)rescaler->dst;
+ const int dst_width = rescaler->dst_width;
+ int num_lines_out = 0;
+ while (WebPRescalerHasPendingOutput(rescaler)) {
+ uint8_t* const dst = rgba + num_lines_out * rgba_stride;
+ WebPRescalerExportRow(rescaler);
+ WebPMultARGBRow(src, dst_width, 1);
+ VP8LConvertFromBGRA(src, dst_width, colorspace, dst);
+ ++num_lines_out;
+ }
+ return num_lines_out;
+}
+
+// Emit scaled rows.
+static int EmitRescaledRowsRGBA(const VP8LDecoder* const dec,
+ uint8_t* in, int in_stride, int mb_h,
+ uint8_t* const out, int out_stride) {
+ const WEBP_CSP_MODE colorspace = dec->output_->colorspace;
+ int num_lines_in = 0;
+ int num_lines_out = 0;
+ while (num_lines_in < mb_h) {
+ uint8_t* const row_in = in + num_lines_in * in_stride;
+ uint8_t* const row_out = out + num_lines_out * out_stride;
+ const int lines_left = mb_h - num_lines_in;
+ const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left);
+ int lines_imported;
+ assert(needed_lines > 0 && needed_lines <= lines_left);
+ WebPMultARGBRows(row_in, in_stride,
+ dec->rescaler->src_width, needed_lines, 0);
+ lines_imported =
+ WebPRescalerImport(dec->rescaler, lines_left, row_in, in_stride);
+ assert(lines_imported == needed_lines);
+ num_lines_in += lines_imported;
+ num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out);
+ }
+ return num_lines_out;
+}
+
+#endif // WEBP_REDUCE_SIZE
+
+// Emit rows without any scaling.
+static int EmitRows(WEBP_CSP_MODE colorspace,
+ const uint8_t* row_in, int in_stride,
+ int mb_w, int mb_h,
+ uint8_t* const out, int out_stride) {
+ int lines = mb_h;
+ uint8_t* row_out = out;
+ while (lines-- > 0) {
+ VP8LConvertFromBGRA((const uint32_t*)row_in, mb_w, colorspace, row_out);
+ row_in += in_stride;
+ row_out += out_stride;
+ }
+ return mb_h; // Num rows out == num rows in.
+}
+
+//------------------------------------------------------------------------------
+// Export to YUVA
+
+static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos,
+ const WebPDecBuffer* const output) {
+ const WebPYUVABuffer* const buf = &output->u.YUVA;
+
+ // first, the luma plane
+ WebPConvertARGBToY(src, buf->y + y_pos * buf->y_stride, width);
+
+ // then U/V planes
+ {
+ uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride;
+ uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride;
+ // even lines: store values
+ // odd lines: average with previous values
+ WebPConvertARGBToUV(src, u, v, width, !(y_pos & 1));
+ }
+ // Lastly, store alpha if needed.
+ if (buf->a != NULL) {
+ uint8_t* const a = buf->a + y_pos * buf->a_stride;
+#if defined(WORDS_BIGENDIAN)
+ WebPExtractAlpha((uint8_t*)src + 0, 0, width, 1, a, 0);
+#else
+ WebPExtractAlpha((uint8_t*)src + 3, 0, width, 1, a, 0);
+#endif
+ }
+}
+
+static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) {
+ WebPRescaler* const rescaler = dec->rescaler;
+ uint32_t* const src = (uint32_t*)rescaler->dst;
+ const int dst_width = rescaler->dst_width;
+ int num_lines_out = 0;
+ while (WebPRescalerHasPendingOutput(rescaler)) {
+ WebPRescalerExportRow(rescaler);
+ WebPMultARGBRow(src, dst_width, 1);
+ ConvertToYUVA(src, dst_width, y_pos, dec->output_);
+ ++y_pos;
+ ++num_lines_out;
+ }
+ return num_lines_out;
+}
+
+static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec,
+ uint8_t* in, int in_stride, int mb_h) {
+ int num_lines_in = 0;
+ int y_pos = dec->last_out_row_;
+ while (num_lines_in < mb_h) {
+ const int lines_left = mb_h - num_lines_in;
+ const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left);
+ int lines_imported;
+ WebPMultARGBRows(in, in_stride, dec->rescaler->src_width, needed_lines, 0);
+ lines_imported =
+ WebPRescalerImport(dec->rescaler, lines_left, in, in_stride);
+ assert(lines_imported == needed_lines);
+ num_lines_in += lines_imported;
+ in += needed_lines * in_stride;
+ y_pos += ExportYUVA(dec, y_pos);
+ }
+ return y_pos;
+}
+
+static int EmitRowsYUVA(const VP8LDecoder* const dec,
+ const uint8_t* in, int in_stride,
+ int mb_w, int num_rows) {
+ int y_pos = dec->last_out_row_;
+ while (num_rows-- > 0) {
+ ConvertToYUVA((const uint32_t*)in, mb_w, y_pos, dec->output_);
+ in += in_stride;
+ ++y_pos;
+ }
+ return y_pos;
+}
+
+//------------------------------------------------------------------------------
+// Cropping.
+
+// Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and
+// crop options. Also updates the input data pointer, so that it points to the
+// start of the cropped window. Note that pixels are in ARGB format even if
+// 'in_data' is uint8_t*.
+// Returns true if the crop window is not empty.
+static int SetCropWindow(VP8Io* const io, int y_start, int y_end,
+ uint8_t** const in_data, int pixel_stride) {
+ assert(y_start < y_end);
+ assert(io->crop_left < io->crop_right);
+ if (y_end > io->crop_bottom) {
+ y_end = io->crop_bottom; // make sure we don't overflow on last row.
+ }
+ if (y_start < io->crop_top) {
+ const int delta = io->crop_top - y_start;
+ y_start = io->crop_top;
+ *in_data += delta * pixel_stride;
+ }
+ if (y_start >= y_end) return 0; // Crop window is empty.
+
+ *in_data += io->crop_left * sizeof(uint32_t);
+
+ io->mb_y = y_start - io->crop_top;
+ io->mb_w = io->crop_right - io->crop_left;
+ io->mb_h = y_end - y_start;
+ return 1; // Non-empty crop window.
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE int GetMetaIndex(
+ const uint32_t* const image, int xsize, int bits, int x, int y) {
+ if (bits == 0) return 0;
+ return image[xsize * (y >> bits) + (x >> bits)];
+}
+
+static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr,
+ int x, int y) {
+ const int meta_index = GetMetaIndex(hdr->huffman_image_, hdr->huffman_xsize_,
+ hdr->huffman_subsample_bits_, x, y);
+ assert(meta_index < hdr->num_htree_groups_);
+ return hdr->htree_groups_ + meta_index;
+}
+
+//------------------------------------------------------------------------------
+// Main loop, with custom row-processing function
+
+typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row);
+
+static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows,
+ const uint32_t* const rows) {
+ int n = dec->next_transform_;
+ const int cache_pixs = dec->width_ * num_rows;
+ const int start_row = dec->last_row_;
+ const int end_row = start_row + num_rows;
+ const uint32_t* rows_in = rows;
+ uint32_t* const rows_out = dec->argb_cache_;
+
+ // Inverse transforms.
+ while (n-- > 0) {
+ VP8LTransform* const transform = &dec->transforms_[n];
+ VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out);
+ rows_in = rows_out;
+ }
+ if (rows_in != rows_out) {
+ // No transform called, hence just copy.
+ memcpy(rows_out, rows_in, cache_pixs * sizeof(*rows_out));
+ }
+}
+
+// Processes (transforms, scales & color-converts) the rows decoded after the
+// last call.
+static void ProcessRows(VP8LDecoder* const dec, int row) {
+ const uint32_t* const rows = dec->pixels_ + dec->width_ * dec->last_row_;
+ const int num_rows = row - dec->last_row_;
+
+ assert(row <= dec->io_->crop_bottom);
+ // We can't process more than NUM_ARGB_CACHE_ROWS at a time (that's the size
+ // of argb_cache_), but we currently don't need more than that.
+ assert(num_rows <= NUM_ARGB_CACHE_ROWS);
+ if (num_rows > 0) { // Emit output.
+ VP8Io* const io = dec->io_;
+ uint8_t* rows_data = (uint8_t*)dec->argb_cache_;
+ const int in_stride = io->width * sizeof(uint32_t); // in unit of RGBA
+
+ ApplyInverseTransforms(dec, num_rows, rows);
+ if (!SetCropWindow(io, dec->last_row_, row, &rows_data, in_stride)) {
+ // Nothing to output (this time).
+ } else {
+ const WebPDecBuffer* const output = dec->output_;
+ if (WebPIsRGBMode(output->colorspace)) { // convert to RGBA
+ const WebPRGBABuffer* const buf = &output->u.RGBA;
+ uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride;
+ const int num_rows_out =
+#if !defined(WEBP_REDUCE_SIZE)
+ io->use_scaling ?
+ EmitRescaledRowsRGBA(dec, rows_data, in_stride, io->mb_h,
+ rgba, buf->stride) :
+#endif // WEBP_REDUCE_SIZE
+ EmitRows(output->colorspace, rows_data, in_stride,
+ io->mb_w, io->mb_h, rgba, buf->stride);
+ // Update 'last_out_row_'.
+ dec->last_out_row_ += num_rows_out;
+ } else { // convert to YUVA
+ dec->last_out_row_ = io->use_scaling ?
+ EmitRescaledRowsYUVA(dec, rows_data, in_stride, io->mb_h) :
+ EmitRowsYUVA(dec, rows_data, in_stride, io->mb_w, io->mb_h);
+ }
+ assert(dec->last_out_row_ <= output->height);
+ }
+ }
+
+ // Update 'last_row_'.
+ dec->last_row_ = row;
+ assert(dec->last_row_ <= dec->height_);
+}
+
+// Row-processing for the special case when alpha data contains only one
+// transform (color indexing), and trivial non-green literals.
+static int Is8bOptimizable(const VP8LMetadata* const hdr) {
+ int i;
+ if (hdr->color_cache_size_ > 0) return 0;
+ // When the Huffman tree contains only one symbol, we can skip the
+ // call to ReadSymbol() for red/blue/alpha channels.
+ for (i = 0; i < hdr->num_htree_groups_; ++i) {
+ HuffmanCode** const htrees = hdr->htree_groups_[i].htrees;
+ if (htrees[RED][0].bits > 0) return 0;
+ if (htrees[BLUE][0].bits > 0) return 0;
+ if (htrees[ALPHA][0].bits > 0) return 0;
+ }
+ return 1;
+}
+
+static void AlphaApplyFilter(ALPHDecoder* const alph_dec,
+ int first_row, int last_row,
+ uint8_t* out, int stride) {
+ if (alph_dec->filter_ != WEBP_FILTER_NONE) {
+ int y;
+ const uint8_t* prev_line = alph_dec->prev_line_;
+ assert(WebPUnfilters[alph_dec->filter_] != NULL);
+ for (y = first_row; y < last_row; ++y) {
+ WebPUnfilters[alph_dec->filter_](prev_line, out, out, stride);
+ prev_line = out;
+ out += stride;
+ }
+ alph_dec->prev_line_ = prev_line;
+ }
+}
+
+static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int last_row) {
+ // For vertical and gradient filtering, we need to decode the part above the
+ // crop_top row, in order to have the correct spatial predictors.
+ ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque;
+ const int top_row =
+ (alph_dec->filter_ == WEBP_FILTER_NONE ||
+ alph_dec->filter_ == WEBP_FILTER_HORIZONTAL) ? dec->io_->crop_top
+ : dec->last_row_;
+ const int first_row = (dec->last_row_ < top_row) ? top_row : dec->last_row_;
+ assert(last_row <= dec->io_->crop_bottom);
+ if (last_row > first_row) {
+ // Special method for paletted alpha data. We only process the cropped area.
+ const int width = dec->io_->width;
+ uint8_t* out = alph_dec->output_ + width * first_row;
+ const uint8_t* const in =
+ (uint8_t*)dec->pixels_ + dec->width_ * first_row;
+ VP8LTransform* const transform = &dec->transforms_[0];
+ assert(dec->next_transform_ == 1);
+ assert(transform->type_ == COLOR_INDEXING_TRANSFORM);
+ VP8LColorIndexInverseTransformAlpha(transform, first_row, last_row,
+ in, out);
+ AlphaApplyFilter(alph_dec, first_row, last_row, out, width);
+ }
+ dec->last_row_ = dec->last_out_row_ = last_row;
+}
+
+//------------------------------------------------------------------------------
+// Helper functions for fast pattern copy (8b and 32b)
+
+// cyclic rotation of pattern word
+static WEBP_INLINE uint32_t Rotate8b(uint32_t V) {
+#if defined(WORDS_BIGENDIAN)
+ return ((V & 0xff000000u) >> 24) | (V << 8);
+#else
+ return ((V & 0xffu) << 24) | (V >> 8);
+#endif
+}
+
+// copy 1, 2 or 4-bytes pattern
+static WEBP_INLINE void CopySmallPattern8b(const uint8_t* src, uint8_t* dst,
+ int length, uint32_t pattern) {
+ int i;
+ // align 'dst' to 4-bytes boundary. Adjust the pattern along the way.
+ while ((uintptr_t)dst & 3) {
+ *dst++ = *src++;
+ pattern = Rotate8b(pattern);
+ --length;
+ }
+ // Copy the pattern 4 bytes at a time.
+ for (i = 0; i < (length >> 2); ++i) {
+ ((uint32_t*)dst)[i] = pattern;
+ }
+ // Finish with left-overs. 'pattern' is still correctly positioned,
+ // so no Rotate8b() call is needed.
+ for (i <<= 2; i < length; ++i) {
+ dst[i] = src[i];
+ }
+}
+
+static WEBP_INLINE void CopyBlock8b(uint8_t* const dst, int dist, int length) {
+ const uint8_t* src = dst - dist;
+ if (length >= 8) {
+ uint32_t pattern = 0;
+ switch (dist) {
+ case 1:
+ pattern = src[0];
+#if defined(__arm__) || defined(_M_ARM) // arm doesn't like multiply that much
+ pattern |= pattern << 8;
+ pattern |= pattern << 16;
+#elif defined(WEBP_USE_MIPS_DSP_R2)
+ __asm__ volatile ("replv.qb %0, %0" : "+r"(pattern));
+#else
+ pattern = 0x01010101u * pattern;
+#endif
+ break;
+ case 2:
+ memcpy(&pattern, src, sizeof(uint16_t));
+#if defined(__arm__) || defined(_M_ARM)
+ pattern |= pattern << 16;
+#elif defined(WEBP_USE_MIPS_DSP_R2)
+ __asm__ volatile ("replv.ph %0, %0" : "+r"(pattern));
+#else
+ pattern = 0x00010001u * pattern;
+#endif
+ break;
+ case 4:
+ memcpy(&pattern, src, sizeof(uint32_t));
+ break;
+ default:
+ goto Copy;
+ break;
+ }
+ CopySmallPattern8b(src, dst, length, pattern);
+ return;
+ }
+ Copy:
+ if (dist >= length) { // no overlap -> use memcpy()
+ memcpy(dst, src, length * sizeof(*dst));
+ } else {
+ int i;
+ for (i = 0; i < length; ++i) dst[i] = src[i];
+ }
+}
+
+// copy pattern of 1 or 2 uint32_t's
+static WEBP_INLINE void CopySmallPattern32b(const uint32_t* src,
+ uint32_t* dst,
+ int length, uint64_t pattern) {
+ int i;
+ if ((uintptr_t)dst & 4) { // Align 'dst' to 8-bytes boundary.
+ *dst++ = *src++;
+ pattern = (pattern >> 32) | (pattern << 32);
+ --length;
+ }
+ assert(0 == ((uintptr_t)dst & 7));
+ for (i = 0; i < (length >> 1); ++i) {
+ ((uint64_t*)dst)[i] = pattern; // Copy the pattern 8 bytes at a time.
+ }
+ if (length & 1) { // Finish with left-over.
+ dst[i << 1] = src[i << 1];
+ }
+}
+
+static WEBP_INLINE void CopyBlock32b(uint32_t* const dst,
+ int dist, int length) {
+ const uint32_t* const src = dst - dist;
+ if (dist <= 2 && length >= 4 && ((uintptr_t)dst & 3) == 0) {
+ uint64_t pattern;
+ if (dist == 1) {
+ pattern = (uint64_t)src[0];
+ pattern |= pattern << 32;
+ } else {
+ memcpy(&pattern, src, sizeof(pattern));
+ }
+ CopySmallPattern32b(src, dst, length, pattern);
+ } else if (dist >= length) { // no overlap
+ memcpy(dst, src, length * sizeof(*dst));
+ } else {
+ int i;
+ for (i = 0; i < length; ++i) dst[i] = src[i];
+ }
+}
+
+//------------------------------------------------------------------------------
+
+static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data,
+ int width, int height, int last_row) {
+ int ok = 1;
+ int row = dec->last_pixel_ / width;
+ int col = dec->last_pixel_ % width;
+ VP8LBitReader* const br = &dec->br_;
+ VP8LMetadata* const hdr = &dec->hdr_;
+ int pos = dec->last_pixel_; // current position
+ const int end = width * height; // End of data
+ const int last = width * last_row; // Last pixel to decode
+ const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
+ const int mask = hdr->huffman_mask_;
+ const HTreeGroup* htree_group =
+ (pos < last) ? GetHtreeGroupForPos(hdr, col, row) : NULL;
+ assert(pos <= end);
+ assert(last_row <= height);
+ assert(Is8bOptimizable(hdr));
+
+ while (!br->eos_ && pos < last) {
+ int code;
+ // Only update when changing tile.
+ if ((col & mask) == 0) {
+ htree_group = GetHtreeGroupForPos(hdr, col, row);
+ }
+ assert(htree_group != NULL);
+ VP8LFillBitWindow(br);
+ code = ReadSymbol(htree_group->htrees[GREEN], br);
+ if (code < NUM_LITERAL_CODES) { // Literal
+ data[pos] = code;
+ ++pos;
+ ++col;
+ if (col >= width) {
+ col = 0;
+ ++row;
+ if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) {
+ ExtractPalettedAlphaRows(dec, row);
+ }
+ }
+ } else if (code < len_code_limit) { // Backward reference
+ int dist_code, dist;
+ const int length_sym = code - NUM_LITERAL_CODES;
+ const int length = GetCopyLength(length_sym, br);
+ const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br);
+ VP8LFillBitWindow(br);
+ dist_code = GetCopyDistance(dist_symbol, br);
+ dist = PlaneCodeToDistance(width, dist_code);
+ if (pos >= dist && end - pos >= length) {
+ CopyBlock8b(data + pos, dist, length);
+ } else {
+ ok = 0;
+ goto End;
+ }
+ pos += length;
+ col += length;
+ while (col >= width) {
+ col -= width;
+ ++row;
+ if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) {
+ ExtractPalettedAlphaRows(dec, row);
+ }
+ }
+ if (pos < last && (col & mask)) {
+ htree_group = GetHtreeGroupForPos(hdr, col, row);
+ }
+ } else { // Not reached
+ ok = 0;
+ goto End;
+ }
+ br->eos_ = VP8LIsEndOfStream(br);
+ }
+ // Process the remaining rows corresponding to last row-block.
+ ExtractPalettedAlphaRows(dec, row > last_row ? last_row : row);
+
+ End:
+ br->eos_ = VP8LIsEndOfStream(br);
+ if (!ok || (br->eos_ && pos < end)) {
+ ok = 0;
+ dec->status_ = br->eos_ ? VP8_STATUS_SUSPENDED
+ : VP8_STATUS_BITSTREAM_ERROR;
+ } else {
+ dec->last_pixel_ = pos;
+ }
+ return ok;
+}
+
+static void SaveState(VP8LDecoder* const dec, int last_pixel) {
+ assert(dec->incremental_);
+ dec->saved_br_ = dec->br_;
+ dec->saved_last_pixel_ = last_pixel;
+ if (dec->hdr_.color_cache_size_ > 0) {
+ VP8LColorCacheCopy(&dec->hdr_.color_cache_, &dec->hdr_.saved_color_cache_);
+ }
+}
+
+static void RestoreState(VP8LDecoder* const dec) {
+ assert(dec->br_.eos_);
+ dec->status_ = VP8_STATUS_SUSPENDED;
+ dec->br_ = dec->saved_br_;
+ dec->last_pixel_ = dec->saved_last_pixel_;
+ if (dec->hdr_.color_cache_size_ > 0) {
+ VP8LColorCacheCopy(&dec->hdr_.saved_color_cache_, &dec->hdr_.color_cache_);
+ }
+}
+
+#define SYNC_EVERY_N_ROWS 8 // minimum number of rows between check-points
+static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data,
+ int width, int height, int last_row,
+ ProcessRowsFunc process_func) {
+ int row = dec->last_pixel_ / width;
+ int col = dec->last_pixel_ % width;
+ VP8LBitReader* const br = &dec->br_;
+ VP8LMetadata* const hdr = &dec->hdr_;
+ uint32_t* src = data + dec->last_pixel_;
+ uint32_t* last_cached = src;
+ uint32_t* const src_end = data + width * height; // End of data
+ uint32_t* const src_last = data + width * last_row; // Last pixel to decode
+ const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES;
+ const int color_cache_limit = len_code_limit + hdr->color_cache_size_;
+ int next_sync_row = dec->incremental_ ? row : 1 << 24;
+ VP8LColorCache* const color_cache =
+ (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL;
+ const int mask = hdr->huffman_mask_;
+ const HTreeGroup* htree_group =
+ (src < src_last) ? GetHtreeGroupForPos(hdr, col, row) : NULL;
+ assert(dec->last_row_ < last_row);
+ assert(src_last <= src_end);
+
+ while (src < src_last) {
+ int code;
+ if (row >= next_sync_row) {
+ SaveState(dec, (int)(src - data));
+ next_sync_row = row + SYNC_EVERY_N_ROWS;
+ }
+ // Only update when changing tile. Note we could use this test:
+ // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed
+ // but that's actually slower and needs storing the previous col/row.
+ if ((col & mask) == 0) {
+ htree_group = GetHtreeGroupForPos(hdr, col, row);
+ }
+ assert(htree_group != NULL);
+ if (htree_group->is_trivial_code) {
+ *src = htree_group->literal_arb;
+ goto AdvanceByOne;
+ }
+ VP8LFillBitWindow(br);
+ if (htree_group->use_packed_table) {
+ code = ReadPackedSymbols(htree_group, br, src);
+ if (VP8LIsEndOfStream(br)) break;
+ if (code == PACKED_NON_LITERAL_CODE) goto AdvanceByOne;
+ } else {
+ code = ReadSymbol(htree_group->htrees[GREEN], br);
+ }
+ if (VP8LIsEndOfStream(br)) break;
+ if (code < NUM_LITERAL_CODES) { // Literal
+ if (htree_group->is_trivial_literal) {
+ *src = htree_group->literal_arb | (code << 8);
+ } else {
+ int red, blue, alpha;
+ red = ReadSymbol(htree_group->htrees[RED], br);
+ VP8LFillBitWindow(br);
+ blue = ReadSymbol(htree_group->htrees[BLUE], br);
+ alpha = ReadSymbol(htree_group->htrees[ALPHA], br);
+ if (VP8LIsEndOfStream(br)) break;
+ *src = ((uint32_t)alpha << 24) | (red << 16) | (code << 8) | blue;
+ }
+ AdvanceByOne:
+ ++src;
+ ++col;
+ if (col >= width) {
+ col = 0;
+ ++row;
+ if (process_func != NULL) {
+ if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) {
+ process_func(dec, row);
+ }
+ }
+ if (color_cache != NULL) {
+ while (last_cached < src) {
+ VP8LColorCacheInsert(color_cache, *last_cached++);
+ }
+ }
+ }
+ } else if (code < len_code_limit) { // Backward reference
+ int dist_code, dist;
+ const int length_sym = code - NUM_LITERAL_CODES;
+ const int length = GetCopyLength(length_sym, br);
+ const int dist_symbol = ReadSymbol(htree_group->htrees[DIST], br);
+ VP8LFillBitWindow(br);
+ dist_code = GetCopyDistance(dist_symbol, br);
+ dist = PlaneCodeToDistance(width, dist_code);
+ if (VP8LIsEndOfStream(br)) break;
+ if (src - data < (ptrdiff_t)dist || src_end - src < (ptrdiff_t)length) {
+ goto Error;
+ } else {
+ CopyBlock32b(src, dist, length);
+ }
+ src += length;
+ col += length;
+ while (col >= width) {
+ col -= width;
+ ++row;
+ if (process_func != NULL) {
+ if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) {
+ process_func(dec, row);
+ }
+ }
+ }
+ // Because of the check done above (before 'src' was incremented by
+ // 'length'), the following holds true.
+ assert(src <= src_end);
+ if (col & mask) htree_group = GetHtreeGroupForPos(hdr, col, row);
+ if (color_cache != NULL) {
+ while (last_cached < src) {
+ VP8LColorCacheInsert(color_cache, *last_cached++);
+ }
+ }
+ } else if (code < color_cache_limit) { // Color cache
+ const int key = code - len_code_limit;
+ assert(color_cache != NULL);
+ while (last_cached < src) {
+ VP8LColorCacheInsert(color_cache, *last_cached++);
+ }
+ *src = VP8LColorCacheLookup(color_cache, key);
+ goto AdvanceByOne;
+ } else { // Not reached
+ goto Error;
+ }
+ }
+
+ br->eos_ = VP8LIsEndOfStream(br);
+ if (dec->incremental_ && br->eos_ && src < src_end) {
+ RestoreState(dec);
+ } else if (!br->eos_) {
+ // Process the remaining rows corresponding to last row-block.
+ if (process_func != NULL) {
+ process_func(dec, row > last_row ? last_row : row);
+ }
+ dec->status_ = VP8_STATUS_OK;
+ dec->last_pixel_ = (int)(src - data); // end-of-scan marker
+ } else {
+ // if not incremental, and we are past the end of buffer (eos_=1), then this
+ // is a real bitstream error.
+ goto Error;
+ }
+ return 1;
+
+ Error:
+ dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+// VP8LTransform
+
+static void ClearTransform(VP8LTransform* const transform) {
+ WebPSafeFree(transform->data_);
+ transform->data_ = NULL;
+}
+
+// For security reason, we need to remap the color map to span
+// the total possible bundled values, and not just the num_colors.
+static int ExpandColorMap(int num_colors, VP8LTransform* const transform) {
+ int i;
+ const int final_num_colors = 1 << (8 >> transform->bits_);
+ uint32_t* const new_color_map =
+ (uint32_t*)WebPSafeMalloc((uint64_t)final_num_colors,
+ sizeof(*new_color_map));
+ if (new_color_map == NULL) {
+ return 0;
+ } else {
+ uint8_t* const data = (uint8_t*)transform->data_;
+ uint8_t* const new_data = (uint8_t*)new_color_map;
+ new_color_map[0] = transform->data_[0];
+ for (i = 4; i < 4 * num_colors; ++i) {
+ // Equivalent to AddPixelEq(), on a byte-basis.
+ new_data[i] = (data[i] + new_data[i - 4]) & 0xff;
+ }
+ for (; i < 4 * final_num_colors; ++i) {
+ new_data[i] = 0; // black tail.
+ }
+ WebPSafeFree(transform->data_);
+ transform->data_ = new_color_map;
+ }
+ return 1;
+}
+
+static int ReadTransform(int* const xsize, int const* ysize,
+ VP8LDecoder* const dec) {
+ int ok = 1;
+ VP8LBitReader* const br = &dec->br_;
+ VP8LTransform* transform = &dec->transforms_[dec->next_transform_];
+ const VP8LImageTransformType type =
+ (VP8LImageTransformType)VP8LReadBits(br, 2);
+
+ // Each transform type can only be present once in the stream.
+ if (dec->transforms_seen_ & (1U << type)) {
+ return 0; // Already there, let's not accept the second same transform.
+ }
+ dec->transforms_seen_ |= (1U << type);
+
+ transform->type_ = type;
+ transform->xsize_ = *xsize;
+ transform->ysize_ = *ysize;
+ transform->data_ = NULL;
+ ++dec->next_transform_;
+ assert(dec->next_transform_ <= NUM_TRANSFORMS);
+
+ switch (type) {
+ case PREDICTOR_TRANSFORM:
+ case CROSS_COLOR_TRANSFORM:
+ transform->bits_ = VP8LReadBits(br, 3) + 2;
+ ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_,
+ transform->bits_),
+ VP8LSubSampleSize(transform->ysize_,
+ transform->bits_),
+ 0, dec, &transform->data_);
+ break;
+ case COLOR_INDEXING_TRANSFORM: {
+ const int num_colors = VP8LReadBits(br, 8) + 1;
+ const int bits = (num_colors > 16) ? 0
+ : (num_colors > 4) ? 1
+ : (num_colors > 2) ? 2
+ : 3;
+ *xsize = VP8LSubSampleSize(transform->xsize_, bits);
+ transform->bits_ = bits;
+ ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_);
+ ok = ok && ExpandColorMap(num_colors, transform);
+ break;
+ }
+ case SUBTRACT_GREEN:
+ break;
+ default:
+ assert(0); // can't happen
+ break;
+ }
+
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+// VP8LMetadata
+
+static void InitMetadata(VP8LMetadata* const hdr) {
+ assert(hdr != NULL);
+ memset(hdr, 0, sizeof(*hdr));
+}
+
+static void ClearMetadata(VP8LMetadata* const hdr) {
+ assert(hdr != NULL);
+
+ WebPSafeFree(hdr->huffman_image_);
+ WebPSafeFree(hdr->huffman_tables_);
+ VP8LHtreeGroupsFree(hdr->htree_groups_);
+ VP8LColorCacheClear(&hdr->color_cache_);
+ VP8LColorCacheClear(&hdr->saved_color_cache_);
+ InitMetadata(hdr);
+}
+
+// -----------------------------------------------------------------------------
+// VP8LDecoder
+
+VP8LDecoder* VP8LNew(void) {
+ VP8LDecoder* const dec = (VP8LDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
+ if (dec == NULL) return NULL;
+ dec->status_ = VP8_STATUS_OK;
+ dec->state_ = READ_DIM;
+
+ VP8LDspInit(); // Init critical function pointers.
+
+ return dec;
+}
+
+void VP8LClear(VP8LDecoder* const dec) {
+ int i;
+ if (dec == NULL) return;
+ ClearMetadata(&dec->hdr_);
+
+ WebPSafeFree(dec->pixels_);
+ dec->pixels_ = NULL;
+ for (i = 0; i < dec->next_transform_; ++i) {
+ ClearTransform(&dec->transforms_[i]);
+ }
+ dec->next_transform_ = 0;
+ dec->transforms_seen_ = 0;
+
+ WebPSafeFree(dec->rescaler_memory);
+ dec->rescaler_memory = NULL;
+
+ dec->output_ = NULL; // leave no trace behind
+}
+
+void VP8LDelete(VP8LDecoder* const dec) {
+ if (dec != NULL) {
+ VP8LClear(dec);
+ WebPSafeFree(dec);
+ }
+}
+
+static void UpdateDecoder(VP8LDecoder* const dec, int width, int height) {
+ VP8LMetadata* const hdr = &dec->hdr_;
+ const int num_bits = hdr->huffman_subsample_bits_;
+ dec->width_ = width;
+ dec->height_ = height;
+
+ hdr->huffman_xsize_ = VP8LSubSampleSize(width, num_bits);
+ hdr->huffman_mask_ = (num_bits == 0) ? ~0 : (1 << num_bits) - 1;
+}
+
+static int DecodeImageStream(int xsize, int ysize,
+ int is_level0,
+ VP8LDecoder* const dec,
+ uint32_t** const decoded_data) {
+ int ok = 1;
+ int transform_xsize = xsize;
+ int transform_ysize = ysize;
+ VP8LBitReader* const br = &dec->br_;
+ VP8LMetadata* const hdr = &dec->hdr_;
+ uint32_t* data = NULL;
+ int color_cache_bits = 0;
+
+ // Read the transforms (may recurse).
+ if (is_level0) {
+ while (ok && VP8LReadBits(br, 1)) {
+ ok = ReadTransform(&transform_xsize, &transform_ysize, dec);
+ }
+ }
+
+ // Color cache
+ if (ok && VP8LReadBits(br, 1)) {
+ color_cache_bits = VP8LReadBits(br, 4);
+ ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS);
+ if (!ok) {
+ dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ goto End;
+ }
+ }
+
+ // Read the Huffman codes (may recurse).
+ ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize,
+ color_cache_bits, is_level0);
+ if (!ok) {
+ dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ goto End;
+ }
+
+ // Finish setting up the color-cache
+ if (color_cache_bits > 0) {
+ hdr->color_cache_size_ = 1 << color_cache_bits;
+ if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) {
+ dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ ok = 0;
+ goto End;
+ }
+ } else {
+ hdr->color_cache_size_ = 0;
+ }
+ UpdateDecoder(dec, transform_xsize, transform_ysize);
+
+ if (is_level0) { // level 0 complete
+ dec->state_ = READ_HDR;
+ goto End;
+ }
+
+ {
+ const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize;
+ data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data));
+ if (data == NULL) {
+ dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ ok = 0;
+ goto End;
+ }
+ }
+
+ // Use the Huffman trees to decode the LZ77 encoded data.
+ ok = DecodeImageData(dec, data, transform_xsize, transform_ysize,
+ transform_ysize, NULL);
+ ok = ok && !br->eos_;
+
+ End:
+ if (!ok) {
+ WebPSafeFree(data);
+ ClearMetadata(hdr);
+ } else {
+ if (decoded_data != NULL) {
+ *decoded_data = data;
+ } else {
+ // We allocate image data in this function only for transforms. At level 0
+ // (that is: not the transforms), we shouldn't have allocated anything.
+ assert(data == NULL);
+ assert(is_level0);
+ }
+ dec->last_pixel_ = 0; // Reset for future DECODE_DATA_FUNC() calls.
+ if (!is_level0) ClearMetadata(hdr); // Clean up temporary data behind.
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// Allocate internal buffers dec->pixels_ and dec->argb_cache_.
+static int AllocateInternalBuffers32b(VP8LDecoder* const dec, int final_width) {
+ const uint64_t num_pixels = (uint64_t)dec->width_ * dec->height_;
+ // Scratch buffer corresponding to top-prediction row for transforming the
+ // first row in the row-blocks. Not needed for paletted alpha.
+ const uint64_t cache_top_pixels = (uint16_t)final_width;
+ // Scratch buffer for temporary BGRA storage. Not needed for paletted alpha.
+ const uint64_t cache_pixels = (uint64_t)final_width * NUM_ARGB_CACHE_ROWS;
+ const uint64_t total_num_pixels =
+ num_pixels + cache_top_pixels + cache_pixels;
+
+ assert(dec->width_ <= final_width);
+ dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint32_t));
+ if (dec->pixels_ == NULL) {
+ dec->argb_cache_ = NULL; // for sanity check
+ dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ return 0;
+ }
+ dec->argb_cache_ = dec->pixels_ + num_pixels + cache_top_pixels;
+ return 1;
+}
+
+static int AllocateInternalBuffers8b(VP8LDecoder* const dec) {
+ const uint64_t total_num_pixels = (uint64_t)dec->width_ * dec->height_;
+ dec->argb_cache_ = NULL; // for sanity check
+ dec->pixels_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(uint8_t));
+ if (dec->pixels_ == NULL) {
+ dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ return 0;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+// Special row-processing that only stores the alpha data.
+static void ExtractAlphaRows(VP8LDecoder* const dec, int last_row) {
+ int cur_row = dec->last_row_;
+ int num_rows = last_row - cur_row;
+ const uint32_t* in = dec->pixels_ + dec->width_ * cur_row;
+
+ assert(last_row <= dec->io_->crop_bottom);
+ while (num_rows > 0) {
+ const int num_rows_to_process =
+ (num_rows > NUM_ARGB_CACHE_ROWS) ? NUM_ARGB_CACHE_ROWS : num_rows;
+ // Extract alpha (which is stored in the green plane).
+ ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque;
+ uint8_t* const output = alph_dec->output_;
+ const int width = dec->io_->width; // the final width (!= dec->width_)
+ const int cache_pixs = width * num_rows_to_process;
+ uint8_t* const dst = output + width * cur_row;
+ const uint32_t* const src = dec->argb_cache_;
+ ApplyInverseTransforms(dec, num_rows_to_process, in);
+ WebPExtractGreen(src, dst, cache_pixs);
+ AlphaApplyFilter(alph_dec,
+ cur_row, cur_row + num_rows_to_process, dst, width);
+ num_rows -= num_rows_to_process;
+ in += num_rows_to_process * dec->width_;
+ cur_row += num_rows_to_process;
+ }
+ assert(cur_row == last_row);
+ dec->last_row_ = dec->last_out_row_ = last_row;
+}
+
+int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec,
+ const uint8_t* const data, size_t data_size) {
+ int ok = 0;
+ VP8LDecoder* dec = VP8LNew();
+
+ if (dec == NULL) return 0;
+
+ assert(alph_dec != NULL);
+ alph_dec->vp8l_dec_ = dec;
+
+ dec->width_ = alph_dec->width_;
+ dec->height_ = alph_dec->height_;
+ dec->io_ = &alph_dec->io_;
+ dec->io_->opaque = alph_dec;
+ dec->io_->width = alph_dec->width_;
+ dec->io_->height = alph_dec->height_;
+
+ dec->status_ = VP8_STATUS_OK;
+ VP8LInitBitReader(&dec->br_, data, data_size);
+
+ if (!DecodeImageStream(alph_dec->width_, alph_dec->height_, 1, dec, NULL)) {
+ goto Err;
+ }
+
+ // Special case: if alpha data uses only the color indexing transform and
+ // doesn't use color cache (a frequent case), we will use DecodeAlphaData()
+ // method that only needs allocation of 1 byte per pixel (alpha channel).
+ if (dec->next_transform_ == 1 &&
+ dec->transforms_[0].type_ == COLOR_INDEXING_TRANSFORM &&
+ Is8bOptimizable(&dec->hdr_)) {
+ alph_dec->use_8b_decode_ = 1;
+ ok = AllocateInternalBuffers8b(dec);
+ } else {
+ // Allocate internal buffers (note that dec->width_ may have changed here).
+ alph_dec->use_8b_decode_ = 0;
+ ok = AllocateInternalBuffers32b(dec, alph_dec->width_);
+ }
+
+ if (!ok) goto Err;
+
+ return 1;
+
+ Err:
+ VP8LDelete(alph_dec->vp8l_dec_);
+ alph_dec->vp8l_dec_ = NULL;
+ return 0;
+}
+
+int VP8LDecodeAlphaImageStream(ALPHDecoder* const alph_dec, int last_row) {
+ VP8LDecoder* const dec = alph_dec->vp8l_dec_;
+ assert(dec != NULL);
+ assert(last_row <= dec->height_);
+
+ if (dec->last_row_ >= last_row) {
+ return 1; // done
+ }
+
+ if (!alph_dec->use_8b_decode_) WebPInitAlphaProcessing();
+
+ // Decode (with special row processing).
+ return alph_dec->use_8b_decode_ ?
+ DecodeAlphaData(dec, (uint8_t*)dec->pixels_, dec->width_, dec->height_,
+ last_row) :
+ DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_,
+ last_row, ExtractAlphaRows);
+}
+
+//------------------------------------------------------------------------------
+
+int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) {
+ int width, height, has_alpha;
+
+ if (dec == NULL) return 0;
+ if (io == NULL) {
+ dec->status_ = VP8_STATUS_INVALID_PARAM;
+ return 0;
+ }
+
+ dec->io_ = io;
+ dec->status_ = VP8_STATUS_OK;
+ VP8LInitBitReader(&dec->br_, io->data, io->data_size);
+ if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) {
+ dec->status_ = VP8_STATUS_BITSTREAM_ERROR;
+ goto Error;
+ }
+ dec->state_ = READ_DIM;
+ io->width = width;
+ io->height = height;
+
+ if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error;
+ return 1;
+
+ Error:
+ VP8LClear(dec);
+ assert(dec->status_ != VP8_STATUS_OK);
+ return 0;
+}
+
+int VP8LDecodeImage(VP8LDecoder* const dec) {
+ VP8Io* io = NULL;
+ WebPDecParams* params = NULL;
+
+ // Sanity checks.
+ if (dec == NULL) return 0;
+
+ assert(dec->hdr_.huffman_tables_ != NULL);
+ assert(dec->hdr_.htree_groups_ != NULL);
+ assert(dec->hdr_.num_htree_groups_ > 0);
+
+ io = dec->io_;
+ assert(io != NULL);
+ params = (WebPDecParams*)io->opaque;
+ assert(params != NULL);
+
+ // Initialization.
+ if (dec->state_ != READ_DATA) {
+ dec->output_ = params->output;
+ assert(dec->output_ != NULL);
+
+ if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) {
+ dec->status_ = VP8_STATUS_INVALID_PARAM;
+ goto Err;
+ }
+
+ if (!AllocateInternalBuffers32b(dec, io->width)) goto Err;
+
+#if !defined(WEBP_REDUCE_SIZE)
+ if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err;
+#else
+ if (io->use_scaling) {
+ dec->status_ = VP8_STATUS_INVALID_PARAM;
+ goto Err;
+ }
+#endif
+ if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) {
+ // need the alpha-multiply functions for premultiplied output or rescaling
+ WebPInitAlphaProcessing();
+ }
+
+ if (!WebPIsRGBMode(dec->output_->colorspace)) {
+ WebPInitConvertARGBToYUV();
+ if (dec->output_->u.YUVA.a != NULL) WebPInitAlphaProcessing();
+ }
+ if (dec->incremental_) {
+ if (dec->hdr_.color_cache_size_ > 0 &&
+ dec->hdr_.saved_color_cache_.colors_ == NULL) {
+ if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_,
+ dec->hdr_.color_cache_.hash_bits_)) {
+ dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
+ goto Err;
+ }
+ }
+ }
+ dec->state_ = READ_DATA;
+ }
+
+ // Decode.
+ if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_,
+ io->crop_bottom, ProcessRows)) {
+ goto Err;
+ }
+
+ params->last_y = dec->last_out_row_;
+ return 1;
+
+ Err:
+ VP8LClear(dec);
+ assert(dec->status_ != VP8_STATUS_OK);
+ return 0;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dec/vp8li_dec.h b/src/third_party/libwebp/src/dec/vp8li_dec.h
new file mode 100644
index 0000000..cea22f7
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/vp8li_dec.h
@@ -0,0 +1,137 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Lossless decoder: internal header.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+// Vikas Arora(vikaas.arora@gmail.com)
+
+#ifndef WEBP_DEC_VP8LI_DEC_H_
+#define WEBP_DEC_VP8LI_DEC_H_
+
+#if !defined(STARBOARD)
+#include <string.h> // for memcpy()
+#endif
+#include "src/dec/webpi_dec.h"
+#include "src/utils/bit_reader_utils.h"
+#include "src/utils/color_cache_utils.h"
+#include "src/utils/huffman_utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ READ_DATA = 0,
+ READ_HDR = 1,
+ READ_DIM = 2
+} VP8LDecodeState;
+
+typedef struct VP8LTransform VP8LTransform;
+struct VP8LTransform {
+ VP8LImageTransformType type_; // transform type.
+ int bits_; // subsampling bits defining transform window.
+ int xsize_; // transform window X index.
+ int ysize_; // transform window Y index.
+ uint32_t *data_; // transform data.
+};
+
+typedef struct {
+ int color_cache_size_;
+ VP8LColorCache color_cache_;
+ VP8LColorCache saved_color_cache_; // for incremental
+
+ int huffman_mask_;
+ int huffman_subsample_bits_;
+ int huffman_xsize_;
+ uint32_t *huffman_image_;
+ int num_htree_groups_;
+ HTreeGroup *htree_groups_;
+ HuffmanCode *huffman_tables_;
+} VP8LMetadata;
+
+typedef struct VP8LDecoder VP8LDecoder;
+struct VP8LDecoder {
+ VP8StatusCode status_;
+ VP8LDecodeState state_;
+ VP8Io *io_;
+
+ const WebPDecBuffer *output_; // shortcut to io->opaque->output
+
+ uint32_t *pixels_; // Internal data: either uint8_t* for alpha
+ // or uint32_t* for BGRA.
+ uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
+
+ VP8LBitReader br_;
+ int incremental_; // if true, incremental decoding is expected
+ VP8LBitReader saved_br_; // note: could be local variables too
+ int saved_last_pixel_;
+
+ int width_;
+ int height_;
+ int last_row_; // last input row decoded so far.
+ int last_pixel_; // last pixel decoded so far. However, it may
+ // not be transformed, scaled and
+ // color-converted yet.
+ int last_out_row_; // last row output so far.
+
+ VP8LMetadata hdr_;
+
+ int next_transform_;
+ VP8LTransform transforms_[NUM_TRANSFORMS];
+ // or'd bitset storing the transforms types.
+ uint32_t transforms_seen_;
+
+ uint8_t *rescaler_memory; // Working memory for rescaling work.
+ WebPRescaler *rescaler; // Common rescaler for all channels.
+};
+
+//------------------------------------------------------------------------------
+// internal functions. Not public.
+
+struct ALPHDecoder; // Defined in dec/alphai.h.
+
+// in vp8l.c
+
+// Decodes image header for alpha data stored using lossless compression.
+// Returns false in case of error.
+int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
+ const uint8_t* const data, size_t data_size);
+
+// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
+// already decoded in previous call(s), it will resume decoding from where it
+// was paused.
+// Returns false in case of bitstream error.
+int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec,
+ int last_row);
+
+// Allocates and initialize a new lossless decoder instance.
+VP8LDecoder* VP8LNew(void);
+
+// Decodes the image header. Returns false in case of error.
+int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
+
+// Decodes an image. It's required to decode the lossless header before calling
+// this function. Returns false in case of error, with updated dec->status_.
+int VP8LDecodeImage(VP8LDecoder* const dec);
+
+// Resets the decoder in its initial state, reclaiming memory.
+// Preserves the dec->status_ value.
+void VP8LClear(VP8LDecoder* const dec);
+
+// Clears and deallocate a lossless decoder instance.
+void VP8LDelete(VP8LDecoder* const dec);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_DEC_VP8LI_DEC_H_ */
diff --git a/src/third_party/libwebp/src/dec/webp_dec.c b/src/third_party/libwebp/src/dec/webp_dec.c
new file mode 100644
index 0000000..24d4227
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/webp_dec.c
@@ -0,0 +1,851 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Main decoding functions for WEBP images.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <stdlib.h>
+#endif
+
+#include "src/dec/vp8i_dec.h"
+#include "src/dec/vp8li_dec.h"
+#include "src/dec/webpi_dec.h"
+#include "src/utils/utils.h"
+#include "src/webp/mux_types.h" // ALPHA_FLAG
+
+//------------------------------------------------------------------------------
+// RIFF layout is:
+// Offset tag
+// 0...3 "RIFF" 4-byte tag
+// 4...7 size of image data (including metadata) starting at offset 8
+// 8...11 "WEBP" our form-type signature
+// The RIFF container (12 bytes) is followed by appropriate chunks:
+// 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format
+// 16..19 size of the raw VP8 image data, starting at offset 20
+// 20.... the VP8 bytes
+// Or,
+// 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
+// 16..19 size of the raw VP8L image data, starting at offset 20
+// 20.... the VP8L bytes
+// Or,
+// 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
+// 16..19 size of the VP8X chunk starting at offset 20.
+// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
+// 24..26 Width of the Canvas Image.
+// 27..29 Height of the Canvas Image.
+// There can be extra chunks after the "VP8X" chunk (ICCP, ANMF, VP8, VP8L,
+// XMP, EXIF ...)
+// All sizes are in little-endian order.
+// Note: chunk data size must be padded to multiple of 2 when written.
+
+// Validates the RIFF container (if detected) and skips over it.
+// If a RIFF container is detected, returns:
+// VP8_STATUS_BITSTREAM_ERROR for invalid header,
+// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true,
+// and VP8_STATUS_OK otherwise.
+// In case there are not enough bytes (partial RIFF container), return 0 for
+// *riff_size. Else return the RIFF size extracted from the header.
+static VP8StatusCode ParseRIFF(const uint8_t** const data,
+ size_t* const data_size, int have_all_data,
+ size_t* const riff_size) {
+ assert(data != NULL);
+ assert(data_size != NULL);
+ assert(riff_size != NULL);
+
+ *riff_size = 0; // Default: no RIFF present.
+ if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) {
+ if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
+ return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
+ } else {
+ const uint32_t size = GetLE32(*data + TAG_SIZE);
+ // Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
+ if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ if (size > MAX_CHUNK_PAYLOAD) {
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
+ return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
+ }
+ // We have a RIFF container. Skip it.
+ *riff_size = size;
+ *data += RIFF_HEADER_SIZE;
+ *data_size -= RIFF_HEADER_SIZE;
+ }
+ }
+ return VP8_STATUS_OK;
+}
+
+// Validates the VP8X header and skips over it.
+// Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
+// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
+// VP8_STATUS_OK otherwise.
+// If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
+// *height_ptr and *flags_ptr are set to the corresponding values extracted
+// from the VP8X chunk.
+static VP8StatusCode ParseVP8X(const uint8_t** const data,
+ size_t* const data_size,
+ int* const found_vp8x,
+ int* const width_ptr, int* const height_ptr,
+ uint32_t* const flags_ptr) {
+ const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
+ assert(data != NULL);
+ assert(data_size != NULL);
+ assert(found_vp8x != NULL);
+
+ *found_vp8x = 0;
+
+ if (*data_size < CHUNK_HEADER_SIZE) {
+ return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
+ }
+
+ if (!memcmp(*data, "VP8X", TAG_SIZE)) {
+ int width, height;
+ uint32_t flags;
+ const uint32_t chunk_size = GetLE32(*data + TAG_SIZE);
+ if (chunk_size != VP8X_CHUNK_SIZE) {
+ return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
+ }
+
+ // Verify if enough data is available to validate the VP8X chunk.
+ if (*data_size < vp8x_size) {
+ return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
+ }
+ flags = GetLE32(*data + 8);
+ width = 1 + GetLE24(*data + 12);
+ height = 1 + GetLE24(*data + 15);
+ if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
+ return VP8_STATUS_BITSTREAM_ERROR; // image is too large
+ }
+
+ if (flags_ptr != NULL) *flags_ptr = flags;
+ if (width_ptr != NULL) *width_ptr = width;
+ if (height_ptr != NULL) *height_ptr = height;
+ // Skip over VP8X header bytes.
+ *data += vp8x_size;
+ *data_size -= vp8x_size;
+ *found_vp8x = 1;
+ }
+ return VP8_STATUS_OK;
+}
+
+// Skips to the next VP8/VP8L chunk header in the data given the size of the
+// RIFF chunk 'riff_size'.
+// Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
+// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
+// VP8_STATUS_OK otherwise.
+// If an alpha chunk is found, *alpha_data and *alpha_size are set
+// appropriately.
+static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
+ size_t* const data_size,
+ size_t const riff_size,
+ const uint8_t** const alpha_data,
+ size_t* const alpha_size) {
+ const uint8_t* buf;
+ size_t buf_size;
+ uint32_t total_size = TAG_SIZE + // "WEBP".
+ CHUNK_HEADER_SIZE + // "VP8Xnnnn".
+ VP8X_CHUNK_SIZE; // data.
+ assert(data != NULL);
+ assert(data_size != NULL);
+ buf = *data;
+ buf_size = *data_size;
+
+ assert(alpha_data != NULL);
+ assert(alpha_size != NULL);
+ *alpha_data = NULL;
+ *alpha_size = 0;
+
+ while (1) {
+ uint32_t chunk_size;
+ uint32_t disk_chunk_size; // chunk_size with padding
+
+ *data = buf;
+ *data_size = buf_size;
+
+ if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data.
+ return VP8_STATUS_NOT_ENOUGH_DATA;
+ }
+
+ chunk_size = GetLE32(buf + TAG_SIZE);
+ if (chunk_size > MAX_CHUNK_PAYLOAD) {
+ return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
+ }
+ // For odd-sized chunk-payload, there's one byte padding at the end.
+ disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
+ total_size += disk_chunk_size;
+
+ // Check that total bytes skipped so far does not exceed riff_size.
+ if (riff_size > 0 && (total_size > riff_size)) {
+ return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
+ }
+
+ // Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
+ // parsed all the optional chunks.
+ // Note: This check must occur before the check 'buf_size < disk_chunk_size'
+ // below to allow incomplete VP8/VP8L chunks.
+ if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
+ !memcmp(buf, "VP8L", TAG_SIZE)) {
+ return VP8_STATUS_OK;
+ }
+
+ if (buf_size < disk_chunk_size) { // Insufficient data.
+ return VP8_STATUS_NOT_ENOUGH_DATA;
+ }
+
+ if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
+ *alpha_data = buf + CHUNK_HEADER_SIZE;
+ *alpha_size = chunk_size;
+ }
+
+ // We have a full and valid chunk; skip it.
+ buf += disk_chunk_size;
+ buf_size -= disk_chunk_size;
+ }
+}
+
+// Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
+// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
+// riff_size) VP8/VP8L header,
+// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
+// VP8_STATUS_OK otherwise.
+// If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
+// extracted from the VP8/VP8L chunk header.
+// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
+static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
+ size_t* const data_size, int have_all_data,
+ size_t riff_size, size_t* const chunk_size,
+ int* const is_lossless) {
+ const uint8_t* const data = *data_ptr;
+ const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
+ const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE);
+ const uint32_t minimal_size =
+ TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR
+ // "WEBP" + "VP8Lnnnn"
+ assert(data != NULL);
+ assert(data_size != NULL);
+ assert(chunk_size != NULL);
+ assert(is_lossless != NULL);
+
+ if (*data_size < CHUNK_HEADER_SIZE) {
+ return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
+ }
+
+ if (is_vp8 || is_vp8l) {
+ // Bitstream contains VP8/VP8L header.
+ const uint32_t size = GetLE32(data + TAG_SIZE);
+ if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
+ return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
+ }
+ if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
+ return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
+ }
+ // Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
+ *chunk_size = size;
+ *data_ptr += CHUNK_HEADER_SIZE;
+ *data_size -= CHUNK_HEADER_SIZE;
+ *is_lossless = is_vp8l;
+ } else {
+ // Raw VP8/VP8L bitstream (no header).
+ *is_lossless = VP8LCheckSignature(data, *data_size);
+ *chunk_size = *data_size;
+ }
+
+ return VP8_STATUS_OK;
+}
+
+//------------------------------------------------------------------------------
+
+// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
+// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
+// minimal amount will be read to fetch the remaining parameters.
+// If 'headers' is non-NULL this function will attempt to locate both alpha
+// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
+// Note: The following chunk sequences (before the raw VP8/VP8L data) are
+// considered valid by this function:
+// RIFF + VP8(L)
+// RIFF + VP8X + (optional chunks) + VP8(L)
+// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
+// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
+static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
+ size_t data_size,
+ int* const width,
+ int* const height,
+ int* const has_alpha,
+ int* const has_animation,
+ int* const format,
+ WebPHeaderStructure* const headers) {
+ int canvas_width = 0;
+ int canvas_height = 0;
+ int image_width = 0;
+ int image_height = 0;
+ int found_riff = 0;
+ int found_vp8x = 0;
+ int animation_present = 0;
+ const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
+
+ VP8StatusCode status;
+ WebPHeaderStructure hdrs;
+
+ if (data == NULL || data_size < RIFF_HEADER_SIZE) {
+ return VP8_STATUS_NOT_ENOUGH_DATA;
+ }
+ memset(&hdrs, 0, sizeof(hdrs));
+ hdrs.data = data;
+ hdrs.data_size = data_size;
+
+ // Skip over RIFF header.
+ status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size);
+ if (status != VP8_STATUS_OK) {
+ return status; // Wrong RIFF header / insufficient data.
+ }
+ found_riff = (hdrs.riff_size > 0);
+
+ // Skip over VP8X.
+ {
+ uint32_t flags = 0;
+ status = ParseVP8X(&data, &data_size, &found_vp8x,
+ &canvas_width, &canvas_height, &flags);
+ if (status != VP8_STATUS_OK) {
+ return status; // Wrong VP8X / insufficient data.
+ }
+ animation_present = !!(flags & ANIMATION_FLAG);
+ if (!found_riff && found_vp8x) {
+ // Note: This restriction may be removed in the future, if it becomes
+ // necessary to send VP8X chunk to the decoder.
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
+ if (has_animation != NULL) *has_animation = animation_present;
+ if (format != NULL) *format = 0; // default = undefined
+
+ image_width = canvas_width;
+ image_height = canvas_height;
+ if (found_vp8x && animation_present && headers == NULL) {
+ status = VP8_STATUS_OK;
+ goto ReturnWidthHeight; // Just return features from VP8X header.
+ }
+ }
+
+ if (data_size < TAG_SIZE) {
+ status = VP8_STATUS_NOT_ENOUGH_DATA;
+ goto ReturnWidthHeight;
+ }
+
+ // Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
+ if ((found_riff && found_vp8x) ||
+ (!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
+ status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
+ &hdrs.alpha_data, &hdrs.alpha_data_size);
+ if (status != VP8_STATUS_OK) {
+ goto ReturnWidthHeight; // Invalid chunk size / insufficient data.
+ }
+ }
+
+ // Skip over VP8/VP8L header.
+ status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size,
+ &hdrs.compressed_size, &hdrs.is_lossless);
+ if (status != VP8_STATUS_OK) {
+ goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data.
+ }
+ if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+
+ if (format != NULL && !animation_present) {
+ *format = hdrs.is_lossless ? 2 : 1;
+ }
+
+ if (!hdrs.is_lossless) {
+ if (data_size < VP8_FRAME_HEADER_SIZE) {
+ status = VP8_STATUS_NOT_ENOUGH_DATA;
+ goto ReturnWidthHeight;
+ }
+ // Validates raw VP8 data.
+ if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size,
+ &image_width, &image_height)) {
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ } else {
+ if (data_size < VP8L_FRAME_HEADER_SIZE) {
+ status = VP8_STATUS_NOT_ENOUGH_DATA;
+ goto ReturnWidthHeight;
+ }
+ // Validates raw VP8L data.
+ if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) {
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ }
+ // Validates image size coherency.
+ if (found_vp8x) {
+ if (canvas_width != image_width || canvas_height != image_height) {
+ return VP8_STATUS_BITSTREAM_ERROR;
+ }
+ }
+ if (headers != NULL) {
+ *headers = hdrs;
+ headers->offset = data - headers->data;
+ assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
+ assert(headers->offset == headers->data_size - data_size);
+ }
+ ReturnWidthHeight:
+ if (status == VP8_STATUS_OK ||
+ (status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) {
+ if (has_alpha != NULL) {
+ // If the data did not contain a VP8X/VP8L chunk the only definitive way
+ // to set this is by looking for alpha data (from an ALPH chunk).
+ *has_alpha |= (hdrs.alpha_data != NULL);
+ }
+ if (width != NULL) *width = image_width;
+ if (height != NULL) *height = image_height;
+ return VP8_STATUS_OK;
+ } else {
+ return status;
+ }
+}
+
+VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
+ // status is marked volatile as a workaround for a clang-3.8 (aarch64) bug
+ volatile VP8StatusCode status;
+ int has_animation = 0;
+ assert(headers != NULL);
+ // fill out headers, ignore width/height/has_alpha.
+ status = ParseHeadersInternal(headers->data, headers->data_size,
+ NULL, NULL, NULL, &has_animation,
+ NULL, headers);
+ if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
+ // The WebPDemux API + libwebp can be used to decode individual
+ // uncomposited frames or the WebPAnimDecoder can be used to fully
+ // reconstruct them (see webp/demux.h).
+ if (has_animation) {
+ status = VP8_STATUS_UNSUPPORTED_FEATURE;
+ }
+ }
+ return status;
+}
+
+//------------------------------------------------------------------------------
+// WebPDecParams
+
+void WebPResetDecParams(WebPDecParams* const params) {
+ if (params != NULL) {
+ memset(params, 0, sizeof(*params));
+ }
+}
+
+//------------------------------------------------------------------------------
+// "Into" decoding variants
+
+// Main flow
+static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
+ WebPDecParams* const params) {
+ VP8StatusCode status;
+ VP8Io io;
+ WebPHeaderStructure headers;
+
+ headers.data = data;
+ headers.data_size = data_size;
+ headers.have_all_data = 1;
+ status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
+ if (status != VP8_STATUS_OK) {
+ return status;
+ }
+
+ assert(params != NULL);
+ VP8InitIo(&io);
+ io.data = headers.data + headers.offset;
+ io.data_size = headers.data_size - headers.offset;
+ WebPInitCustomIo(params, &io); // Plug the I/O functions.
+
+ if (!headers.is_lossless) {
+ VP8Decoder* const dec = VP8New();
+ if (dec == NULL) {
+ return VP8_STATUS_OUT_OF_MEMORY;
+ }
+ dec->alpha_data_ = headers.alpha_data;
+ dec->alpha_data_size_ = headers.alpha_data_size;
+
+ // Decode bitstream header, update io->width/io->height.
+ if (!VP8GetHeaders(dec, &io)) {
+ status = dec->status_; // An error occurred. Grab error status.
+ } else {
+ // Allocate/check output buffers.
+ status = WebPAllocateDecBuffer(io.width, io.height, params->options,
+ params->output);
+ if (status == VP8_STATUS_OK) { // Decode
+ // This change must be done before calling VP8Decode()
+ dec->mt_method_ = VP8GetThreadMethod(params->options, &headers,
+ io.width, io.height);
+ VP8InitDithering(params->options, dec);
+ if (!VP8Decode(dec, &io)) {
+ status = dec->status_;
+ }
+ }
+ }
+ VP8Delete(dec);
+ } else {
+ VP8LDecoder* const dec = VP8LNew();
+ if (dec == NULL) {
+ return VP8_STATUS_OUT_OF_MEMORY;
+ }
+ if (!VP8LDecodeHeader(dec, &io)) {
+ status = dec->status_; // An error occurred. Grab error status.
+ } else {
+ // Allocate/check output buffers.
+ status = WebPAllocateDecBuffer(io.width, io.height, params->options,
+ params->output);
+ if (status == VP8_STATUS_OK) { // Decode
+ if (!VP8LDecodeImage(dec)) {
+ status = dec->status_;
+ }
+ }
+ }
+ VP8LDelete(dec);
+ }
+
+ if (status != VP8_STATUS_OK) {
+ WebPFreeDecBuffer(params->output);
+ } else {
+ if (params->options != NULL && params->options->flip) {
+ // This restores the original stride values if options->flip was used
+ // during the call to WebPAllocateDecBuffer above.
+ status = WebPFlipBuffer(params->output);
+ }
+ }
+ return status;
+}
+
+// Helpers
+static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
+ const uint8_t* const data,
+ size_t data_size,
+ uint8_t* const rgba,
+ int stride, size_t size) {
+ WebPDecParams params;
+ WebPDecBuffer buf;
+ if (rgba == NULL) {
+ return NULL;
+ }
+ WebPInitDecBuffer(&buf);
+ WebPResetDecParams(¶ms);
+ params.output = &buf;
+ buf.colorspace = colorspace;
+ buf.u.RGBA.rgba = rgba;
+ buf.u.RGBA.stride = stride;
+ buf.u.RGBA.size = size;
+ buf.is_external_memory = 1;
+ if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
+ return NULL;
+ }
+ return rgba;
+}
+
+uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
+ uint8_t* output, size_t size, int stride) {
+ return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
+}
+
+uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
+ uint8_t* output, size_t size, int stride) {
+ return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
+}
+
+uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
+ uint8_t* output, size_t size, int stride) {
+ return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
+}
+
+uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
+ uint8_t* output, size_t size, int stride) {
+ return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
+}
+
+uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
+ uint8_t* output, size_t size, int stride) {
+ return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
+}
+
+uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
+ uint8_t* luma, size_t luma_size, int luma_stride,
+ uint8_t* u, size_t u_size, int u_stride,
+ uint8_t* v, size_t v_size, int v_stride) {
+ WebPDecParams params;
+ WebPDecBuffer output;
+ if (luma == NULL) return NULL;
+ WebPInitDecBuffer(&output);
+ WebPResetDecParams(¶ms);
+ params.output = &output;
+ output.colorspace = MODE_YUV;
+ output.u.YUVA.y = luma;
+ output.u.YUVA.y_stride = luma_stride;
+ output.u.YUVA.y_size = luma_size;
+ output.u.YUVA.u = u;
+ output.u.YUVA.u_stride = u_stride;
+ output.u.YUVA.u_size = u_size;
+ output.u.YUVA.v = v;
+ output.u.YUVA.v_stride = v_stride;
+ output.u.YUVA.v_size = v_size;
+ output.is_external_memory = 1;
+ if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
+ return NULL;
+ }
+ return luma;
+}
+
+//------------------------------------------------------------------------------
+
+static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
+ size_t data_size, int* const width, int* const height,
+ WebPDecBuffer* const keep_info) {
+ WebPDecParams params;
+ WebPDecBuffer output;
+
+ WebPInitDecBuffer(&output);
+ WebPResetDecParams(¶ms);
+ params.output = &output;
+ output.colorspace = mode;
+
+ // Retrieve (and report back) the required dimensions from bitstream.
+ if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
+ return NULL;
+ }
+ if (width != NULL) *width = output.width;
+ if (height != NULL) *height = output.height;
+
+ // Decode
+ if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
+ return NULL;
+ }
+ if (keep_info != NULL) { // keep track of the side-info
+ WebPCopyDecBuffer(&output, keep_info);
+ }
+ // return decoded samples (don't clear 'output'!)
+ return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
+}
+
+uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
+ int* width, int* height) {
+ return Decode(MODE_RGB, data, data_size, width, height, NULL);
+}
+
+uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
+ int* width, int* height) {
+ return Decode(MODE_RGBA, data, data_size, width, height, NULL);
+}
+
+uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
+ int* width, int* height) {
+ return Decode(MODE_ARGB, data, data_size, width, height, NULL);
+}
+
+uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
+ int* width, int* height) {
+ return Decode(MODE_BGR, data, data_size, width, height, NULL);
+}
+
+uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
+ int* width, int* height) {
+ return Decode(MODE_BGRA, data, data_size, width, height, NULL);
+}
+
+uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
+ int* width, int* height, uint8_t** u, uint8_t** v,
+ int* stride, int* uv_stride) {
+ WebPDecBuffer output; // only to preserve the side-infos
+ uint8_t* const out = Decode(MODE_YUV, data, data_size,
+ width, height, &output);
+
+ if (out != NULL) {
+ const WebPYUVABuffer* const buf = &output.u.YUVA;
+ *u = buf->u;
+ *v = buf->v;
+ *stride = buf->y_stride;
+ *uv_stride = buf->u_stride;
+ assert(buf->u_stride == buf->v_stride);
+ }
+ return out;
+}
+
+static void DefaultFeatures(WebPBitstreamFeatures* const features) {
+ assert(features != NULL);
+ memset(features, 0, sizeof(*features));
+}
+
+static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
+ WebPBitstreamFeatures* const features) {
+ if (features == NULL || data == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ DefaultFeatures(features);
+
+ // Only parse enough of the data to retrieve the features.
+ return ParseHeadersInternal(data, data_size,
+ &features->width, &features->height,
+ &features->has_alpha, &features->has_animation,
+ &features->format, NULL);
+}
+
+//------------------------------------------------------------------------------
+// WebPGetInfo()
+
+int WebPGetInfo(const uint8_t* data, size_t data_size,
+ int* width, int* height) {
+ WebPBitstreamFeatures features;
+
+ if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) {
+ return 0;
+ }
+
+ if (width != NULL) {
+ *width = features.width;
+ }
+ if (height != NULL) {
+ *height = features.height;
+ }
+
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Advance decoding API
+
+int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
+ int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
+ return 0; // version mismatch
+ }
+ if (config == NULL) {
+ return 0;
+ }
+ memset(config, 0, sizeof(*config));
+ DefaultFeatures(&config->input);
+ WebPInitDecBuffer(&config->output);
+ return 1;
+}
+
+VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size,
+ WebPBitstreamFeatures* features,
+ int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
+ return VP8_STATUS_INVALID_PARAM; // version mismatch
+ }
+ if (features == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+ return GetFeatures(data, data_size, features);
+}
+
+VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
+ WebPDecoderConfig* config) {
+ WebPDecParams params;
+ VP8StatusCode status;
+
+ if (config == NULL) {
+ return VP8_STATUS_INVALID_PARAM;
+ }
+
+ status = GetFeatures(data, data_size, &config->input);
+ if (status != VP8_STATUS_OK) {
+ if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
+ return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error.
+ }
+ return status;
+ }
+
+ WebPResetDecParams(¶ms);
+ params.options = &config->options;
+ params.output = &config->output;
+ if (WebPAvoidSlowMemory(params.output, &config->input)) {
+ // decoding to slow memory: use a temporary in-mem buffer to decode into.
+ WebPDecBuffer in_mem_buffer;
+ WebPInitDecBuffer(&in_mem_buffer);
+ in_mem_buffer.colorspace = config->output.colorspace;
+ in_mem_buffer.width = config->input.width;
+ in_mem_buffer.height = config->input.height;
+ params.output = &in_mem_buffer;
+ status = DecodeInto(data, data_size, ¶ms);
+ if (status == VP8_STATUS_OK) { // do the slow-copy
+ status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output);
+ }
+ WebPFreeDecBuffer(&in_mem_buffer);
+ } else {
+ status = DecodeInto(data, data_size, ¶ms);
+ }
+
+ return status;
+}
+
+//------------------------------------------------------------------------------
+// Cropping and rescaling.
+
+int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
+ VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
+ const int W = io->width;
+ const int H = io->height;
+ int x = 0, y = 0, w = W, h = H;
+
+ // Cropping
+ io->use_cropping = (options != NULL) && (options->use_cropping > 0);
+ if (io->use_cropping) {
+ w = options->crop_width;
+ h = options->crop_height;
+ x = options->crop_left;
+ y = options->crop_top;
+ if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420
+ x &= ~1;
+ y &= ~1;
+ }
+ if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
+ return 0; // out of frame boundary error
+ }
+ }
+ io->crop_left = x;
+ io->crop_top = y;
+ io->crop_right = x + w;
+ io->crop_bottom = y + h;
+ io->mb_w = w;
+ io->mb_h = h;
+
+ // Scaling
+ io->use_scaling = (options != NULL) && (options->use_scaling > 0);
+ if (io->use_scaling) {
+ int scaled_width = options->scaled_width;
+ int scaled_height = options->scaled_height;
+ if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) {
+ return 0;
+ }
+ io->scaled_width = scaled_width;
+ io->scaled_height = scaled_height;
+ }
+
+ // Filter
+ io->bypass_filtering = (options != NULL) && options->bypass_filtering;
+
+ // Fancy upsampler
+#ifdef FANCY_UPSAMPLING
+ io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
+#endif
+
+ if (io->use_scaling) {
+ // disable filter (only for large downscaling ratio).
+ io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
+ (io->scaled_height < H * 3 / 4);
+ io->fancy_upsampling = 0;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dec/webpi_dec.h b/src/third_party/libwebp/src/dec/webpi_dec.h
new file mode 100644
index 0000000..c378ba6
--- /dev/null
+++ b/src/third_party/libwebp/src/dec/webpi_dec.h
@@ -0,0 +1,133 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Internal header: WebP decoding parameters and custom IO on buffer
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#ifndef WEBP_DEC_WEBPI_DEC_H_
+#define WEBP_DEC_WEBPI_DEC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "src/utils/rescaler_utils.h"
+#include "src/dec/vp8_dec.h"
+
+//------------------------------------------------------------------------------
+// WebPDecParams: Decoding output parameters. Transient internal object.
+
+typedef struct WebPDecParams WebPDecParams;
+typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
+typedef int (*OutputAlphaFunc)(const VP8Io* const io, WebPDecParams* const p,
+ int expected_num_out_lines);
+typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos,
+ int max_out_lines);
+
+struct WebPDecParams {
+ WebPDecBuffer* output; // output buffer.
+ uint8_t* tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler
+ // or used for tmp rescaling
+
+ int last_y; // coordinate of the line that was last output
+ const WebPDecoderOptions* options; // if not NULL, use alt decoding features
+
+ WebPRescaler* scaler_y, *scaler_u, *scaler_v, *scaler_a; // rescalers
+ void* memory; // overall scratch memory for the output work.
+
+ OutputFunc emit; // output RGB or YUV samples
+ OutputAlphaFunc emit_alpha; // output alpha channel
+ OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
+};
+
+// Should be called first, before any use of the WebPDecParams object.
+void WebPResetDecParams(WebPDecParams* const params);
+
+//------------------------------------------------------------------------------
+// Header parsing helpers
+
+// Structure storing a description of the RIFF headers.
+typedef struct {
+ const uint8_t* data; // input buffer
+ size_t data_size; // input buffer size
+ int have_all_data; // true if all data is known to be available
+ size_t offset; // offset to main data chunk (VP8 or VP8L)
+ const uint8_t* alpha_data; // points to alpha chunk (if present)
+ size_t alpha_data_size; // alpha chunk size
+ size_t compressed_size; // VP8/VP8L compressed data size
+ size_t riff_size; // size of the riff payload (or 0 if absent)
+ int is_lossless; // true if a VP8L chunk is present
+} WebPHeaderStructure;
+
+// Skips over all valid chunks prior to the first VP8/VP8L frame header.
+// Returns: VP8_STATUS_OK, VP8_STATUS_BITSTREAM_ERROR (invalid header/chunk),
+// VP8_STATUS_NOT_ENOUGH_DATA (partial input) or VP8_STATUS_UNSUPPORTED_FEATURE
+// in the case of non-decodable features (animation for instance).
+// In 'headers', compressed_size, offset, alpha_data, alpha_size, and lossless
+// fields are updated appropriately upon success.
+VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers);
+
+//------------------------------------------------------------------------------
+// Misc utils
+
+// Initializes VP8Io with custom setup, io and teardown functions. The default
+// hooks will use the supplied 'params' as io->opaque handle.
+void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io);
+
+// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers
+// to the *compressed* format, not the output one.
+int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
+ VP8Io* const io, WEBP_CSP_MODE src_colorspace);
+
+//------------------------------------------------------------------------------
+// Internal functions regarding WebPDecBuffer memory (in buffer.c).
+// Don't really need to be externally visible for now.
+
+// Prepare 'buffer' with the requested initial dimensions width/height.
+// If no external storage is supplied, initializes buffer by allocating output
+// memory and setting up the stride information. Validate the parameters. Return
+// an error code in case of problem (no memory, or invalid stride / size /
+// dimension / etc.). If *options is not NULL, also verify that the options'
+// parameters are valid and apply them to the width/height dimensions of the
+// output buffer. This takes cropping / scaling / rotation into account.
+// Also incorporates the options->flip flag to flip the buffer parameters if
+// needed.
+VP8StatusCode WebPAllocateDecBuffer(int width, int height,
+ const WebPDecoderOptions* const options,
+ WebPDecBuffer* const buffer);
+
+// Flip buffer vertically by negating the various strides.
+VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer);
+
+// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the
+// memory (still held by 'src'). No pixels are copied.
+void WebPCopyDecBuffer(const WebPDecBuffer* const src,
+ WebPDecBuffer* const dst);
+
+// Copy and transfer ownership from src to dst (beware of parameter order!)
+void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
+
+// Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns
+// VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy.
+VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src,
+ WebPDecBuffer* const dst);
+
+// Returns true if decoding will be slow with the current configuration
+// and bitstream features.
+int WebPAvoidSlowMemory(const WebPDecBuffer* const output,
+ const WebPBitstreamFeatures* const features);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_DEC_WEBPI_DEC_H_ */
diff --git a/src/third_party/libwebp/src/demux/Makefile.am b/src/third_party/libwebp/src/demux/Makefile.am
new file mode 100644
index 0000000..7e80bdc
--- /dev/null
+++ b/src/third_party/libwebp/src/demux/Makefile.am
@@ -0,0 +1,18 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+lib_LTLIBRARIES = libwebpdemux.la
+
+libwebpdemux_la_SOURCES =
+libwebpdemux_la_SOURCES += anim_decode.c demux.c
+
+libwebpdemuxinclude_HEADERS =
+libwebpdemuxinclude_HEADERS += ../webp/decode.h
+libwebpdemuxinclude_HEADERS += ../webp/demux.h
+libwebpdemuxinclude_HEADERS += ../webp/mux_types.h
+libwebpdemuxinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpdemux_la_LIBADD = ../libwebp.la
+libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:4:0
+libwebpdemuxincludedir = $(includedir)/webp
+pkgconfig_DATA = libwebpdemux.pc
diff --git a/src/third_party/libwebp/src/demux/anim_decode.c b/src/third_party/libwebp/src/demux/anim_decode.c
new file mode 100644
index 0000000..6de3121
--- /dev/null
+++ b/src/third_party/libwebp/src/demux/anim_decode.c
@@ -0,0 +1,459 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// AnimDecoder implementation.
+//
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <string.h>
+#endif
+
+#include "src/utils/utils.h"
+#include "src/webp/decode.h"
+#include "src/webp/demux.h"
+
+#define NUM_CHANNELS 4
+
+typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int);
+static void BlendPixelRowNonPremult(uint32_t* const src,
+ const uint32_t* const dst, int num_pixels);
+static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
+ int num_pixels);
+
+struct WebPAnimDecoder {
+ WebPDemuxer* demux_; // Demuxer created from given WebP bitstream.
+ WebPDecoderConfig config_; // Decoder config.
+ // Note: we use a pointer to a function blending multiple pixels at a time to
+ // allow possible inlining of per-pixel blending function.
+ BlendRowFunc blend_func_; // Pointer to the chose blend row function.
+ WebPAnimInfo info_; // Global info about the animation.
+ uint8_t* curr_frame_; // Current canvas (not disposed).
+ uint8_t* prev_frame_disposed_; // Previous canvas (properly disposed).
+ int prev_frame_timestamp_; // Previous frame timestamp (milliseconds).
+ WebPIterator prev_iter_; // Iterator object for previous frame.
+ int prev_frame_was_keyframe_; // True if previous frame was a keyframe.
+ int next_frame_; // Index of the next frame to be decoded
+ // (starting from 1).
+};
+
+static void DefaultDecoderOptions(WebPAnimDecoderOptions* const dec_options) {
+ dec_options->color_mode = MODE_RGBA;
+ dec_options->use_threads = 0;
+}
+
+int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options,
+ int abi_version) {
+ if (dec_options == NULL ||
+ WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
+ return 0;
+ }
+ DefaultDecoderOptions(dec_options);
+ return 1;
+}
+
+static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
+ WebPAnimDecoder* const dec) {
+ WEBP_CSP_MODE mode;
+ WebPDecoderConfig* config = &dec->config_;
+ assert(dec_options != NULL);
+
+ mode = dec_options->color_mode;
+ if (mode != MODE_RGBA && mode != MODE_BGRA &&
+ mode != MODE_rgbA && mode != MODE_bgrA) {
+ return 0;
+ }
+ dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA)
+ ? &BlendPixelRowNonPremult
+ : &BlendPixelRowPremult;
+ WebPInitDecoderConfig(config);
+ config->output.colorspace = mode;
+ config->output.is_external_memory = 1;
+ config->options.use_threads = dec_options->use_threads;
+ // Note: config->output.u.RGBA is set at the time of decoding each frame.
+ return 1;
+}
+
+WebPAnimDecoder* WebPAnimDecoderNewInternal(
+ const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options,
+ int abi_version) {
+ WebPAnimDecoderOptions options;
+ WebPAnimDecoder* dec = NULL;
+ if (webp_data == NULL ||
+ WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
+ return NULL;
+ }
+
+ // Note: calloc() so that the pointer members are initialized to NULL.
+ dec = (WebPAnimDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
+ if (dec == NULL) goto Error;
+
+ if (dec_options != NULL) {
+ options = *dec_options;
+ } else {
+ DefaultDecoderOptions(&options);
+ }
+ if (!ApplyDecoderOptions(&options, dec)) goto Error;
+
+ dec->demux_ = WebPDemux(webp_data);
+ if (dec->demux_ == NULL) goto Error;
+
+ dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH);
+ dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT);
+ dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT);
+ dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR);
+ dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT);
+
+ // Note: calloc() because we fill frame with zeroes as well.
+ dec->curr_frame_ = (uint8_t*)WebPSafeCalloc(
+ dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
+ if (dec->curr_frame_ == NULL) goto Error;
+ dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc(
+ dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
+ if (dec->prev_frame_disposed_ == NULL) goto Error;
+
+ WebPAnimDecoderReset(dec);
+ return dec;
+
+ Error:
+ WebPAnimDecoderDelete(dec);
+ return NULL;
+}
+
+int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) {
+ if (dec == NULL || info == NULL) return 0;
+ *info = dec->info_;
+ return 1;
+}
+
+// Returns true if the frame covers the full canvas.
+static int IsFullFrame(int width, int height, int canvas_width,
+ int canvas_height) {
+ return (width == canvas_width && height == canvas_height);
+}
+
+// Clear the canvas to transparent.
+static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
+ uint32_t canvas_height) {
+ const uint64_t size =
+ (uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
+ if (size != (size_t)size) return 0;
+ memset(buf, 0, (size_t)size);
+ return 1;
+}
+
+// Clear given frame rectangle to transparent.
+static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
+ int y_offset, int width, int height) {
+ int j;
+ assert(width * NUM_CHANNELS <= buf_stride);
+ buf += y_offset * buf_stride + x_offset * NUM_CHANNELS;
+ for (j = 0; j < height; ++j) {
+ memset(buf, 0, width * NUM_CHANNELS);
+ buf += buf_stride;
+ }
+}
+
+// Copy width * height pixels from 'src' to 'dst'.
+static int CopyCanvas(const uint8_t* src, uint8_t* dst,
+ uint32_t width, uint32_t height) {
+ const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
+ if (size != (size_t)size) return 0;
+ assert(src != NULL && dst != NULL);
+ memcpy(dst, src, (size_t)size);
+ return 1;
+}
+
+// Returns true if the current frame is a key-frame.
+static int IsKeyFrame(const WebPIterator* const curr,
+ const WebPIterator* const prev,
+ int prev_frame_was_key_frame,
+ int canvas_width, int canvas_height) {
+ if (curr->frame_num == 1) {
+ return 1;
+ } else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) &&
+ IsFullFrame(curr->width, curr->height,
+ canvas_width, canvas_height)) {
+ return 1;
+ } else {
+ return (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
+ (IsFullFrame(prev->width, prev->height, canvas_width,
+ canvas_height) ||
+ prev_frame_was_key_frame);
+ }
+}
+
+
+// Blend a single channel of 'src' over 'dst', given their alpha channel values.
+// 'src' and 'dst' are assumed to be NOT pre-multiplied by alpha.
+static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a,
+ uint32_t dst, uint8_t dst_a,
+ uint32_t scale, int shift) {
+ const uint8_t src_channel = (src >> shift) & 0xff;
+ const uint8_t dst_channel = (dst >> shift) & 0xff;
+ const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a;
+ assert(blend_unscaled < (1ULL << 32) / scale);
+ return (blend_unscaled * scale) >> 24;
+}
+
+// Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha.
+static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) {
+ const uint8_t src_a = (src >> 24) & 0xff;
+
+ if (src_a == 0) {
+ return dst;
+ } else {
+ const uint8_t dst_a = (dst >> 24) & 0xff;
+ // This is the approximate integer arithmetic for the actual formula:
+ // dst_factor_a = (dst_a * (255 - src_a)) / 255.
+ const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8;
+ const uint8_t blend_a = src_a + dst_factor_a;
+ const uint32_t scale = (1UL << 24) / blend_a;
+
+ const uint8_t blend_r =
+ BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 0);
+ const uint8_t blend_g =
+ BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 8);
+ const uint8_t blend_b =
+ BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 16);
+ assert(src_a + dst_factor_a < 256);
+
+ return (blend_r << 0) |
+ (blend_g << 8) |
+ (blend_b << 16) |
+ ((uint32_t)blend_a << 24);
+ }
+}
+
+// Blend 'num_pixels' in 'src' over 'dst' assuming they are NOT pre-multiplied
+// by alpha.
+static void BlendPixelRowNonPremult(uint32_t* const src,
+ const uint32_t* const dst, int num_pixels) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) {
+ const uint8_t src_alpha = (src[i] >> 24) & 0xff;
+ if (src_alpha != 0xff) {
+ src[i] = BlendPixelNonPremult(src[i], dst[i]);
+ }
+ }
+}
+
+// Individually multiply each channel in 'pix' by 'scale'.
+static WEBP_INLINE uint32_t ChannelwiseMultiply(uint32_t pix, uint32_t scale) {
+ uint32_t mask = 0x00FF00FF;
+ uint32_t rb = ((pix & mask) * scale) >> 8;
+ uint32_t ag = ((pix >> 8) & mask) * scale;
+ return (rb & mask) | (ag & ~mask);
+}
+
+// Blend 'src' over 'dst' assuming they are pre-multiplied by alpha.
+static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) {
+ const uint8_t src_a = (src >> 24) & 0xff;
+ return src + ChannelwiseMultiply(dst, 256 - src_a);
+}
+
+// Blend 'num_pixels' in 'src' over 'dst' assuming they are pre-multiplied by
+// alpha.
+static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
+ int num_pixels) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) {
+ const uint8_t src_alpha = (src[i] >> 24) & 0xff;
+ if (src_alpha != 0xff) {
+ src[i] = BlendPixelPremult(src[i], dst[i]);
+ }
+ }
+}
+
+// Returns two ranges (<left, width> pairs) at row 'canvas_y', that belong to
+// 'src' but not 'dst'. A point range is empty if the corresponding width is 0.
+static void FindBlendRangeAtRow(const WebPIterator* const src,
+ const WebPIterator* const dst, int canvas_y,
+ int* const left1, int* const width1,
+ int* const left2, int* const width2) {
+ const int src_max_x = src->x_offset + src->width;
+ const int dst_max_x = dst->x_offset + dst->width;
+ const int dst_max_y = dst->y_offset + dst->height;
+ assert(canvas_y >= src->y_offset && canvas_y < (src->y_offset + src->height));
+ *left1 = -1;
+ *width1 = 0;
+ *left2 = -1;
+ *width2 = 0;
+
+ if (canvas_y < dst->y_offset || canvas_y >= dst_max_y ||
+ src->x_offset >= dst_max_x || src_max_x <= dst->x_offset) {
+ *left1 = src->x_offset;
+ *width1 = src->width;
+ return;
+ }
+
+ if (src->x_offset < dst->x_offset) {
+ *left1 = src->x_offset;
+ *width1 = dst->x_offset - src->x_offset;
+ }
+
+ if (src_max_x > dst_max_x) {
+ *left2 = dst_max_x;
+ *width2 = src_max_x - dst_max_x;
+ }
+}
+
+int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
+ uint8_t** buf_ptr, int* timestamp_ptr) {
+ WebPIterator iter;
+ uint32_t width;
+ uint32_t height;
+ int is_key_frame;
+ int timestamp;
+ BlendRowFunc blend_row;
+
+ if (dec == NULL || buf_ptr == NULL || timestamp_ptr == NULL) return 0;
+ if (!WebPAnimDecoderHasMoreFrames(dec)) return 0;
+
+ width = dec->info_.canvas_width;
+ height = dec->info_.canvas_height;
+ blend_row = dec->blend_func_;
+
+ // Get compressed frame.
+ if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) {
+ return 0;
+ }
+ timestamp = dec->prev_frame_timestamp_ + iter.duration;
+
+ // Initialize.
+ is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_,
+ dec->prev_frame_was_keyframe_, width, height);
+ if (is_key_frame) {
+ if (!ZeroFillCanvas(dec->curr_frame_, width, height)) {
+ goto Error;
+ }
+ } else {
+ if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_,
+ width, height)) {
+ goto Error;
+ }
+ }
+
+ // Decode.
+ {
+ const uint8_t* in = iter.fragment.bytes;
+ const size_t in_size = iter.fragment.size;
+ const size_t out_offset =
+ (iter.y_offset * width + iter.x_offset) * NUM_CHANNELS;
+ WebPDecoderConfig* const config = &dec->config_;
+ WebPRGBABuffer* const buf = &config->output.u.RGBA;
+ buf->stride = NUM_CHANNELS * width;
+ buf->size = buf->stride * iter.height;
+ buf->rgba = dec->curr_frame_ + out_offset;
+
+ if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) {
+ goto Error;
+ }
+ }
+
+ // During the decoding of current frame, we may have set some pixels to be
+ // transparent (i.e. alpha < 255). However, the value of each of these
+ // pixels should have been determined by blending it against the value of
+ // that pixel in the previous frame if blending method of is WEBP_MUX_BLEND.
+ if (iter.frame_num > 1 && iter.blend_method == WEBP_MUX_BLEND &&
+ !is_key_frame) {
+ if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_NONE) {
+ int y;
+ // Blend transparent pixels with pixels in previous canvas.
+ for (y = 0; y < iter.height; ++y) {
+ const size_t offset =
+ (iter.y_offset + y) * width + iter.x_offset;
+ blend_row((uint32_t*)dec->curr_frame_ + offset,
+ (uint32_t*)dec->prev_frame_disposed_ + offset, iter.width);
+ }
+ } else {
+ int y;
+ assert(dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND);
+ // We need to blend a transparent pixel with its value just after
+ // initialization. That is, blend it with:
+ // * Fully transparent pixel if it belongs to prevRect <-- No-op.
+ // * The pixel in the previous canvas otherwise <-- Need alpha-blending.
+ for (y = 0; y < iter.height; ++y) {
+ const int canvas_y = iter.y_offset + y;
+ int left1, width1, left2, width2;
+ FindBlendRangeAtRow(&iter, &dec->prev_iter_, canvas_y, &left1, &width1,
+ &left2, &width2);
+ if (width1 > 0) {
+ const size_t offset1 = canvas_y * width + left1;
+ blend_row((uint32_t*)dec->curr_frame_ + offset1,
+ (uint32_t*)dec->prev_frame_disposed_ + offset1, width1);
+ }
+ if (width2 > 0) {
+ const size_t offset2 = canvas_y * width + left2;
+ blend_row((uint32_t*)dec->curr_frame_ + offset2,
+ (uint32_t*)dec->prev_frame_disposed_ + offset2, width2);
+ }
+ }
+ }
+ }
+
+ // Update info of the previous frame and dispose it for the next iteration.
+ dec->prev_frame_timestamp_ = timestamp;
+ WebPDemuxReleaseIterator(&dec->prev_iter_);
+ dec->prev_iter_ = iter;
+ dec->prev_frame_was_keyframe_ = is_key_frame;
+ CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height);
+ if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
+ ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS,
+ dec->prev_iter_.x_offset, dec->prev_iter_.y_offset,
+ dec->prev_iter_.width, dec->prev_iter_.height);
+ }
+ ++dec->next_frame_;
+
+ // All OK, fill in the values.
+ *buf_ptr = dec->curr_frame_;
+ *timestamp_ptr = timestamp;
+ return 1;
+
+ Error:
+ WebPDemuxReleaseIterator(&iter);
+ return 0;
+}
+
+int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) {
+ if (dec == NULL) return 0;
+ return (dec->next_frame_ <= (int)dec->info_.frame_count);
+}
+
+void WebPAnimDecoderReset(WebPAnimDecoder* dec) {
+ if (dec != NULL) {
+ dec->prev_frame_timestamp_ = 0;
+ WebPDemuxReleaseIterator(&dec->prev_iter_);
+ memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_));
+ dec->prev_frame_was_keyframe_ = 0;
+ dec->next_frame_ = 1;
+ }
+}
+
+const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) {
+ if (dec == NULL) return NULL;
+ return dec->demux_;
+}
+
+void WebPAnimDecoderDelete(WebPAnimDecoder* dec) {
+ if (dec != NULL) {
+ WebPDemuxReleaseIterator(&dec->prev_iter_);
+ WebPDemuxDelete(dec->demux_);
+ WebPSafeFree(dec->curr_frame_);
+ WebPSafeFree(dec->prev_frame_disposed_);
+ WebPSafeFree(dec);
+ }
+}
diff --git a/src/third_party/libwebp/src/demux/demux.c b/src/third_party/libwebp/src/demux/demux.c
new file mode 100644
index 0000000..684215e
--- /dev/null
+++ b/src/third_party/libwebp/src/demux/demux.c
@@ -0,0 +1,967 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP container demux.
+//
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/utils/utils.h"
+#include "src/webp/decode.h" // WebPGetFeatures
+#include "src/webp/demux.h"
+#include "src/webp/format_constants.h"
+
+#define DMUX_MAJ_VERSION 1
+#define DMUX_MIN_VERSION 0
+#define DMUX_REV_VERSION 0
+
+typedef struct {
+ size_t start_; // start location of the data
+ size_t end_; // end location
+ size_t riff_end_; // riff chunk end location, can be > end_.
+ size_t buf_size_; // size of the buffer
+ const uint8_t* buf_;
+} MemBuffer;
+
+typedef struct {
+ size_t offset_;
+ size_t size_;
+} ChunkData;
+
+typedef struct Frame {
+ int x_offset_, y_offset_;
+ int width_, height_;
+ int has_alpha_;
+ int duration_;
+ WebPMuxAnimDispose dispose_method_;
+ WebPMuxAnimBlend blend_method_;
+ int frame_num_;
+ int complete_; // img_components_ contains a full image.
+ ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH
+ struct Frame* next_;
+} Frame;
+
+typedef struct Chunk {
+ ChunkData data_;
+ struct Chunk* next_;
+} Chunk;
+
+struct WebPDemuxer {
+ MemBuffer mem_;
+ WebPDemuxState state_;
+ int is_ext_format_;
+ uint32_t feature_flags_;
+ int canvas_width_, canvas_height_;
+ int loop_count_;
+ uint32_t bgcolor_;
+ int num_frames_;
+ Frame* frames_;
+ Frame** frames_tail_;
+ Chunk* chunks_; // non-image chunks
+ Chunk** chunks_tail_;
+};
+
+typedef enum {
+ PARSE_OK,
+ PARSE_NEED_MORE_DATA,
+ PARSE_ERROR
+} ParseStatus;
+
+typedef struct ChunkParser {
+ uint8_t id[4];
+ ParseStatus (*parse)(WebPDemuxer* const dmux);
+ int (*valid)(const WebPDemuxer* const dmux);
+} ChunkParser;
+
+static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
+static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
+static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
+static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
+
+static const ChunkParser kMasterChunks[] = {
+ { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
+ { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
+ { { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat },
+ { { '0', '0', '0', '0' }, NULL, NULL },
+};
+
+//------------------------------------------------------------------------------
+
+int WebPGetDemuxVersion(void) {
+ return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
+}
+
+// -----------------------------------------------------------------------------
+// MemBuffer
+
+static int RemapMemBuffer(MemBuffer* const mem,
+ const uint8_t* data, size_t size) {
+ if (size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
+
+ mem->buf_ = data;
+ mem->end_ = mem->buf_size_ = size;
+ return 1;
+}
+
+static int InitMemBuffer(MemBuffer* const mem,
+ const uint8_t* data, size_t size) {
+ memset(mem, 0, sizeof(*mem));
+ return RemapMemBuffer(mem, data, size);
+}
+
+// Return the remaining data size available in 'mem'.
+static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
+ return (mem->end_ - mem->start_);
+}
+
+// Return true if 'size' exceeds the end of the RIFF chunk.
+static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
+ return (size > mem->riff_end_ - mem->start_);
+}
+
+static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
+ mem->start_ += size;
+}
+
+static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
+ mem->start_ -= size;
+}
+
+static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
+ return mem->buf_ + mem->start_;
+}
+
+// Read from 'mem' and skip the read bytes.
+static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
+ const uint8_t byte = mem->buf_[mem->start_];
+ Skip(mem, 1);
+ return byte;
+}
+
+static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
+ const uint8_t* const data = mem->buf_ + mem->start_;
+ const int val = GetLE16(data);
+ Skip(mem, 2);
+ return val;
+}
+
+static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
+ const uint8_t* const data = mem->buf_ + mem->start_;
+ const int val = GetLE24(data);
+ Skip(mem, 3);
+ return val;
+}
+
+static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
+ const uint8_t* const data = mem->buf_ + mem->start_;
+ const uint32_t val = GetLE32(data);
+ Skip(mem, 4);
+ return val;
+}
+
+// -----------------------------------------------------------------------------
+// Secondary chunk parsing
+
+static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
+ *dmux->chunks_tail_ = chunk;
+ chunk->next_ = NULL;
+ dmux->chunks_tail_ = &chunk->next_;
+}
+
+// Add a frame to the end of the list, ensuring the last frame is complete.
+// Returns true on success, false otherwise.
+static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
+ const Frame* const last_frame = *dmux->frames_tail_;
+ if (last_frame != NULL && !last_frame->complete_) return 0;
+
+ *dmux->frames_tail_ = frame;
+ frame->next_ = NULL;
+ dmux->frames_tail_ = &frame->next_;
+ return 1;
+}
+
+static void SetFrameInfo(size_t start_offset, size_t size,
+ int frame_num, int complete,
+ const WebPBitstreamFeatures* const features,
+ Frame* const frame) {
+ frame->img_components_[0].offset_ = start_offset;
+ frame->img_components_[0].size_ = size;
+ frame->width_ = features->width;
+ frame->height_ = features->height;
+ frame->has_alpha_ |= features->has_alpha;
+ frame->frame_num_ = frame_num;
+ frame->complete_ = complete;
+}
+
+// Store image bearing chunks to 'frame'. 'min_size' is an optional size
+// requirement, it may be zero.
+static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
+ MemBuffer* const mem, Frame* const frame) {
+ int alpha_chunks = 0;
+ int image_chunks = 0;
+ int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE ||
+ MemDataSize(mem) < min_size);
+ ParseStatus status = PARSE_OK;
+
+ if (done) return PARSE_NEED_MORE_DATA;
+
+ do {
+ const size_t chunk_start_offset = mem->start_;
+ const uint32_t fourcc = ReadLE32(mem);
+ const uint32_t payload_size = ReadLE32(mem);
+ const uint32_t payload_size_padded = payload_size + (payload_size & 1);
+ const size_t payload_available = (payload_size_padded > MemDataSize(mem))
+ ? MemDataSize(mem) : payload_size_padded;
+ const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
+
+ if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
+ if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
+ if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
+
+ switch (fourcc) {
+ case MKFOURCC('A', 'L', 'P', 'H'):
+ if (alpha_chunks == 0) {
+ ++alpha_chunks;
+ frame->img_components_[1].offset_ = chunk_start_offset;
+ frame->img_components_[1].size_ = chunk_size;
+ frame->has_alpha_ = 1;
+ frame->frame_num_ = frame_num;
+ Skip(mem, payload_available);
+ } else {
+ goto Done;
+ }
+ break;
+ case MKFOURCC('V', 'P', '8', 'L'):
+ if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha
+ // fall through
+ case MKFOURCC('V', 'P', '8', ' '):
+ if (image_chunks == 0) {
+ // Extract the bitstream features, tolerating failures when the data
+ // is incomplete.
+ WebPBitstreamFeatures features;
+ const VP8StatusCode vp8_status =
+ WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
+ &features);
+ if (status == PARSE_NEED_MORE_DATA &&
+ vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
+ return PARSE_NEED_MORE_DATA;
+ } else if (vp8_status != VP8_STATUS_OK) {
+ // We have enough data, and yet WebPGetFeatures() failed.
+ return PARSE_ERROR;
+ }
+ ++image_chunks;
+ SetFrameInfo(chunk_start_offset, chunk_size, frame_num,
+ status == PARSE_OK, &features, frame);
+ Skip(mem, payload_available);
+ } else {
+ goto Done;
+ }
+ break;
+ Done:
+ default:
+ // Restore fourcc/size when moving up one level in parsing.
+ Rewind(mem, CHUNK_HEADER_SIZE);
+ done = 1;
+ break;
+ }
+
+ if (mem->start_ == mem->riff_end_) {
+ done = 1;
+ } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
+ status = PARSE_NEED_MORE_DATA;
+ }
+ } while (!done && status == PARSE_OK);
+
+ return status;
+}
+
+// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
+// enough data ('min_size') to parse the payload.
+// Returns PARSE_OK on success with *frame pointing to the new Frame.
+// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
+static ParseStatus NewFrame(const MemBuffer* const mem,
+ uint32_t min_size, uint32_t actual_size,
+ Frame** frame) {
+ if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
+ if (actual_size < min_size) return PARSE_ERROR;
+ if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
+
+ *frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame));
+ return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
+}
+
+// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
+// 'frame_chunk_size' is the previously validated, padded chunk size.
+static ParseStatus ParseAnimationFrame(
+ WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
+ const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
+ const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
+ int added_frame = 0;
+ int bits;
+ MemBuffer* const mem = &dmux->mem_;
+ Frame* frame;
+ ParseStatus status =
+ NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
+ if (status != PARSE_OK) return status;
+
+ frame->x_offset_ = 2 * ReadLE24s(mem);
+ frame->y_offset_ = 2 * ReadLE24s(mem);
+ frame->width_ = 1 + ReadLE24s(mem);
+ frame->height_ = 1 + ReadLE24s(mem);
+ frame->duration_ = ReadLE24s(mem);
+ bits = ReadByte(mem);
+ frame->dispose_method_ =
+ (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
+ frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
+ if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
+ WebPSafeFree(frame);
+ return PARSE_ERROR;
+ }
+
+ // Store a frame only if the animation flag is set there is some data for
+ // this frame is available.
+ status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame);
+ if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) {
+ added_frame = AddFrame(dmux, frame);
+ if (added_frame) {
+ ++dmux->num_frames_;
+ } else {
+ status = PARSE_ERROR;
+ }
+ }
+
+ if (!added_frame) WebPSafeFree(frame);
+ return status;
+}
+
+// General chunk storage, starting with the header at 'start_offset', allowing
+// the user to request the payload via a fourcc string. 'size' includes the
+// header and the unpadded payload size.
+// Returns true on success, false otherwise.
+static int StoreChunk(WebPDemuxer* const dmux,
+ size_t start_offset, uint32_t size) {
+ Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk));
+ if (chunk == NULL) return 0;
+
+ chunk->data_.offset_ = start_offset;
+ chunk->data_.size_ = size;
+ AddChunk(dmux, chunk);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+// Primary chunk parsing
+
+static ParseStatus ReadHeader(MemBuffer* const mem) {
+ const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
+ uint32_t riff_size;
+
+ // Basic file level validation.
+ if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
+ if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
+ memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
+ return PARSE_ERROR;
+ }
+
+ riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
+ if (riff_size < CHUNK_HEADER_SIZE) return PARSE_ERROR;
+ if (riff_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
+
+ // There's no point in reading past the end of the RIFF chunk
+ mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
+ if (mem->buf_size_ > mem->riff_end_) {
+ mem->buf_size_ = mem->end_ = mem->riff_end_;
+ }
+
+ Skip(mem, RIFF_HEADER_SIZE);
+ return PARSE_OK;
+}
+
+static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
+ const size_t min_size = CHUNK_HEADER_SIZE;
+ MemBuffer* const mem = &dmux->mem_;
+ Frame* frame;
+ ParseStatus status;
+ int image_added = 0;
+
+ if (dmux->frames_ != NULL) return PARSE_ERROR;
+ if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
+ if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
+
+ frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
+ if (frame == NULL) return PARSE_ERROR;
+
+ // For the single image case we allow parsing of a partial frame, so no
+ // minimum size is imposed here.
+ status = StoreFrame(1, 0, &dmux->mem_, frame);
+ if (status != PARSE_ERROR) {
+ const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
+ // Clear any alpha when the alpha flag is missing.
+ if (!has_alpha && frame->img_components_[1].size_ > 0) {
+ frame->img_components_[1].offset_ = 0;
+ frame->img_components_[1].size_ = 0;
+ frame->has_alpha_ = 0;
+ }
+
+ // Use the frame width/height as the canvas values for non-vp8x files.
+ // Also, set ALPHA_FLAG if this is a lossless image with alpha.
+ if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
+ dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
+ dmux->canvas_width_ = frame->width_;
+ dmux->canvas_height_ = frame->height_;
+ dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
+ }
+ if (!AddFrame(dmux, frame)) {
+ status = PARSE_ERROR; // last frame was left incomplete
+ } else {
+ image_added = 1;
+ dmux->num_frames_ = 1;
+ }
+ }
+
+ if (!image_added) WebPSafeFree(frame);
+ return status;
+}
+
+static ParseStatus ParseVP8XChunks(WebPDemuxer* const dmux) {
+ const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
+ MemBuffer* const mem = &dmux->mem_;
+ int anim_chunks = 0;
+ ParseStatus status = PARSE_OK;
+
+ do {
+ int store_chunk = 1;
+ const size_t chunk_start_offset = mem->start_;
+ const uint32_t fourcc = ReadLE32(mem);
+ const uint32_t chunk_size = ReadLE32(mem);
+ const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
+
+ if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
+ if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
+
+ switch (fourcc) {
+ case MKFOURCC('V', 'P', '8', 'X'): {
+ return PARSE_ERROR;
+ }
+ case MKFOURCC('A', 'L', 'P', 'H'):
+ case MKFOURCC('V', 'P', '8', ' '):
+ case MKFOURCC('V', 'P', '8', 'L'): {
+ // check that this isn't an animation (all frames should be in an ANMF).
+ if (anim_chunks > 0 || is_animation) return PARSE_ERROR;
+
+ Rewind(mem, CHUNK_HEADER_SIZE);
+ status = ParseSingleImage(dmux);
+ break;
+ }
+ case MKFOURCC('A', 'N', 'I', 'M'): {
+ if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
+
+ if (MemDataSize(mem) < chunk_size_padded) {
+ status = PARSE_NEED_MORE_DATA;
+ } else if (anim_chunks == 0) {
+ ++anim_chunks;
+ dmux->bgcolor_ = ReadLE32(mem);
+ dmux->loop_count_ = ReadLE16s(mem);
+ Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
+ } else {
+ store_chunk = 0;
+ goto Skip;
+ }
+ break;
+ }
+ case MKFOURCC('A', 'N', 'M', 'F'): {
+ if (anim_chunks == 0) return PARSE_ERROR; // 'ANIM' precedes frames.
+ status = ParseAnimationFrame(dmux, chunk_size_padded);
+ break;
+ }
+ case MKFOURCC('I', 'C', 'C', 'P'): {
+ store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
+ goto Skip;
+ }
+ case MKFOURCC('E', 'X', 'I', 'F'): {
+ store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
+ goto Skip;
+ }
+ case MKFOURCC('X', 'M', 'P', ' '): {
+ store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
+ goto Skip;
+ }
+ Skip:
+ default: {
+ if (chunk_size_padded <= MemDataSize(mem)) {
+ if (store_chunk) {
+ // Store only the chunk header and unpadded size as only the payload
+ // will be returned to the user.
+ if (!StoreChunk(dmux, chunk_start_offset,
+ CHUNK_HEADER_SIZE + chunk_size)) {
+ return PARSE_ERROR;
+ }
+ }
+ Skip(mem, chunk_size_padded);
+ } else {
+ status = PARSE_NEED_MORE_DATA;
+ }
+ }
+ }
+
+ if (mem->start_ == mem->riff_end_) {
+ break;
+ } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
+ status = PARSE_NEED_MORE_DATA;
+ }
+ } while (status == PARSE_OK);
+
+ return status;
+}
+
+static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
+ MemBuffer* const mem = &dmux->mem_;
+ uint32_t vp8x_size;
+
+ if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
+
+ dmux->is_ext_format_ = 1;
+ Skip(mem, TAG_SIZE); // VP8X
+ vp8x_size = ReadLE32(mem);
+ if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
+ if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
+ vp8x_size += vp8x_size & 1;
+ if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
+ if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
+
+ dmux->feature_flags_ = ReadByte(mem);
+ Skip(mem, 3); // Reserved.
+ dmux->canvas_width_ = 1 + ReadLE24s(mem);
+ dmux->canvas_height_ = 1 + ReadLE24s(mem);
+ if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
+ return PARSE_ERROR; // image final dimension is too large
+ }
+ Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
+ dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
+
+ if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
+ if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
+
+ return ParseVP8XChunks(dmux);
+}
+
+// -----------------------------------------------------------------------------
+// Format validation
+
+static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
+ const Frame* const frame = dmux->frames_;
+ if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
+
+ if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
+ if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
+
+ if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
+ return 1;
+}
+
+// If 'exact' is true, check that the image resolution matches the canvas.
+// If 'exact' is false, check that the x/y offsets do not exceed the canvas.
+static int CheckFrameBounds(const Frame* const frame, int exact,
+ int canvas_width, int canvas_height) {
+ if (exact) {
+ if (frame->x_offset_ != 0 || frame->y_offset_ != 0) {
+ return 0;
+ }
+ if (frame->width_ != canvas_width || frame->height_ != canvas_height) {
+ return 0;
+ }
+ } else {
+ if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0;
+ if (frame->width_ + frame->x_offset_ > canvas_width) return 0;
+ if (frame->height_ + frame->y_offset_ > canvas_height) return 0;
+ }
+ return 1;
+}
+
+static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
+ const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG);
+ const Frame* f = dmux->frames_;
+
+ if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
+
+ if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
+ if (dmux->loop_count_ < 0) return 0;
+ if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
+ if (dmux->feature_flags_ & ~ALL_VALID_FLAGS) return 0; // invalid bitstream
+
+ while (f != NULL) {
+ const int cur_frame_set = f->frame_num_;
+ int frame_count = 0;
+
+ // Check frame properties.
+ for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
+ const ChunkData* const image = f->img_components_;
+ const ChunkData* const alpha = f->img_components_ + 1;
+
+ if (!is_animation && f->frame_num_ > 1) return 0;
+
+ if (f->complete_) {
+ if (alpha->size_ == 0 && image->size_ == 0) return 0;
+ // Ensure alpha precedes image bitstream.
+ if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
+ return 0;
+ }
+
+ if (f->width_ <= 0 || f->height_ <= 0) return 0;
+ } else {
+ // There shouldn't be a partial frame in a complete file.
+ if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
+
+ // Ensure alpha precedes image bitstream.
+ if (alpha->size_ > 0 && image->size_ > 0 &&
+ alpha->offset_ > image->offset_) {
+ return 0;
+ }
+ // There shouldn't be any frames after an incomplete one.
+ if (f->next_ != NULL) return 0;
+ }
+
+ if (f->width_ > 0 && f->height_ > 0 &&
+ !CheckFrameBounds(f, !is_animation,
+ dmux->canvas_width_, dmux->canvas_height_)) {
+ return 0;
+ }
+
+ ++frame_count;
+ }
+ }
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+// WebPDemuxer object
+
+static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
+ dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
+ dmux->loop_count_ = 1;
+ dmux->bgcolor_ = 0xFFFFFFFF; // White background by default.
+ dmux->canvas_width_ = -1;
+ dmux->canvas_height_ = -1;
+ dmux->frames_tail_ = &dmux->frames_;
+ dmux->chunks_tail_ = &dmux->chunks_;
+ dmux->mem_ = *mem;
+}
+
+static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem,
+ WebPDemuxer** demuxer) {
+ WebPBitstreamFeatures features;
+ const VP8StatusCode status =
+ WebPGetFeatures(mem->buf_, mem->buf_size_, &features);
+ *demuxer = NULL;
+ if (status != VP8_STATUS_OK) {
+ return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA
+ : PARSE_ERROR;
+ }
+
+ {
+ WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
+ Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame));
+ if (dmux == NULL || frame == NULL) goto Error;
+ InitDemux(dmux, mem);
+ SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features,
+ frame);
+ if (!AddFrame(dmux, frame)) goto Error;
+ dmux->state_ = WEBP_DEMUX_DONE;
+ dmux->canvas_width_ = frame->width_;
+ dmux->canvas_height_ = frame->height_;
+ dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0;
+ dmux->num_frames_ = 1;
+ assert(IsValidSimpleFormat(dmux));
+ *demuxer = dmux;
+ return PARSE_OK;
+
+ Error:
+ WebPSafeFree(dmux);
+ WebPSafeFree(frame);
+ return PARSE_ERROR;
+ }
+}
+
+WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
+ WebPDemuxState* state, int version) {
+ const ChunkParser* parser;
+ int partial;
+ ParseStatus status = PARSE_ERROR;
+ MemBuffer mem;
+ WebPDemuxer* dmux;
+
+ if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR;
+
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
+ if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
+
+ if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
+ status = ReadHeader(&mem);
+ if (status != PARSE_OK) {
+ // If parsing of the webp file header fails attempt to handle a raw
+ // VP8/VP8L frame. Note 'allow_partial' is ignored in this case.
+ if (status == PARSE_ERROR) {
+ status = CreateRawImageDemuxer(&mem, &dmux);
+ if (status == PARSE_OK) {
+ if (state != NULL) *state = WEBP_DEMUX_DONE;
+ return dmux;
+ }
+ }
+ if (state != NULL) {
+ *state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER
+ : WEBP_DEMUX_PARSE_ERROR;
+ }
+ return NULL;
+ }
+
+ partial = (mem.buf_size_ < mem.riff_end_);
+ if (!allow_partial && partial) return NULL;
+
+ dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux));
+ if (dmux == NULL) return NULL;
+ InitDemux(dmux, &mem);
+
+ status = PARSE_ERROR;
+ for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
+ if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
+ status = parser->parse(dmux);
+ if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
+ if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
+ if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
+ if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR;
+ break;
+ }
+ }
+ if (state != NULL) *state = dmux->state_;
+
+ if (status == PARSE_ERROR) {
+ WebPDemuxDelete(dmux);
+ return NULL;
+ }
+ return dmux;
+}
+
+void WebPDemuxDelete(WebPDemuxer* dmux) {
+ Chunk* c;
+ Frame* f;
+ if (dmux == NULL) return;
+
+ for (f = dmux->frames_; f != NULL;) {
+ Frame* const cur_frame = f;
+ f = f->next_;
+ WebPSafeFree(cur_frame);
+ }
+ for (c = dmux->chunks_; c != NULL;) {
+ Chunk* const cur_chunk = c;
+ c = c->next_;
+ WebPSafeFree(cur_chunk);
+ }
+ WebPSafeFree(dmux);
+}
+
+// -----------------------------------------------------------------------------
+
+uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
+ if (dmux == NULL) return 0;
+
+ switch (feature) {
+ case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags_;
+ case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_;
+ case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_;
+ case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_;
+ case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
+ case WEBP_FF_FRAME_COUNT: return (uint32_t)dmux->num_frames_;
+ }
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+// Frame iteration
+
+static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
+ const Frame* f;
+ for (f = dmux->frames_; f != NULL; f = f->next_) {
+ if (frame_num == f->frame_num_) break;
+ }
+ return f;
+}
+
+static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
+ const Frame* const frame,
+ size_t* const data_size) {
+ *data_size = 0;
+ if (frame != NULL) {
+ const ChunkData* const image = frame->img_components_;
+ const ChunkData* const alpha = frame->img_components_ + 1;
+ size_t start_offset = image->offset_;
+ *data_size = image->size_;
+
+ // if alpha exists it precedes image, update the size allowing for
+ // intervening chunks.
+ if (alpha->size_ > 0) {
+ const size_t inter_size = (image->offset_ > 0)
+ ? image->offset_ - (alpha->offset_ + alpha->size_)
+ : 0;
+ start_offset = alpha->offset_;
+ *data_size += alpha->size_ + inter_size;
+ }
+ return mem_buf + start_offset;
+ }
+ return NULL;
+}
+
+// Create a whole 'frame' from VP8 (+ alpha) or lossless.
+static int SynthesizeFrame(const WebPDemuxer* const dmux,
+ const Frame* const frame,
+ WebPIterator* const iter) {
+ const uint8_t* const mem_buf = dmux->mem_.buf_;
+ size_t payload_size = 0;
+ const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size);
+ if (payload == NULL) return 0;
+ assert(frame != NULL);
+
+ iter->frame_num = frame->frame_num_;
+ iter->num_frames = dmux->num_frames_;
+ iter->x_offset = frame->x_offset_;
+ iter->y_offset = frame->y_offset_;
+ iter->width = frame->width_;
+ iter->height = frame->height_;
+ iter->has_alpha = frame->has_alpha_;
+ iter->duration = frame->duration_;
+ iter->dispose_method = frame->dispose_method_;
+ iter->blend_method = frame->blend_method_;
+ iter->complete = frame->complete_;
+ iter->fragment.bytes = payload;
+ iter->fragment.size = payload_size;
+ return 1;
+}
+
+static int SetFrame(int frame_num, WebPIterator* const iter) {
+ const Frame* frame;
+ const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
+ if (dmux == NULL || frame_num < 0) return 0;
+ if (frame_num > dmux->num_frames_) return 0;
+ if (frame_num == 0) frame_num = dmux->num_frames_;
+
+ frame = GetFrame(dmux, frame_num);
+ if (frame == NULL) return 0;
+
+ return SynthesizeFrame(dmux, frame, iter);
+}
+
+int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
+ if (iter == NULL) return 0;
+
+ memset(iter, 0, sizeof(*iter));
+ iter->private_ = (void*)dmux;
+ return SetFrame(frame, iter);
+}
+
+int WebPDemuxNextFrame(WebPIterator* iter) {
+ if (iter == NULL) return 0;
+ return SetFrame(iter->frame_num + 1, iter);
+}
+
+int WebPDemuxPrevFrame(WebPIterator* iter) {
+ if (iter == NULL) return 0;
+ if (iter->frame_num <= 1) return 0;
+ return SetFrame(iter->frame_num - 1, iter);
+}
+
+void WebPDemuxReleaseIterator(WebPIterator* iter) {
+ (void)iter;
+}
+
+// -----------------------------------------------------------------------------
+// Chunk iteration
+
+static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
+ const uint8_t* const mem_buf = dmux->mem_.buf_;
+ const Chunk* c;
+ int count = 0;
+ for (c = dmux->chunks_; c != NULL; c = c->next_) {
+ const uint8_t* const header = mem_buf + c->data_.offset_;
+ if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
+ }
+ return count;
+}
+
+static const Chunk* GetChunk(const WebPDemuxer* const dmux,
+ const char fourcc[4], int chunk_num) {
+ const uint8_t* const mem_buf = dmux->mem_.buf_;
+ const Chunk* c;
+ int count = 0;
+ for (c = dmux->chunks_; c != NULL; c = c->next_) {
+ const uint8_t* const header = mem_buf + c->data_.offset_;
+ if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
+ if (count == chunk_num) break;
+ }
+ return c;
+}
+
+static int SetChunk(const char fourcc[4], int chunk_num,
+ WebPChunkIterator* const iter) {
+ const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
+ int count;
+
+ if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
+ count = ChunkCount(dmux, fourcc);
+ if (count == 0) return 0;
+ if (chunk_num == 0) chunk_num = count;
+
+ if (chunk_num <= count) {
+ const uint8_t* const mem_buf = dmux->mem_.buf_;
+ const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
+ iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
+ iter->chunk.size = chunk->data_.size_ - CHUNK_HEADER_SIZE;
+ iter->num_chunks = count;
+ iter->chunk_num = chunk_num;
+ return 1;
+ }
+ return 0;
+}
+
+int WebPDemuxGetChunk(const WebPDemuxer* dmux,
+ const char fourcc[4], int chunk_num,
+ WebPChunkIterator* iter) {
+ if (iter == NULL) return 0;
+
+ memset(iter, 0, sizeof(*iter));
+ iter->private_ = (void*)dmux;
+ return SetChunk(fourcc, chunk_num, iter);
+}
+
+int WebPDemuxNextChunk(WebPChunkIterator* iter) {
+ if (iter != NULL) {
+ const char* const fourcc =
+ (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
+ return SetChunk(fourcc, iter->chunk_num + 1, iter);
+ }
+ return 0;
+}
+
+int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
+ if (iter != NULL && iter->chunk_num > 1) {
+ const char* const fourcc =
+ (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
+ return SetChunk(fourcc, iter->chunk_num - 1, iter);
+ }
+ return 0;
+}
+
+void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
+ (void)iter;
+}
+
diff --git a/src/third_party/libwebp/src/demux/libwebpdemux.pc.in b/src/third_party/libwebp/src/demux/libwebpdemux.pc.in
new file mode 100644
index 0000000..6dfbbbd
--- /dev/null
+++ b/src/third_party/libwebp/src/demux/libwebpdemux.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebpdemux
+Description: Library for parsing the WebP graphics format container
+Version: @PACKAGE_VERSION@
+Requires: libwebp >= 0.2.0
+Cflags: -I${includedir}
+Libs: -L${libdir} -lwebpdemux
diff --git a/src/third_party/libwebp/src/demux/libwebpdemux.rc b/src/third_party/libwebp/src/demux/libwebpdemux.rc
new file mode 100644
index 0000000..544a8b2
--- /dev/null
+++ b/src/third_party/libwebp/src/demux/libwebpdemux.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Google, Inc."
+ VALUE "FileDescription", "libwebpdemux DLL"
+ VALUE "FileVersion", "1.0.0"
+ VALUE "InternalName", "libwebpdemux.dll"
+ VALUE "LegalCopyright", "Copyright (C) 2018"
+ VALUE "OriginalFilename", "libwebpdemux.dll"
+ VALUE "ProductName", "WebP Image Demuxer"
+ VALUE "ProductVersion", "1.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
diff --git a/src/third_party/libwebp/src/dsp/Makefile.am b/src/third_party/libwebp/src/dsp/Makefile.am
new file mode 100644
index 0000000..0836d8f
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/Makefile.am
@@ -0,0 +1,163 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES =
+noinst_LTLIBRARIES += libwebpdsp.la
+noinst_LTLIBRARIES += libwebpdsp_avx2.la
+noinst_LTLIBRARIES += libwebpdsp_sse2.la
+noinst_LTLIBRARIES += libwebpdspdecode_sse2.la
+noinst_LTLIBRARIES += libwebpdsp_sse41.la
+noinst_LTLIBRARIES += libwebpdspdecode_sse41.la
+noinst_LTLIBRARIES += libwebpdsp_neon.la
+noinst_LTLIBRARIES += libwebpdspdecode_neon.la
+noinst_LTLIBRARIES += libwebpdsp_msa.la
+noinst_LTLIBRARIES += libwebpdspdecode_msa.la
+
+if BUILD_LIBWEBPDECODER
+ noinst_LTLIBRARIES += libwebpdspdecode.la
+endif
+
+common_HEADERS = ../webp/types.h
+commondir = $(includedir)/webp
+
+COMMON_SOURCES =
+COMMON_SOURCES += alpha_processing.c
+COMMON_SOURCES += alpha_processing_mips_dsp_r2.c
+COMMON_SOURCES += common_sse2.h
+COMMON_SOURCES += cpu.c
+COMMON_SOURCES += dec.c
+COMMON_SOURCES += dec_clip_tables.c
+COMMON_SOURCES += dec_mips32.c
+COMMON_SOURCES += dec_mips_dsp_r2.c
+COMMON_SOURCES += dsp.h
+COMMON_SOURCES += filters.c
+COMMON_SOURCES += filters_mips_dsp_r2.c
+COMMON_SOURCES += lossless.c
+COMMON_SOURCES += lossless.h
+COMMON_SOURCES += lossless_common.h
+COMMON_SOURCES += lossless_mips_dsp_r2.c
+COMMON_SOURCES += mips_macro.h
+COMMON_SOURCES += rescaler.c
+COMMON_SOURCES += rescaler_mips32.c
+COMMON_SOURCES += rescaler_mips_dsp_r2.c
+COMMON_SOURCES += upsampling.c
+COMMON_SOURCES += upsampling_mips_dsp_r2.c
+COMMON_SOURCES += yuv.c
+COMMON_SOURCES += yuv.h
+COMMON_SOURCES += yuv_mips32.c
+COMMON_SOURCES += yuv_mips_dsp_r2.c
+
+ENC_SOURCES =
+ENC_SOURCES += cost.c
+ENC_SOURCES += cost_mips32.c
+ENC_SOURCES += cost_mips_dsp_r2.c
+ENC_SOURCES += enc.c
+ENC_SOURCES += enc_mips32.c
+ENC_SOURCES += enc_mips_dsp_r2.c
+ENC_SOURCES += lossless_enc.c
+ENC_SOURCES += lossless_enc_mips32.c
+ENC_SOURCES += lossless_enc_mips_dsp_r2.c
+ENC_SOURCES += ssim.c
+
+libwebpdsp_avx2_la_SOURCES =
+libwebpdsp_avx2_la_SOURCES += enc_avx2.c
+libwebpdsp_avx2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_avx2_la_CFLAGS = $(AM_CFLAGS) $(AVX2_FLAGS)
+
+libwebpdspdecode_sse41_la_SOURCES =
+libwebpdspdecode_sse41_la_SOURCES += alpha_processing_sse41.c
+libwebpdspdecode_sse41_la_SOURCES += dec_sse41.c
+libwebpdspdecode_sse41_la_SOURCES += upsampling_sse41.c
+libwebpdspdecode_sse41_la_SOURCES += yuv_sse41.c
+libwebpdspdecode_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdspdecode_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
+
+libwebpdspdecode_sse2_la_SOURCES =
+libwebpdspdecode_sse2_la_SOURCES += alpha_processing_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += dec_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += filters_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += lossless_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += rescaler_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += upsampling_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += yuv_sse2.c
+libwebpdspdecode_sse2_la_CPPFLAGS = $(libwebpdsp_sse2_la_CPPFLAGS)
+libwebpdspdecode_sse2_la_CFLAGS = $(libwebpdsp_sse2_la_CFLAGS)
+
+libwebpdspdecode_neon_la_SOURCES =
+libwebpdspdecode_neon_la_SOURCES += alpha_processing_neon.c
+libwebpdspdecode_neon_la_SOURCES += dec_neon.c
+libwebpdspdecode_neon_la_SOURCES += filters_neon.c
+libwebpdspdecode_neon_la_SOURCES += lossless_neon.c
+libwebpdspdecode_neon_la_SOURCES += neon.h
+libwebpdspdecode_neon_la_SOURCES += rescaler_neon.c
+libwebpdspdecode_neon_la_SOURCES += upsampling_neon.c
+libwebpdspdecode_neon_la_SOURCES += yuv_neon.c
+libwebpdspdecode_neon_la_CPPFLAGS = $(libwebpdsp_neon_la_CPPFLAGS)
+libwebpdspdecode_neon_la_CFLAGS = $(libwebpdsp_neon_la_CFLAGS)
+
+libwebpdspdecode_msa_la_SOURCES =
+libwebpdspdecode_msa_la_SOURCES += dec_msa.c
+libwebpdspdecode_msa_la_SOURCES += filters_msa.c
+libwebpdspdecode_msa_la_SOURCES += lossless_msa.c
+libwebpdspdecode_msa_la_SOURCES += msa_macro.h
+libwebpdspdecode_msa_la_SOURCES += rescaler_msa.c
+libwebpdspdecode_msa_la_SOURCES += upsampling_msa.c
+libwebpdspdecode_msa_la_CPPFLAGS = $(libwebpdsp_msa_la_CPPFLAGS)
+libwebpdspdecode_msa_la_CFLAGS = $(libwebpdsp_msa_la_CFLAGS)
+
+libwebpdsp_sse2_la_SOURCES =
+libwebpdsp_sse2_la_SOURCES += cost_sse2.c
+libwebpdsp_sse2_la_SOURCES += enc_sse2.c
+libwebpdsp_sse2_la_SOURCES += lossless_enc_sse2.c
+libwebpdsp_sse2_la_SOURCES += ssim_sse2.c
+libwebpdsp_sse2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_sse2_la_CFLAGS = $(AM_CFLAGS) $(SSE2_FLAGS)
+libwebpdsp_sse2_la_LIBADD = libwebpdspdecode_sse2.la
+
+libwebpdsp_sse41_la_SOURCES =
+libwebpdsp_sse41_la_SOURCES += enc_sse41.c
+libwebpdsp_sse41_la_SOURCES += lossless_enc_sse41.c
+libwebpdsp_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
+libwebpdsp_sse41_la_LIBADD = libwebpdspdecode_sse41.la
+
+libwebpdsp_neon_la_SOURCES =
+libwebpdsp_neon_la_SOURCES += enc_neon.c
+libwebpdsp_neon_la_SOURCES += lossless_enc_neon.c
+libwebpdsp_neon_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS)
+libwebpdsp_neon_la_LIBADD = libwebpdspdecode_neon.la
+
+libwebpdsp_msa_la_SOURCES =
+libwebpdsp_msa_la_SOURCES += enc_msa.c
+libwebpdsp_msa_la_SOURCES += lossless_enc_msa.c
+libwebpdsp_msa_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_msa_la_CFLAGS = $(AM_CFLAGS)
+libwebpdsp_msa_la_LIBADD = libwebpdspdecode_msa.la
+
+libwebpdsp_la_SOURCES = $(COMMON_SOURCES) $(ENC_SOURCES)
+
+noinst_HEADERS =
+noinst_HEADERS += ../dec/vp8_dec.h
+noinst_HEADERS += ../webp/decode.h
+
+libwebpdsp_la_CPPFLAGS =
+libwebpdsp_la_CPPFLAGS += $(AM_CPPFLAGS)
+libwebpdsp_la_CPPFLAGS += $(USE_SWAP_16BIT_CSP)
+libwebpdsp_la_LDFLAGS = -lm
+libwebpdsp_la_LIBADD =
+libwebpdsp_la_LIBADD += libwebpdsp_avx2.la
+libwebpdsp_la_LIBADD += libwebpdsp_sse2.la
+libwebpdsp_la_LIBADD += libwebpdsp_sse41.la
+libwebpdsp_la_LIBADD += libwebpdsp_neon.la
+libwebpdsp_la_LIBADD += libwebpdsp_msa.la
+
+if BUILD_LIBWEBPDECODER
+ libwebpdspdecode_la_SOURCES = $(COMMON_SOURCES)
+
+ libwebpdspdecode_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+ libwebpdspdecode_la_LDFLAGS = $(libwebpdsp_la_LDFLAGS)
+ libwebpdspdecode_la_LIBADD =
+ libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse2.la
+ libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse41.la
+ libwebpdspdecode_la_LIBADD += libwebpdspdecode_neon.la
+ libwebpdspdecode_la_LIBADD += libwebpdspdecode_msa.la
+endif
diff --git a/src/third_party/libwebp/src/dsp/alpha_processing.c b/src/third_party/libwebp/src/dsp/alpha_processing.c
new file mode 100644
index 0000000..819d139
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/alpha_processing.c
@@ -0,0 +1,472 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for processing transparent channel.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include "src/dsp/dsp.h"
+
+// Tables can be faster on some platform but incur some extra binary size (~2k).
+#if !defined(USE_TABLES_FOR_ALPHA_MULT)
+#define USE_TABLES_FOR_ALPHA_MULT 0 // ALTERNATE_CODE
+#endif
+
+
+// -----------------------------------------------------------------------------
+
+#define MFIX 24 // 24bit fixed-point arithmetic
+#define HALF ((1u << MFIX) >> 1)
+#define KINV_255 ((1u << MFIX) / 255u)
+
+static uint32_t Mult(uint8_t x, uint32_t mult) {
+ const uint32_t v = (x * mult + HALF) >> MFIX;
+ assert(v <= 255); // <- 24bit precision is enough to ensure that.
+ return v;
+}
+
+#if (USE_TABLES_FOR_ALPHA_MULT == 1)
+
+static const uint32_t kMultTables[2][256] = {
+ { // (255u << MFIX) / alpha
+ 0x00000000, 0xff000000, 0x7f800000, 0x55000000, 0x3fc00000, 0x33000000,
+ 0x2a800000, 0x246db6db, 0x1fe00000, 0x1c555555, 0x19800000, 0x172e8ba2,
+ 0x15400000, 0x139d89d8, 0x1236db6d, 0x11000000, 0x0ff00000, 0x0f000000,
+ 0x0e2aaaaa, 0x0d6bca1a, 0x0cc00000, 0x0c249249, 0x0b9745d1, 0x0b1642c8,
+ 0x0aa00000, 0x0a333333, 0x09cec4ec, 0x0971c71c, 0x091b6db6, 0x08cb08d3,
+ 0x08800000, 0x0839ce73, 0x07f80000, 0x07ba2e8b, 0x07800000, 0x07492492,
+ 0x07155555, 0x06e45306, 0x06b5e50d, 0x0689d89d, 0x06600000, 0x063831f3,
+ 0x06124924, 0x05ee23b8, 0x05cba2e8, 0x05aaaaaa, 0x058b2164, 0x056cefa8,
+ 0x05500000, 0x05343eb1, 0x05199999, 0x05000000, 0x04e76276, 0x04cfb2b7,
+ 0x04b8e38e, 0x04a2e8ba, 0x048db6db, 0x0479435e, 0x04658469, 0x045270d0,
+ 0x04400000, 0x042e29f7, 0x041ce739, 0x040c30c3, 0x03fc0000, 0x03ec4ec4,
+ 0x03dd1745, 0x03ce540f, 0x03c00000, 0x03b21642, 0x03a49249, 0x03976fc6,
+ 0x038aaaaa, 0x037e3f1f, 0x03722983, 0x03666666, 0x035af286, 0x034fcace,
+ 0x0344ec4e, 0x033a5440, 0x03300000, 0x0325ed09, 0x031c18f9, 0x0312818a,
+ 0x03092492, 0x03000000, 0x02f711dc, 0x02ee5846, 0x02e5d174, 0x02dd7baf,
+ 0x02d55555, 0x02cd5cd5, 0x02c590b2, 0x02bdef7b, 0x02b677d4, 0x02af286b,
+ 0x02a80000, 0x02a0fd5c, 0x029a1f58, 0x029364d9, 0x028ccccc, 0x0286562d,
+ 0x02800000, 0x0279c952, 0x0273b13b, 0x026db6db, 0x0267d95b, 0x026217ec,
+ 0x025c71c7, 0x0256e62a, 0x0251745d, 0x024c1bac, 0x0246db6d, 0x0241b2f9,
+ 0x023ca1af, 0x0237a6f4, 0x0232c234, 0x022df2df, 0x02293868, 0x02249249,
+ 0x02200000, 0x021b810e, 0x021714fb, 0x0212bb51, 0x020e739c, 0x020a3d70,
+ 0x02061861, 0x02020408, 0x01fe0000, 0x01fa0be8, 0x01f62762, 0x01f25213,
+ 0x01ee8ba2, 0x01ead3ba, 0x01e72a07, 0x01e38e38, 0x01e00000, 0x01dc7f10,
+ 0x01d90b21, 0x01d5a3e9, 0x01d24924, 0x01cefa8d, 0x01cbb7e3, 0x01c880e5,
+ 0x01c55555, 0x01c234f7, 0x01bf1f8f, 0x01bc14e5, 0x01b914c1, 0x01b61eed,
+ 0x01b33333, 0x01b05160, 0x01ad7943, 0x01aaaaaa, 0x01a7e567, 0x01a5294a,
+ 0x01a27627, 0x019fcbd2, 0x019d2a20, 0x019a90e7, 0x01980000, 0x01957741,
+ 0x0192f684, 0x01907da4, 0x018e0c7c, 0x018ba2e8, 0x018940c5, 0x0186e5f0,
+ 0x01849249, 0x018245ae, 0x01800000, 0x017dc11f, 0x017b88ee, 0x0179574e,
+ 0x01772c23, 0x01750750, 0x0172e8ba, 0x0170d045, 0x016ebdd7, 0x016cb157,
+ 0x016aaaaa, 0x0168a9b9, 0x0166ae6a, 0x0164b8a7, 0x0162c859, 0x0160dd67,
+ 0x015ef7bd, 0x015d1745, 0x015b3bea, 0x01596596, 0x01579435, 0x0155c7b4,
+ 0x01540000, 0x01523d03, 0x01507eae, 0x014ec4ec, 0x014d0fac, 0x014b5edc,
+ 0x0149b26c, 0x01480a4a, 0x01466666, 0x0144c6af, 0x01432b16, 0x0141938b,
+ 0x01400000, 0x013e7063, 0x013ce4a9, 0x013b5cc0, 0x0139d89d, 0x01385830,
+ 0x0136db6d, 0x01356246, 0x0133ecad, 0x01327a97, 0x01310bf6, 0x012fa0be,
+ 0x012e38e3, 0x012cd459, 0x012b7315, 0x012a150a, 0x0128ba2e, 0x01276276,
+ 0x01260dd6, 0x0124bc44, 0x01236db6, 0x01222222, 0x0120d97c, 0x011f93bc,
+ 0x011e50d7, 0x011d10c4, 0x011bd37a, 0x011a98ef, 0x0119611a, 0x01182bf2,
+ 0x0116f96f, 0x0115c988, 0x01149c34, 0x0113716a, 0x01124924, 0x01112358,
+ 0x01100000, 0x010edf12, 0x010dc087, 0x010ca458, 0x010b8a7d, 0x010a72f0,
+ 0x01095da8, 0x01084a9f, 0x010739ce, 0x01062b2e, 0x01051eb8, 0x01041465,
+ 0x01030c30, 0x01020612, 0x01010204, 0x01000000 },
+ { // alpha * KINV_255
+ 0x00000000, 0x00010101, 0x00020202, 0x00030303, 0x00040404, 0x00050505,
+ 0x00060606, 0x00070707, 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b,
+ 0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, 0x00101010, 0x00111111,
+ 0x00121212, 0x00131313, 0x00141414, 0x00151515, 0x00161616, 0x00171717,
+ 0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, 0x001c1c1c, 0x001d1d1d,
+ 0x001e1e1e, 0x001f1f1f, 0x00202020, 0x00212121, 0x00222222, 0x00232323,
+ 0x00242424, 0x00252525, 0x00262626, 0x00272727, 0x00282828, 0x00292929,
+ 0x002a2a2a, 0x002b2b2b, 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f,
+ 0x00303030, 0x00313131, 0x00323232, 0x00333333, 0x00343434, 0x00353535,
+ 0x00363636, 0x00373737, 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b,
+ 0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, 0x00404040, 0x00414141,
+ 0x00424242, 0x00434343, 0x00444444, 0x00454545, 0x00464646, 0x00474747,
+ 0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, 0x004c4c4c, 0x004d4d4d,
+ 0x004e4e4e, 0x004f4f4f, 0x00505050, 0x00515151, 0x00525252, 0x00535353,
+ 0x00545454, 0x00555555, 0x00565656, 0x00575757, 0x00585858, 0x00595959,
+ 0x005a5a5a, 0x005b5b5b, 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f,
+ 0x00606060, 0x00616161, 0x00626262, 0x00636363, 0x00646464, 0x00656565,
+ 0x00666666, 0x00676767, 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b,
+ 0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, 0x00707070, 0x00717171,
+ 0x00727272, 0x00737373, 0x00747474, 0x00757575, 0x00767676, 0x00777777,
+ 0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, 0x007c7c7c, 0x007d7d7d,
+ 0x007e7e7e, 0x007f7f7f, 0x00808080, 0x00818181, 0x00828282, 0x00838383,
+ 0x00848484, 0x00858585, 0x00868686, 0x00878787, 0x00888888, 0x00898989,
+ 0x008a8a8a, 0x008b8b8b, 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f,
+ 0x00909090, 0x00919191, 0x00929292, 0x00939393, 0x00949494, 0x00959595,
+ 0x00969696, 0x00979797, 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b,
+ 0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, 0x00a0a0a0, 0x00a1a1a1,
+ 0x00a2a2a2, 0x00a3a3a3, 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7,
+ 0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, 0x00acacac, 0x00adadad,
+ 0x00aeaeae, 0x00afafaf, 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3,
+ 0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, 0x00b8b8b8, 0x00b9b9b9,
+ 0x00bababa, 0x00bbbbbb, 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf,
+ 0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, 0x00c4c4c4, 0x00c5c5c5,
+ 0x00c6c6c6, 0x00c7c7c7, 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb,
+ 0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, 0x00d0d0d0, 0x00d1d1d1,
+ 0x00d2d2d2, 0x00d3d3d3, 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7,
+ 0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, 0x00dcdcdc, 0x00dddddd,
+ 0x00dedede, 0x00dfdfdf, 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3,
+ 0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, 0x00e8e8e8, 0x00e9e9e9,
+ 0x00eaeaea, 0x00ebebeb, 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef,
+ 0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, 0x00f4f4f4, 0x00f5f5f5,
+ 0x00f6f6f6, 0x00f7f7f7, 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb,
+ 0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff }
+};
+
+static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) {
+ return kMultTables[!inverse][a];
+}
+
+#else
+
+static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) {
+ return inverse ? (255u << MFIX) / a : a * KINV_255;
+}
+
+#endif // USE_TABLES_FOR_ALPHA_MULT
+
+void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse) {
+ int x;
+ for (x = 0; x < width; ++x) {
+ const uint32_t argb = ptr[x];
+ if (argb < 0xff000000u) { // alpha < 255
+ if (argb <= 0x00ffffffu) { // alpha == 0
+ ptr[x] = 0;
+ } else {
+ const uint32_t alpha = (argb >> 24) & 0xff;
+ const uint32_t scale = GetScale(alpha, inverse);
+ uint32_t out = argb & 0xff000000u;
+ out |= Mult(argb >> 0, scale) << 0;
+ out |= Mult(argb >> 8, scale) << 8;
+ out |= Mult(argb >> 16, scale) << 16;
+ ptr[x] = out;
+ }
+ }
+ }
+}
+
+void WebPMultRow_C(uint8_t* const ptr, const uint8_t* const alpha,
+ int width, int inverse) {
+ int x;
+ for (x = 0; x < width; ++x) {
+ const uint32_t a = alpha[x];
+ if (a != 255) {
+ if (a == 0) {
+ ptr[x] = 0;
+ } else {
+ const uint32_t scale = GetScale(a, inverse);
+ ptr[x] = Mult(ptr[x], scale);
+ }
+ }
+ }
+}
+
+#undef KINV_255
+#undef HALF
+#undef MFIX
+
+void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse);
+void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha,
+ int width, int inverse);
+
+//------------------------------------------------------------------------------
+// Generic per-plane calls
+
+void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows,
+ int inverse) {
+ int n;
+ for (n = 0; n < num_rows; ++n) {
+ WebPMultARGBRow((uint32_t*)ptr, width, inverse);
+ ptr += stride;
+ }
+}
+
+void WebPMultRows(uint8_t* ptr, int stride,
+ const uint8_t* alpha, int alpha_stride,
+ int width, int num_rows, int inverse) {
+ int n;
+ for (n = 0; n < num_rows; ++n) {
+ WebPMultRow(ptr, alpha, width, inverse);
+ ptr += stride;
+ alpha += alpha_stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Premultiplied modes
+
+// non dithered-modes
+
+// (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.)
+// for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5),
+// one can use instead: (x * a * 65793 + (1 << 23)) >> 24
+#if 1 // (int)(x * a / 255.)
+#define MULTIPLIER(a) ((a) * 32897U)
+#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
+#else // (int)(x * a / 255. + .5)
+#define MULTIPLIER(a) ((a) * 65793U)
+#define PREMULTIPLY(x, m) (((x) * (m) + (1U << 23)) >> 24)
+#endif
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void ApplyAlphaMultiply_C(uint8_t* rgba, int alpha_first,
+ int w, int h, int stride) {
+ while (h-- > 0) {
+ uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
+ const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
+ int i;
+ for (i = 0; i < w; ++i) {
+ const uint32_t a = alpha[4 * i];
+ if (a != 0xff) {
+ const uint32_t mult = MULTIPLIER(a);
+ rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
+ rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
+ rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
+ }
+ }
+ rgba += stride;
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+#undef MULTIPLIER
+#undef PREMULTIPLY
+
+// rgbA4444
+
+#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
+
+static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
+ return (x & 0xf0) | (x >> 4);
+}
+
+static WEBP_INLINE uint8_t dither_lo(uint8_t x) {
+ return (x & 0x0f) | (x << 4);
+}
+
+static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
+ return (x * m) >> 16;
+}
+
+static WEBP_INLINE void ApplyAlphaMultiply4444_C(uint8_t* rgba4444,
+ int w, int h, int stride,
+ int rg_byte_pos /* 0 or 1 */) {
+ while (h-- > 0) {
+ int i;
+ for (i = 0; i < w; ++i) {
+ const uint32_t rg = rgba4444[2 * i + rg_byte_pos];
+ const uint32_t ba = rgba4444[2 * i + (rg_byte_pos ^ 1)];
+ const uint8_t a = ba & 0x0f;
+ const uint32_t mult = MULTIPLIER(a);
+ const uint8_t r = multiply(dither_hi(rg), mult);
+ const uint8_t g = multiply(dither_lo(rg), mult);
+ const uint8_t b = multiply(dither_hi(ba), mult);
+ rgba4444[2 * i + rg_byte_pos] = (r & 0xf0) | ((g >> 4) & 0x0f);
+ rgba4444[2 * i + (rg_byte_pos ^ 1)] = (b & 0xf0) | a;
+ }
+ rgba4444 += stride;
+ }
+}
+#undef MULTIPLIER
+
+static void ApplyAlphaMultiply_16b_C(uint8_t* rgba4444,
+ int w, int h, int stride) {
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ ApplyAlphaMultiply4444_C(rgba4444, w, h, stride, 1);
+#else
+ ApplyAlphaMultiply4444_C(rgba4444, w, h, stride, 0);
+#endif
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static int DispatchAlpha_C(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint8_t* dst, int dst_stride) {
+ uint32_t alpha_mask = 0xff;
+ int i, j;
+
+ for (j = 0; j < height; ++j) {
+ for (i = 0; i < width; ++i) {
+ const uint32_t alpha_value = alpha[i];
+ dst[4 * i] = alpha_value;
+ alpha_mask &= alpha_value;
+ }
+ alpha += alpha_stride;
+ dst += dst_stride;
+ }
+
+ return (alpha_mask != 0xff);
+}
+
+static void DispatchAlphaToGreen_C(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint32_t* dst, int dst_stride) {
+ int i, j;
+ for (j = 0; j < height; ++j) {
+ for (i = 0; i < width; ++i) {
+ dst[i] = alpha[i] << 8; // leave A/R/B channels zero'd.
+ }
+ alpha += alpha_stride;
+ dst += dst_stride;
+ }
+}
+
+static int ExtractAlpha_C(const uint8_t* argb, int argb_stride,
+ int width, int height,
+ uint8_t* alpha, int alpha_stride) {
+ uint8_t alpha_mask = 0xff;
+ int i, j;
+
+ for (j = 0; j < height; ++j) {
+ for (i = 0; i < width; ++i) {
+ const uint8_t alpha_value = argb[4 * i];
+ alpha[i] = alpha_value;
+ alpha_mask &= alpha_value;
+ }
+ argb += argb_stride;
+ alpha += alpha_stride;
+ }
+ return (alpha_mask == 0xff);
+}
+
+static void ExtractGreen_C(const uint32_t* argb, uint8_t* alpha, int size) {
+ int i;
+ for (i = 0; i < size; ++i) alpha[i] = argb[i] >> 8;
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//------------------------------------------------------------------------------
+
+static int HasAlpha8b_C(const uint8_t* src, int length) {
+ while (length-- > 0) if (*src++ != 0xff) return 1;
+ return 0;
+}
+
+static int HasAlpha32b_C(const uint8_t* src, int length) {
+ int x;
+ for (x = 0; length-- > 0; x += 4) if (src[x] != 0xff) return 1;
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// Simple channel manipulations.
+
+static WEBP_INLINE uint32_t MakeARGB32(int a, int r, int g, int b) {
+ return (((uint32_t)a << 24) | (r << 16) | (g << 8) | b);
+}
+
+#ifdef WORDS_BIGENDIAN
+static void PackARGB_C(const uint8_t* a, const uint8_t* r, const uint8_t* g,
+ const uint8_t* b, int len, uint32_t* out) {
+ int i;
+ for (i = 0; i < len; ++i) {
+ out[i] = MakeARGB32(a[4 * i], r[4 * i], g[4 * i], b[4 * i]);
+ }
+}
+#endif
+
+static void PackRGB_C(const uint8_t* r, const uint8_t* g, const uint8_t* b,
+ int len, int step, uint32_t* out) {
+ int i, offset = 0;
+ for (i = 0; i < len; ++i) {
+ out[i] = MakeARGB32(0xff, r[offset], g[offset], b[offset]);
+ offset += step;
+ }
+}
+
+void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int);
+void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int);
+int (*WebPDispatchAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
+void (*WebPDispatchAlphaToGreen)(const uint8_t*, int, int, int, uint32_t*, int);
+int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
+void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size);
+#ifdef WORDS_BIGENDIAN
+void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r, const uint8_t* g,
+ const uint8_t* b, int, uint32_t*);
+#endif
+void (*WebPPackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
+ int len, int step, uint32_t* out);
+
+int (*WebPHasAlpha8b)(const uint8_t* src, int length);
+int (*WebPHasAlpha32b)(const uint8_t* src, int length);
+
+//------------------------------------------------------------------------------
+// Init function
+
+extern void WebPInitAlphaProcessingMIPSdspR2(void);
+extern void WebPInitAlphaProcessingSSE2(void);
+extern void WebPInitAlphaProcessingSSE41(void);
+extern void WebPInitAlphaProcessingNEON(void);
+
+WEBP_DSP_INIT_FUNC(WebPInitAlphaProcessing) {
+ WebPMultARGBRow = WebPMultARGBRow_C;
+ WebPMultRow = WebPMultRow_C;
+ WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply_16b_C;
+
+#ifdef WORDS_BIGENDIAN
+ WebPPackARGB = PackARGB_C;
+#endif
+ WebPPackRGB = PackRGB_C;
+#if !WEBP_NEON_OMIT_C_CODE
+ WebPApplyAlphaMultiply = ApplyAlphaMultiply_C;
+ WebPDispatchAlpha = DispatchAlpha_C;
+ WebPDispatchAlphaToGreen = DispatchAlphaToGreen_C;
+ WebPExtractAlpha = ExtractAlpha_C;
+ WebPExtractGreen = ExtractGreen_C;
+#endif
+
+ WebPHasAlpha8b = HasAlpha8b_C;
+ WebPHasAlpha32b = HasAlpha32b_C;
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ WebPInitAlphaProcessingSSE2();
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitAlphaProcessingSSE41();
+ }
+#endif
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ WebPInitAlphaProcessingMIPSdspR2();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ WebPInitAlphaProcessingNEON();
+ }
+#endif
+
+ assert(WebPMultARGBRow != NULL);
+ assert(WebPMultRow != NULL);
+ assert(WebPApplyAlphaMultiply != NULL);
+ assert(WebPApplyAlphaMultiply4444 != NULL);
+ assert(WebPDispatchAlpha != NULL);
+ assert(WebPDispatchAlphaToGreen != NULL);
+ assert(WebPExtractAlpha != NULL);
+ assert(WebPExtractGreen != NULL);
+#ifdef WORDS_BIGENDIAN
+ assert(WebPPackARGB != NULL);
+#endif
+ assert(WebPPackRGB != NULL);
+ assert(WebPHasAlpha8b != NULL);
+ assert(WebPHasAlpha32b != NULL);
+}
diff --git a/src/third_party/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c
new file mode 100644
index 0000000..0090e87
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/alpha_processing_mips_dsp_r2.c
@@ -0,0 +1,228 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for processing transparent channel.
+//
+// Author(s): Branimir Vasic (branimir.vasic@imgtec.com)
+// Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+static int DispatchAlpha_MIPSdspR2(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint8_t* dst, int dst_stride) {
+ uint32_t alpha_mask = 0xffffffff;
+ int i, j, temp0;
+
+ for (j = 0; j < height; ++j) {
+ uint8_t* pdst = dst;
+ const uint8_t* palpha = alpha;
+ for (i = 0; i < (width >> 2); ++i) {
+ int temp1, temp2, temp3;
+
+ __asm__ volatile (
+ "ulw %[temp0], 0(%[palpha]) \n\t"
+ "addiu %[palpha], %[palpha], 4 \n\t"
+ "addiu %[pdst], %[pdst], 16 \n\t"
+ "srl %[temp1], %[temp0], 8 \n\t"
+ "srl %[temp2], %[temp0], 16 \n\t"
+ "srl %[temp3], %[temp0], 24 \n\t"
+ "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
+ "sb %[temp0], -16(%[pdst]) \n\t"
+ "sb %[temp1], -12(%[pdst]) \n\t"
+ "sb %[temp2], -8(%[pdst]) \n\t"
+ "sb %[temp3], -4(%[pdst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [palpha]"+r"(palpha), [pdst]"+r"(pdst),
+ [alpha_mask]"+r"(alpha_mask)
+ :
+ : "memory"
+ );
+ }
+
+ for (i = 0; i < (width & 3); ++i) {
+ __asm__ volatile (
+ "lbu %[temp0], 0(%[palpha]) \n\t"
+ "addiu %[palpha], %[palpha], 1 \n\t"
+ "sb %[temp0], 0(%[pdst]) \n\t"
+ "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
+ "addiu %[pdst], %[pdst], 4 \n\t"
+ : [temp0]"=&r"(temp0), [palpha]"+r"(palpha), [pdst]"+r"(pdst),
+ [alpha_mask]"+r"(alpha_mask)
+ :
+ : "memory"
+ );
+ }
+ alpha += alpha_stride;
+ dst += dst_stride;
+ }
+
+ __asm__ volatile (
+ "ext %[temp0], %[alpha_mask], 0, 16 \n\t"
+ "srl %[alpha_mask], %[alpha_mask], 16 \n\t"
+ "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
+ "ext %[temp0], %[alpha_mask], 0, 8 \n\t"
+ "srl %[alpha_mask], %[alpha_mask], 8 \n\t"
+ "and %[alpha_mask], %[alpha_mask], %[temp0] \n\t"
+ : [temp0]"=&r"(temp0), [alpha_mask]"+r"(alpha_mask)
+ :
+ );
+
+ return (alpha_mask != 0xff);
+}
+
+static void MultARGBRow_MIPSdspR2(uint32_t* const ptr, int width,
+ int inverse) {
+ int x;
+ const uint32_t c_00ffffff = 0x00ffffffu;
+ const uint32_t c_ff000000 = 0xff000000u;
+ const uint32_t c_8000000 = 0x00800000u;
+ const uint32_t c_8000080 = 0x00800080u;
+ for (x = 0; x < width; ++x) {
+ const uint32_t argb = ptr[x];
+ if (argb < 0xff000000u) { // alpha < 255
+ if (argb <= 0x00ffffffu) { // alpha == 0
+ ptr[x] = 0;
+ } else {
+ int temp0, temp1, temp2, temp3, alpha;
+ __asm__ volatile (
+ "srl %[alpha], %[argb], 24 \n\t"
+ "replv.qb %[temp0], %[alpha] \n\t"
+ "and %[temp0], %[temp0], %[c_00ffffff] \n\t"
+ "beqz %[inverse], 0f \n\t"
+ "divu $zero, %[c_ff000000], %[alpha] \n\t"
+ "mflo %[temp0] \n\t"
+ "0: \n\t"
+ "andi %[temp1], %[argb], 0xff \n\t"
+ "ext %[temp2], %[argb], 8, 8 \n\t"
+ "ext %[temp3], %[argb], 16, 8 \n\t"
+ "mul %[temp1], %[temp1], %[temp0] \n\t"
+ "mul %[temp2], %[temp2], %[temp0] \n\t"
+ "mul %[temp3], %[temp3], %[temp0] \n\t"
+ "precrq.ph.w %[temp1], %[temp2], %[temp1] \n\t"
+ "addu %[temp3], %[temp3], %[c_8000000] \n\t"
+ "addu %[temp1], %[temp1], %[c_8000080] \n\t"
+ "precrq.ph.w %[temp3], %[argb], %[temp3] \n\t"
+ "precrq.qb.ph %[temp1], %[temp3], %[temp1] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [alpha]"=&r"(alpha)
+ : [inverse]"r"(inverse), [c_00ffffff]"r"(c_00ffffff),
+ [c_8000000]"r"(c_8000000), [c_8000080]"r"(c_8000080),
+ [c_ff000000]"r"(c_ff000000), [argb]"r"(argb)
+ : "memory", "hi", "lo"
+ );
+ ptr[x] = temp1;
+ }
+ }
+ }
+}
+
+#ifdef WORDS_BIGENDIAN
+static void PackARGB_MIPSdspR2(const uint8_t* a, const uint8_t* r,
+ const uint8_t* g, const uint8_t* b, int len,
+ uint32_t* out) {
+ int temp0, temp1, temp2, temp3, offset;
+ const int rest = len & 1;
+ const uint32_t* const loop_end = out + len - rest;
+ const int step = 4;
+ __asm__ volatile (
+ "xor %[offset], %[offset], %[offset] \n\t"
+ "beq %[loop_end], %[out], 0f \n\t"
+ "2: \n\t"
+ "lbux %[temp0], %[offset](%[a]) \n\t"
+ "lbux %[temp1], %[offset](%[r]) \n\t"
+ "lbux %[temp2], %[offset](%[g]) \n\t"
+ "lbux %[temp3], %[offset](%[b]) \n\t"
+ "ins %[temp1], %[temp0], 16, 16 \n\t"
+ "ins %[temp3], %[temp2], 16, 16 \n\t"
+ "addiu %[out], %[out], 4 \n\t"
+ "precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
+ "sw %[temp0], -4(%[out]) \n\t"
+ "addu %[offset], %[offset], %[step] \n\t"
+ "bne %[loop_end], %[out], 2b \n\t"
+ "0: \n\t"
+ "beq %[rest], $zero, 1f \n\t"
+ "lbux %[temp0], %[offset](%[a]) \n\t"
+ "lbux %[temp1], %[offset](%[r]) \n\t"
+ "lbux %[temp2], %[offset](%[g]) \n\t"
+ "lbux %[temp3], %[offset](%[b]) \n\t"
+ "ins %[temp1], %[temp0], 16, 16 \n\t"
+ "ins %[temp3], %[temp2], 16, 16 \n\t"
+ "precr.qb.ph %[temp0], %[temp1], %[temp3] \n\t"
+ "sw %[temp0], 0(%[out]) \n\t"
+ "1: \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [offset]"=&r"(offset), [out]"+&r"(out)
+ : [a]"r"(a), [r]"r"(r), [g]"r"(g), [b]"r"(b), [step]"r"(step),
+ [loop_end]"r"(loop_end), [rest]"r"(rest)
+ : "memory"
+ );
+}
+#endif // WORDS_BIGENDIAN
+
+static void PackRGB_MIPSdspR2(const uint8_t* r, const uint8_t* g,
+ const uint8_t* b, int len, int step,
+ uint32_t* out) {
+ int temp0, temp1, temp2, offset;
+ const int rest = len & 1;
+ const int a = 0xff;
+ const uint32_t* const loop_end = out + len - rest;
+ __asm__ volatile (
+ "xor %[offset], %[offset], %[offset] \n\t"
+ "beq %[loop_end], %[out], 0f \n\t"
+ "2: \n\t"
+ "lbux %[temp0], %[offset](%[r]) \n\t"
+ "lbux %[temp1], %[offset](%[g]) \n\t"
+ "lbux %[temp2], %[offset](%[b]) \n\t"
+ "ins %[temp0], %[a], 16, 16 \n\t"
+ "ins %[temp2], %[temp1], 16, 16 \n\t"
+ "addiu %[out], %[out], 4 \n\t"
+ "precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t"
+ "sw %[temp0], -4(%[out]) \n\t"
+ "addu %[offset], %[offset], %[step] \n\t"
+ "bne %[loop_end], %[out], 2b \n\t"
+ "0: \n\t"
+ "beq %[rest], $zero, 1f \n\t"
+ "lbux %[temp0], %[offset](%[r]) \n\t"
+ "lbux %[temp1], %[offset](%[g]) \n\t"
+ "lbux %[temp2], %[offset](%[b]) \n\t"
+ "ins %[temp0], %[a], 16, 16 \n\t"
+ "ins %[temp2], %[temp1], 16, 16 \n\t"
+ "precr.qb.ph %[temp0], %[temp0], %[temp2] \n\t"
+ "sw %[temp0], 0(%[out]) \n\t"
+ "1: \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [offset]"=&r"(offset), [out]"+&r"(out)
+ : [a]"r"(a), [r]"r"(r), [g]"r"(g), [b]"r"(b), [step]"r"(step),
+ [loop_end]"r"(loop_end), [rest]"r"(rest)
+ : "memory"
+ );
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitAlphaProcessingMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingMIPSdspR2(void) {
+ WebPDispatchAlpha = DispatchAlpha_MIPSdspR2;
+ WebPMultARGBRow = MultARGBRow_MIPSdspR2;
+#ifdef WORDS_BIGENDIAN
+ WebPPackARGB = PackARGB_MIPSdspR2;
+#endif
+ WebPPackRGB = PackRGB_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/alpha_processing_neon.c b/src/third_party/libwebp/src/dsp/alpha_processing_neon.c
new file mode 100644
index 0000000..9d55421
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/alpha_processing_neon.c
@@ -0,0 +1,191 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for processing transparent channel, NEON version.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON)
+
+#include "src/dsp/neon.h"
+
+//------------------------------------------------------------------------------
+
+#define MULTIPLIER(a) ((a) * 0x8081)
+#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
+
+#define MULTIPLY_BY_ALPHA(V, ALPHA, OTHER) do { \
+ const uint8x8_t alpha = (V).val[(ALPHA)]; \
+ const uint16x8_t r1 = vmull_u8((V).val[1], alpha); \
+ const uint16x8_t g1 = vmull_u8((V).val[2], alpha); \
+ const uint16x8_t b1 = vmull_u8((V).val[(OTHER)], alpha); \
+ /* we use: v / 255 = (v + 1 + (v >> 8)) >> 8 */ \
+ const uint16x8_t r2 = vsraq_n_u16(r1, r1, 8); \
+ const uint16x8_t g2 = vsraq_n_u16(g1, g1, 8); \
+ const uint16x8_t b2 = vsraq_n_u16(b1, b1, 8); \
+ const uint16x8_t r3 = vaddq_u16(r2, kOne); \
+ const uint16x8_t g3 = vaddq_u16(g2, kOne); \
+ const uint16x8_t b3 = vaddq_u16(b2, kOne); \
+ (V).val[1] = vshrn_n_u16(r3, 8); \
+ (V).val[2] = vshrn_n_u16(g3, 8); \
+ (V).val[(OTHER)] = vshrn_n_u16(b3, 8); \
+} while (0)
+
+static void ApplyAlphaMultiply_NEON(uint8_t* rgba, int alpha_first,
+ int w, int h, int stride) {
+ const uint16x8_t kOne = vdupq_n_u16(1u);
+ while (h-- > 0) {
+ uint32_t* const rgbx = (uint32_t*)rgba;
+ int i = 0;
+ if (alpha_first) {
+ for (; i + 8 <= w; i += 8) {
+ // load aaaa...|rrrr...|gggg...|bbbb...
+ uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i));
+ MULTIPLY_BY_ALPHA(RGBX, 0, 3);
+ vst4_u8((uint8_t*)(rgbx + i), RGBX);
+ }
+ } else {
+ for (; i + 8 <= w; i += 8) {
+ uint8x8x4_t RGBX = vld4_u8((const uint8_t*)(rgbx + i));
+ MULTIPLY_BY_ALPHA(RGBX, 3, 0);
+ vst4_u8((uint8_t*)(rgbx + i), RGBX);
+ }
+ }
+ // Finish with left-overs.
+ for (; i < w; ++i) {
+ uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
+ const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
+ const uint32_t a = alpha[4 * i];
+ if (a != 0xff) {
+ const uint32_t mult = MULTIPLIER(a);
+ rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
+ rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
+ rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
+ }
+ }
+ rgba += stride;
+ }
+}
+#undef MULTIPLY_BY_ALPHA
+#undef MULTIPLIER
+#undef PREMULTIPLY
+
+//------------------------------------------------------------------------------
+
+static int DispatchAlpha_NEON(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint8_t* dst, int dst_stride) {
+ uint32_t alpha_mask = 0xffffffffu;
+ uint8x8_t mask8 = vdup_n_u8(0xff);
+ uint32_t tmp[2];
+ int i, j;
+ for (j = 0; j < height; ++j) {
+ // We don't know if alpha is first or last in dst[] (depending on rgbA/Argb
+ // mode). So we must be sure dst[4*i + 8 - 1] is writable for the store.
+ // Hence the test with 'width - 1' instead of just 'width'.
+ for (i = 0; i + 8 <= width - 1; i += 8) {
+ uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(dst + 4 * i));
+ const uint8x8_t alphas = vld1_u8(alpha + i);
+ rgbX.val[0] = alphas;
+ vst4_u8((uint8_t*)(dst + 4 * i), rgbX);
+ mask8 = vand_u8(mask8, alphas);
+ }
+ for (; i < width; ++i) {
+ const uint32_t alpha_value = alpha[i];
+ dst[4 * i] = alpha_value;
+ alpha_mask &= alpha_value;
+ }
+ alpha += alpha_stride;
+ dst += dst_stride;
+ }
+ vst1_u8((uint8_t*)tmp, mask8);
+ alpha_mask &= tmp[0];
+ alpha_mask &= tmp[1];
+ return (alpha_mask != 0xffffffffu);
+}
+
+static void DispatchAlphaToGreen_NEON(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint32_t* dst, int dst_stride) {
+ int i, j;
+ uint8x8x4_t greens; // leave A/R/B channels zero'd.
+ greens.val[0] = vdup_n_u8(0);
+ greens.val[2] = vdup_n_u8(0);
+ greens.val[3] = vdup_n_u8(0);
+ for (j = 0; j < height; ++j) {
+ for (i = 0; i + 8 <= width; i += 8) {
+ greens.val[1] = vld1_u8(alpha + i);
+ vst4_u8((uint8_t*)(dst + i), greens);
+ }
+ for (; i < width; ++i) dst[i] = alpha[i] << 8;
+ alpha += alpha_stride;
+ dst += dst_stride;
+ }
+}
+
+static int ExtractAlpha_NEON(const uint8_t* argb, int argb_stride,
+ int width, int height,
+ uint8_t* alpha, int alpha_stride) {
+ uint32_t alpha_mask = 0xffffffffu;
+ uint8x8_t mask8 = vdup_n_u8(0xff);
+ uint32_t tmp[2];
+ int i, j;
+ for (j = 0; j < height; ++j) {
+ // We don't know if alpha is first or last in dst[] (depending on rgbA/Argb
+ // mode). So we must be sure dst[4*i + 8 - 1] is writable for the store.
+ // Hence the test with 'width - 1' instead of just 'width'.
+ for (i = 0; i + 8 <= width - 1; i += 8) {
+ const uint8x8x4_t rgbX = vld4_u8((const uint8_t*)(argb + 4 * i));
+ const uint8x8_t alphas = rgbX.val[0];
+ vst1_u8((uint8_t*)(alpha + i), alphas);
+ mask8 = vand_u8(mask8, alphas);
+ }
+ for (; i < width; ++i) {
+ alpha[i] = argb[4 * i];
+ alpha_mask &= alpha[i];
+ }
+ argb += argb_stride;
+ alpha += alpha_stride;
+ }
+ vst1_u8((uint8_t*)tmp, mask8);
+ alpha_mask &= tmp[0];
+ alpha_mask &= tmp[1];
+ return (alpha_mask == 0xffffffffu);
+}
+
+static void ExtractGreen_NEON(const uint32_t* argb,
+ uint8_t* alpha, int size) {
+ int i;
+ for (i = 0; i + 16 <= size; i += 16) {
+ const uint8x16x4_t rgbX = vld4q_u8((const uint8_t*)(argb + i));
+ const uint8x16_t greens = rgbX.val[1];
+ vst1q_u8(alpha + i, greens);
+ }
+ for (; i < size; ++i) alpha[i] = (argb[i] >> 8) & 0xff;
+}
+
+//------------------------------------------------------------------------------
+
+extern void WebPInitAlphaProcessingNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingNEON(void) {
+ WebPApplyAlphaMultiply = ApplyAlphaMultiply_NEON;
+ WebPDispatchAlpha = DispatchAlpha_NEON;
+ WebPDispatchAlphaToGreen = DispatchAlphaToGreen_NEON;
+ WebPExtractAlpha = ExtractAlpha_NEON;
+ WebPExtractGreen = ExtractGreen_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/alpha_processing_sse2.c b/src/third_party/libwebp/src/dsp/alpha_processing_sse2.c
new file mode 100644
index 0000000..7658700
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/alpha_processing_sse2.c
@@ -0,0 +1,343 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for processing transparent channel.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
+
+//------------------------------------------------------------------------------
+
+static int DispatchAlpha_SSE2(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint8_t* dst, int dst_stride) {
+ // alpha_and stores an 'and' operation of all the alpha[] values. The final
+ // value is not 0xff if any of the alpha[] is not equal to 0xff.
+ uint32_t alpha_and = 0xff;
+ int i, j;
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i rgb_mask = _mm_set1_epi32(0xffffff00u); // to preserve RGB
+ const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
+ __m128i all_alphas = all_0xff;
+
+ // We must be able to access 3 extra bytes after the last written byte
+ // 'dst[4 * width - 4]', because we don't know if alpha is the first or the
+ // last byte of the quadruplet.
+ const int limit = (width - 1) & ~7;
+
+ for (j = 0; j < height; ++j) {
+ __m128i* out = (__m128i*)dst;
+ for (i = 0; i < limit; i += 8) {
+ // load 8 alpha bytes
+ const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[i]);
+ const __m128i a1 = _mm_unpacklo_epi8(a0, zero);
+ const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero);
+ const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero);
+ // load 8 dst pixels (32 bytes)
+ const __m128i b0_lo = _mm_loadu_si128(out + 0);
+ const __m128i b0_hi = _mm_loadu_si128(out + 1);
+ // mask dst alpha values
+ const __m128i b1_lo = _mm_and_si128(b0_lo, rgb_mask);
+ const __m128i b1_hi = _mm_and_si128(b0_hi, rgb_mask);
+ // combine
+ const __m128i b2_lo = _mm_or_si128(b1_lo, a2_lo);
+ const __m128i b2_hi = _mm_or_si128(b1_hi, a2_hi);
+ // store
+ _mm_storeu_si128(out + 0, b2_lo);
+ _mm_storeu_si128(out + 1, b2_hi);
+ // accumulate eight alpha 'and' in parallel
+ all_alphas = _mm_and_si128(all_alphas, a0);
+ out += 2;
+ }
+ for (; i < width; ++i) {
+ const uint32_t alpha_value = alpha[i];
+ dst[4 * i] = alpha_value;
+ alpha_and &= alpha_value;
+ }
+ alpha += alpha_stride;
+ dst += dst_stride;
+ }
+ // Combine the eight alpha 'and' into a 8-bit mask.
+ alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));
+ return (alpha_and != 0xff);
+}
+
+static void DispatchAlphaToGreen_SSE2(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint32_t* dst, int dst_stride) {
+ int i, j;
+ const __m128i zero = _mm_setzero_si128();
+ const int limit = width & ~15;
+ for (j = 0; j < height; ++j) {
+ for (i = 0; i < limit; i += 16) { // process 16 alpha bytes
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)&alpha[i]);
+ const __m128i a1 = _mm_unpacklo_epi8(zero, a0); // note the 'zero' first!
+ const __m128i b1 = _mm_unpackhi_epi8(zero, a0);
+ const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero);
+ const __m128i b2_lo = _mm_unpacklo_epi16(b1, zero);
+ const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero);
+ const __m128i b2_hi = _mm_unpackhi_epi16(b1, zero);
+ _mm_storeu_si128((__m128i*)&dst[i + 0], a2_lo);
+ _mm_storeu_si128((__m128i*)&dst[i + 4], a2_hi);
+ _mm_storeu_si128((__m128i*)&dst[i + 8], b2_lo);
+ _mm_storeu_si128((__m128i*)&dst[i + 12], b2_hi);
+ }
+ for (; i < width; ++i) dst[i] = alpha[i] << 8;
+ alpha += alpha_stride;
+ dst += dst_stride;
+ }
+}
+
+static int ExtractAlpha_SSE2(const uint8_t* argb, int argb_stride,
+ int width, int height,
+ uint8_t* alpha, int alpha_stride) {
+ // alpha_and stores an 'and' operation of all the alpha[] values. The final
+ // value is not 0xff if any of the alpha[] is not equal to 0xff.
+ uint32_t alpha_and = 0xff;
+ int i, j;
+ const __m128i a_mask = _mm_set1_epi32(0xffu); // to preserve alpha
+ const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
+ __m128i all_alphas = all_0xff;
+
+ // We must be able to access 3 extra bytes after the last written byte
+ // 'src[4 * width - 4]', because we don't know if alpha is the first or the
+ // last byte of the quadruplet.
+ const int limit = (width - 1) & ~7;
+
+ for (j = 0; j < height; ++j) {
+ const __m128i* src = (const __m128i*)argb;
+ for (i = 0; i < limit; i += 8) {
+ // load 32 argb bytes
+ const __m128i a0 = _mm_loadu_si128(src + 0);
+ const __m128i a1 = _mm_loadu_si128(src + 1);
+ const __m128i b0 = _mm_and_si128(a0, a_mask);
+ const __m128i b1 = _mm_and_si128(a1, a_mask);
+ const __m128i c0 = _mm_packs_epi32(b0, b1);
+ const __m128i d0 = _mm_packus_epi16(c0, c0);
+ // store
+ _mm_storel_epi64((__m128i*)&alpha[i], d0);
+ // accumulate eight alpha 'and' in parallel
+ all_alphas = _mm_and_si128(all_alphas, d0);
+ src += 2;
+ }
+ for (; i < width; ++i) {
+ const uint32_t alpha_value = argb[4 * i];
+ alpha[i] = alpha_value;
+ alpha_and &= alpha_value;
+ }
+ argb += argb_stride;
+ alpha += alpha_stride;
+ }
+ // Combine the eight alpha 'and' into a 8-bit mask.
+ alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));
+ return (alpha_and == 0xff);
+}
+
+//------------------------------------------------------------------------------
+// Non-dither premultiplied modes
+
+#define MULTIPLIER(a) ((a) * 0x8081)
+#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
+
+// We can't use a 'const int' for the SHUFFLE value, because it has to be an
+// immediate in the _mm_shufflexx_epi16() instruction. We really need a macro.
+// We use: v / 255 = (v * 0x8081) >> 23, where v = alpha * {r,g,b} is a 16bit
+// value.
+#define APPLY_ALPHA(RGBX, SHUFFLE) do { \
+ const __m128i argb0 = _mm_loadu_si128((const __m128i*)&(RGBX)); \
+ const __m128i argb1_lo = _mm_unpacklo_epi8(argb0, zero); \
+ const __m128i argb1_hi = _mm_unpackhi_epi8(argb0, zero); \
+ const __m128i alpha0_lo = _mm_or_si128(argb1_lo, kMask); \
+ const __m128i alpha0_hi = _mm_or_si128(argb1_hi, kMask); \
+ const __m128i alpha1_lo = _mm_shufflelo_epi16(alpha0_lo, SHUFFLE); \
+ const __m128i alpha1_hi = _mm_shufflelo_epi16(alpha0_hi, SHUFFLE); \
+ const __m128i alpha2_lo = _mm_shufflehi_epi16(alpha1_lo, SHUFFLE); \
+ const __m128i alpha2_hi = _mm_shufflehi_epi16(alpha1_hi, SHUFFLE); \
+ /* alpha2 = [ff a0 a0 a0][ff a1 a1 a1] */ \
+ const __m128i A0_lo = _mm_mullo_epi16(alpha2_lo, argb1_lo); \
+ const __m128i A0_hi = _mm_mullo_epi16(alpha2_hi, argb1_hi); \
+ const __m128i A1_lo = _mm_mulhi_epu16(A0_lo, kMult); \
+ const __m128i A1_hi = _mm_mulhi_epu16(A0_hi, kMult); \
+ const __m128i A2_lo = _mm_srli_epi16(A1_lo, 7); \
+ const __m128i A2_hi = _mm_srli_epi16(A1_hi, 7); \
+ const __m128i A3 = _mm_packus_epi16(A2_lo, A2_hi); \
+ _mm_storeu_si128((__m128i*)&(RGBX), A3); \
+} while (0)
+
+static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,
+ int w, int h, int stride) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i kMult = _mm_set1_epi16(0x8081u);
+ const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0);
+ const int kSpan = 4;
+ while (h-- > 0) {
+ uint32_t* const rgbx = (uint32_t*)rgba;
+ int i;
+ if (!alpha_first) {
+ for (i = 0; i + kSpan <= w; i += kSpan) {
+ APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(2, 3, 3, 3));
+ }
+ } else {
+ for (i = 0; i + kSpan <= w; i += kSpan) {
+ APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 0, 0, 1));
+ }
+ }
+ // Finish with left-overs.
+ for (; i < w; ++i) {
+ uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
+ const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
+ const uint32_t a = alpha[4 * i];
+ if (a != 0xff) {
+ const uint32_t mult = MULTIPLIER(a);
+ rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
+ rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
+ rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
+ }
+ }
+ rgba += stride;
+ }
+}
+#undef MULTIPLIER
+#undef PREMULTIPLY
+
+//------------------------------------------------------------------------------
+// Alpha detection
+
+static int HasAlpha8b_SSE2(const uint8_t* src, int length) {
+ const __m128i all_0xff = _mm_set1_epi8(0xff);
+ int i = 0;
+ for (; i + 16 <= length; i += 16) {
+ const __m128i v = _mm_loadu_si128((const __m128i*)(src + i));
+ const __m128i bits = _mm_cmpeq_epi8(v, all_0xff);
+ const int mask = _mm_movemask_epi8(bits);
+ if (mask != 0xffff) return 1;
+ }
+ for (; i < length; ++i) if (src[i] != 0xff) return 1;
+ return 0;
+}
+
+static int HasAlpha32b_SSE2(const uint8_t* src, int length) {
+ const __m128i alpha_mask = _mm_set1_epi32(0xff);
+ const __m128i all_0xff = _mm_set1_epi8(0xff);
+ int i = 0;
+ // We don't know if we can access the last 3 bytes after the last alpha
+ // value 'src[4 * length - 4]' (because we don't know if alpha is the first
+ // or the last byte of the quadruplet). Hence the '-3' protection below.
+ length = length * 4 - 3; // size in bytes
+ for (; i + 64 <= length; i += 64) {
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16));
+ const __m128i a2 = _mm_loadu_si128((const __m128i*)(src + i + 32));
+ const __m128i a3 = _mm_loadu_si128((const __m128i*)(src + i + 48));
+ const __m128i b0 = _mm_and_si128(a0, alpha_mask);
+ const __m128i b1 = _mm_and_si128(a1, alpha_mask);
+ const __m128i b2 = _mm_and_si128(a2, alpha_mask);
+ const __m128i b3 = _mm_and_si128(a3, alpha_mask);
+ const __m128i c0 = _mm_packs_epi32(b0, b1);
+ const __m128i c1 = _mm_packs_epi32(b2, b3);
+ const __m128i d = _mm_packus_epi16(c0, c1);
+ const __m128i bits = _mm_cmpeq_epi8(d, all_0xff);
+ const int mask = _mm_movemask_epi8(bits);
+ if (mask != 0xffff) return 1;
+ }
+ for (; i + 32 <= length; i += 32) {
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16));
+ const __m128i b0 = _mm_and_si128(a0, alpha_mask);
+ const __m128i b1 = _mm_and_si128(a1, alpha_mask);
+ const __m128i c = _mm_packs_epi32(b0, b1);
+ const __m128i d = _mm_packus_epi16(c, c);
+ const __m128i bits = _mm_cmpeq_epi8(d, all_0xff);
+ const int mask = _mm_movemask_epi8(bits);
+ if (mask != 0xffff) return 1;
+ }
+ for (; i <= length; i += 4) if (src[i] != 0xff) return 1;
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+// Apply alpha value to rows
+
+static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) {
+ int x = 0;
+ if (!inverse) {
+ const int kSpan = 2;
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i k128 = _mm_set1_epi16(128);
+ const __m128i kMult = _mm_set1_epi16(0x0101);
+ const __m128i kMask = _mm_set_epi16(0, 0xff, 0, 0, 0, 0xff, 0, 0);
+ for (x = 0; x + kSpan <= width; x += kSpan) {
+ // To compute 'result = (int)(a * x / 255. + .5)', we use:
+ // tmp = a * v + 128, result = (tmp * 0x0101u) >> 16
+ const __m128i A0 = _mm_loadl_epi64((const __m128i*)&ptr[x]);
+ const __m128i A1 = _mm_unpacklo_epi8(A0, zero);
+ const __m128i A2 = _mm_or_si128(A1, kMask);
+ const __m128i A3 = _mm_shufflelo_epi16(A2, _MM_SHUFFLE(2, 3, 3, 3));
+ const __m128i A4 = _mm_shufflehi_epi16(A3, _MM_SHUFFLE(2, 3, 3, 3));
+ // here, A4 = [ff a0 a0 a0][ff a1 a1 a1]
+ const __m128i A5 = _mm_mullo_epi16(A4, A1);
+ const __m128i A6 = _mm_add_epi16(A5, k128);
+ const __m128i A7 = _mm_mulhi_epu16(A6, kMult);
+ const __m128i A10 = _mm_packus_epi16(A7, zero);
+ _mm_storel_epi64((__m128i*)&ptr[x], A10);
+ }
+ }
+ width -= x;
+ if (width > 0) WebPMultARGBRow_C(ptr + x, width, inverse);
+}
+
+static void MultRow_SSE2(uint8_t* const ptr, const uint8_t* const alpha,
+ int width, int inverse) {
+ int x = 0;
+ if (!inverse) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i k128 = _mm_set1_epi16(128);
+ const __m128i kMult = _mm_set1_epi16(0x0101);
+ for (x = 0; x + 8 <= width; x += 8) {
+ const __m128i v0 = _mm_loadl_epi64((__m128i*)&ptr[x]);
+ const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[x]);
+ const __m128i v1 = _mm_unpacklo_epi8(v0, zero);
+ const __m128i a1 = _mm_unpacklo_epi8(a0, zero);
+ const __m128i v2 = _mm_mullo_epi16(v1, a1);
+ const __m128i v3 = _mm_add_epi16(v2, k128);
+ const __m128i v4 = _mm_mulhi_epu16(v3, kMult);
+ const __m128i v5 = _mm_packus_epi16(v4, zero);
+ _mm_storel_epi64((__m128i*)&ptr[x], v5);
+ }
+ }
+ width -= x;
+ if (width > 0) WebPMultRow_C(ptr + x, alpha + x, width, inverse);
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitAlphaProcessingSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) {
+ WebPMultARGBRow = MultARGBRow_SSE2;
+ WebPMultRow = MultRow_SSE2;
+ WebPApplyAlphaMultiply = ApplyAlphaMultiply_SSE2;
+ WebPDispatchAlpha = DispatchAlpha_SSE2;
+ WebPDispatchAlphaToGreen = DispatchAlphaToGreen_SSE2;
+ WebPExtractAlpha = ExtractAlpha_SSE2;
+
+ WebPHasAlpha8b = HasAlpha8b_SSE2;
+ WebPHasAlpha32b = HasAlpha32b_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/alpha_processing_sse41.c b/src/third_party/libwebp/src/dsp/alpha_processing_sse41.c
new file mode 100644
index 0000000..56040f9
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/alpha_processing_sse41.c
@@ -0,0 +1,92 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for processing transparent channel, SSE4.1 variant.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE41)
+
+#include <smmintrin.h>
+
+//------------------------------------------------------------------------------
+
+static int ExtractAlpha_SSE41(const uint8_t* argb, int argb_stride,
+ int width, int height,
+ uint8_t* alpha, int alpha_stride) {
+ // alpha_and stores an 'and' operation of all the alpha[] values. The final
+ // value is not 0xff if any of the alpha[] is not equal to 0xff.
+ uint32_t alpha_and = 0xff;
+ int i, j;
+ const __m128i all_0xff = _mm_set1_epi32(~0u);
+ __m128i all_alphas = all_0xff;
+
+ // We must be able to access 3 extra bytes after the last written byte
+ // 'src[4 * width - 4]', because we don't know if alpha is the first or the
+ // last byte of the quadruplet.
+ const int limit = (width - 1) & ~15;
+ const __m128i kCstAlpha0 = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 12, 8, 4, 0);
+ const __m128i kCstAlpha1 = _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1,
+ 12, 8, 4, 0, -1, -1, -1, -1);
+ const __m128i kCstAlpha2 = _mm_set_epi8(-1, -1, -1, -1, 12, 8, 4, 0,
+ -1, -1, -1, -1, -1, -1, -1, -1);
+ const __m128i kCstAlpha3 = _mm_set_epi8(12, 8, 4, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1);
+ for (j = 0; j < height; ++j) {
+ const __m128i* src = (const __m128i*)argb;
+ for (i = 0; i < limit; i += 16) {
+ // load 64 argb bytes
+ const __m128i a0 = _mm_loadu_si128(src + 0);
+ const __m128i a1 = _mm_loadu_si128(src + 1);
+ const __m128i a2 = _mm_loadu_si128(src + 2);
+ const __m128i a3 = _mm_loadu_si128(src + 3);
+ const __m128i b0 = _mm_shuffle_epi8(a0, kCstAlpha0);
+ const __m128i b1 = _mm_shuffle_epi8(a1, kCstAlpha1);
+ const __m128i b2 = _mm_shuffle_epi8(a2, kCstAlpha2);
+ const __m128i b3 = _mm_shuffle_epi8(a3, kCstAlpha3);
+ const __m128i c0 = _mm_or_si128(b0, b1);
+ const __m128i c1 = _mm_or_si128(b2, b3);
+ const __m128i d0 = _mm_or_si128(c0, c1);
+ // store
+ _mm_storeu_si128((__m128i*)&alpha[i], d0);
+ // accumulate sixteen alpha 'and' in parallel
+ all_alphas = _mm_and_si128(all_alphas, d0);
+ src += 4;
+ }
+ for (; i < width; ++i) {
+ const uint32_t alpha_value = argb[4 * i];
+ alpha[i] = alpha_value;
+ alpha_and &= alpha_value;
+ }
+ argb += argb_stride;
+ alpha += alpha_stride;
+ }
+ // Combine the sixteen alpha 'and' into an 8-bit mask.
+ alpha_and |= 0xff00u; // pretend the upper bits [8..15] were tested ok.
+ alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));
+ return (alpha_and == 0xffffu);
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitAlphaProcessingSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE41(void) {
+ WebPExtractAlpha = ExtractAlpha_SSE41;
+}
+
+#else // !WEBP_USE_SSE41
+
+WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE41)
+
+#endif // WEBP_USE_SSE41
diff --git a/src/third_party/libwebp/src/dsp/common_sse2.h b/src/third_party/libwebp/src/dsp/common_sse2.h
new file mode 100644
index 0000000..e9f1ebf
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/common_sse2.h
@@ -0,0 +1,194 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 code common to several files.
+//
+// Author: Vincent Rabaud (vrabaud@google.com)
+
+#ifndef WEBP_DSP_COMMON_SSE2_H_
+#define WEBP_DSP_COMMON_SSE2_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(WEBP_USE_SSE2)
+
+#include <emmintrin.h>
+
+//------------------------------------------------------------------------------
+// Quite useful macro for debugging. Left here for convenience.
+
+#if 0
+#include <stdio.h>
+static WEBP_INLINE void PrintReg(const __m128i r, const char* const name,
+ int size) {
+ int n;
+ union {
+ __m128i r;
+ uint8_t i8[16];
+ uint16_t i16[8];
+ uint32_t i32[4];
+ uint64_t i64[2];
+ } tmp;
+ tmp.r = r;
+ fprintf(stderr, "%s\t: ", name);
+ if (size == 8) {
+ for (n = 0; n < 16; ++n) fprintf(stderr, "%.2x ", tmp.i8[n]);
+ } else if (size == 16) {
+ for (n = 0; n < 8; ++n) fprintf(stderr, "%.4x ", tmp.i16[n]);
+ } else if (size == 32) {
+ for (n = 0; n < 4; ++n) fprintf(stderr, "%.8x ", tmp.i32[n]);
+ } else {
+ for (n = 0; n < 2; ++n) fprintf(stderr, "%.16lx ", tmp.i64[n]);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+//------------------------------------------------------------------------------
+// Math functions.
+
+// Return the sum of all the 8b in the register.
+static WEBP_INLINE int VP8HorizontalAdd8b(const __m128i* const a) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i sad8x2 = _mm_sad_epu8(*a, zero);
+ // sum the two sads: sad8x2[0:1] + sad8x2[8:9]
+ const __m128i sum = _mm_add_epi32(sad8x2, _mm_shuffle_epi32(sad8x2, 2));
+ return _mm_cvtsi128_si32(sum);
+}
+
+// Transpose two 4x4 16b matrices horizontally stored in registers.
+static WEBP_INLINE void VP8Transpose_2_4x4_16b(
+ const __m128i* const in0, const __m128i* const in1,
+ const __m128i* const in2, const __m128i* const in3, __m128i* const out0,
+ __m128i* const out1, __m128i* const out2, __m128i* const out3) {
+ // Transpose the two 4x4.
+ // a00 a01 a02 a03 b00 b01 b02 b03
+ // a10 a11 a12 a13 b10 b11 b12 b13
+ // a20 a21 a22 a23 b20 b21 b22 b23
+ // a30 a31 a32 a33 b30 b31 b32 b33
+ const __m128i transpose0_0 = _mm_unpacklo_epi16(*in0, *in1);
+ const __m128i transpose0_1 = _mm_unpacklo_epi16(*in2, *in3);
+ const __m128i transpose0_2 = _mm_unpackhi_epi16(*in0, *in1);
+ const __m128i transpose0_3 = _mm_unpackhi_epi16(*in2, *in3);
+ // a00 a10 a01 a11 a02 a12 a03 a13
+ // a20 a30 a21 a31 a22 a32 a23 a33
+ // b00 b10 b01 b11 b02 b12 b03 b13
+ // b20 b30 b21 b31 b22 b32 b23 b33
+ const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
+ const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
+ const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
+ const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // b00 b10 b20 b30 b01 b11 b21 b31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ // b02 b12 a22 b32 b03 b13 b23 b33
+ *out0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
+ *out1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
+ *out2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
+ *out3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
+ // a00 a10 a20 a30 b00 b10 b20 b30
+ // a01 a11 a21 a31 b01 b11 b21 b31
+ // a02 a12 a22 a32 b02 b12 b22 b32
+ // a03 a13 a23 a33 b03 b13 b23 b33
+}
+
+//------------------------------------------------------------------------------
+// Channel mixing.
+
+// Function used several times in VP8PlanarTo24b.
+// It samples the in buffer as follows: one every two unsigned char is stored
+// at the beginning of the buffer, while the other half is stored at the end.
+#define VP8PlanarTo24bHelper(IN, OUT) \
+ do { \
+ const __m128i v_mask = _mm_set1_epi16(0x00ff); \
+ /* Take one every two upper 8b values.*/ \
+ (OUT##0) = _mm_packus_epi16(_mm_and_si128((IN##0), v_mask), \
+ _mm_and_si128((IN##1), v_mask)); \
+ (OUT##1) = _mm_packus_epi16(_mm_and_si128((IN##2), v_mask), \
+ _mm_and_si128((IN##3), v_mask)); \
+ (OUT##2) = _mm_packus_epi16(_mm_and_si128((IN##4), v_mask), \
+ _mm_and_si128((IN##5), v_mask)); \
+ /* Take one every two lower 8b values.*/ \
+ (OUT##3) = _mm_packus_epi16(_mm_srli_epi16((IN##0), 8), \
+ _mm_srli_epi16((IN##1), 8)); \
+ (OUT##4) = _mm_packus_epi16(_mm_srli_epi16((IN##2), 8), \
+ _mm_srli_epi16((IN##3), 8)); \
+ (OUT##5) = _mm_packus_epi16(_mm_srli_epi16((IN##4), 8), \
+ _mm_srli_epi16((IN##5), 8)); \
+ } while (0)
+
+// Pack the planar buffers
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ...
+static WEBP_INLINE void VP8PlanarTo24b_SSE2(
+ __m128i* const in0, __m128i* const in1, __m128i* const in2,
+ __m128i* const in3, __m128i* const in4, __m128i* const in5) {
+ // The input is 6 registers of sixteen 8b but for the sake of explanation,
+ // let's take 6 registers of four 8b values.
+ // To pack, we will keep taking one every two 8b integer and move it
+ // around as follows:
+ // Input:
+ // r0r1r2r3 | r4r5r6r7 | g0g1g2g3 | g4g5g6g7 | b0b1b2b3 | b4b5b6b7
+ // Split the 6 registers in two sets of 3 registers: the first set as the even
+ // 8b bytes, the second the odd ones:
+ // r0r2r4r6 | g0g2g4g6 | b0b2b4b6 | r1r3r5r7 | g1g3g5g7 | b1b3b5b7
+ // Repeat the same permutations twice more:
+ // r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7
+ // r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7
+ __m128i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5;
+ VP8PlanarTo24bHelper(*in, tmp);
+ VP8PlanarTo24bHelper(tmp, *in);
+ VP8PlanarTo24bHelper(*in, tmp);
+ // We need to do it two more times than the example as we have sixteen bytes.
+ {
+ __m128i out0, out1, out2, out3, out4, out5;
+ VP8PlanarTo24bHelper(tmp, out);
+ VP8PlanarTo24bHelper(out, *in);
+ }
+}
+
+#undef VP8PlanarTo24bHelper
+
+// Convert four packed four-channel buffers like argbargbargbargb... into the
+// split channels aaaaa ... rrrr ... gggg .... bbbbb ......
+static WEBP_INLINE void VP8L32bToPlanar_SSE2(__m128i* const in0,
+ __m128i* const in1,
+ __m128i* const in2,
+ __m128i* const in3) {
+ // Column-wise transpose.
+ const __m128i A0 = _mm_unpacklo_epi8(*in0, *in1);
+ const __m128i A1 = _mm_unpackhi_epi8(*in0, *in1);
+ const __m128i A2 = _mm_unpacklo_epi8(*in2, *in3);
+ const __m128i A3 = _mm_unpackhi_epi8(*in2, *in3);
+ const __m128i B0 = _mm_unpacklo_epi8(A0, A1);
+ const __m128i B1 = _mm_unpackhi_epi8(A0, A1);
+ const __m128i B2 = _mm_unpacklo_epi8(A2, A3);
+ const __m128i B3 = _mm_unpackhi_epi8(A2, A3);
+ // C0 = g7 g6 ... g1 g0 | b7 b6 ... b1 b0
+ // C1 = a7 a6 ... a1 a0 | r7 r6 ... r1 r0
+ const __m128i C0 = _mm_unpacklo_epi8(B0, B1);
+ const __m128i C1 = _mm_unpackhi_epi8(B0, B1);
+ const __m128i C2 = _mm_unpacklo_epi8(B2, B3);
+ const __m128i C3 = _mm_unpackhi_epi8(B2, B3);
+ // Gather the channels.
+ *in0 = _mm_unpackhi_epi64(C1, C3);
+ *in1 = _mm_unpacklo_epi64(C1, C3);
+ *in2 = _mm_unpackhi_epi64(C0, C2);
+ *in3 = _mm_unpacklo_epi64(C0, C2);
+}
+
+#endif // WEBP_USE_SSE2
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_DSP_COMMON_SSE2_H_
diff --git a/src/third_party/libwebp/src/dsp/common_sse41.h b/src/third_party/libwebp/src/dsp/common_sse41.h
new file mode 100644
index 0000000..2f173c0
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/common_sse41.h
@@ -0,0 +1,132 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE4 code common to several files.
+//
+// Author: Vincent Rabaud (vrabaud@google.com)
+
+#ifndef WEBP_DSP_COMMON_SSE41_H_
+#define WEBP_DSP_COMMON_SSE41_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(WEBP_USE_SSE41)
+#include <smmintrin.h>
+
+//------------------------------------------------------------------------------
+// Channel mixing.
+// Shuffles the input buffer as A0 0 0 A1 0 0 A2 ...
+#define WEBP_SSE41_SHUFF(OUT, IN0, IN1) \
+ OUT##0 = _mm_shuffle_epi8(*IN0, shuff0); \
+ OUT##1 = _mm_shuffle_epi8(*IN0, shuff1); \
+ OUT##2 = _mm_shuffle_epi8(*IN0, shuff2); \
+ OUT##3 = _mm_shuffle_epi8(*IN1, shuff0); \
+ OUT##4 = _mm_shuffle_epi8(*IN1, shuff1); \
+ OUT##5 = _mm_shuffle_epi8(*IN1, shuff2);
+
+// Pack the planar buffers
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ...
+static WEBP_INLINE void VP8PlanarTo24b_SSE41(
+ __m128i* const in0, __m128i* const in1, __m128i* const in2,
+ __m128i* const in3, __m128i* const in4, __m128i* const in5) {
+ __m128i R0, R1, R2, R3, R4, R5;
+ __m128i G0, G1, G2, G3, G4, G5;
+ __m128i B0, B1, B2, B3, B4, B5;
+
+ // Process R.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ 5, -1, -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, 10, -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ -1, -1, 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1);
+ WEBP_SSE41_SHUFF(R, in0, in1)
+ }
+
+ // Process G.
+ {
+ // Same as before, just shifted to the left by one and including the right
+ // padding.
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1);
+ const __m128i shuff1 = _mm_set_epi8(
+ 10, -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1, 5);
+ const __m128i shuff2 = _mm_set_epi8(
+ -1, 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1, -1);
+ WEBP_SSE41_SHUFF(G, in2, in3)
+ }
+
+ // Process B.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, 4, -1, -1, 3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1, -1);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, 9, -1, -1, 8, -1, -1, 7, -1, -1, 6, -1, -1, 5, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 15, -1, -1, 14, -1, -1, 13, -1, -1, 12, -1, -1, 11, -1, -1, 10);
+ WEBP_SSE41_SHUFF(B, in4, in5)
+ }
+
+ // OR the different channels.
+ {
+ const __m128i RG0 = _mm_or_si128(R0, G0);
+ const __m128i RG1 = _mm_or_si128(R1, G1);
+ const __m128i RG2 = _mm_or_si128(R2, G2);
+ const __m128i RG3 = _mm_or_si128(R3, G3);
+ const __m128i RG4 = _mm_or_si128(R4, G4);
+ const __m128i RG5 = _mm_or_si128(R5, G5);
+ *in0 = _mm_or_si128(RG0, B0);
+ *in1 = _mm_or_si128(RG1, B1);
+ *in2 = _mm_or_si128(RG2, B2);
+ *in3 = _mm_or_si128(RG3, B3);
+ *in4 = _mm_or_si128(RG4, B4);
+ *in5 = _mm_or_si128(RG5, B5);
+ }
+}
+
+#undef WEBP_SSE41_SHUFF
+
+// Convert four packed four-channel buffers like argbargbargbargb... into the
+// split channels aaaaa ... rrrr ... gggg .... bbbbb ......
+static WEBP_INLINE void VP8L32bToPlanar_SSE41(__m128i* const in0,
+ __m128i* const in1,
+ __m128i* const in2,
+ __m128i* const in3) {
+ // aaaarrrrggggbbbb
+ const __m128i shuff0 =
+ _mm_set_epi8(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0);
+ const __m128i A0 = _mm_shuffle_epi8(*in0, shuff0);
+ const __m128i A1 = _mm_shuffle_epi8(*in1, shuff0);
+ const __m128i A2 = _mm_shuffle_epi8(*in2, shuff0);
+ const __m128i A3 = _mm_shuffle_epi8(*in3, shuff0);
+ // A0A1R0R1
+ // G0G1B0B1
+ // A2A3R2R3
+ // G0G1B0B1
+ const __m128i B0 = _mm_unpacklo_epi32(A0, A1);
+ const __m128i B1 = _mm_unpackhi_epi32(A0, A1);
+ const __m128i B2 = _mm_unpacklo_epi32(A2, A3);
+ const __m128i B3 = _mm_unpackhi_epi32(A2, A3);
+ *in3 = _mm_unpacklo_epi64(B0, B2);
+ *in2 = _mm_unpackhi_epi64(B0, B2);
+ *in1 = _mm_unpacklo_epi64(B1, B3);
+ *in0 = _mm_unpackhi_epi64(B1, B3);
+}
+
+#endif // WEBP_USE_SSE41
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_DSP_COMMON_SSE41_H_
diff --git a/src/third_party/libwebp/src/dsp/cost.c b/src/third_party/libwebp/src/dsp/cost.c
new file mode 100644
index 0000000..48fb3db
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/cost.c
@@ -0,0 +1,410 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#endif
+
+#include "src/dsp/dsp.h"
+#include "src/enc/cost_enc.h"
+
+//------------------------------------------------------------------------------
+// Boolean-cost cost table
+
+const uint16_t VP8EntropyCost[256] = {
+ 1792, 1792, 1792, 1536, 1536, 1408, 1366, 1280, 1280, 1216,
+ 1178, 1152, 1110, 1076, 1061, 1024, 1024, 992, 968, 951,
+ 939, 911, 896, 878, 871, 854, 838, 820, 811, 794,
+ 786, 768, 768, 752, 740, 732, 720, 709, 704, 690,
+ 683, 672, 666, 655, 647, 640, 631, 622, 615, 607,
+ 598, 592, 586, 576, 572, 564, 559, 555, 547, 541,
+ 534, 528, 522, 512, 512, 504, 500, 494, 488, 483,
+ 477, 473, 467, 461, 458, 452, 448, 443, 438, 434,
+ 427, 424, 419, 415, 410, 406, 403, 399, 394, 390,
+ 384, 384, 377, 374, 370, 366, 362, 359, 355, 351,
+ 347, 342, 342, 336, 333, 330, 326, 323, 320, 316,
+ 312, 308, 305, 302, 299, 296, 293, 288, 287, 283,
+ 280, 277, 274, 272, 268, 266, 262, 256, 256, 256,
+ 251, 248, 245, 242, 240, 237, 234, 232, 228, 226,
+ 223, 221, 218, 216, 214, 211, 208, 205, 203, 201,
+ 198, 196, 192, 191, 188, 187, 183, 181, 179, 176,
+ 175, 171, 171, 168, 165, 163, 160, 159, 156, 154,
+ 152, 150, 148, 146, 144, 142, 139, 138, 135, 133,
+ 131, 128, 128, 125, 123, 121, 119, 117, 115, 113,
+ 111, 110, 107, 105, 103, 102, 100, 98, 96, 94,
+ 92, 91, 89, 86, 86, 83, 82, 80, 77, 76,
+ 74, 73, 71, 69, 67, 66, 64, 63, 61, 59,
+ 57, 55, 54, 52, 51, 49, 47, 46, 44, 43,
+ 41, 40, 38, 36, 35, 33, 32, 30, 29, 27,
+ 25, 24, 22, 21, 19, 18, 16, 15, 13, 12,
+ 10, 9, 7, 6, 4, 3
+};
+
+//------------------------------------------------------------------------------
+// Level cost tables
+
+// fixed costs for coding levels, deduce from the coding tree.
+// This is only the part that doesn't depend on the probability state.
+const uint16_t VP8LevelFixedCosts[MAX_LEVEL + 1] = {
+ 0, 256, 256, 256, 256, 432, 618, 630,
+ 731, 640, 640, 828, 901, 948, 1021, 1101,
+ 1174, 1221, 1294, 1042, 1085, 1115, 1158, 1202,
+ 1245, 1275, 1318, 1337, 1380, 1410, 1453, 1497,
+ 1540, 1570, 1613, 1280, 1295, 1317, 1332, 1358,
+ 1373, 1395, 1410, 1454, 1469, 1491, 1506, 1532,
+ 1547, 1569, 1584, 1601, 1616, 1638, 1653, 1679,
+ 1694, 1716, 1731, 1775, 1790, 1812, 1827, 1853,
+ 1868, 1890, 1905, 1727, 1733, 1742, 1748, 1759,
+ 1765, 1774, 1780, 1800, 1806, 1815, 1821, 1832,
+ 1838, 1847, 1853, 1878, 1884, 1893, 1899, 1910,
+ 1916, 1925, 1931, 1951, 1957, 1966, 1972, 1983,
+ 1989, 1998, 2004, 2027, 2033, 2042, 2048, 2059,
+ 2065, 2074, 2080, 2100, 2106, 2115, 2121, 2132,
+ 2138, 2147, 2153, 2178, 2184, 2193, 2199, 2210,
+ 2216, 2225, 2231, 2251, 2257, 2266, 2272, 2283,
+ 2289, 2298, 2304, 2168, 2174, 2183, 2189, 2200,
+ 2206, 2215, 2221, 2241, 2247, 2256, 2262, 2273,
+ 2279, 2288, 2294, 2319, 2325, 2334, 2340, 2351,
+ 2357, 2366, 2372, 2392, 2398, 2407, 2413, 2424,
+ 2430, 2439, 2445, 2468, 2474, 2483, 2489, 2500,
+ 2506, 2515, 2521, 2541, 2547, 2556, 2562, 2573,
+ 2579, 2588, 2594, 2619, 2625, 2634, 2640, 2651,
+ 2657, 2666, 2672, 2692, 2698, 2707, 2713, 2724,
+ 2730, 2739, 2745, 2540, 2546, 2555, 2561, 2572,
+ 2578, 2587, 2593, 2613, 2619, 2628, 2634, 2645,
+ 2651, 2660, 2666, 2691, 2697, 2706, 2712, 2723,
+ 2729, 2738, 2744, 2764, 2770, 2779, 2785, 2796,
+ 2802, 2811, 2817, 2840, 2846, 2855, 2861, 2872,
+ 2878, 2887, 2893, 2913, 2919, 2928, 2934, 2945,
+ 2951, 2960, 2966, 2991, 2997, 3006, 3012, 3023,
+ 3029, 3038, 3044, 3064, 3070, 3079, 3085, 3096,
+ 3102, 3111, 3117, 2981, 2987, 2996, 3002, 3013,
+ 3019, 3028, 3034, 3054, 3060, 3069, 3075, 3086,
+ 3092, 3101, 3107, 3132, 3138, 3147, 3153, 3164,
+ 3170, 3179, 3185, 3205, 3211, 3220, 3226, 3237,
+ 3243, 3252, 3258, 3281, 3287, 3296, 3302, 3313,
+ 3319, 3328, 3334, 3354, 3360, 3369, 3375, 3386,
+ 3392, 3401, 3407, 3432, 3438, 3447, 3453, 3464,
+ 3470, 3479, 3485, 3505, 3511, 3520, 3526, 3537,
+ 3543, 3552, 3558, 2816, 2822, 2831, 2837, 2848,
+ 2854, 2863, 2869, 2889, 2895, 2904, 2910, 2921,
+ 2927, 2936, 2942, 2967, 2973, 2982, 2988, 2999,
+ 3005, 3014, 3020, 3040, 3046, 3055, 3061, 3072,
+ 3078, 3087, 3093, 3116, 3122, 3131, 3137, 3148,
+ 3154, 3163, 3169, 3189, 3195, 3204, 3210, 3221,
+ 3227, 3236, 3242, 3267, 3273, 3282, 3288, 3299,
+ 3305, 3314, 3320, 3340, 3346, 3355, 3361, 3372,
+ 3378, 3387, 3393, 3257, 3263, 3272, 3278, 3289,
+ 3295, 3304, 3310, 3330, 3336, 3345, 3351, 3362,
+ 3368, 3377, 3383, 3408, 3414, 3423, 3429, 3440,
+ 3446, 3455, 3461, 3481, 3487, 3496, 3502, 3513,
+ 3519, 3528, 3534, 3557, 3563, 3572, 3578, 3589,
+ 3595, 3604, 3610, 3630, 3636, 3645, 3651, 3662,
+ 3668, 3677, 3683, 3708, 3714, 3723, 3729, 3740,
+ 3746, 3755, 3761, 3781, 3787, 3796, 3802, 3813,
+ 3819, 3828, 3834, 3629, 3635, 3644, 3650, 3661,
+ 3667, 3676, 3682, 3702, 3708, 3717, 3723, 3734,
+ 3740, 3749, 3755, 3780, 3786, 3795, 3801, 3812,
+ 3818, 3827, 3833, 3853, 3859, 3868, 3874, 3885,
+ 3891, 3900, 3906, 3929, 3935, 3944, 3950, 3961,
+ 3967, 3976, 3982, 4002, 4008, 4017, 4023, 4034,
+ 4040, 4049, 4055, 4080, 4086, 4095, 4101, 4112,
+ 4118, 4127, 4133, 4153, 4159, 4168, 4174, 4185,
+ 4191, 4200, 4206, 4070, 4076, 4085, 4091, 4102,
+ 4108, 4117, 4123, 4143, 4149, 4158, 4164, 4175,
+ 4181, 4190, 4196, 4221, 4227, 4236, 4242, 4253,
+ 4259, 4268, 4274, 4294, 4300, 4309, 4315, 4326,
+ 4332, 4341, 4347, 4370, 4376, 4385, 4391, 4402,
+ 4408, 4417, 4423, 4443, 4449, 4458, 4464, 4475,
+ 4481, 4490, 4496, 4521, 4527, 4536, 4542, 4553,
+ 4559, 4568, 4574, 4594, 4600, 4609, 4615, 4626,
+ 4632, 4641, 4647, 3515, 3521, 3530, 3536, 3547,
+ 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
+ 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
+ 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
+ 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
+ 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
+ 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
+ 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
+ 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
+ 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
+ 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
+ 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
+ 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
+ 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
+ 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
+ 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
+ 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
+ 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
+ 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
+ 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
+ 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
+ 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
+ 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
+ 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
+ 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
+ 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
+ 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
+ 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
+ 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
+ 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
+ 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
+ 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
+ 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
+ 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
+ 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
+ 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
+ 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
+ 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
+ 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
+ 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
+ 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
+ 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
+ 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
+ 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
+ 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
+ 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
+ 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
+ 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
+ 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
+ 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
+ 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
+ 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
+ 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
+ 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
+ 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
+ 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
+ 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
+ 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
+ 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
+ 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
+ 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
+ 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
+ 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
+ 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
+ 6420, 6429, 6435, 3515, 3521, 3530, 3536, 3547,
+ 3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
+ 3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
+ 3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
+ 3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
+ 3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
+ 3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
+ 4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
+ 4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
+ 3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
+ 4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
+ 4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
+ 4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
+ 4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
+ 4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
+ 4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
+ 4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
+ 4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
+ 4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
+ 4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
+ 4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
+ 4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
+ 4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
+ 4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
+ 4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
+ 4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
+ 4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
+ 4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
+ 5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
+ 5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
+ 5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
+ 5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
+ 5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
+ 4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
+ 4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
+ 4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
+ 4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
+ 4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
+ 5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
+ 5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
+ 5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
+ 5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
+ 5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
+ 5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
+ 5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
+ 5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
+ 5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
+ 5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
+ 5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
+ 5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
+ 5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
+ 5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
+ 5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
+ 5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
+ 5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
+ 5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
+ 5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
+ 5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
+ 5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
+ 6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
+ 6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
+ 6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
+ 6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
+ 6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
+ 6420, 6429, 6435, 5303, 5309, 5318, 5324, 5335,
+ 5341, 5350, 5356, 5376, 5382, 5391, 5397, 5408,
+ 5414, 5423, 5429, 5454, 5460, 5469, 5475, 5486,
+ 5492, 5501, 5507, 5527, 5533, 5542, 5548, 5559,
+ 5565, 5574, 5580, 5603, 5609, 5618, 5624, 5635,
+ 5641, 5650, 5656, 5676, 5682, 5691, 5697, 5708,
+ 5714, 5723, 5729, 5754, 5760, 5769, 5775, 5786,
+ 5792, 5801, 5807, 5827, 5833, 5842, 5848, 5859,
+ 5865, 5874, 5880, 5744, 5750, 5759, 5765, 5776,
+ 5782, 5791, 5797, 5817, 5823, 5832, 5838, 5849,
+ 5855, 5864, 5870, 5895, 5901, 5910, 5916, 5927,
+ 5933, 5942, 5948, 5968, 5974, 5983, 5989, 6000,
+ 6006, 6015, 6021, 6044, 6050, 6059, 6065, 6076,
+ 6082, 6091, 6097, 6117, 6123, 6132, 6138, 6149,
+ 6155, 6164, 6170, 6195, 6201, 6210, 6216, 6227,
+ 6233, 6242, 6248, 6268, 6274, 6283, 6289, 6300,
+ 6306, 6315, 6321, 6116, 6122, 6131, 6137, 6148,
+ 6154, 6163, 6169, 6189, 6195, 6204, 6210, 6221,
+ 6227, 6236, 6242, 6267, 6273, 6282, 6288, 6299,
+ 6305, 6314, 6320, 6340, 6346, 6355, 6361, 6372,
+ 6378, 6387, 6393, 6416, 6422, 6431, 6437, 6448,
+ 6454, 6463, 6469, 6489, 6495, 6504, 6510, 6521,
+ 6527, 6536, 6542, 6567, 6573, 6582, 6588, 6599,
+ 6605, 6614, 6620, 6640, 6646, 6655, 6661, 6672,
+ 6678, 6687, 6693, 6557, 6563, 6572, 6578, 6589,
+ 6595, 6604, 6610, 6630, 6636, 6645, 6651, 6662,
+ 6668, 6677, 6683, 6708, 6714, 6723, 6729, 6740,
+ 6746, 6755, 6761, 6781, 6787, 6796, 6802, 6813,
+ 6819, 6828, 6834, 6857, 6863, 6872, 6878, 6889,
+ 6895, 6904, 6910, 6930, 6936, 6945, 6951, 6962,
+ 6968, 6977, 6983, 7008, 7014, 7023, 7029, 7040,
+ 7046, 7055, 7061, 7081, 7087, 7096, 7102, 7113,
+ 7119, 7128, 7134, 6392, 6398, 6407, 6413, 6424,
+ 6430, 6439, 6445, 6465, 6471, 6480, 6486, 6497,
+ 6503, 6512, 6518, 6543, 6549, 6558, 6564, 6575,
+ 6581, 6590, 6596, 6616, 6622, 6631, 6637, 6648,
+ 6654, 6663, 6669, 6692, 6698, 6707, 6713, 6724,
+ 6730, 6739, 6745, 6765, 6771, 6780, 6786, 6797,
+ 6803, 6812, 6818, 6843, 6849, 6858, 6864, 6875,
+ 6881, 6890, 6896, 6916, 6922, 6931, 6937, 6948,
+ 6954, 6963, 6969, 6833, 6839, 6848, 6854, 6865,
+ 6871, 6880, 6886, 6906, 6912, 6921, 6927, 6938,
+ 6944, 6953, 6959, 6984, 6990, 6999, 7005, 7016,
+ 7022, 7031, 7037, 7057, 7063, 7072, 7078, 7089,
+ 7095, 7104, 7110, 7133, 7139, 7148, 7154, 7165,
+ 7171, 7180, 7186, 7206, 7212, 7221, 7227, 7238,
+ 7244, 7253, 7259, 7284, 7290, 7299, 7305, 7316,
+ 7322, 7331, 7337, 7357, 7363, 7372, 7378, 7389,
+ 7395, 7404, 7410, 7205, 7211, 7220, 7226, 7237,
+ 7243, 7252, 7258, 7278, 7284, 7293, 7299, 7310,
+ 7316, 7325, 7331, 7356, 7362, 7371, 7377, 7388,
+ 7394, 7403, 7409, 7429, 7435, 7444, 7450, 7461,
+ 7467, 7476, 7482, 7505, 7511, 7520, 7526, 7537,
+ 7543, 7552, 7558, 7578, 7584, 7593, 7599, 7610,
+ 7616, 7625, 7631, 7656, 7662, 7671, 7677, 7688,
+ 7694, 7703, 7709, 7729, 7735, 7744, 7750, 7761
+};
+
+//------------------------------------------------------------------------------
+// Tables for level coding
+
+const uint8_t VP8EncBands[16 + 1] = {
+ 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
+ 0 // sentinel
+};
+
+//------------------------------------------------------------------------------
+// Mode costs
+
+static int GetResidualCost_C(int ctx0, const VP8Residual* const res) {
+ int n = res->first;
+ // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ const int p0 = res->prob[n][ctx0][0];
+ CostArrayPtr const costs = res->costs;
+ const uint16_t* t = costs[n][ctx0];
+ // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0
+ // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
+ // be missing during the loop.
+ int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0;
+
+ if (res->last < 0) {
+ return VP8BitCost(0, p0);
+ }
+ for (; n < res->last; ++n) {
+ const int v = abs(res->coeffs[n]);
+ const int ctx = (v >= 2) ? 2 : v;
+ cost += VP8LevelCost(t, v);
+ t = costs[n + 1][ctx];
+ }
+ // Last coefficient is always non-zero
+ {
+ const int v = abs(res->coeffs[n]);
+ assert(v != 0);
+ cost += VP8LevelCost(t, v);
+ if (n < 15) {
+ const int b = VP8EncBands[n + 1];
+ const int ctx = (v == 1) ? 1 : 2;
+ const int last_p0 = res->prob[b][ctx][0];
+ cost += VP8BitCost(0, last_p0);
+ }
+ }
+ return cost;
+}
+
+static void SetResidualCoeffs_C(const int16_t* const coeffs,
+ VP8Residual* const res) {
+ int n;
+ res->last = -1;
+ assert(res->first == 0 || coeffs[0] == 0);
+ for (n = 15; n >= 0; --n) {
+ if (coeffs[n]) {
+ res->last = n;
+ break;
+ }
+ }
+ res->coeffs = coeffs;
+}
+
+//------------------------------------------------------------------------------
+// init function
+
+VP8GetResidualCostFunc VP8GetResidualCost;
+VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
+
+extern void VP8EncDspCostInitMIPS32(void);
+extern void VP8EncDspCostInitMIPSdspR2(void);
+extern void VP8EncDspCostInitSSE2(void);
+
+WEBP_DSP_INIT_FUNC(VP8EncDspCostInit) {
+ VP8GetResidualCost = GetResidualCost_C;
+ VP8SetResidualCoeffs = SetResidualCoeffs_C;
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_MIPS32)
+ if (VP8GetCPUInfo(kMIPS32)) {
+ VP8EncDspCostInitMIPS32();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ VP8EncDspCostInitMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ VP8EncDspCostInitSSE2();
+ }
+#endif
+ }
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dsp/cost_mips32.c b/src/third_party/libwebp/src/dsp/cost_mips32.c
new file mode 100644
index 0000000..efc58e0
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/cost_mips32.c
@@ -0,0 +1,159 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS32)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#endif
+
+#include "src/enc/cost_enc.h"
+
+static int GetResidualCost_MIPS32(int ctx0, const VP8Residual* const res) {
+ int temp0, temp1;
+ int v_reg, ctx_reg;
+ int n = res->first;
+ // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ int p0 = res->prob[n][ctx0][0];
+ CostArrayPtr const costs = res->costs;
+ const uint16_t* t = costs[n][ctx0];
+ // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0
+ // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
+ // be missing during the loop.
+ int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0;
+ const int16_t* res_coeffs = res->coeffs;
+ const int res_last = res->last;
+ const int const_max_level = MAX_VARIABLE_LEVEL;
+ const int const_2 = 2;
+ const uint16_t** p_costs = &costs[n][0];
+ const size_t inc_p_costs = NUM_CTX * sizeof(*p_costs);
+
+ if (res->last < 0) {
+ return VP8BitCost(0, p0);
+ }
+
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "subu %[temp1], %[res_last], %[n] \n\t"
+ "sll %[temp0], %[n], 1 \n\t"
+ "blez %[temp1], 2f \n\t"
+ " addu %[res_coeffs], %[res_coeffs], %[temp0] \n\t"
+ "1: \n\t"
+ "lh %[v_reg], 0(%[res_coeffs]) \n\t"
+ "addiu %[n], %[n], 1 \n\t"
+ "negu %[temp0], %[v_reg] \n\t"
+ "slti %[temp1], %[v_reg], 0 \n\t"
+ "movn %[v_reg], %[temp0], %[temp1] \n\t"
+ "sltiu %[temp0], %[v_reg], 2 \n\t"
+ "move %[ctx_reg], %[v_reg] \n\t"
+ "movz %[ctx_reg], %[const_2], %[temp0] \n\t"
+ "sll %[temp1], %[v_reg], 1 \n\t"
+ "addu %[temp1], %[temp1], %[VP8LevelFixedCosts] \n\t"
+ "lhu %[temp1], 0(%[temp1]) \n\t"
+ "slt %[temp0], %[v_reg], %[const_max_level] \n\t"
+ "movz %[v_reg], %[const_max_level], %[temp0] \n\t"
+ "addu %[cost], %[cost], %[temp1] \n\t"
+ "sll %[v_reg], %[v_reg], 1 \n\t"
+ "sll %[ctx_reg], %[ctx_reg], 2 \n\t"
+ "addu %[v_reg], %[v_reg], %[t] \n\t"
+ "lhu %[temp0], 0(%[v_reg]) \n\t"
+ "addu %[p_costs], %[p_costs], %[inc_p_costs] \n\t"
+ "addu %[t], %[p_costs], %[ctx_reg] \n\t"
+ "addu %[cost], %[cost], %[temp0] \n\t"
+ "addiu %[res_coeffs], %[res_coeffs], 2 \n\t"
+ "bne %[n], %[res_last], 1b \n\t"
+ " lw %[t], 0(%[t]) \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [cost]"+&r"(cost), [t]"+&r"(t), [n]"+&r"(n), [v_reg]"=&r"(v_reg),
+ [ctx_reg]"=&r"(ctx_reg), [p_costs]"+&r"(p_costs), [temp0]"=&r"(temp0),
+ [temp1]"=&r"(temp1), [res_coeffs]"+&r"(res_coeffs)
+ : [const_2]"r"(const_2), [const_max_level]"r"(const_max_level),
+ [VP8LevelFixedCosts]"r"(VP8LevelFixedCosts), [res_last]"r"(res_last),
+ [inc_p_costs]"r"(inc_p_costs)
+ : "memory"
+ );
+
+ // Last coefficient is always non-zero
+ {
+ const int v = abs(res->coeffs[n]);
+ assert(v != 0);
+ cost += VP8LevelCost(t, v);
+ if (n < 15) {
+ const int b = VP8EncBands[n + 1];
+ const int ctx = (v == 1) ? 1 : 2;
+ const int last_p0 = res->prob[b][ctx][0];
+ cost += VP8BitCost(0, last_p0);
+ }
+ }
+ return cost;
+}
+
+static void SetResidualCoeffs_MIPS32(const int16_t* const coeffs,
+ VP8Residual* const res) {
+ const int16_t* p_coeffs = (int16_t*)coeffs;
+ int temp0, temp1, temp2, n, n1;
+ assert(res->first == 0 || coeffs[0] == 0);
+
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "addiu %[p_coeffs], %[p_coeffs], 28 \n\t"
+ "li %[n], 15 \n\t"
+ "li %[temp2], -1 \n\t"
+ "0: \n\t"
+ "ulw %[temp0], 0(%[p_coeffs]) \n\t"
+ "beqz %[temp0], 1f \n\t"
+#if defined(WORDS_BIGENDIAN)
+ " sll %[temp1], %[temp0], 16 \n\t"
+#else
+ " srl %[temp1], %[temp0], 16 \n\t"
+#endif
+ "addiu %[n1], %[n], -1 \n\t"
+ "movz %[temp0], %[n1], %[temp1] \n\t"
+ "movn %[temp0], %[n], %[temp1] \n\t"
+ "j 2f \n\t"
+ " addiu %[temp2], %[temp0], 0 \n\t"
+ "1: \n\t"
+ "addiu %[n], %[n], -2 \n\t"
+ "bgtz %[n], 0b \n\t"
+ " addiu %[p_coeffs], %[p_coeffs], -4 \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [p_coeffs]"+&r"(p_coeffs), [temp0]"=&r"(temp0),
+ [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [n]"=&r"(n), [n1]"=&r"(n1)
+ :
+ : "memory"
+ );
+ res->last = temp2;
+ res->coeffs = coeffs;
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspCostInitMIPS32(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitMIPS32(void) {
+ VP8GetResidualCost = GetResidualCost_MIPS32;
+ VP8SetResidualCoeffs = SetResidualCoeffs_MIPS32;
+}
+
+#else // !WEBP_USE_MIPS32
+
+WEBP_DSP_INIT_STUB(VP8EncDspCostInitMIPS32)
+
+#endif // WEBP_USE_MIPS32
diff --git a/src/third_party/libwebp/src/dsp/cost_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/cost_mips_dsp_r2.c
new file mode 100644
index 0000000..b571518
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/cost_mips_dsp_r2.c
@@ -0,0 +1,112 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#endif
+
+#include "src/enc/cost_enc.h"
+
+static int GetResidualCost_MIPSdspR2(int ctx0, const VP8Residual* const res) {
+ int temp0, temp1;
+ int v_reg, ctx_reg;
+ int n = res->first;
+ // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ int p0 = res->prob[n][ctx0][0];
+ CostArrayPtr const costs = res->costs;
+ const uint16_t* t = costs[n][ctx0];
+ // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0
+ // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
+ // be missing during the loop.
+ int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0;
+ const int16_t* res_coeffs = res->coeffs;
+ const int res_last = res->last;
+ const int const_max_level = MAX_VARIABLE_LEVEL;
+ const int const_2 = 2;
+ const uint16_t** p_costs = &costs[n][0];
+ const size_t inc_p_costs = NUM_CTX * sizeof(*p_costs);
+
+ if (res->last < 0) {
+ return VP8BitCost(0, p0);
+ }
+
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "subu %[temp1], %[res_last], %[n] \n\t"
+ "blez %[temp1], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "sll %[temp0], %[n], 1 \n\t"
+ "lhx %[v_reg], %[temp0](%[res_coeffs]) \n\t"
+ "addiu %[n], %[n], 1 \n\t"
+ "absq_s.w %[v_reg], %[v_reg] \n\t"
+ "sltiu %[temp0], %[v_reg], 2 \n\t"
+ "move %[ctx_reg], %[v_reg] \n\t"
+ "movz %[ctx_reg], %[const_2], %[temp0] \n\t"
+ "sll %[temp1], %[v_reg], 1 \n\t"
+ "lhx %[temp1], %[temp1](%[VP8LevelFixedCosts]) \n\t"
+ "slt %[temp0], %[v_reg], %[const_max_level] \n\t"
+ "movz %[v_reg], %[const_max_level], %[temp0] \n\t"
+ "addu %[cost], %[cost], %[temp1] \n\t"
+ "sll %[v_reg], %[v_reg], 1 \n\t"
+ "sll %[ctx_reg], %[ctx_reg], 2 \n\t"
+ "lhx %[temp0], %[v_reg](%[t]) \n\t"
+ "addu %[p_costs], %[p_costs], %[inc_p_costs] \n\t"
+ "addu %[t], %[p_costs], %[ctx_reg] \n\t"
+ "addu %[cost], %[cost], %[temp0] \n\t"
+ "bne %[n], %[res_last], 1b \n\t"
+ " lw %[t], 0(%[t]) \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [cost]"+&r"(cost), [t]"+&r"(t), [n]"+&r"(n), [v_reg]"=&r"(v_reg),
+ [ctx_reg]"=&r"(ctx_reg), [p_costs]"+&r"(p_costs), [temp0]"=&r"(temp0),
+ [temp1]"=&r"(temp1)
+ : [const_2]"r"(const_2), [const_max_level]"r"(const_max_level),
+ [VP8LevelFixedCosts]"r"(VP8LevelFixedCosts), [res_last]"r"(res_last),
+ [res_coeffs]"r"(res_coeffs), [inc_p_costs]"r"(inc_p_costs)
+ : "memory"
+ );
+
+ // Last coefficient is always non-zero
+ {
+ const int v = abs(res->coeffs[n]);
+ assert(v != 0);
+ cost += VP8LevelCost(t, v);
+ if (n < 15) {
+ const int b = VP8EncBands[n + 1];
+ const int ctx = (v == 1) ? 1 : 2;
+ const int last_p0 = res->prob[b][ctx][0];
+ cost += VP8BitCost(0, last_p0);
+ }
+ }
+ return cost;
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspCostInitMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitMIPSdspR2(void) {
+ VP8GetResidualCost = GetResidualCost_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(VP8EncDspCostInitMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/cost_sse2.c b/src/third_party/libwebp/src/dsp/cost_sse2.c
new file mode 100644
index 0000000..f9b0003
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/cost_sse2.c
@@ -0,0 +1,123 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 version of cost functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#endif
+
+#include "src/enc/cost_enc.h"
+#include "src/enc/vp8i_enc.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+
+static void SetResidualCoeffs_SSE2(const int16_t* const coeffs,
+ VP8Residual* const res) {
+ const __m128i c0 = _mm_loadu_si128((const __m128i*)(coeffs + 0));
+ const __m128i c1 = _mm_loadu_si128((const __m128i*)(coeffs + 8));
+ // Use SSE2 to compare 16 values with a single instruction.
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i m0 = _mm_packs_epi16(c0, c1);
+ const __m128i m1 = _mm_cmpeq_epi8(m0, zero);
+ // Get the comparison results as a bitmask into 16bits. Negate the mask to get
+ // the position of entries that are not equal to zero. We don't need to mask
+ // out least significant bits according to res->first, since coeffs[0] is 0
+ // if res->first > 0.
+ const uint32_t mask = 0x0000ffffu ^ (uint32_t)_mm_movemask_epi8(m1);
+ // The position of the most significant non-zero bit indicates the position of
+ // the last non-zero value.
+ assert(res->first == 0 || coeffs[0] == 0);
+ res->last = mask ? BitsLog2Floor(mask) : -1;
+ res->coeffs = coeffs;
+}
+
+static int GetResidualCost_SSE2(int ctx0, const VP8Residual* const res) {
+ uint8_t levels[16], ctxs[16];
+ uint16_t abs_levels[16];
+ int n = res->first;
+ // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ const int p0 = res->prob[n][ctx0][0];
+ CostArrayPtr const costs = res->costs;
+ const uint16_t* t = costs[n][ctx0];
+ // bit_cost(1, p0) is already incorporated in t[] tables, but only if ctx != 0
+ // (as required by the syntax). For ctx0 == 0, we need to add it here or it'll
+ // be missing during the loop.
+ int cost = (ctx0 == 0) ? VP8BitCost(1, p0) : 0;
+
+ if (res->last < 0) {
+ return VP8BitCost(0, p0);
+ }
+
+ { // precompute clamped levels and contexts, packed to 8b.
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i kCst2 = _mm_set1_epi8(2);
+ const __m128i kCst67 = _mm_set1_epi8(MAX_VARIABLE_LEVEL);
+ const __m128i c0 = _mm_loadu_si128((const __m128i*)&res->coeffs[0]);
+ const __m128i c1 = _mm_loadu_si128((const __m128i*)&res->coeffs[8]);
+ const __m128i D0 = _mm_sub_epi16(zero, c0);
+ const __m128i D1 = _mm_sub_epi16(zero, c1);
+ const __m128i E0 = _mm_max_epi16(c0, D0); // abs(v), 16b
+ const __m128i E1 = _mm_max_epi16(c1, D1);
+ const __m128i F = _mm_packs_epi16(E0, E1);
+ const __m128i G = _mm_min_epu8(F, kCst2); // context = 0,1,2
+ const __m128i H = _mm_min_epu8(F, kCst67); // clamp_level in [0..67]
+
+ _mm_storeu_si128((__m128i*)&ctxs[0], G);
+ _mm_storeu_si128((__m128i*)&levels[0], H);
+
+ _mm_storeu_si128((__m128i*)&abs_levels[0], E0);
+ _mm_storeu_si128((__m128i*)&abs_levels[8], E1);
+ }
+ for (; n < res->last; ++n) {
+ const int ctx = ctxs[n];
+ const int level = levels[n];
+ const int flevel = abs_levels[n]; // full level
+ cost += VP8LevelFixedCosts[flevel] + t[level]; // simplified VP8LevelCost()
+ t = costs[n + 1][ctx];
+ }
+ // Last coefficient is always non-zero
+ {
+ const int level = levels[n];
+ const int flevel = abs_levels[n];
+ assert(flevel != 0);
+ cost += VP8LevelFixedCosts[flevel] + t[level];
+ if (n < 15) {
+ const int b = VP8EncBands[n + 1];
+ const int ctx = ctxs[n];
+ const int last_p0 = res->prob[b][ctx][0];
+ cost += VP8BitCost(0, last_p0);
+ }
+ }
+ return cost;
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspCostInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspCostInitSSE2(void) {
+ VP8SetResidualCoeffs = SetResidualCoeffs_SSE2;
+ VP8GetResidualCost = GetResidualCost_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(VP8EncDspCostInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/cpu.c b/src/third_party/libwebp/src/dsp/cpu.c
new file mode 100644
index 0000000..8b40fee
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/cpu.c
@@ -0,0 +1,222 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// CPU detection
+//
+// Author: Christian Duvivier (cduvivier@google.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_HAVE_NEON_RTCD)
+#include <stdio.h>
+#include <string.h>
+#endif
+
+#if defined(WEBP_ANDROID_NEON)
+#include <cpu-features.h>
+#endif
+
+//------------------------------------------------------------------------------
+// SSE2 detection.
+//
+
+// apple/darwin gcc-4.0.1 defines __PIC__, but not __pic__ with -fPIC.
+#if (defined(__pic__) || defined(__PIC__)) && defined(__i386__)
+static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
+ __asm__ volatile (
+ "mov %%ebx, %%edi\n"
+ "cpuid\n"
+ "xchg %%edi, %%ebx\n"
+ : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type), "c"(0));
+}
+#elif defined(__x86_64__) && \
+ (defined(__code_model_medium__) || defined(__code_model_large__)) && \
+ defined(__PIC__)
+static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
+ __asm__ volatile (
+ "xchg{q}\t{%%rbx}, %q1\n"
+ "cpuid\n"
+ "xchg{q}\t{%%rbx}, %q1\n"
+ : "=a"(cpu_info[0]), "=&r"(cpu_info[1]), "=c"(cpu_info[2]),
+ "=d"(cpu_info[3])
+ : "a"(info_type), "c"(0));
+}
+#elif defined(__i386__) || defined(__x86_64__)
+static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
+ __asm__ volatile (
+ "cpuid\n"
+ : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type), "c"(0));
+}
+#elif (defined(_M_X64) || defined(_M_IX86)) && \
+ defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // >= VS2008 SP1
+#include <intrin.h>
+#define GetCPUInfo(info, type) __cpuidex(info, type, 0) // set ecx=0
+#elif defined(WEBP_MSC_SSE2)
+#define GetCPUInfo __cpuid
+#endif
+
+// NaCl has no support for xgetbv or the raw opcode.
+#if !defined(__native_client__) && (defined(__i386__) || defined(__x86_64__))
+static WEBP_INLINE uint64_t xgetbv(void) {
+ const uint32_t ecx = 0;
+ uint32_t eax, edx;
+ // Use the raw opcode for xgetbv for compatibility with older toolchains.
+ __asm__ volatile (
+ ".byte 0x0f, 0x01, 0xd0\n"
+ : "=a"(eax), "=d"(edx) : "c" (ecx));
+ return ((uint64_t)edx << 32) | eax;
+}
+#elif (defined(_M_X64) || defined(_M_IX86)) && \
+ defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 // >= VS2010 SP1
+#include <immintrin.h>
+#define xgetbv() _xgetbv(0)
+#elif defined(_MSC_VER) && defined(_M_IX86)
+static WEBP_INLINE uint64_t xgetbv(void) {
+ uint32_t eax_, edx_;
+ __asm {
+ xor ecx, ecx // ecx = 0
+ // Use the raw opcode for xgetbv for compatibility with older toolchains.
+ __asm _emit 0x0f __asm _emit 0x01 __asm _emit 0xd0
+ mov eax_, eax
+ mov edx_, edx
+ }
+ return ((uint64_t)edx_ << 32) | eax_;
+}
+#else
+#define xgetbv() 0U // no AVX for older x64 or unrecognized toolchains.
+#endif
+
+#if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2)
+
+// helper function for run-time detection of slow SSSE3 platforms
+static int CheckSlowModel(int info) {
+ // Table listing display models with longer latencies for the bsr instruction
+ // (ie 2 cycles vs 10/16 cycles) and some SSSE3 instructions like pshufb.
+ // Refer to Intel 64 and IA-32 Architectures Optimization Reference Manual.
+ static const uint8_t kSlowModels[] = {
+ 0x37, 0x4a, 0x4d, // Silvermont Microarchitecture
+ 0x1c, 0x26, 0x27 // Atom Microarchitecture
+ };
+ const uint32_t model = ((info & 0xf0000) >> 12) | ((info >> 4) & 0xf);
+ const uint32_t family = (info >> 8) & 0xf;
+ if (family == 0x06) {
+ size_t i;
+ for (i = 0; i < sizeof(kSlowModels) / sizeof(kSlowModels[0]); ++i) {
+ if (model == kSlowModels[i]) return 1;
+ }
+ }
+ return 0;
+}
+
+static int x86CPUInfo(CPUFeature feature) {
+ int max_cpuid_value;
+ int cpu_info[4];
+ int is_intel = 0;
+
+ // get the highest feature value cpuid supports
+ GetCPUInfo(cpu_info, 0);
+ max_cpuid_value = cpu_info[0];
+ if (max_cpuid_value < 1) {
+ return 0;
+ } else {
+ const int VENDOR_ID_INTEL_EBX = 0x756e6547; // uneG
+ const int VENDOR_ID_INTEL_EDX = 0x49656e69; // Ieni
+ const int VENDOR_ID_INTEL_ECX = 0x6c65746e; // letn
+ is_intel = (cpu_info[1] == VENDOR_ID_INTEL_EBX &&
+ cpu_info[2] == VENDOR_ID_INTEL_ECX &&
+ cpu_info[3] == VENDOR_ID_INTEL_EDX); // genuine Intel?
+ }
+
+ GetCPUInfo(cpu_info, 1);
+ if (feature == kSSE2) {
+ return !!(cpu_info[3] & (1 << 26));
+ }
+ if (feature == kSSE3) {
+ return !!(cpu_info[2] & (1 << 0));
+ }
+ if (feature == kSlowSSSE3) {
+ if (is_intel && (cpu_info[2] & (1 << 9))) { // SSSE3?
+ return CheckSlowModel(cpu_info[0]);
+ }
+ return 0;
+ }
+
+ if (feature == kSSE4_1) {
+ return !!(cpu_info[2] & (1 << 19));
+ }
+ if (feature == kAVX) {
+ // bits 27 (OSXSAVE) & 28 (256-bit AVX)
+ if ((cpu_info[2] & 0x18000000) == 0x18000000) {
+ // XMM state and YMM state enabled by the OS.
+ return (xgetbv() & 0x6) == 0x6;
+ }
+ }
+ if (feature == kAVX2) {
+ if (x86CPUInfo(kAVX) && max_cpuid_value >= 7) {
+ GetCPUInfo(cpu_info, 7);
+ return !!(cpu_info[1] & (1 << 5));
+ }
+ }
+ return 0;
+}
+VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
+#elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test.
+static int AndroidCPUInfo(CPUFeature feature) {
+ const AndroidCpuFamily cpu_family = android_getCpuFamily();
+ const uint64_t cpu_features = android_getCpuFeatures();
+ if (feature == kNEON) {
+ return (cpu_family == ANDROID_CPU_FAMILY_ARM &&
+ 0 != (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON));
+ }
+ return 0;
+}
+VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
+#elif defined(WEBP_USE_NEON)
+// define a dummy function to enable turning off NEON at runtime by setting
+// VP8DecGetCPUInfo = NULL
+static int armCPUInfo(CPUFeature feature) {
+ if (feature != kNEON) return 0;
+#if defined(__linux__) && defined(WEBP_HAVE_NEON_RTCD)
+ {
+ int has_neon = 0;
+ char line[200];
+ FILE* const cpuinfo = fopen("/proc/cpuinfo", "r");
+ if (cpuinfo == NULL) return 0;
+ while (fgets(line, sizeof(line), cpuinfo)) {
+ if (!strncmp(line, "Features", 8)) {
+ if (strstr(line, " neon ") != NULL) {
+ has_neon = 1;
+ break;
+ }
+ }
+ }
+ fclose(cpuinfo);
+ return has_neon;
+ }
+#else
+ return 1;
+#endif
+}
+VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
+#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) || \
+ defined(WEBP_USE_MSA)
+static int mipsCPUInfo(CPUFeature feature) {
+ if ((feature == kMIPS32) || (feature == kMIPSdspR2) || (feature == kMSA)) {
+ return 1;
+ } else {
+ return 0;
+ }
+
+}
+VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo;
+#else
+VP8CPUInfo VP8GetCPUInfo = NULL;
+#endif
diff --git a/src/third_party/libwebp/src/dsp/dec.c b/src/third_party/libwebp/src/dsp/dec.c
new file mode 100644
index 0000000..aa2079b
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec.c
@@ -0,0 +1,891 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical decoding functions, default plain-C implementations.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#endif
+#include "src/dsp/dsp.h"
+#include "src/dec/vp8i_dec.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE uint8_t clip_8b(int v) {
+ return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
+}
+
+//------------------------------------------------------------------------------
+// Transforms (Paragraph 14.4)
+
+#define STORE(x, y, v) \
+ dst[(x) + (y) * BPS] = clip_8b(dst[(x) + (y) * BPS] + ((v) >> 3))
+
+#define STORE2(y, dc, d, c) do { \
+ const int DC = (dc); \
+ STORE(0, y, DC + (d)); \
+ STORE(1, y, DC + (c)); \
+ STORE(2, y, DC - (c)); \
+ STORE(3, y, DC - (d)); \
+} while (0)
+
+#define MUL1(a) ((((a) * 20091) >> 16) + (a))
+#define MUL2(a) (((a) * 35468) >> 16)
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void TransformOne_C(const int16_t* in, uint8_t* dst) {
+ int C[4 * 4], *tmp;
+ int i;
+ tmp = C;
+ for (i = 0; i < 4; ++i) { // vertical pass
+ const int a = in[0] + in[8]; // [-4096, 4094]
+ const int b = in[0] - in[8]; // [-4095, 4095]
+ const int c = MUL2(in[4]) - MUL1(in[12]); // [-3783, 3783]
+ const int d = MUL1(in[4]) + MUL2(in[12]); // [-3785, 3781]
+ tmp[0] = a + d; // [-7881, 7875]
+ tmp[1] = b + c; // [-7878, 7878]
+ tmp[2] = b - c; // [-7878, 7878]
+ tmp[3] = a - d; // [-7877, 7879]
+ tmp += 4;
+ in++;
+ }
+ // Each pass is expanding the dynamic range by ~3.85 (upper bound).
+ // The exact value is (2. + (20091 + 35468) / 65536).
+ // After the second pass, maximum interval is [-3794, 3794], assuming
+ // an input in [-2048, 2047] interval. We then need to add a dst value
+ // in the [0, 255] range.
+ // In the worst case scenario, the input to clip_8b() can be as large as
+ // [-60713, 60968].
+ tmp = C;
+ for (i = 0; i < 4; ++i) { // horizontal pass
+ const int dc = tmp[0] + 4;
+ const int a = dc + tmp[8];
+ const int b = dc - tmp[8];
+ const int c = MUL2(tmp[4]) - MUL1(tmp[12]);
+ const int d = MUL1(tmp[4]) + MUL2(tmp[12]);
+ STORE(0, 0, a + d);
+ STORE(1, 0, b + c);
+ STORE(2, 0, b - c);
+ STORE(3, 0, a - d);
+ tmp++;
+ dst += BPS;
+ }
+}
+
+// Simplified transform when only in[0], in[1] and in[4] are non-zero
+static void TransformAC3_C(const int16_t* in, uint8_t* dst) {
+ const int a = in[0] + 4;
+ const int c4 = MUL2(in[4]);
+ const int d4 = MUL1(in[4]);
+ const int c1 = MUL2(in[1]);
+ const int d1 = MUL1(in[1]);
+ STORE2(0, a + d4, d1, c1);
+ STORE2(1, a + c4, d1, c1);
+ STORE2(2, a - c4, d1, c1);
+ STORE2(3, a - d4, d1, c1);
+}
+#undef MUL1
+#undef MUL2
+#undef STORE2
+
+static void TransformTwo_C(const int16_t* in, uint8_t* dst, int do_two) {
+ TransformOne_C(in, dst);
+ if (do_two) {
+ TransformOne_C(in + 16, dst + 4);
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+static void TransformUV_C(const int16_t* in, uint8_t* dst) {
+ VP8Transform(in + 0 * 16, dst, 1);
+ VP8Transform(in + 2 * 16, dst + 4 * BPS, 1);
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void TransformDC_C(const int16_t* in, uint8_t* dst) {
+ const int DC = in[0] + 4;
+ int i, j;
+ for (j = 0; j < 4; ++j) {
+ for (i = 0; i < 4; ++i) {
+ STORE(i, j, DC);
+ }
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+static void TransformDCUV_C(const int16_t* in, uint8_t* dst) {
+ if (in[0 * 16]) VP8TransformDC(in + 0 * 16, dst);
+ if (in[1 * 16]) VP8TransformDC(in + 1 * 16, dst + 4);
+ if (in[2 * 16]) VP8TransformDC(in + 2 * 16, dst + 4 * BPS);
+ if (in[3 * 16]) VP8TransformDC(in + 3 * 16, dst + 4 * BPS + 4);
+}
+
+#undef STORE
+
+//------------------------------------------------------------------------------
+// Paragraph 14.3
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void TransformWHT_C(const int16_t* in, int16_t* out) {
+ int tmp[16];
+ int i;
+ for (i = 0; i < 4; ++i) {
+ const int a0 = in[0 + i] + in[12 + i];
+ const int a1 = in[4 + i] + in[ 8 + i];
+ const int a2 = in[4 + i] - in[ 8 + i];
+ const int a3 = in[0 + i] - in[12 + i];
+ tmp[0 + i] = a0 + a1;
+ tmp[8 + i] = a0 - a1;
+ tmp[4 + i] = a3 + a2;
+ tmp[12 + i] = a3 - a2;
+ }
+ for (i = 0; i < 4; ++i) {
+ const int dc = tmp[0 + i * 4] + 3; // w/ rounder
+ const int a0 = dc + tmp[3 + i * 4];
+ const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
+ const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
+ const int a3 = dc - tmp[3 + i * 4];
+ out[ 0] = (a0 + a1) >> 3;
+ out[16] = (a3 + a2) >> 3;
+ out[32] = (a0 - a1) >> 3;
+ out[48] = (a3 - a2) >> 3;
+ out += 64;
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+void (*VP8TransformWHT)(const int16_t* in, int16_t* out);
+
+//------------------------------------------------------------------------------
+// Intra predictions
+
+#define DST(x, y) dst[(x) + (y) * BPS]
+
+#if !WEBP_NEON_OMIT_C_CODE
+static WEBP_INLINE void TrueMotion(uint8_t* dst, int size) {
+ const uint8_t* top = dst - BPS;
+ const uint8_t* const clip0 = VP8kclip1 - top[-1];
+ int y;
+ for (y = 0; y < size; ++y) {
+ const uint8_t* const clip = clip0 + dst[-1];
+ int x;
+ for (x = 0; x < size; ++x) {
+ dst[x] = clip[top[x]];
+ }
+ dst += BPS;
+ }
+}
+static void TM4_C(uint8_t* dst) { TrueMotion(dst, 4); }
+static void TM8uv_C(uint8_t* dst) { TrueMotion(dst, 8); }
+static void TM16_C(uint8_t* dst) { TrueMotion(dst, 16); }
+
+//------------------------------------------------------------------------------
+// 16x16
+
+static void VE16_C(uint8_t* dst) { // vertical
+ int j;
+ for (j = 0; j < 16; ++j) {
+ memcpy(dst + j * BPS, dst - BPS, 16);
+ }
+}
+
+static void HE16_C(uint8_t* dst) { // horizontal
+ int j;
+ for (j = 16; j > 0; --j) {
+ memset(dst, dst[-1], 16);
+ dst += BPS;
+ }
+}
+
+static WEBP_INLINE void Put16(int v, uint8_t* dst) {
+ int j;
+ for (j = 0; j < 16; ++j) {
+ memset(dst + j * BPS, v, 16);
+ }
+}
+
+static void DC16_C(uint8_t* dst) { // DC
+ int DC = 16;
+ int j;
+ for (j = 0; j < 16; ++j) {
+ DC += dst[-1 + j * BPS] + dst[j - BPS];
+ }
+ Put16(DC >> 5, dst);
+}
+
+static void DC16NoTop_C(uint8_t* dst) { // DC with top samples not available
+ int DC = 8;
+ int j;
+ for (j = 0; j < 16; ++j) {
+ DC += dst[-1 + j * BPS];
+ }
+ Put16(DC >> 4, dst);
+}
+
+static void DC16NoLeft_C(uint8_t* dst) { // DC with left samples not available
+ int DC = 8;
+ int i;
+ for (i = 0; i < 16; ++i) {
+ DC += dst[i - BPS];
+ }
+ Put16(DC >> 4, dst);
+}
+
+static void DC16NoTopLeft_C(uint8_t* dst) { // DC with no top and left samples
+ Put16(0x80, dst);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES];
+
+//------------------------------------------------------------------------------
+// 4x4
+
+#define AVG3(a, b, c) ((uint8_t)(((a) + 2 * (b) + (c) + 2) >> 2))
+#define AVG2(a, b) (((a) + (b) + 1) >> 1)
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void VE4_C(uint8_t* dst) { // vertical
+ const uint8_t* top = dst - BPS;
+ const uint8_t vals[4] = {
+ AVG3(top[-1], top[0], top[1]),
+ AVG3(top[ 0], top[1], top[2]),
+ AVG3(top[ 1], top[2], top[3]),
+ AVG3(top[ 2], top[3], top[4])
+ };
+ int i;
+ for (i = 0; i < 4; ++i) {
+ memcpy(dst + i * BPS, vals, sizeof(vals));
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+static void HE4_C(uint8_t* dst) { // horizontal
+ const int A = dst[-1 - BPS];
+ const int B = dst[-1];
+ const int C = dst[-1 + BPS];
+ const int D = dst[-1 + 2 * BPS];
+ const int E = dst[-1 + 3 * BPS];
+ WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(A, B, C));
+ WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(B, C, D));
+ WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(C, D, E));
+ WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(D, E, E));
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void DC4_C(uint8_t* dst) { // DC
+ uint32_t dc = 4;
+ int i;
+ for (i = 0; i < 4; ++i) dc += dst[i - BPS] + dst[-1 + i * BPS];
+ dc >>= 3;
+ for (i = 0; i < 4; ++i) memset(dst + i * BPS, dc, 4);
+}
+
+static void RD4_C(uint8_t* dst) { // Down-right
+ const int I = dst[-1 + 0 * BPS];
+ const int J = dst[-1 + 1 * BPS];
+ const int K = dst[-1 + 2 * BPS];
+ const int L = dst[-1 + 3 * BPS];
+ const int X = dst[-1 - BPS];
+ const int A = dst[0 - BPS];
+ const int B = dst[1 - BPS];
+ const int C = dst[2 - BPS];
+ const int D = dst[3 - BPS];
+ DST(0, 3) = AVG3(J, K, L);
+ DST(1, 3) = DST(0, 2) = AVG3(I, J, K);
+ DST(2, 3) = DST(1, 2) = DST(0, 1) = AVG3(X, I, J);
+ DST(3, 3) = DST(2, 2) = DST(1, 1) = DST(0, 0) = AVG3(A, X, I);
+ DST(3, 2) = DST(2, 1) = DST(1, 0) = AVG3(B, A, X);
+ DST(3, 1) = DST(2, 0) = AVG3(C, B, A);
+ DST(3, 0) = AVG3(D, C, B);
+}
+
+static void LD4_C(uint8_t* dst) { // Down-Left
+ const int A = dst[0 - BPS];
+ const int B = dst[1 - BPS];
+ const int C = dst[2 - BPS];
+ const int D = dst[3 - BPS];
+ const int E = dst[4 - BPS];
+ const int F = dst[5 - BPS];
+ const int G = dst[6 - BPS];
+ const int H = dst[7 - BPS];
+ DST(0, 0) = AVG3(A, B, C);
+ DST(1, 0) = DST(0, 1) = AVG3(B, C, D);
+ DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E);
+ DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F);
+ DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G);
+ DST(3, 2) = DST(2, 3) = AVG3(F, G, H);
+ DST(3, 3) = AVG3(G, H, H);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+static void VR4_C(uint8_t* dst) { // Vertical-Right
+ const int I = dst[-1 + 0 * BPS];
+ const int J = dst[-1 + 1 * BPS];
+ const int K = dst[-1 + 2 * BPS];
+ const int X = dst[-1 - BPS];
+ const int A = dst[0 - BPS];
+ const int B = dst[1 - BPS];
+ const int C = dst[2 - BPS];
+ const int D = dst[3 - BPS];
+ DST(0, 0) = DST(1, 2) = AVG2(X, A);
+ DST(1, 0) = DST(2, 2) = AVG2(A, B);
+ DST(2, 0) = DST(3, 2) = AVG2(B, C);
+ DST(3, 0) = AVG2(C, D);
+
+ DST(0, 3) = AVG3(K, J, I);
+ DST(0, 2) = AVG3(J, I, X);
+ DST(0, 1) = DST(1, 3) = AVG3(I, X, A);
+ DST(1, 1) = DST(2, 3) = AVG3(X, A, B);
+ DST(2, 1) = DST(3, 3) = AVG3(A, B, C);
+ DST(3, 1) = AVG3(B, C, D);
+}
+
+static void VL4_C(uint8_t* dst) { // Vertical-Left
+ const int A = dst[0 - BPS];
+ const int B = dst[1 - BPS];
+ const int C = dst[2 - BPS];
+ const int D = dst[3 - BPS];
+ const int E = dst[4 - BPS];
+ const int F = dst[5 - BPS];
+ const int G = dst[6 - BPS];
+ const int H = dst[7 - BPS];
+ DST(0, 0) = AVG2(A, B);
+ DST(1, 0) = DST(0, 2) = AVG2(B, C);
+ DST(2, 0) = DST(1, 2) = AVG2(C, D);
+ DST(3, 0) = DST(2, 2) = AVG2(D, E);
+
+ DST(0, 1) = AVG3(A, B, C);
+ DST(1, 1) = DST(0, 3) = AVG3(B, C, D);
+ DST(2, 1) = DST(1, 3) = AVG3(C, D, E);
+ DST(3, 1) = DST(2, 3) = AVG3(D, E, F);
+ DST(3, 2) = AVG3(E, F, G);
+ DST(3, 3) = AVG3(F, G, H);
+}
+
+static void HU4_C(uint8_t* dst) { // Horizontal-Up
+ const int I = dst[-1 + 0 * BPS];
+ const int J = dst[-1 + 1 * BPS];
+ const int K = dst[-1 + 2 * BPS];
+ const int L = dst[-1 + 3 * BPS];
+ DST(0, 0) = AVG2(I, J);
+ DST(2, 0) = DST(0, 1) = AVG2(J, K);
+ DST(2, 1) = DST(0, 2) = AVG2(K, L);
+ DST(1, 0) = AVG3(I, J, K);
+ DST(3, 0) = DST(1, 1) = AVG3(J, K, L);
+ DST(3, 1) = DST(1, 2) = AVG3(K, L, L);
+ DST(3, 2) = DST(2, 2) =
+ DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
+}
+
+static void HD4_C(uint8_t* dst) { // Horizontal-Down
+ const int I = dst[-1 + 0 * BPS];
+ const int J = dst[-1 + 1 * BPS];
+ const int K = dst[-1 + 2 * BPS];
+ const int L = dst[-1 + 3 * BPS];
+ const int X = dst[-1 - BPS];
+ const int A = dst[0 - BPS];
+ const int B = dst[1 - BPS];
+ const int C = dst[2 - BPS];
+
+ DST(0, 0) = DST(2, 1) = AVG2(I, X);
+ DST(0, 1) = DST(2, 2) = AVG2(J, I);
+ DST(0, 2) = DST(2, 3) = AVG2(K, J);
+ DST(0, 3) = AVG2(L, K);
+
+ DST(3, 0) = AVG3(A, B, C);
+ DST(2, 0) = AVG3(X, A, B);
+ DST(1, 0) = DST(3, 1) = AVG3(I, X, A);
+ DST(1, 1) = DST(3, 2) = AVG3(J, I, X);
+ DST(1, 2) = DST(3, 3) = AVG3(K, J, I);
+ DST(1, 3) = AVG3(L, K, J);
+}
+
+#undef DST
+#undef AVG3
+#undef AVG2
+
+VP8PredFunc VP8PredLuma4[NUM_BMODES];
+
+//------------------------------------------------------------------------------
+// Chroma
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void VE8uv_C(uint8_t* dst) { // vertical
+ int j;
+ for (j = 0; j < 8; ++j) {
+ memcpy(dst + j * BPS, dst - BPS, 8);
+ }
+}
+
+static void HE8uv_C(uint8_t* dst) { // horizontal
+ int j;
+ for (j = 0; j < 8; ++j) {
+ memset(dst, dst[-1], 8);
+ dst += BPS;
+ }
+}
+
+// helper for chroma-DC predictions
+static WEBP_INLINE void Put8x8uv(uint8_t value, uint8_t* dst) {
+ int j;
+ for (j = 0; j < 8; ++j) {
+ memset(dst + j * BPS, value, 8);
+ }
+}
+
+static void DC8uv_C(uint8_t* dst) { // DC
+ int dc0 = 8;
+ int i;
+ for (i = 0; i < 8; ++i) {
+ dc0 += dst[i - BPS] + dst[-1 + i * BPS];
+ }
+ Put8x8uv(dc0 >> 4, dst);
+}
+
+static void DC8uvNoLeft_C(uint8_t* dst) { // DC with no left samples
+ int dc0 = 4;
+ int i;
+ for (i = 0; i < 8; ++i) {
+ dc0 += dst[i - BPS];
+ }
+ Put8x8uv(dc0 >> 3, dst);
+}
+
+static void DC8uvNoTop_C(uint8_t* dst) { // DC with no top samples
+ int dc0 = 4;
+ int i;
+ for (i = 0; i < 8; ++i) {
+ dc0 += dst[-1 + i * BPS];
+ }
+ Put8x8uv(dc0 >> 3, dst);
+}
+
+static void DC8uvNoTopLeft_C(uint8_t* dst) { // DC with nothing
+ Put8x8uv(0x80, dst);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES];
+
+//------------------------------------------------------------------------------
+// Edge filtering functions
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+// 4 pixels in, 2 pixels out
+static WEBP_INLINE void DoFilter2_C(uint8_t* p, int step) {
+ const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ const int a = 3 * (q0 - p0) + VP8ksclip1[p1 - q1]; // in [-893,892]
+ const int a1 = VP8ksclip2[(a + 4) >> 3]; // in [-16,15]
+ const int a2 = VP8ksclip2[(a + 3) >> 3];
+ p[-step] = VP8kclip1[p0 + a2];
+ p[ 0] = VP8kclip1[q0 - a1];
+}
+
+// 4 pixels in, 4 pixels out
+static WEBP_INLINE void DoFilter4_C(uint8_t* p, int step) {
+ const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ const int a = 3 * (q0 - p0);
+ const int a1 = VP8ksclip2[(a + 4) >> 3];
+ const int a2 = VP8ksclip2[(a + 3) >> 3];
+ const int a3 = (a1 + 1) >> 1;
+ p[-2*step] = VP8kclip1[p1 + a3];
+ p[- step] = VP8kclip1[p0 + a2];
+ p[ 0] = VP8kclip1[q0 - a1];
+ p[ step] = VP8kclip1[q1 - a3];
+}
+
+// 6 pixels in, 6 pixels out
+static WEBP_INLINE void DoFilter6_C(uint8_t* p, int step) {
+ const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
+ const int q0 = p[0], q1 = p[step], q2 = p[2*step];
+ const int a = VP8ksclip1[3 * (q0 - p0) + VP8ksclip1[p1 - q1]];
+ // a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9]
+ const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7
+ const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7
+ const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7
+ p[-3*step] = VP8kclip1[p2 + a3];
+ p[-2*step] = VP8kclip1[p1 + a2];
+ p[- step] = VP8kclip1[p0 + a1];
+ p[ 0] = VP8kclip1[q0 - a1];
+ p[ step] = VP8kclip1[q1 - a2];
+ p[ 2*step] = VP8kclip1[q2 - a3];
+}
+
+static WEBP_INLINE int Hev(const uint8_t* p, int step, int thresh) {
+ const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ return (VP8kabs0[p1 - p0] > thresh) || (VP8kabs0[q1 - q0] > thresh);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+
+#if !WEBP_NEON_OMIT_C_CODE
+static WEBP_INLINE int NeedsFilter_C(const uint8_t* p, int step, int t) {
+ const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ return ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) <= t);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+static WEBP_INLINE int NeedsFilter2_C(const uint8_t* p,
+ int step, int t, int it) {
+ const int p3 = p[-4 * step], p2 = p[-3 * step], p1 = p[-2 * step];
+ const int p0 = p[-step], q0 = p[0];
+ const int q1 = p[step], q2 = p[2 * step], q3 = p[3 * step];
+ if ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) > t) return 0;
+ return VP8kabs0[p3 - p2] <= it && VP8kabs0[p2 - p1] <= it &&
+ VP8kabs0[p1 - p0] <= it && VP8kabs0[q3 - q2] <= it &&
+ VP8kabs0[q2 - q1] <= it && VP8kabs0[q1 - q0] <= it;
+}
+#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+
+//------------------------------------------------------------------------------
+// Simple In-loop filtering (Paragraph 15.2)
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void SimpleVFilter16_C(uint8_t* p, int stride, int thresh) {
+ int i;
+ const int thresh2 = 2 * thresh + 1;
+ for (i = 0; i < 16; ++i) {
+ if (NeedsFilter_C(p + i, stride, thresh2)) {
+ DoFilter2_C(p + i, stride);
+ }
+ }
+}
+
+static void SimpleHFilter16_C(uint8_t* p, int stride, int thresh) {
+ int i;
+ const int thresh2 = 2 * thresh + 1;
+ for (i = 0; i < 16; ++i) {
+ if (NeedsFilter_C(p + i * stride, 1, thresh2)) {
+ DoFilter2_C(p + i * stride, 1);
+ }
+ }
+}
+
+static void SimpleVFilter16i_C(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ SimpleVFilter16_C(p, stride, thresh);
+ }
+}
+
+static void SimpleHFilter16i_C(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ SimpleHFilter16_C(p, stride, thresh);
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//------------------------------------------------------------------------------
+// Complex In-loop filtering (Paragraph 15.3)
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+static WEBP_INLINE void FilterLoop26_C(uint8_t* p,
+ int hstride, int vstride, int size,
+ int thresh, int ithresh,
+ int hev_thresh) {
+ const int thresh2 = 2 * thresh + 1;
+ while (size-- > 0) {
+ if (NeedsFilter2_C(p, hstride, thresh2, ithresh)) {
+ if (Hev(p, hstride, hev_thresh)) {
+ DoFilter2_C(p, hstride);
+ } else {
+ DoFilter6_C(p, hstride);
+ }
+ }
+ p += vstride;
+ }
+}
+
+static WEBP_INLINE void FilterLoop24_C(uint8_t* p,
+ int hstride, int vstride, int size,
+ int thresh, int ithresh,
+ int hev_thresh) {
+ const int thresh2 = 2 * thresh + 1;
+ while (size-- > 0) {
+ if (NeedsFilter2_C(p, hstride, thresh2, ithresh)) {
+ if (Hev(p, hstride, hev_thresh)) {
+ DoFilter2_C(p, hstride);
+ } else {
+ DoFilter4_C(p, hstride);
+ }
+ }
+ p += vstride;
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+
+#if !WEBP_NEON_OMIT_C_CODE
+// on macroblock edges
+static void VFilter16_C(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26_C(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter16_C(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26_C(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+}
+
+// on three inner edges
+static void VFilter16i_C(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ FilterLoop24_C(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+static void HFilter16i_C(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ FilterLoop24_C(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+
+#if !WEBP_NEON_OMIT_C_CODE
+// 8-pixels wide variant, for chroma filtering
+static void VFilter8_C(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26_C(u, stride, 1, 8, thresh, ithresh, hev_thresh);
+ FilterLoop26_C(v, stride, 1, 8, thresh, ithresh, hev_thresh);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+static void HFilter8_C(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26_C(u, 1, stride, 8, thresh, ithresh, hev_thresh);
+ FilterLoop26_C(v, 1, stride, 8, thresh, ithresh, hev_thresh);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void VFilter8i_C(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24_C(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24_C(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+static void HFilter8i_C(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24_C(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24_C(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+
+//------------------------------------------------------------------------------
+
+static void DitherCombine8x8_C(const uint8_t* dither, uint8_t* dst,
+ int dst_stride) {
+ int i, j;
+ for (j = 0; j < 8; ++j) {
+ for (i = 0; i < 8; ++i) {
+ const int delta0 = dither[i] - VP8_DITHER_AMP_CENTER;
+ const int delta1 =
+ (delta0 + VP8_DITHER_DESCALE_ROUNDER) >> VP8_DITHER_DESCALE;
+ dst[i] = clip_8b((int)dst[i] + delta1);
+ }
+ dst += dst_stride;
+ dither += 8;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+VP8DecIdct2 VP8Transform;
+VP8DecIdct VP8TransformAC3;
+VP8DecIdct VP8TransformUV;
+VP8DecIdct VP8TransformDC;
+VP8DecIdct VP8TransformDCUV;
+
+VP8LumaFilterFunc VP8VFilter16;
+VP8LumaFilterFunc VP8HFilter16;
+VP8ChromaFilterFunc VP8VFilter8;
+VP8ChromaFilterFunc VP8HFilter8;
+VP8LumaFilterFunc VP8VFilter16i;
+VP8LumaFilterFunc VP8HFilter16i;
+VP8ChromaFilterFunc VP8VFilter8i;
+VP8ChromaFilterFunc VP8HFilter8i;
+VP8SimpleFilterFunc VP8SimpleVFilter16;
+VP8SimpleFilterFunc VP8SimpleHFilter16;
+VP8SimpleFilterFunc VP8SimpleVFilter16i;
+VP8SimpleFilterFunc VP8SimpleHFilter16i;
+
+void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst,
+ int dst_stride);
+
+extern void VP8DspInitSSE2(void);
+extern void VP8DspInitSSE41(void);
+extern void VP8DspInitNEON(void);
+extern void VP8DspInitMIPS32(void);
+extern void VP8DspInitMIPSdspR2(void);
+extern void VP8DspInitMSA(void);
+
+WEBP_DSP_INIT_FUNC(VP8DspInit) {
+ VP8InitClipTables();
+
+#if !WEBP_NEON_OMIT_C_CODE
+ VP8TransformWHT = TransformWHT_C;
+ VP8Transform = TransformTwo_C;
+ VP8TransformDC = TransformDC_C;
+ VP8TransformAC3 = TransformAC3_C;
+#endif
+ VP8TransformUV = TransformUV_C;
+ VP8TransformDCUV = TransformDCUV_C;
+
+#if !WEBP_NEON_OMIT_C_CODE
+ VP8VFilter16 = VFilter16_C;
+ VP8VFilter16i = VFilter16i_C;
+ VP8HFilter16 = HFilter16_C;
+ VP8VFilter8 = VFilter8_C;
+ VP8VFilter8i = VFilter8i_C;
+ VP8SimpleVFilter16 = SimpleVFilter16_C;
+ VP8SimpleHFilter16 = SimpleHFilter16_C;
+ VP8SimpleVFilter16i = SimpleVFilter16i_C;
+ VP8SimpleHFilter16i = SimpleHFilter16i_C;
+#endif
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+ VP8HFilter16i = HFilter16i_C;
+ VP8HFilter8 = HFilter8_C;
+ VP8HFilter8i = HFilter8i_C;
+#endif
+
+#if !WEBP_NEON_OMIT_C_CODE
+ VP8PredLuma4[0] = DC4_C;
+ VP8PredLuma4[1] = TM4_C;
+ VP8PredLuma4[2] = VE4_C;
+ VP8PredLuma4[4] = RD4_C;
+ VP8PredLuma4[6] = LD4_C;
+#endif
+
+ VP8PredLuma4[3] = HE4_C;
+ VP8PredLuma4[5] = VR4_C;
+ VP8PredLuma4[7] = VL4_C;
+ VP8PredLuma4[8] = HD4_C;
+ VP8PredLuma4[9] = HU4_C;
+
+#if !WEBP_NEON_OMIT_C_CODE
+ VP8PredLuma16[0] = DC16_C;
+ VP8PredLuma16[1] = TM16_C;
+ VP8PredLuma16[2] = VE16_C;
+ VP8PredLuma16[3] = HE16_C;
+ VP8PredLuma16[4] = DC16NoTop_C;
+ VP8PredLuma16[5] = DC16NoLeft_C;
+ VP8PredLuma16[6] = DC16NoTopLeft_C;
+
+ VP8PredChroma8[0] = DC8uv_C;
+ VP8PredChroma8[1] = TM8uv_C;
+ VP8PredChroma8[2] = VE8uv_C;
+ VP8PredChroma8[3] = HE8uv_C;
+ VP8PredChroma8[4] = DC8uvNoTop_C;
+ VP8PredChroma8[5] = DC8uvNoLeft_C;
+ VP8PredChroma8[6] = DC8uvNoTopLeft_C;
+#endif
+
+ VP8DitherCombine8x8 = DitherCombine8x8_C;
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ VP8DspInitSSE2();
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ VP8DspInitSSE41();
+ }
+#endif
+ }
+#endif
+#if defined(WEBP_USE_MIPS32)
+ if (VP8GetCPUInfo(kMIPS32)) {
+ VP8DspInitMIPS32();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ VP8DspInitMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_MSA)
+ if (VP8GetCPUInfo(kMSA)) {
+ VP8DspInitMSA();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ VP8DspInitNEON();
+ }
+#endif
+
+ assert(VP8TransformWHT != NULL);
+ assert(VP8Transform != NULL);
+ assert(VP8TransformDC != NULL);
+ assert(VP8TransformAC3 != NULL);
+ assert(VP8TransformUV != NULL);
+ assert(VP8TransformDCUV != NULL);
+ assert(VP8VFilter16 != NULL);
+ assert(VP8HFilter16 != NULL);
+ assert(VP8VFilter8 != NULL);
+ assert(VP8HFilter8 != NULL);
+ assert(VP8VFilter16i != NULL);
+ assert(VP8HFilter16i != NULL);
+ assert(VP8VFilter8i != NULL);
+ assert(VP8HFilter8i != NULL);
+ assert(VP8SimpleVFilter16 != NULL);
+ assert(VP8SimpleHFilter16 != NULL);
+ assert(VP8SimpleVFilter16i != NULL);
+ assert(VP8SimpleHFilter16i != NULL);
+ assert(VP8PredLuma4[0] != NULL);
+ assert(VP8PredLuma4[1] != NULL);
+ assert(VP8PredLuma4[2] != NULL);
+ assert(VP8PredLuma4[3] != NULL);
+ assert(VP8PredLuma4[4] != NULL);
+ assert(VP8PredLuma4[5] != NULL);
+ assert(VP8PredLuma4[6] != NULL);
+ assert(VP8PredLuma4[7] != NULL);
+ assert(VP8PredLuma4[8] != NULL);
+ assert(VP8PredLuma4[9] != NULL);
+ assert(VP8PredLuma16[0] != NULL);
+ assert(VP8PredLuma16[1] != NULL);
+ assert(VP8PredLuma16[2] != NULL);
+ assert(VP8PredLuma16[3] != NULL);
+ assert(VP8PredLuma16[4] != NULL);
+ assert(VP8PredLuma16[5] != NULL);
+ assert(VP8PredLuma16[6] != NULL);
+ assert(VP8PredChroma8[0] != NULL);
+ assert(VP8PredChroma8[1] != NULL);
+ assert(VP8PredChroma8[2] != NULL);
+ assert(VP8PredChroma8[3] != NULL);
+ assert(VP8PredChroma8[4] != NULL);
+ assert(VP8PredChroma8[5] != NULL);
+ assert(VP8PredChroma8[6] != NULL);
+ assert(VP8DitherCombine8x8 != NULL);
+}
diff --git a/src/third_party/libwebp/src/dsp/dec_clip_tables.c b/src/third_party/libwebp/src/dsp/dec_clip_tables.c
new file mode 100644
index 0000000..427b74f
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec_clip_tables.c
@@ -0,0 +1,369 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Clipping tables for filtering
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+// define to 0 to have run-time table initialization
+#if !defined(USE_STATIC_TABLES)
+#define USE_STATIC_TABLES 1 // ALTERNATE_CODE
+#endif
+
+#if (USE_STATIC_TABLES == 1)
+
+static const uint8_t abs0[255 + 255 + 1] = {
+ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4,
+ 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
+ 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xdf, 0xde, 0xdd, 0xdc,
+ 0xdb, 0xda, 0xd9, 0xd8, 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0,
+ 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, 0xc7, 0xc6, 0xc5, 0xc4,
+ 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
+ 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, 0xaf, 0xae, 0xad, 0xac,
+ 0xab, 0xaa, 0xa9, 0xa8, 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0,
+ 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94,
+ 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88,
+ 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x7f, 0x7e, 0x7d, 0x7c,
+ 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
+ 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64,
+ 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58,
+ 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, 0x4d, 0x4c,
+ 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40,
+ 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34,
+ 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
+ 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c,
+ 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
+ 0x03, 0x02, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c,
+ 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80,
+ 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c,
+ 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
+ 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc,
+ 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0,
+ 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec,
+ 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+static const uint8_t sclip1[1020 + 1020 + 1] = {
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab,
+ 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
+ 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
+};
+
+static const uint8_t sclip2[112 + 112 + 1] = {
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+ 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f
+};
+
+static const uint8_t clip1[255 + 511 + 1] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c,
+ 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80,
+ 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c,
+ 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
+ 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc,
+ 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0,
+ 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec,
+ 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+#else
+
+// uninitialized tables
+static uint8_t abs0[255 + 255 + 1];
+static int8_t sclip1[1020 + 1020 + 1];
+static int8_t sclip2[112 + 112 + 1];
+static uint8_t clip1[255 + 511 + 1];
+
+// We declare this variable 'volatile' to prevent instruction reordering
+// and make sure it's set to true _last_ (so as to be thread-safe)
+static volatile int tables_ok = 0;
+
+#endif // USE_STATIC_TABLES
+
+const int8_t* const VP8ksclip1 = (const int8_t*)&sclip1[1020];
+const int8_t* const VP8ksclip2 = (const int8_t*)&sclip2[112];
+const uint8_t* const VP8kclip1 = &clip1[255];
+const uint8_t* const VP8kabs0 = &abs0[255];
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8InitClipTables(void) {
+#if (USE_STATIC_TABLES == 0)
+ int i;
+ if (!tables_ok) {
+ for (i = -255; i <= 255; ++i) {
+ abs0[255 + i] = (i < 0) ? -i : i;
+ }
+ for (i = -1020; i <= 1020; ++i) {
+ sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
+ }
+ for (i = -112; i <= 112; ++i) {
+ sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
+ }
+ for (i = -255; i <= 255 + 255; ++i) {
+ clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
+ }
+ tables_ok = 1;
+ }
+#endif // USE_STATIC_TABLES
+}
diff --git a/src/third_party/libwebp/src/dsp/dec_mips32.c b/src/third_party/libwebp/src/dsp/dec_mips32.c
new file mode 100644
index 0000000..e4e7096
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec_mips32.c
@@ -0,0 +1,587 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of dsp functions
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+// Jovan Zelincevic (jovan.zelincevic@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS32)
+
+#include "src/dsp/mips_macro.h"
+
+static const int kC1 = 20091 + (1 << 16);
+static const int kC2 = 35468;
+
+static WEBP_INLINE int abs_mips32(int x) {
+ const int sign = x >> 31;
+ return (x ^ sign) - sign;
+}
+
+// 4 pixels in, 2 pixels out
+static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
+ const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ const int a = 3 * (q0 - p0) + VP8ksclip1[p1 - q1];
+ const int a1 = VP8ksclip2[(a + 4) >> 3];
+ const int a2 = VP8ksclip2[(a + 3) >> 3];
+ p[-step] = VP8kclip1[p0 + a2];
+ p[ 0] = VP8kclip1[q0 - a1];
+}
+
+// 4 pixels in, 4 pixels out
+static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
+ const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ const int a = 3 * (q0 - p0);
+ const int a1 = VP8ksclip2[(a + 4) >> 3];
+ const int a2 = VP8ksclip2[(a + 3) >> 3];
+ const int a3 = (a1 + 1) >> 1;
+ p[-2 * step] = VP8kclip1[p1 + a3];
+ p[- step] = VP8kclip1[p0 + a2];
+ p[ 0] = VP8kclip1[q0 - a1];
+ p[ step] = VP8kclip1[q1 - a3];
+}
+
+// 6 pixels in, 6 pixels out
+static WEBP_INLINE void do_filter6(uint8_t* p, int step) {
+ const int p2 = p[-3 * step], p1 = p[-2 * step], p0 = p[-step];
+ const int q0 = p[0], q1 = p[step], q2 = p[2 * step];
+ const int a = VP8ksclip1[3 * (q0 - p0) + VP8ksclip1[p1 - q1]];
+ // a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9]
+ const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7
+ const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7
+ const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7
+ p[-3 * step] = VP8kclip1[p2 + a3];
+ p[-2 * step] = VP8kclip1[p1 + a2];
+ p[- step] = VP8kclip1[p0 + a1];
+ p[ 0] = VP8kclip1[q0 - a1];
+ p[ step] = VP8kclip1[q1 - a2];
+ p[ 2 * step] = VP8kclip1[q2 - a3];
+}
+
+static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
+ const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ return (abs_mips32(p1 - p0) > thresh) || (abs_mips32(q1 - q0) > thresh);
+}
+
+static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int t) {
+ const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step];
+ return ((4 * abs_mips32(p0 - q0) + abs_mips32(p1 - q1)) <= t);
+}
+
+static WEBP_INLINE int needs_filter2(const uint8_t* p,
+ int step, int t, int it) {
+ const int p3 = p[-4 * step], p2 = p[-3 * step];
+ const int p1 = p[-2 * step], p0 = p[-step];
+ const int q0 = p[0], q1 = p[step], q2 = p[2 * step], q3 = p[3 * step];
+ if ((4 * abs_mips32(p0 - q0) + abs_mips32(p1 - q1)) > t) {
+ return 0;
+ }
+ return abs_mips32(p3 - p2) <= it && abs_mips32(p2 - p1) <= it &&
+ abs_mips32(p1 - p0) <= it && abs_mips32(q3 - q2) <= it &&
+ abs_mips32(q2 - q1) <= it && abs_mips32(q1 - q0) <= it;
+}
+
+static WEBP_INLINE void FilterLoop26(uint8_t* p,
+ int hstride, int vstride, int size,
+ int thresh, int ithresh, int hev_thresh) {
+ const int thresh2 = 2 * thresh + 1;
+ while (size-- > 0) {
+ if (needs_filter2(p, hstride, thresh2, ithresh)) {
+ if (hev(p, hstride, hev_thresh)) {
+ do_filter2(p, hstride);
+ } else {
+ do_filter6(p, hstride);
+ }
+ }
+ p += vstride;
+ }
+}
+
+static WEBP_INLINE void FilterLoop24(uint8_t* p,
+ int hstride, int vstride, int size,
+ int thresh, int ithresh, int hev_thresh) {
+ const int thresh2 = 2 * thresh + 1;
+ while (size-- > 0) {
+ if (needs_filter2(p, hstride, thresh2, ithresh)) {
+ if (hev(p, hstride, hev_thresh)) {
+ do_filter2(p, hstride);
+ } else {
+ do_filter4(p, hstride);
+ }
+ }
+ p += vstride;
+ }
+}
+
+// on macroblock edges
+static void VFilter16(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter16(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+}
+
+// 8-pixels wide variant, for chroma filtering
+static void VFilter8(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh);
+ FilterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter8(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh);
+ FilterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh);
+}
+
+static void VFilter8i(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+}
+
+// on three inner edges
+static void VFilter16i(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+ }
+}
+
+static void HFilter16i(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Simple In-loop filtering (Paragraph 15.2)
+
+static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
+ int i;
+ const int thresh2 = 2 * thresh + 1;
+ for (i = 0; i < 16; ++i) {
+ if (needs_filter(p + i, stride, thresh2)) {
+ do_filter2(p + i, stride);
+ }
+ }
+}
+
+static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
+ int i;
+ const int thresh2 = 2 * thresh + 1;
+ for (i = 0; i < 16; ++i) {
+ if (needs_filter(p + i * stride, 1, thresh2)) {
+ do_filter2(p + i * stride, 1);
+ }
+ }
+}
+
+static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ SimpleVFilter16(p, stride, thresh);
+ }
+}
+
+static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ SimpleHFilter16(p, stride, thresh);
+ }
+}
+
+static void TransformOne(const int16_t* in, uint8_t* dst) {
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8, temp9;
+ int temp10, temp11, temp12, temp13, temp14;
+ int temp15, temp16, temp17, temp18;
+ int16_t* p_in = (int16_t*)in;
+
+ // loops unrolled and merged to avoid usage of tmp buffer
+ // and to reduce number of stalls. MUL macro is written
+ // in assembler and inlined
+ __asm__ volatile(
+ "lh %[temp0], 0(%[in]) \n\t"
+ "lh %[temp8], 16(%[in]) \n\t"
+ "lh %[temp4], 8(%[in]) \n\t"
+ "lh %[temp12], 24(%[in]) \n\t"
+ "addu %[temp16], %[temp0], %[temp8] \n\t"
+ "subu %[temp0], %[temp0], %[temp8] \n\t"
+ "mul %[temp8], %[temp4], %[kC2] \n\t"
+ "mul %[temp17], %[temp12], %[kC1] \n\t"
+ "mul %[temp4], %[temp4], %[kC1] \n\t"
+ "mul %[temp12], %[temp12], %[kC2] \n\t"
+ "lh %[temp1], 2(%[in]) \n\t"
+ "lh %[temp5], 10(%[in]) \n\t"
+ "lh %[temp9], 18(%[in]) \n\t"
+ "lh %[temp13], 26(%[in]) \n\t"
+ "sra %[temp8], %[temp8], 16 \n\t"
+ "sra %[temp17], %[temp17], 16 \n\t"
+ "sra %[temp4], %[temp4], 16 \n\t"
+ "sra %[temp12], %[temp12], 16 \n\t"
+ "lh %[temp2], 4(%[in]) \n\t"
+ "lh %[temp6], 12(%[in]) \n\t"
+ "lh %[temp10], 20(%[in]) \n\t"
+ "lh %[temp14], 28(%[in]) \n\t"
+ "subu %[temp17], %[temp8], %[temp17] \n\t"
+ "addu %[temp4], %[temp4], %[temp12] \n\t"
+ "addu %[temp8], %[temp16], %[temp4] \n\t"
+ "subu %[temp4], %[temp16], %[temp4] \n\t"
+ "addu %[temp16], %[temp1], %[temp9] \n\t"
+ "subu %[temp1], %[temp1], %[temp9] \n\t"
+ "lh %[temp3], 6(%[in]) \n\t"
+ "lh %[temp7], 14(%[in]) \n\t"
+ "lh %[temp11], 22(%[in]) \n\t"
+ "lh %[temp15], 30(%[in]) \n\t"
+ "addu %[temp12], %[temp0], %[temp17] \n\t"
+ "subu %[temp0], %[temp0], %[temp17] \n\t"
+ "mul %[temp9], %[temp5], %[kC2] \n\t"
+ "mul %[temp17], %[temp13], %[kC1] \n\t"
+ "mul %[temp5], %[temp5], %[kC1] \n\t"
+ "mul %[temp13], %[temp13], %[kC2] \n\t"
+ "sra %[temp9], %[temp9], 16 \n\t"
+ "sra %[temp17], %[temp17], 16 \n\t"
+ "subu %[temp17], %[temp9], %[temp17] \n\t"
+ "sra %[temp5], %[temp5], 16 \n\t"
+ "sra %[temp13], %[temp13], 16 \n\t"
+ "addu %[temp5], %[temp5], %[temp13] \n\t"
+ "addu %[temp13], %[temp1], %[temp17] \n\t"
+ "subu %[temp1], %[temp1], %[temp17] \n\t"
+ "mul %[temp17], %[temp14], %[kC1] \n\t"
+ "mul %[temp14], %[temp14], %[kC2] \n\t"
+ "addu %[temp9], %[temp16], %[temp5] \n\t"
+ "subu %[temp5], %[temp16], %[temp5] \n\t"
+ "addu %[temp16], %[temp2], %[temp10] \n\t"
+ "subu %[temp2], %[temp2], %[temp10] \n\t"
+ "mul %[temp10], %[temp6], %[kC2] \n\t"
+ "mul %[temp6], %[temp6], %[kC1] \n\t"
+ "sra %[temp17], %[temp17], 16 \n\t"
+ "sra %[temp14], %[temp14], 16 \n\t"
+ "sra %[temp10], %[temp10], 16 \n\t"
+ "sra %[temp6], %[temp6], 16 \n\t"
+ "subu %[temp17], %[temp10], %[temp17] \n\t"
+ "addu %[temp6], %[temp6], %[temp14] \n\t"
+ "addu %[temp10], %[temp16], %[temp6] \n\t"
+ "subu %[temp6], %[temp16], %[temp6] \n\t"
+ "addu %[temp14], %[temp2], %[temp17] \n\t"
+ "subu %[temp2], %[temp2], %[temp17] \n\t"
+ "mul %[temp17], %[temp15], %[kC1] \n\t"
+ "mul %[temp15], %[temp15], %[kC2] \n\t"
+ "addu %[temp16], %[temp3], %[temp11] \n\t"
+ "subu %[temp3], %[temp3], %[temp11] \n\t"
+ "mul %[temp11], %[temp7], %[kC2] \n\t"
+ "mul %[temp7], %[temp7], %[kC1] \n\t"
+ "addiu %[temp8], %[temp8], 4 \n\t"
+ "addiu %[temp12], %[temp12], 4 \n\t"
+ "addiu %[temp0], %[temp0], 4 \n\t"
+ "addiu %[temp4], %[temp4], 4 \n\t"
+ "sra %[temp17], %[temp17], 16 \n\t"
+ "sra %[temp15], %[temp15], 16 \n\t"
+ "sra %[temp11], %[temp11], 16 \n\t"
+ "sra %[temp7], %[temp7], 16 \n\t"
+ "subu %[temp17], %[temp11], %[temp17] \n\t"
+ "addu %[temp7], %[temp7], %[temp15] \n\t"
+ "addu %[temp15], %[temp3], %[temp17] \n\t"
+ "subu %[temp3], %[temp3], %[temp17] \n\t"
+ "addu %[temp11], %[temp16], %[temp7] \n\t"
+ "subu %[temp7], %[temp16], %[temp7] \n\t"
+ "addu %[temp16], %[temp8], %[temp10] \n\t"
+ "subu %[temp8], %[temp8], %[temp10] \n\t"
+ "mul %[temp10], %[temp9], %[kC2] \n\t"
+ "mul %[temp17], %[temp11], %[kC1] \n\t"
+ "mul %[temp9], %[temp9], %[kC1] \n\t"
+ "mul %[temp11], %[temp11], %[kC2] \n\t"
+ "sra %[temp10], %[temp10], 16 \n\t"
+ "sra %[temp17], %[temp17], 16 \n\t"
+ "sra %[temp9], %[temp9], 16 \n\t"
+ "sra %[temp11], %[temp11], 16 \n\t"
+ "subu %[temp17], %[temp10], %[temp17] \n\t"
+ "addu %[temp11], %[temp9], %[temp11] \n\t"
+ "addu %[temp10], %[temp12], %[temp14] \n\t"
+ "subu %[temp12], %[temp12], %[temp14] \n\t"
+ "mul %[temp14], %[temp13], %[kC2] \n\t"
+ "mul %[temp9], %[temp15], %[kC1] \n\t"
+ "mul %[temp13], %[temp13], %[kC1] \n\t"
+ "mul %[temp15], %[temp15], %[kC2] \n\t"
+ "sra %[temp14], %[temp14], 16 \n\t"
+ "sra %[temp9], %[temp9], 16 \n\t"
+ "sra %[temp13], %[temp13], 16 \n\t"
+ "sra %[temp15], %[temp15], 16 \n\t"
+ "subu %[temp9], %[temp14], %[temp9] \n\t"
+ "addu %[temp15], %[temp13], %[temp15] \n\t"
+ "addu %[temp14], %[temp0], %[temp2] \n\t"
+ "subu %[temp0], %[temp0], %[temp2] \n\t"
+ "mul %[temp2], %[temp1], %[kC2] \n\t"
+ "mul %[temp13], %[temp3], %[kC1] \n\t"
+ "mul %[temp1], %[temp1], %[kC1] \n\t"
+ "mul %[temp3], %[temp3], %[kC2] \n\t"
+ "sra %[temp2], %[temp2], 16 \n\t"
+ "sra %[temp13], %[temp13], 16 \n\t"
+ "sra %[temp1], %[temp1], 16 \n\t"
+ "sra %[temp3], %[temp3], 16 \n\t"
+ "subu %[temp13], %[temp2], %[temp13] \n\t"
+ "addu %[temp3], %[temp1], %[temp3] \n\t"
+ "addu %[temp2], %[temp4], %[temp6] \n\t"
+ "subu %[temp4], %[temp4], %[temp6] \n\t"
+ "mul %[temp6], %[temp5], %[kC2] \n\t"
+ "mul %[temp1], %[temp7], %[kC1] \n\t"
+ "mul %[temp5], %[temp5], %[kC1] \n\t"
+ "mul %[temp7], %[temp7], %[kC2] \n\t"
+ "sra %[temp6], %[temp6], 16 \n\t"
+ "sra %[temp1], %[temp1], 16 \n\t"
+ "sra %[temp5], %[temp5], 16 \n\t"
+ "sra %[temp7], %[temp7], 16 \n\t"
+ "subu %[temp1], %[temp6], %[temp1] \n\t"
+ "addu %[temp7], %[temp5], %[temp7] \n\t"
+ "addu %[temp5], %[temp16], %[temp11] \n\t"
+ "subu %[temp16], %[temp16], %[temp11] \n\t"
+ "addu %[temp11], %[temp8], %[temp17] \n\t"
+ "subu %[temp8], %[temp8], %[temp17] \n\t"
+ "sra %[temp5], %[temp5], 3 \n\t"
+ "sra %[temp16], %[temp16], 3 \n\t"
+ "sra %[temp11], %[temp11], 3 \n\t"
+ "sra %[temp8], %[temp8], 3 \n\t"
+ "addu %[temp17], %[temp10], %[temp15] \n\t"
+ "subu %[temp10], %[temp10], %[temp15] \n\t"
+ "addu %[temp15], %[temp12], %[temp9] \n\t"
+ "subu %[temp12], %[temp12], %[temp9] \n\t"
+ "sra %[temp17], %[temp17], 3 \n\t"
+ "sra %[temp10], %[temp10], 3 \n\t"
+ "sra %[temp15], %[temp15], 3 \n\t"
+ "sra %[temp12], %[temp12], 3 \n\t"
+ "addu %[temp9], %[temp14], %[temp3] \n\t"
+ "subu %[temp14], %[temp14], %[temp3] \n\t"
+ "addu %[temp3], %[temp0], %[temp13] \n\t"
+ "subu %[temp0], %[temp0], %[temp13] \n\t"
+ "sra %[temp9], %[temp9], 3 \n\t"
+ "sra %[temp14], %[temp14], 3 \n\t"
+ "sra %[temp3], %[temp3], 3 \n\t"
+ "sra %[temp0], %[temp0], 3 \n\t"
+ "addu %[temp13], %[temp2], %[temp7] \n\t"
+ "subu %[temp2], %[temp2], %[temp7] \n\t"
+ "addu %[temp7], %[temp4], %[temp1] \n\t"
+ "subu %[temp4], %[temp4], %[temp1] \n\t"
+ "sra %[temp13], %[temp13], 3 \n\t"
+ "sra %[temp2], %[temp2], 3 \n\t"
+ "sra %[temp7], %[temp7], 3 \n\t"
+ "sra %[temp4], %[temp4], 3 \n\t"
+ "addiu %[temp6], $zero, 255 \n\t"
+ "lbu %[temp1], 0+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "addu %[temp1], %[temp1], %[temp5] \n\t"
+ "sra %[temp5], %[temp1], 8 \n\t"
+ "sra %[temp18], %[temp1], 31 \n\t"
+ "beqz %[temp5], 1f \n\t"
+ "xor %[temp1], %[temp1], %[temp1] \n\t"
+ "movz %[temp1], %[temp6], %[temp18] \n\t"
+ "1: \n\t"
+ "lbu %[temp18], 1+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp1], 0+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "addu %[temp18], %[temp18], %[temp11] \n\t"
+ "sra %[temp11], %[temp18], 8 \n\t"
+ "sra %[temp1], %[temp18], 31 \n\t"
+ "beqz %[temp11], 2f \n\t"
+ "xor %[temp18], %[temp18], %[temp18] \n\t"
+ "movz %[temp18], %[temp6], %[temp1] \n\t"
+ "2: \n\t"
+ "lbu %[temp1], 2+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp18], 1+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "addu %[temp1], %[temp1], %[temp8] \n\t"
+ "sra %[temp8], %[temp1], 8 \n\t"
+ "sra %[temp18], %[temp1], 31 \n\t"
+ "beqz %[temp8], 3f \n\t"
+ "xor %[temp1], %[temp1], %[temp1] \n\t"
+ "movz %[temp1], %[temp6], %[temp18] \n\t"
+ "3: \n\t"
+ "lbu %[temp18], 3+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp1], 2+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "addu %[temp18], %[temp18], %[temp16] \n\t"
+ "sra %[temp16], %[temp18], 8 \n\t"
+ "sra %[temp1], %[temp18], 31 \n\t"
+ "beqz %[temp16], 4f \n\t"
+ "xor %[temp18], %[temp18], %[temp18] \n\t"
+ "movz %[temp18], %[temp6], %[temp1] \n\t"
+ "4: \n\t"
+ "sb %[temp18], 3+0*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp5], 0+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp8], 1+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp11], 2+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp16], 3+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "addu %[temp5], %[temp5], %[temp17] \n\t"
+ "addu %[temp8], %[temp8], %[temp15] \n\t"
+ "addu %[temp11], %[temp11], %[temp12] \n\t"
+ "addu %[temp16], %[temp16], %[temp10] \n\t"
+ "sra %[temp18], %[temp5], 8 \n\t"
+ "sra %[temp1], %[temp5], 31 \n\t"
+ "beqz %[temp18], 5f \n\t"
+ "xor %[temp5], %[temp5], %[temp5] \n\t"
+ "movz %[temp5], %[temp6], %[temp1] \n\t"
+ "5: \n\t"
+ "sra %[temp18], %[temp8], 8 \n\t"
+ "sra %[temp1], %[temp8], 31 \n\t"
+ "beqz %[temp18], 6f \n\t"
+ "xor %[temp8], %[temp8], %[temp8] \n\t"
+ "movz %[temp8], %[temp6], %[temp1] \n\t"
+ "6: \n\t"
+ "sra %[temp18], %[temp11], 8 \n\t"
+ "sra %[temp1], %[temp11], 31 \n\t"
+ "sra %[temp17], %[temp16], 8 \n\t"
+ "sra %[temp15], %[temp16], 31 \n\t"
+ "beqz %[temp18], 7f \n\t"
+ "xor %[temp11], %[temp11], %[temp11] \n\t"
+ "movz %[temp11], %[temp6], %[temp1] \n\t"
+ "7: \n\t"
+ "beqz %[temp17], 8f \n\t"
+ "xor %[temp16], %[temp16], %[temp16] \n\t"
+ "movz %[temp16], %[temp6], %[temp15] \n\t"
+ "8: \n\t"
+ "sb %[temp5], 0+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp8], 1+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp11], 2+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp16], 3+1*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp5], 0+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp8], 1+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp11], 2+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp16], 3+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "addu %[temp5], %[temp5], %[temp9] \n\t"
+ "addu %[temp8], %[temp8], %[temp3] \n\t"
+ "addu %[temp11], %[temp11], %[temp0] \n\t"
+ "addu %[temp16], %[temp16], %[temp14] \n\t"
+ "sra %[temp18], %[temp5], 8 \n\t"
+ "sra %[temp1], %[temp5], 31 \n\t"
+ "sra %[temp17], %[temp8], 8 \n\t"
+ "sra %[temp15], %[temp8], 31 \n\t"
+ "sra %[temp12], %[temp11], 8 \n\t"
+ "sra %[temp10], %[temp11], 31 \n\t"
+ "sra %[temp9], %[temp16], 8 \n\t"
+ "sra %[temp3], %[temp16], 31 \n\t"
+ "beqz %[temp18], 9f \n\t"
+ "xor %[temp5], %[temp5], %[temp5] \n\t"
+ "movz %[temp5], %[temp6], %[temp1] \n\t"
+ "9: \n\t"
+ "beqz %[temp17], 10f \n\t"
+ "xor %[temp8], %[temp8], %[temp8] \n\t"
+ "movz %[temp8], %[temp6], %[temp15] \n\t"
+ "10: \n\t"
+ "beqz %[temp12], 11f \n\t"
+ "xor %[temp11], %[temp11], %[temp11] \n\t"
+ "movz %[temp11], %[temp6], %[temp10] \n\t"
+ "11: \n\t"
+ "beqz %[temp9], 12f \n\t"
+ "xor %[temp16], %[temp16], %[temp16] \n\t"
+ "movz %[temp16], %[temp6], %[temp3] \n\t"
+ "12: \n\t"
+ "sb %[temp5], 0+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp8], 1+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp11], 2+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp16], 3+2*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp5], 0+3*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp8], 1+3*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp11], 2+3*" XSTR(BPS) "(%[dst]) \n\t"
+ "lbu %[temp16], 3+3*" XSTR(BPS) "(%[dst]) \n\t"
+ "addu %[temp5], %[temp5], %[temp13] \n\t"
+ "addu %[temp8], %[temp8], %[temp7] \n\t"
+ "addu %[temp11], %[temp11], %[temp4] \n\t"
+ "addu %[temp16], %[temp16], %[temp2] \n\t"
+ "sra %[temp18], %[temp5], 8 \n\t"
+ "sra %[temp1], %[temp5], 31 \n\t"
+ "sra %[temp17], %[temp8], 8 \n\t"
+ "sra %[temp15], %[temp8], 31 \n\t"
+ "sra %[temp12], %[temp11], 8 \n\t"
+ "sra %[temp10], %[temp11], 31 \n\t"
+ "sra %[temp9], %[temp16], 8 \n\t"
+ "sra %[temp3], %[temp16], 31 \n\t"
+ "beqz %[temp18], 13f \n\t"
+ "xor %[temp5], %[temp5], %[temp5] \n\t"
+ "movz %[temp5], %[temp6], %[temp1] \n\t"
+ "13: \n\t"
+ "beqz %[temp17], 14f \n\t"
+ "xor %[temp8], %[temp8], %[temp8] \n\t"
+ "movz %[temp8], %[temp6], %[temp15] \n\t"
+ "14: \n\t"
+ "beqz %[temp12], 15f \n\t"
+ "xor %[temp11], %[temp11], %[temp11] \n\t"
+ "movz %[temp11], %[temp6], %[temp10] \n\t"
+ "15: \n\t"
+ "beqz %[temp9], 16f \n\t"
+ "xor %[temp16], %[temp16], %[temp16] \n\t"
+ "movz %[temp16], %[temp6], %[temp3] \n\t"
+ "16: \n\t"
+ "sb %[temp5], 0+3*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp8], 1+3*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp11], 2+3*" XSTR(BPS) "(%[dst]) \n\t"
+ "sb %[temp16], 3+3*" XSTR(BPS) "(%[dst]) \n\t"
+
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11),
+ [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14),
+ [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17),
+ [temp18]"=&r"(temp18)
+ : [in]"r"(p_in), [kC1]"r"(kC1), [kC2]"r"(kC2), [dst]"r"(dst)
+ : "memory", "hi", "lo"
+ );
+}
+
+static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) {
+ TransformOne(in, dst);
+ if (do_two) {
+ TransformOne(in + 16, dst + 4);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8DspInitMIPS32(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMIPS32(void) {
+ VP8InitClipTables();
+
+ VP8Transform = TransformTwo;
+
+ VP8VFilter16 = VFilter16;
+ VP8HFilter16 = HFilter16;
+ VP8VFilter8 = VFilter8;
+ VP8HFilter8 = HFilter8;
+ VP8VFilter16i = VFilter16i;
+ VP8HFilter16i = HFilter16i;
+ VP8VFilter8i = VFilter8i;
+ VP8HFilter8i = HFilter8i;
+
+ VP8SimpleVFilter16 = SimpleVFilter16;
+ VP8SimpleHFilter16 = SimpleHFilter16;
+ VP8SimpleVFilter16i = SimpleVFilter16i;
+ VP8SimpleHFilter16i = SimpleHFilter16i;
+}
+
+#else // !WEBP_USE_MIPS32
+
+WEBP_DSP_INIT_STUB(VP8DspInitMIPS32)
+
+#endif // WEBP_USE_MIPS32
diff --git a/src/third_party/libwebp/src/dsp/dec_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/dec_mips_dsp_r2.c
new file mode 100644
index 0000000..b0936bc
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec_mips_dsp_r2.c
@@ -0,0 +1,994 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of dsp functions
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+// Jovan Zelincevic (jovan.zelincevic@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#include "src/dsp/mips_macro.h"
+
+static const int kC1 = 20091 + (1 << 16);
+static const int kC2 = 35468;
+
+#define MUL(a, b) (((a) * (b)) >> 16)
+
+static void TransformDC(const int16_t* in, uint8_t* dst) {
+ int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9, temp10;
+
+ __asm__ volatile (
+ LOAD_WITH_OFFSET_X4(temp1, temp2, temp3, temp4, dst,
+ 0, 0, 0, 0,
+ 0, 1, 2, 3,
+ BPS)
+ "lh %[temp5], 0(%[in]) \n\t"
+ "addiu %[temp5], %[temp5], 4 \n\t"
+ "ins %[temp5], %[temp5], 16, 16 \n\t"
+ "shra.ph %[temp5], %[temp5], 3 \n\t"
+ CONVERT_2_BYTES_TO_HALF(temp6, temp7, temp8, temp9, temp10, temp1, temp2,
+ temp3, temp1, temp2, temp3, temp4)
+ STORE_SAT_SUM_X2(temp6, temp7, temp8, temp9, temp10, temp1, temp2, temp3,
+ temp5, temp5, temp5, temp5, temp5, temp5, temp5, temp5,
+ dst, 0, 1, 2, 3, BPS)
+
+ OUTPUT_EARLY_CLOBBER_REGS_10()
+ : [in]"r"(in), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void TransformAC3(const int16_t* in, uint8_t* dst) {
+ const int a = in[0] + 4;
+ int c4 = MUL(in[4], kC2);
+ const int d4 = MUL(in[4], kC1);
+ const int c1 = MUL(in[1], kC2);
+ const int d1 = MUL(in[1], kC1);
+ int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;
+ int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18;
+
+ __asm__ volatile (
+ "ins %[c4], %[d4], 16, 16 \n\t"
+ "replv.ph %[temp1], %[a] \n\t"
+ "replv.ph %[temp4], %[d1] \n\t"
+ ADD_SUB_HALVES(temp2, temp3, temp1, c4)
+ "replv.ph %[temp5], %[c1] \n\t"
+ SHIFT_R_SUM_X2(temp1, temp6, temp7, temp8, temp2, temp9, temp10, temp4,
+ temp2, temp2, temp3, temp3, temp4, temp5, temp4, temp5)
+ LOAD_WITH_OFFSET_X4(temp3, temp5, temp11, temp12, dst,
+ 0, 0, 0, 0,
+ 0, 1, 2, 3,
+ BPS)
+ CONVERT_2_BYTES_TO_HALF(temp13, temp14, temp3, temp15, temp5, temp16,
+ temp11, temp17, temp3, temp5, temp11, temp12)
+ PACK_2_HALVES_TO_WORD(temp12, temp18, temp7, temp6, temp1, temp8, temp2,
+ temp4, temp7, temp6, temp10, temp9)
+ STORE_SAT_SUM_X2(temp13, temp14, temp3, temp15, temp5, temp16, temp11,
+ temp17, temp12, temp18, temp1, temp8, temp2, temp4,
+ temp7, temp6, dst, 0, 1, 2, 3, BPS)
+
+ OUTPUT_EARLY_CLOBBER_REGS_18(),
+ [c4]"+&r"(c4)
+ : [dst]"r"(dst), [a]"r"(a), [d1]"r"(d1), [d4]"r"(d4), [c1]"r"(c1)
+ : "memory"
+ );
+}
+
+static void TransformOne(const int16_t* in, uint8_t* dst) {
+ int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;
+ int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18;
+
+ __asm__ volatile (
+ "ulw %[temp1], 0(%[in]) \n\t"
+ "ulw %[temp2], 16(%[in]) \n\t"
+ LOAD_IN_X2(temp5, temp6, 24, 26)
+ ADD_SUB_HALVES(temp3, temp4, temp1, temp2)
+ LOAD_IN_X2(temp1, temp2, 8, 10)
+ MUL_SHIFT_SUM(temp7, temp8, temp9, temp10, temp11, temp12, temp13, temp14,
+ temp10, temp8, temp9, temp7, temp1, temp2, temp5, temp6,
+ temp13, temp11, temp14, temp12)
+ INSERT_HALF_X2(temp8, temp7, temp10, temp9)
+ "ulw %[temp17], 4(%[in]) \n\t"
+ "ulw %[temp18], 20(%[in]) \n\t"
+ ADD_SUB_HALVES(temp1, temp2, temp3, temp8)
+ ADD_SUB_HALVES(temp5, temp6, temp4, temp7)
+ ADD_SUB_HALVES(temp7, temp8, temp17, temp18)
+ LOAD_IN_X2(temp17, temp18, 12, 14)
+ LOAD_IN_X2(temp9, temp10, 28, 30)
+ MUL_SHIFT_SUM(temp11, temp12, temp13, temp14, temp15, temp16, temp4, temp17,
+ temp12, temp14, temp11, temp13, temp17, temp18, temp9, temp10,
+ temp15, temp4, temp16, temp17)
+ INSERT_HALF_X2(temp11, temp12, temp13, temp14)
+ ADD_SUB_HALVES(temp17, temp8, temp8, temp11)
+ ADD_SUB_HALVES(temp3, temp4, temp7, temp12)
+
+ // horizontal
+ SRA_16(temp9, temp10, temp11, temp12, temp1, temp2, temp5, temp6)
+ INSERT_HALF_X2(temp1, temp6, temp5, temp2)
+ SRA_16(temp13, temp14, temp15, temp16, temp3, temp4, temp17, temp8)
+ "repl.ph %[temp2], 0x4 \n\t"
+ INSERT_HALF_X2(temp3, temp8, temp17, temp4)
+ "addq.ph %[temp1], %[temp1], %[temp2] \n\t"
+ "addq.ph %[temp6], %[temp6], %[temp2] \n\t"
+ ADD_SUB_HALVES(temp2, temp4, temp1, temp3)
+ ADD_SUB_HALVES(temp5, temp7, temp6, temp8)
+ MUL_SHIFT_SUM(temp1, temp3, temp6, temp8, temp9, temp13, temp17, temp18,
+ temp3, temp13, temp1, temp9, temp9, temp13, temp11, temp15,
+ temp6, temp17, temp8, temp18)
+ MUL_SHIFT_SUM(temp6, temp8, temp18, temp17, temp11, temp15, temp12, temp16,
+ temp8, temp15, temp6, temp11, temp12, temp16, temp10, temp14,
+ temp18, temp12, temp17, temp16)
+ INSERT_HALF_X2(temp1, temp3, temp9, temp13)
+ INSERT_HALF_X2(temp6, temp8, temp11, temp15)
+ SHIFT_R_SUM_X2(temp9, temp10, temp11, temp12, temp13, temp14, temp15,
+ temp16, temp2, temp4, temp5, temp7, temp3, temp1, temp8,
+ temp6)
+ PACK_2_HALVES_TO_WORD(temp1, temp2, temp3, temp4, temp9, temp12, temp13,
+ temp16, temp11, temp10, temp15, temp14)
+ LOAD_WITH_OFFSET_X4(temp10, temp11, temp14, temp15, dst,
+ 0, 0, 0, 0,
+ 0, 1, 2, 3,
+ BPS)
+ CONVERT_2_BYTES_TO_HALF(temp5, temp6, temp7, temp8, temp17, temp18, temp10,
+ temp11, temp10, temp11, temp14, temp15)
+ STORE_SAT_SUM_X2(temp5, temp6, temp7, temp8, temp17, temp18, temp10, temp11,
+ temp9, temp12, temp1, temp2, temp13, temp16, temp3, temp4,
+ dst, 0, 1, 2, 3, BPS)
+
+ OUTPUT_EARLY_CLOBBER_REGS_18()
+ : [dst]"r"(dst), [in]"r"(in), [kC1]"r"(kC1), [kC2]"r"(kC2)
+ : "memory", "hi", "lo"
+ );
+}
+
+static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) {
+ TransformOne(in, dst);
+ if (do_two) {
+ TransformOne(in + 16, dst + 4);
+ }
+}
+
+static WEBP_INLINE void FilterLoop26(uint8_t* p,
+ int hstride, int vstride, int size,
+ int thresh, int ithresh, int hev_thresh) {
+ const int thresh2 = 2 * thresh + 1;
+ int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;
+ int temp10, temp11, temp12, temp13, temp14, temp15;
+
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "1: \n\t"
+ "negu %[temp1], %[hstride] \n\t"
+ "addiu %[size], %[size], -1 \n\t"
+ "sll %[temp2], %[hstride], 1 \n\t"
+ "sll %[temp3], %[temp1], 1 \n\t"
+ "addu %[temp4], %[temp2], %[hstride] \n\t"
+ "addu %[temp5], %[temp3], %[temp1] \n\t"
+ "lbu %[temp7], 0(%[p]) \n\t"
+ "sll %[temp6], %[temp3], 1 \n\t"
+ "lbux %[temp8], %[temp5](%[p]) \n\t"
+ "lbux %[temp9], %[temp3](%[p]) \n\t"
+ "lbux %[temp10], %[temp1](%[p]) \n\t"
+ "lbux %[temp11], %[temp6](%[p]) \n\t"
+ "lbux %[temp12], %[hstride](%[p]) \n\t"
+ "lbux %[temp13], %[temp2](%[p]) \n\t"
+ "lbux %[temp14], %[temp4](%[p]) \n\t"
+ "subu %[temp1], %[temp10], %[temp7] \n\t"
+ "subu %[temp2], %[temp9], %[temp12] \n\t"
+ "absq_s.w %[temp3], %[temp1] \n\t"
+ "absq_s.w %[temp4], %[temp2] \n\t"
+ "negu %[temp1], %[temp1] \n\t"
+ "sll %[temp3], %[temp3], 2 \n\t"
+ "addu %[temp15], %[temp3], %[temp4] \n\t"
+ "subu %[temp3], %[temp15], %[thresh2] \n\t"
+ "sll %[temp6], %[temp1], 1 \n\t"
+ "bgtz %[temp3], 3f \n\t"
+ " subu %[temp4], %[temp11], %[temp8] \n\t"
+ "absq_s.w %[temp4], %[temp4] \n\t"
+ "shll_s.w %[temp2], %[temp2], 24 \n\t"
+ "subu %[temp4], %[temp4], %[ithresh] \n\t"
+ "bgtz %[temp4], 3f \n\t"
+ " subu %[temp3], %[temp8], %[temp9] \n\t"
+ "absq_s.w %[temp3], %[temp3] \n\t"
+ "subu %[temp3], %[temp3], %[ithresh] \n\t"
+ "bgtz %[temp3], 3f \n\t"
+ " subu %[temp5], %[temp9], %[temp10] \n\t"
+ "absq_s.w %[temp3], %[temp5] \n\t"
+ "absq_s.w %[temp5], %[temp5] \n\t"
+ "subu %[temp3], %[temp3], %[ithresh] \n\t"
+ "bgtz %[temp3], 3f \n\t"
+ " subu %[temp3], %[temp14], %[temp13] \n\t"
+ "absq_s.w %[temp3], %[temp3] \n\t"
+ "slt %[temp5], %[hev_thresh], %[temp5] \n\t"
+ "subu %[temp3], %[temp3], %[ithresh] \n\t"
+ "bgtz %[temp3], 3f \n\t"
+ " subu %[temp3], %[temp13], %[temp12] \n\t"
+ "absq_s.w %[temp3], %[temp3] \n\t"
+ "sra %[temp4], %[temp2], 24 \n\t"
+ "subu %[temp3], %[temp3], %[ithresh] \n\t"
+ "bgtz %[temp3], 3f \n\t"
+ " subu %[temp15], %[temp12], %[temp7] \n\t"
+ "absq_s.w %[temp3], %[temp15] \n\t"
+ "absq_s.w %[temp15], %[temp15] \n\t"
+ "subu %[temp3], %[temp3], %[ithresh] \n\t"
+ "bgtz %[temp3], 3f \n\t"
+ " slt %[temp15], %[hev_thresh], %[temp15] \n\t"
+ "addu %[temp3], %[temp6], %[temp1] \n\t"
+ "or %[temp2], %[temp5], %[temp15] \n\t"
+ "addu %[temp5], %[temp4], %[temp3] \n\t"
+ "beqz %[temp2], 4f \n\t"
+ " shra_r.w %[temp1], %[temp5], 3 \n\t"
+ "addiu %[temp2], %[temp5], 3 \n\t"
+ "sra %[temp2], %[temp2], 3 \n\t"
+ "shll_s.w %[temp1], %[temp1], 27 \n\t"
+ "shll_s.w %[temp2], %[temp2], 27 \n\t"
+ "subu %[temp3], %[p], %[hstride] \n\t"
+ "sra %[temp1], %[temp1], 27 \n\t"
+ "sra %[temp2], %[temp2], 27 \n\t"
+ "subu %[temp1], %[temp7], %[temp1] \n\t"
+ "addu %[temp2], %[temp10], %[temp2] \n\t"
+ "lbux %[temp2], %[temp2](%[VP8kclip1]) \n\t"
+ "lbux %[temp1], %[temp1](%[VP8kclip1]) \n\t"
+ "sb %[temp2], 0(%[temp3]) \n\t"
+ "j 3f \n\t"
+ " sb %[temp1], 0(%[p]) \n\t"
+ "4: \n\t"
+ "shll_s.w %[temp5], %[temp5], 24 \n\t"
+ "subu %[temp14], %[p], %[hstride] \n\t"
+ "subu %[temp11], %[temp14], %[hstride] \n\t"
+ "sra %[temp6], %[temp5], 24 \n\t"
+ "sll %[temp1], %[temp6], 3 \n\t"
+ "subu %[temp15], %[temp11], %[hstride] \n\t"
+ "addu %[temp2], %[temp6], %[temp1] \n\t"
+ "sll %[temp3], %[temp2], 1 \n\t"
+ "addu %[temp4], %[temp3], %[temp2] \n\t"
+ "addiu %[temp2], %[temp2], 63 \n\t"
+ "addiu %[temp3], %[temp3], 63 \n\t"
+ "addiu %[temp4], %[temp4], 63 \n\t"
+ "sra %[temp2], %[temp2], 7 \n\t"
+ "sra %[temp3], %[temp3], 7 \n\t"
+ "sra %[temp4], %[temp4], 7 \n\t"
+ "addu %[temp1], %[temp8], %[temp2] \n\t"
+ "addu %[temp5], %[temp9], %[temp3] \n\t"
+ "addu %[temp6], %[temp10], %[temp4] \n\t"
+ "subu %[temp8], %[temp7], %[temp4] \n\t"
+ "subu %[temp7], %[temp12], %[temp3] \n\t"
+ "addu %[temp10], %[p], %[hstride] \n\t"
+ "subu %[temp9], %[temp13], %[temp2] \n\t"
+ "addu %[temp12], %[temp10], %[hstride] \n\t"
+ "lbux %[temp2], %[temp1](%[VP8kclip1]) \n\t"
+ "lbux %[temp3], %[temp5](%[VP8kclip1]) \n\t"
+ "lbux %[temp4], %[temp6](%[VP8kclip1]) \n\t"
+ "lbux %[temp5], %[temp8](%[VP8kclip1]) \n\t"
+ "lbux %[temp6], %[temp7](%[VP8kclip1]) \n\t"
+ "lbux %[temp8], %[temp9](%[VP8kclip1]) \n\t"
+ "sb %[temp2], 0(%[temp15]) \n\t"
+ "sb %[temp3], 0(%[temp11]) \n\t"
+ "sb %[temp4], 0(%[temp14]) \n\t"
+ "sb %[temp5], 0(%[p]) \n\t"
+ "sb %[temp6], 0(%[temp10]) \n\t"
+ "sb %[temp8], 0(%[temp12]) \n\t"
+ "3: \n\t"
+ "bgtz %[size], 1b \n\t"
+ " addu %[p], %[p], %[vstride] \n\t"
+ ".set pop \n\t"
+ : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),[temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp6]"=&r"(temp6),
+ [temp7]"=&r"(temp7),[temp8]"=&r"(temp8),[temp9]"=&r"(temp9),
+ [temp10]"=&r"(temp10),[temp11]"=&r"(temp11),[temp12]"=&r"(temp12),
+ [temp13]"=&r"(temp13),[temp14]"=&r"(temp14),[temp15]"=&r"(temp15),
+ [size]"+&r"(size), [p]"+&r"(p)
+ : [hstride]"r"(hstride), [thresh2]"r"(thresh2),
+ [ithresh]"r"(ithresh),[vstride]"r"(vstride), [hev_thresh]"r"(hev_thresh),
+ [VP8kclip1]"r"(VP8kclip1)
+ : "memory"
+ );
+}
+
+static WEBP_INLINE void FilterLoop24(uint8_t* p,
+ int hstride, int vstride, int size,
+ int thresh, int ithresh, int hev_thresh) {
+ int p0, q0, p1, q1, p2, q2, p3, q3;
+ int step1, step2, temp1, temp2, temp3, temp4;
+ uint8_t* pTemp0;
+ uint8_t* pTemp1;
+ const int thresh2 = 2 * thresh + 1;
+
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "bltz %[size], 3f \n\t"
+ " nop \n\t"
+ "2: \n\t"
+ "negu %[step1], %[hstride] \n\t"
+ "lbu %[q0], 0(%[p]) \n\t"
+ "lbux %[p0], %[step1](%[p]) \n\t"
+ "subu %[step1], %[step1], %[hstride] \n\t"
+ "lbux %[q1], %[hstride](%[p]) \n\t"
+ "subu %[temp1], %[p0], %[q0] \n\t"
+ "lbux %[p1], %[step1](%[p]) \n\t"
+ "addu %[step2], %[hstride], %[hstride] \n\t"
+ "absq_s.w %[temp2], %[temp1] \n\t"
+ "subu %[temp3], %[p1], %[q1] \n\t"
+ "absq_s.w %[temp4], %[temp3] \n\t"
+ "sll %[temp2], %[temp2], 2 \n\t"
+ "addu %[temp2], %[temp2], %[temp4] \n\t"
+ "subu %[temp4], %[temp2], %[thresh2] \n\t"
+ "subu %[step1], %[step1], %[hstride] \n\t"
+ "bgtz %[temp4], 0f \n\t"
+ " lbux %[p2], %[step1](%[p]) \n\t"
+ "subu %[step1], %[step1], %[hstride] \n\t"
+ "lbux %[q2], %[step2](%[p]) \n\t"
+ "lbux %[p3], %[step1](%[p]) \n\t"
+ "subu %[temp4], %[p2], %[p1] \n\t"
+ "addu %[step2], %[step2], %[hstride] \n\t"
+ "subu %[temp2], %[p3], %[p2] \n\t"
+ "absq_s.w %[temp4], %[temp4] \n\t"
+ "absq_s.w %[temp2], %[temp2] \n\t"
+ "lbux %[q3], %[step2](%[p]) \n\t"
+ "subu %[temp4], %[temp4], %[ithresh] \n\t"
+ "negu %[temp1], %[temp1] \n\t"
+ "bgtz %[temp4], 0f \n\t"
+ " subu %[temp2], %[temp2], %[ithresh] \n\t"
+ "subu %[p3], %[p1], %[p0] \n\t"
+ "bgtz %[temp2], 0f \n\t"
+ " absq_s.w %[p3], %[p3] \n\t"
+ "subu %[temp4], %[q3], %[q2] \n\t"
+ "subu %[pTemp0], %[p], %[hstride] \n\t"
+ "absq_s.w %[temp4], %[temp4] \n\t"
+ "subu %[temp2], %[p3], %[ithresh] \n\t"
+ "sll %[step1], %[temp1], 1 \n\t"
+ "bgtz %[temp2], 0f \n\t"
+ " subu %[temp4], %[temp4], %[ithresh] \n\t"
+ "subu %[temp2], %[q2], %[q1] \n\t"
+ "bgtz %[temp4], 0f \n\t"
+ " absq_s.w %[temp2], %[temp2] \n\t"
+ "subu %[q3], %[q1], %[q0] \n\t"
+ "absq_s.w %[q3], %[q3] \n\t"
+ "subu %[temp2], %[temp2], %[ithresh] \n\t"
+ "addu %[temp1], %[temp1], %[step1] \n\t"
+ "bgtz %[temp2], 0f \n\t"
+ " subu %[temp4], %[q3], %[ithresh] \n\t"
+ "slt %[p3], %[hev_thresh], %[p3] \n\t"
+ "bgtz %[temp4], 0f \n\t"
+ " slt %[q3], %[hev_thresh], %[q3] \n\t"
+ "or %[q3], %[q3], %[p3] \n\t"
+ "bgtz %[q3], 1f \n\t"
+ " shra_r.w %[temp2], %[temp1], 3 \n\t"
+ "addiu %[temp1], %[temp1], 3 \n\t"
+ "sra %[temp1], %[temp1], 3 \n\t"
+ "shll_s.w %[temp2], %[temp2], 27 \n\t"
+ "shll_s.w %[temp1], %[temp1], 27 \n\t"
+ "addu %[pTemp1], %[p], %[hstride] \n\t"
+ "sra %[temp2], %[temp2], 27 \n\t"
+ "sra %[temp1], %[temp1], 27 \n\t"
+ "addiu %[step1], %[temp2], 1 \n\t"
+ "sra %[step1], %[step1], 1 \n\t"
+ "addu %[p0], %[p0], %[temp1] \n\t"
+ "addu %[p1], %[p1], %[step1] \n\t"
+ "subu %[q0], %[q0], %[temp2] \n\t"
+ "subu %[q1], %[q1], %[step1] \n\t"
+ "lbux %[temp2], %[p0](%[VP8kclip1]) \n\t"
+ "lbux %[temp3], %[q0](%[VP8kclip1]) \n\t"
+ "lbux %[temp4], %[q1](%[VP8kclip1]) \n\t"
+ "sb %[temp2], 0(%[pTemp0]) \n\t"
+ "lbux %[temp1], %[p1](%[VP8kclip1]) \n\t"
+ "subu %[pTemp0], %[pTemp0], %[hstride] \n\t"
+ "sb %[temp3], 0(%[p]) \n\t"
+ "sb %[temp4], 0(%[pTemp1]) \n\t"
+ "j 0f \n\t"
+ " sb %[temp1], 0(%[pTemp0]) \n\t"
+ "1: \n\t"
+ "shll_s.w %[temp3], %[temp3], 24 \n\t"
+ "sra %[temp3], %[temp3], 24 \n\t"
+ "addu %[temp1], %[temp1], %[temp3] \n\t"
+ "shra_r.w %[temp2], %[temp1], 3 \n\t"
+ "addiu %[temp1], %[temp1], 3 \n\t"
+ "shll_s.w %[temp2], %[temp2], 27 \n\t"
+ "sra %[temp1], %[temp1], 3 \n\t"
+ "shll_s.w %[temp1], %[temp1], 27 \n\t"
+ "sra %[temp2], %[temp2], 27 \n\t"
+ "sra %[temp1], %[temp1], 27 \n\t"
+ "addu %[p0], %[p0], %[temp1] \n\t"
+ "subu %[q0], %[q0], %[temp2] \n\t"
+ "lbux %[temp1], %[p0](%[VP8kclip1]) \n\t"
+ "lbux %[temp2], %[q0](%[VP8kclip1]) \n\t"
+ "sb %[temp2], 0(%[p]) \n\t"
+ "sb %[temp1], 0(%[pTemp0]) \n\t"
+ "0: \n\t"
+ "subu %[size], %[size], 1 \n\t"
+ "bgtz %[size], 2b \n\t"
+ " addu %[p], %[p], %[vstride] \n\t"
+ "3: \n\t"
+ ".set pop \n\t"
+ : [p0]"=&r"(p0), [q0]"=&r"(q0), [p1]"=&r"(p1), [q1]"=&r"(q1),
+ [p2]"=&r"(p2), [q2]"=&r"(q2), [p3]"=&r"(p3), [q3]"=&r"(q3),
+ [step2]"=&r"(step2), [step1]"=&r"(step1), [temp1]"=&r"(temp1),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4),
+ [pTemp0]"=&r"(pTemp0), [pTemp1]"=&r"(pTemp1), [p]"+&r"(p),
+ [size]"+&r"(size)
+ : [vstride]"r"(vstride), [ithresh]"r"(ithresh),
+ [hev_thresh]"r"(hev_thresh), [hstride]"r"(hstride),
+ [VP8kclip1]"r"(VP8kclip1), [thresh2]"r"(thresh2)
+ : "memory"
+ );
+}
+
+// on macroblock edges
+static void VFilter16(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter16(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+}
+
+// 8-pixels wide variant, for chroma filtering
+static void VFilter8(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh);
+ FilterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter8(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh);
+ FilterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh);
+}
+
+// on three inner edges
+static void VFilter16i(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh);
+ }
+}
+
+static void HFilter16i(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh);
+ }
+}
+
+static void VFilter8i(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
+}
+
+static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+ FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
+}
+
+#undef MUL
+
+//------------------------------------------------------------------------------
+// Simple In-loop filtering (Paragraph 15.2)
+
+static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
+ int i;
+ const int thresh2 = 2 * thresh + 1;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8;
+ uint8_t* p1 = p - stride;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "li %[i], 16 \n\t"
+ "0: \n\t"
+ "negu %[temp4], %[stride] \n\t"
+ "sll %[temp5], %[temp4], 1 \n\t"
+ "lbu %[temp2], 0(%[p]) \n\t"
+ "lbux %[temp3], %[stride](%[p]) \n\t"
+ "lbux %[temp1], %[temp4](%[p]) \n\t"
+ "lbux %[temp0], %[temp5](%[p]) \n\t"
+ "subu %[temp7], %[temp1], %[temp2] \n\t"
+ "subu %[temp6], %[temp0], %[temp3] \n\t"
+ "absq_s.w %[temp4], %[temp7] \n\t"
+ "absq_s.w %[temp5], %[temp6] \n\t"
+ "sll %[temp4], %[temp4], 2 \n\t"
+ "subu %[temp5], %[temp5], %[thresh2] \n\t"
+ "addu %[temp5], %[temp4], %[temp5] \n\t"
+ "negu %[temp8], %[temp7] \n\t"
+ "bgtz %[temp5], 1f \n\t"
+ " addiu %[i], %[i], -1 \n\t"
+ "sll %[temp4], %[temp8], 1 \n\t"
+ "shll_s.w %[temp5], %[temp6], 24 \n\t"
+ "addu %[temp3], %[temp4], %[temp8] \n\t"
+ "sra %[temp5], %[temp5], 24 \n\t"
+ "addu %[temp3], %[temp3], %[temp5] \n\t"
+ "addiu %[temp7], %[temp3], 3 \n\t"
+ "sra %[temp7], %[temp7], 3 \n\t"
+ "shra_r.w %[temp8], %[temp3], 3 \n\t"
+ "shll_s.w %[temp0], %[temp7], 27 \n\t"
+ "shll_s.w %[temp4], %[temp8], 27 \n\t"
+ "sra %[temp0], %[temp0], 27 \n\t"
+ "sra %[temp4], %[temp4], 27 \n\t"
+ "addu %[temp7], %[temp1], %[temp0] \n\t"
+ "subu %[temp2], %[temp2], %[temp4] \n\t"
+ "lbux %[temp3], %[temp7](%[VP8kclip1]) \n\t"
+ "lbux %[temp4], %[temp2](%[VP8kclip1]) \n\t"
+ "sb %[temp3], 0(%[p1]) \n\t"
+ "sb %[temp4], 0(%[p]) \n\t"
+ "1: \n\t"
+ "addiu %[p1], %[p1], 1 \n\t"
+ "bgtz %[i], 0b \n\t"
+ " addiu %[p], %[p], 1 \n\t"
+ " .set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [p]"+&r"(p), [i]"=&r"(i), [p1]"+&r"(p1)
+ : [stride]"r"(stride), [VP8kclip1]"r"(VP8kclip1), [thresh2]"r"(thresh2)
+ : "memory"
+ );
+}
+
+// TEMP0 = SRC[A + A1 * BPS]
+// TEMP1 = SRC[B + B1 * BPS]
+// TEMP2 = SRC[C + C1 * BPS]
+// TEMP3 = SRC[D + D1 * BPS]
+#define LOAD_4_BYTES(TEMP0, TEMP1, TEMP2, TEMP3, \
+ A, A1, B, B1, C, C1, D, D1, SRC) \
+ "lbu %[" #TEMP0 "], " #A "+" #A1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \
+ "lbu %[" #TEMP1 "], " #B "+" #B1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \
+ "lbu %[" #TEMP2 "], " #C "+" #C1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \
+ "lbu %[" #TEMP3 "], " #D "+" #D1 "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \
+
+static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
+ int i;
+ const int thresh2 = 2 * thresh + 1;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "li %[i], 16 \n\t"
+ "0: \n\t"
+ LOAD_4_BYTES(temp0, temp1, temp2, temp3, -2, 0, -1, 0, 0, 0, 1, 0, p)
+ "subu %[temp7], %[temp1], %[temp2] \n\t"
+ "subu %[temp6], %[temp0], %[temp3] \n\t"
+ "absq_s.w %[temp4], %[temp7] \n\t"
+ "absq_s.w %[temp5], %[temp6] \n\t"
+ "sll %[temp4], %[temp4], 2 \n\t"
+ "addu %[temp5], %[temp4], %[temp5] \n\t"
+ "subu %[temp5], %[temp5], %[thresh2] \n\t"
+ "negu %[temp8], %[temp7] \n\t"
+ "bgtz %[temp5], 1f \n\t"
+ " addiu %[i], %[i], -1 \n\t"
+ "sll %[temp4], %[temp8], 1 \n\t"
+ "shll_s.w %[temp5], %[temp6], 24 \n\t"
+ "addu %[temp3], %[temp4], %[temp8] \n\t"
+ "sra %[temp5], %[temp5], 24 \n\t"
+ "addu %[temp3], %[temp3], %[temp5] \n\t"
+ "addiu %[temp7], %[temp3], 3 \n\t"
+ "sra %[temp7], %[temp7], 3 \n\t"
+ "shra_r.w %[temp8], %[temp3], 3 \n\t"
+ "shll_s.w %[temp0], %[temp7], 27 \n\t"
+ "shll_s.w %[temp4], %[temp8], 27 \n\t"
+ "sra %[temp0], %[temp0], 27 \n\t"
+ "sra %[temp4], %[temp4], 27 \n\t"
+ "addu %[temp7], %[temp1], %[temp0] \n\t"
+ "subu %[temp2], %[temp2], %[temp4] \n\t"
+ "lbux %[temp3], %[temp7](%[VP8kclip1]) \n\t"
+ "lbux %[temp4], %[temp2](%[VP8kclip1]) \n\t"
+ "sb %[temp3], -1(%[p]) \n\t"
+ "sb %[temp4], 0(%[p]) \n\t"
+ "1: \n\t"
+ "bgtz %[i], 0b \n\t"
+ " addu %[p], %[p], %[stride] \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [p]"+&r"(p), [i]"=&r"(i)
+ : [stride]"r"(stride), [VP8kclip1]"r"(VP8kclip1), [thresh2]"r"(thresh2)
+ : "memory"
+ );
+}
+
+static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ SimpleVFilter16(p, stride, thresh);
+ }
+}
+
+static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ SimpleHFilter16(p, stride, thresh);
+ }
+}
+
+// DST[A * BPS] = TEMP0
+// DST[B + C * BPS] = TEMP1
+#define STORE_8_BYTES(TEMP0, TEMP1, A, B, C, DST) \
+ "usw %[" #TEMP0 "], " #A "*" XSTR(BPS) "(%[" #DST "]) \n\t" \
+ "usw %[" #TEMP1 "], " #B "+" #C "*" XSTR(BPS) "(%[" #DST "]) \n\t"
+
+static void VE4(uint8_t* dst) { // vertical
+ const uint8_t* top = dst - BPS;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6;
+ __asm__ volatile (
+ "ulw %[temp0], -1(%[top]) \n\t"
+ "ulh %[temp1], 3(%[top]) \n\t"
+ "preceu.ph.qbr %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbl %[temp3], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp4], %[temp1] \n\t"
+ "packrl.ph %[temp5], %[temp3], %[temp2] \n\t"
+ "packrl.ph %[temp6], %[temp4], %[temp3] \n\t"
+ "shll.ph %[temp5], %[temp5], 1 \n\t"
+ "shll.ph %[temp6], %[temp6], 1 \n\t"
+ "addq.ph %[temp2], %[temp5], %[temp2] \n\t"
+ "addq.ph %[temp6], %[temp6], %[temp4] \n\t"
+ "addq.ph %[temp2], %[temp2], %[temp3] \n\t"
+ "addq.ph %[temp6], %[temp6], %[temp3] \n\t"
+ "shra_r.ph %[temp2], %[temp2], 2 \n\t"
+ "shra_r.ph %[temp6], %[temp6], 2 \n\t"
+ "precr.qb.ph %[temp4], %[temp6], %[temp2] \n\t"
+ STORE_8_BYTES(temp4, temp4, 0, 0, 1, dst)
+ STORE_8_BYTES(temp4, temp4, 2, 0, 3, dst)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void DC4(uint8_t* dst) { // DC
+ int temp0, temp1, temp2, temp3, temp4;
+ __asm__ volatile (
+ "ulw %[temp0], -1*" XSTR(BPS) "(%[dst]) \n\t"
+ LOAD_4_BYTES(temp1, temp2, temp3, temp4, -1, 0, -1, 1, -1, 2, -1, 3, dst)
+ "ins %[temp1], %[temp2], 8, 8 \n\t"
+ "ins %[temp1], %[temp3], 16, 8 \n\t"
+ "ins %[temp1], %[temp4], 24, 8 \n\t"
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "shra_r.w %[temp0], %[temp0], 3 \n\t"
+ "replv.qb %[temp0], %[temp0] \n\t"
+ STORE_8_BYTES(temp0, temp0, 0, 0, 1, dst)
+ STORE_8_BYTES(temp0, temp0, 2, 0, 3, dst)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4)
+ : [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void RD4(uint8_t* dst) { // Down-right
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8;
+ __asm__ volatile (
+ LOAD_4_BYTES(temp0, temp1, temp2, temp3, -1, 0, -1, 1, -1, 2, -1, 3, dst)
+ "ulw %[temp7], -1-" XSTR(BPS) "(%[dst]) \n\t"
+ "ins %[temp1], %[temp0], 16, 16 \n\t"
+ "preceu.ph.qbr %[temp5], %[temp7] \n\t"
+ "ins %[temp2], %[temp1], 16, 16 \n\t"
+ "preceu.ph.qbl %[temp4], %[temp7] \n\t"
+ "ins %[temp3], %[temp2], 16, 16 \n\t"
+ "shll.ph %[temp2], %[temp2], 1 \n\t"
+ "addq.ph %[temp3], %[temp3], %[temp1] \n\t"
+ "packrl.ph %[temp6], %[temp5], %[temp1] \n\t"
+ "addq.ph %[temp3], %[temp3], %[temp2] \n\t"
+ "addq.ph %[temp1], %[temp1], %[temp5] \n\t"
+ "shll.ph %[temp6], %[temp6], 1 \n\t"
+ "addq.ph %[temp1], %[temp1], %[temp6] \n\t"
+ "packrl.ph %[temp0], %[temp4], %[temp5] \n\t"
+ "addq.ph %[temp8], %[temp5], %[temp4] \n\t"
+ "shra_r.ph %[temp3], %[temp3], 2 \n\t"
+ "shll.ph %[temp0], %[temp0], 1 \n\t"
+ "shra_r.ph %[temp1], %[temp1], 2 \n\t"
+ "addq.ph %[temp8], %[temp0], %[temp8] \n\t"
+ "lbu %[temp5], 3-" XSTR(BPS) "(%[dst]) \n\t"
+ "precrq.ph.w %[temp7], %[temp7], %[temp7] \n\t"
+ "shra_r.ph %[temp8], %[temp8], 2 \n\t"
+ "ins %[temp7], %[temp5], 0, 8 \n\t"
+ "precr.qb.ph %[temp2], %[temp1], %[temp3] \n\t"
+ "raddu.w.qb %[temp4], %[temp7] \n\t"
+ "precr.qb.ph %[temp6], %[temp8], %[temp1] \n\t"
+ "shra_r.w %[temp4], %[temp4], 2 \n\t"
+ STORE_8_BYTES(temp2, temp6, 3, 0, 1, dst)
+ "prepend %[temp2], %[temp8], 8 \n\t"
+ "prepend %[temp6], %[temp4], 8 \n\t"
+ STORE_8_BYTES(temp2, temp6, 2, 0, 0, dst)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8)
+ : [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+// TEMP0 = SRC[A * BPS]
+// TEMP1 = SRC[B + C * BPS]
+#define LOAD_8_BYTES(TEMP0, TEMP1, A, B, C, SRC) \
+ "ulw %[" #TEMP0 "], " #A "*" XSTR(BPS) "(%[" #SRC "]) \n\t" \
+ "ulw %[" #TEMP1 "], " #B "+" #C "*" XSTR(BPS) "(%[" #SRC "]) \n\t"
+
+static void LD4(uint8_t* dst) { // Down-Left
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8, temp9;
+ __asm__ volatile (
+ LOAD_8_BYTES(temp0, temp1, -1, 4, -1, dst)
+ "preceu.ph.qbl %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp3], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp4], %[temp1] \n\t"
+ "preceu.ph.qbl %[temp5], %[temp1] \n\t"
+ "packrl.ph %[temp6], %[temp2], %[temp3] \n\t"
+ "packrl.ph %[temp7], %[temp4], %[temp2] \n\t"
+ "packrl.ph %[temp8], %[temp5], %[temp4] \n\t"
+ "shll.ph %[temp6], %[temp6], 1 \n\t"
+ "addq.ph %[temp9], %[temp2], %[temp6] \n\t"
+ "shll.ph %[temp7], %[temp7], 1 \n\t"
+ "addq.ph %[temp9], %[temp9], %[temp3] \n\t"
+ "shll.ph %[temp8], %[temp8], 1 \n\t"
+ "shra_r.ph %[temp9], %[temp9], 2 \n\t"
+ "addq.ph %[temp3], %[temp4], %[temp7] \n\t"
+ "addq.ph %[temp0], %[temp5], %[temp8] \n\t"
+ "addq.ph %[temp3], %[temp3], %[temp2] \n\t"
+ "addq.ph %[temp0], %[temp0], %[temp4] \n\t"
+ "shra_r.ph %[temp3], %[temp3], 2 \n\t"
+ "shra_r.ph %[temp0], %[temp0], 2 \n\t"
+ "srl %[temp1], %[temp1], 24 \n\t"
+ "sll %[temp1], %[temp1], 1 \n\t"
+ "raddu.w.qb %[temp5], %[temp5] \n\t"
+ "precr.qb.ph %[temp9], %[temp3], %[temp9] \n\t"
+ "precr.qb.ph %[temp3], %[temp0], %[temp3] \n\t"
+ "addu %[temp1], %[temp1], %[temp5] \n\t"
+ "shra_r.w %[temp1], %[temp1], 2 \n\t"
+ STORE_8_BYTES(temp9, temp3, 0, 0, 2, dst)
+ "prepend %[temp9], %[temp0], 8 \n\t"
+ "prepend %[temp3], %[temp1], 8 \n\t"
+ STORE_8_BYTES(temp9, temp3, 1, 0, 3, dst)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9)
+ : [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+//------------------------------------------------------------------------------
+// Chroma
+
+static void DC8uv(uint8_t* dst) { // DC
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8, temp9;
+ __asm__ volatile (
+ LOAD_8_BYTES(temp0, temp1, -1, 4, -1, dst)
+ LOAD_4_BYTES(temp2, temp3, temp4, temp5, -1, 0, -1, 1, -1, 2, -1, 3, dst)
+ LOAD_4_BYTES(temp6, temp7, temp8, temp9, -1, 4, -1, 5, -1, 6, -1, 7, dst)
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "addu %[temp2], %[temp2], %[temp3] \n\t"
+ "addu %[temp4], %[temp4], %[temp5] \n\t"
+ "addu %[temp6], %[temp6], %[temp7] \n\t"
+ "addu %[temp8], %[temp8], %[temp9] \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "addu %[temp2], %[temp2], %[temp4] \n\t"
+ "addu %[temp6], %[temp6], %[temp8] \n\t"
+ "addu %[temp0], %[temp0], %[temp2] \n\t"
+ "addu %[temp0], %[temp0], %[temp6] \n\t"
+ "shra_r.w %[temp0], %[temp0], 4 \n\t"
+ "replv.qb %[temp0], %[temp0] \n\t"
+ STORE_8_BYTES(temp0, temp0, 0, 4, 0, dst)
+ STORE_8_BYTES(temp0, temp0, 1, 4, 1, dst)
+ STORE_8_BYTES(temp0, temp0, 2, 4, 2, dst)
+ STORE_8_BYTES(temp0, temp0, 3, 4, 3, dst)
+ STORE_8_BYTES(temp0, temp0, 4, 4, 4, dst)
+ STORE_8_BYTES(temp0, temp0, 5, 4, 5, dst)
+ STORE_8_BYTES(temp0, temp0, 6, 4, 6, dst)
+ STORE_8_BYTES(temp0, temp0, 7, 4, 7, dst)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9)
+ : [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void DC8uvNoLeft(uint8_t* dst) { // DC with no left samples
+ int temp0, temp1;
+ __asm__ volatile (
+ LOAD_8_BYTES(temp0, temp1, -1, 4, -1, dst)
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "shra_r.w %[temp0], %[temp0], 3 \n\t"
+ "replv.qb %[temp0], %[temp0] \n\t"
+ STORE_8_BYTES(temp0, temp0, 0, 4, 0, dst)
+ STORE_8_BYTES(temp0, temp0, 1, 4, 1, dst)
+ STORE_8_BYTES(temp0, temp0, 2, 4, 2, dst)
+ STORE_8_BYTES(temp0, temp0, 3, 4, 3, dst)
+ STORE_8_BYTES(temp0, temp0, 4, 4, 4, dst)
+ STORE_8_BYTES(temp0, temp0, 5, 4, 5, dst)
+ STORE_8_BYTES(temp0, temp0, 6, 4, 6, dst)
+ STORE_8_BYTES(temp0, temp0, 7, 4, 7, dst)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1)
+ : [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void DC8uvNoTop(uint8_t* dst) { // DC with no top samples
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8;
+ __asm__ volatile (
+ LOAD_4_BYTES(temp2, temp3, temp4, temp5, -1, 0, -1, 1, -1, 2, -1, 3, dst)
+ LOAD_4_BYTES(temp6, temp7, temp8, temp1, -1, 4, -1, 5, -1, 6, -1, 7, dst)
+ "addu %[temp2], %[temp2], %[temp3] \n\t"
+ "addu %[temp4], %[temp4], %[temp5] \n\t"
+ "addu %[temp6], %[temp6], %[temp7] \n\t"
+ "addu %[temp8], %[temp8], %[temp1] \n\t"
+ "addu %[temp2], %[temp2], %[temp4] \n\t"
+ "addu %[temp6], %[temp6], %[temp8] \n\t"
+ "addu %[temp0], %[temp6], %[temp2] \n\t"
+ "shra_r.w %[temp0], %[temp0], 3 \n\t"
+ "replv.qb %[temp0], %[temp0] \n\t"
+ STORE_8_BYTES(temp0, temp0, 0, 4, 0, dst)
+ STORE_8_BYTES(temp0, temp0, 1, 4, 1, dst)
+ STORE_8_BYTES(temp0, temp0, 2, 4, 2, dst)
+ STORE_8_BYTES(temp0, temp0, 3, 4, 3, dst)
+ STORE_8_BYTES(temp0, temp0, 4, 4, 4, dst)
+ STORE_8_BYTES(temp0, temp0, 5, 4, 5, dst)
+ STORE_8_BYTES(temp0, temp0, 6, 4, 6, dst)
+ STORE_8_BYTES(temp0, temp0, 7, 4, 7, dst)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8)
+ : [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+#undef LOAD_8_BYTES
+#undef STORE_8_BYTES
+#undef LOAD_4_BYTES
+
+#define CLIPPING(SIZE) \
+ "preceu.ph.qbl %[temp2], %[temp0] \n\t" \
+ "preceu.ph.qbr %[temp0], %[temp0] \n\t" \
+".if " #SIZE " == 8 \n\t" \
+ "preceu.ph.qbl %[temp3], %[temp1] \n\t" \
+ "preceu.ph.qbr %[temp1], %[temp1] \n\t" \
+".endif \n\t" \
+ "addu.ph %[temp2], %[temp2], %[dst_1] \n\t" \
+ "addu.ph %[temp0], %[temp0], %[dst_1] \n\t" \
+".if " #SIZE " == 8 \n\t" \
+ "addu.ph %[temp3], %[temp3], %[dst_1] \n\t" \
+ "addu.ph %[temp1], %[temp1], %[dst_1] \n\t" \
+".endif \n\t" \
+ "shll_s.ph %[temp2], %[temp2], 7 \n\t" \
+ "shll_s.ph %[temp0], %[temp0], 7 \n\t" \
+".if " #SIZE " == 8 \n\t" \
+ "shll_s.ph %[temp3], %[temp3], 7 \n\t" \
+ "shll_s.ph %[temp1], %[temp1], 7 \n\t" \
+".endif \n\t" \
+ "precrqu_s.qb.ph %[temp0], %[temp2], %[temp0] \n\t" \
+".if " #SIZE " == 8 \n\t" \
+ "precrqu_s.qb.ph %[temp1], %[temp3], %[temp1] \n\t" \
+".endif \n\t"
+
+
+#define CLIP_8B_TO_DST(DST, TOP, SIZE) do { \
+ int dst_1 = ((int)(DST)[-1] << 16) + (DST)[-1]; \
+ int temp0, temp1, temp2, temp3; \
+ __asm__ volatile ( \
+ ".if " #SIZE " < 8 \n\t" \
+ "ulw %[temp0], 0(%[top]) \n\t" \
+ "subu.ph %[dst_1], %[dst_1], %[top_1] \n\t" \
+ CLIPPING(4) \
+ "usw %[temp0], 0(%[dst]) \n\t" \
+ ".else \n\t" \
+ "ulw %[temp0], 0(%[top]) \n\t" \
+ "ulw %[temp1], 4(%[top]) \n\t" \
+ "subu.ph %[dst_1], %[dst_1], %[top_1] \n\t" \
+ CLIPPING(8) \
+ "usw %[temp0], 0(%[dst]) \n\t" \
+ "usw %[temp1], 4(%[dst]) \n\t" \
+ ".if " #SIZE " == 16 \n\t" \
+ "ulw %[temp0], 8(%[top]) \n\t" \
+ "ulw %[temp1], 12(%[top]) \n\t" \
+ CLIPPING(8) \
+ "usw %[temp0], 8(%[dst]) \n\t" \
+ "usw %[temp1], 12(%[dst]) \n\t" \
+ ".endif \n\t" \
+ ".endif \n\t" \
+ : [dst_1]"+&r"(dst_1), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), \
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \
+ : [top_1]"r"(top_1), [top]"r"((TOP)), [dst]"r"((DST)) \
+ : "memory" \
+ ); \
+} while (0)
+
+#define CLIP_TO_DST(DST, SIZE) do { \
+ int y; \
+ const uint8_t* top = (DST) - BPS; \
+ const int top_1 = ((int)top[-1] << 16) + top[-1]; \
+ for (y = 0; y < (SIZE); ++y) { \
+ CLIP_8B_TO_DST((DST), top, (SIZE)); \
+ (DST) += BPS; \
+ } \
+} while (0)
+
+#define TRUE_MOTION(DST, SIZE) \
+static void TrueMotion##SIZE(uint8_t* (DST)) { \
+ CLIP_TO_DST((DST), (SIZE)); \
+}
+
+TRUE_MOTION(dst, 4)
+TRUE_MOTION(dst, 8)
+TRUE_MOTION(dst, 16)
+
+#undef TRUE_MOTION
+#undef CLIP_TO_DST
+#undef CLIP_8B_TO_DST
+#undef CLIPPING
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8DspInitMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMIPSdspR2(void) {
+ VP8TransformDC = TransformDC;
+ VP8TransformAC3 = TransformAC3;
+ VP8Transform = TransformTwo;
+
+ VP8VFilter16 = VFilter16;
+ VP8HFilter16 = HFilter16;
+ VP8VFilter8 = VFilter8;
+ VP8HFilter8 = HFilter8;
+ VP8VFilter16i = VFilter16i;
+ VP8HFilter16i = HFilter16i;
+ VP8VFilter8i = VFilter8i;
+ VP8HFilter8i = HFilter8i;
+ VP8SimpleVFilter16 = SimpleVFilter16;
+ VP8SimpleHFilter16 = SimpleHFilter16;
+ VP8SimpleVFilter16i = SimpleVFilter16i;
+ VP8SimpleHFilter16i = SimpleHFilter16i;
+
+ VP8PredLuma4[0] = DC4;
+ VP8PredLuma4[1] = TrueMotion4;
+ VP8PredLuma4[2] = VE4;
+ VP8PredLuma4[4] = RD4;
+ VP8PredLuma4[6] = LD4;
+
+ VP8PredChroma8[0] = DC8uv;
+ VP8PredChroma8[1] = TrueMotion8;
+ VP8PredChroma8[4] = DC8uvNoTop;
+ VP8PredChroma8[5] = DC8uvNoLeft;
+
+ VP8PredLuma16[1] = TrueMotion16;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(VP8DspInitMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/dec_msa.c b/src/third_party/libwebp/src/dsp/dec_msa.c
new file mode 100644
index 0000000..8090622
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec_msa.c
@@ -0,0 +1,1020 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA version of dsp functions
+//
+// Author(s): Prashant Patil (prashant.patil@imgtec.com)
+
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA)
+
+#include "src/dsp/msa_macro.h"
+
+//------------------------------------------------------------------------------
+// Transforms
+
+#define IDCT_1D_W(in0, in1, in2, in3, out0, out1, out2, out3) { \
+ v4i32 a1_m, b1_m, c1_m, d1_m; \
+ v4i32 c_tmp1_m, c_tmp2_m, d_tmp1_m, d_tmp2_m; \
+ const v4i32 cospi8sqrt2minus1 = __msa_fill_w(20091); \
+ const v4i32 sinpi8sqrt2 = __msa_fill_w(35468); \
+ \
+ a1_m = in0 + in2; \
+ b1_m = in0 - in2; \
+ c_tmp1_m = (in1 * sinpi8sqrt2) >> 16; \
+ c_tmp2_m = in3 + ((in3 * cospi8sqrt2minus1) >> 16); \
+ c1_m = c_tmp1_m - c_tmp2_m; \
+ d_tmp1_m = in1 + ((in1 * cospi8sqrt2minus1) >> 16); \
+ d_tmp2_m = (in3 * sinpi8sqrt2) >> 16; \
+ d1_m = d_tmp1_m + d_tmp2_m; \
+ BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \
+}
+#define MULT1(a) ((((a) * 20091) >> 16) + (a))
+#define MULT2(a) (((a) * 35468) >> 16)
+
+static void TransformOne(const int16_t* in, uint8_t* dst) {
+ v8i16 input0, input1;
+ v4i32 in0, in1, in2, in3, hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3;
+ v4i32 res0, res1, res2, res3;
+ const v16i8 zero = { 0 };
+ v16i8 dest0, dest1, dest2, dest3;
+
+ LD_SH2(in, 8, input0, input1);
+ UNPCK_SH_SW(input0, in0, in1);
+ UNPCK_SH_SW(input1, in2, in3);
+ IDCT_1D_W(in0, in1, in2, in3, hz0, hz1, hz2, hz3);
+ TRANSPOSE4x4_SW_SW(hz0, hz1, hz2, hz3, hz0, hz1, hz2, hz3);
+ IDCT_1D_W(hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3);
+ SRARI_W4_SW(vt0, vt1, vt2, vt3, 3);
+ TRANSPOSE4x4_SW_SW(vt0, vt1, vt2, vt3, vt0, vt1, vt2, vt3);
+ LD_SB4(dst, BPS, dest0, dest1, dest2, dest3);
+ ILVR_B4_SW(zero, dest0, zero, dest1, zero, dest2, zero, dest3,
+ res0, res1, res2, res3);
+ ILVR_H4_SW(zero, res0, zero, res1, zero, res2, zero, res3,
+ res0, res1, res2, res3);
+ ADD4(res0, vt0, res1, vt1, res2, vt2, res3, vt3, res0, res1, res2, res3);
+ CLIP_SW4_0_255(res0, res1, res2, res3);
+ PCKEV_B2_SW(res0, res1, res2, res3, vt0, vt1);
+ res0 = (v4i32)__msa_pckev_b((v16i8)vt0, (v16i8)vt1);
+ ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS);
+}
+
+static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) {
+ TransformOne(in, dst);
+ if (do_two) {
+ TransformOne(in + 16, dst + 4);
+ }
+}
+
+static void TransformWHT(const int16_t* in, int16_t* out) {
+ v8i16 input0, input1;
+ const v8i16 mask0 = { 0, 1, 2, 3, 8, 9, 10, 11 };
+ const v8i16 mask1 = { 4, 5, 6, 7, 12, 13, 14, 15 };
+ const v8i16 mask2 = { 0, 4, 8, 12, 1, 5, 9, 13 };
+ const v8i16 mask3 = { 3, 7, 11, 15, 2, 6, 10, 14 };
+ v8i16 tmp0, tmp1, tmp2, tmp3;
+ v8i16 out0, out1;
+
+ LD_SH2(in, 8, input0, input1);
+ input1 = SLDI_SH(input1, input1, 8);
+ tmp0 = input0 + input1;
+ tmp1 = input0 - input1;
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3);
+ out0 = tmp2 + tmp3;
+ out1 = tmp2 - tmp3;
+ VSHF_H2_SH(out0, out1, out0, out1, mask2, mask3, input0, input1);
+ tmp0 = input0 + input1;
+ tmp1 = input0 - input1;
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3);
+ tmp0 = tmp2 + tmp3;
+ tmp1 = tmp2 - tmp3;
+ ADDVI_H2_SH(tmp0, 3, tmp1, 3, out0, out1);
+ SRAI_H2_SH(out0, out1, 3);
+ out[0] = __msa_copy_s_h(out0, 0);
+ out[16] = __msa_copy_s_h(out0, 4);
+ out[32] = __msa_copy_s_h(out1, 0);
+ out[48] = __msa_copy_s_h(out1, 4);
+ out[64] = __msa_copy_s_h(out0, 1);
+ out[80] = __msa_copy_s_h(out0, 5);
+ out[96] = __msa_copy_s_h(out1, 1);
+ out[112] = __msa_copy_s_h(out1, 5);
+ out[128] = __msa_copy_s_h(out0, 2);
+ out[144] = __msa_copy_s_h(out0, 6);
+ out[160] = __msa_copy_s_h(out1, 2);
+ out[176] = __msa_copy_s_h(out1, 6);
+ out[192] = __msa_copy_s_h(out0, 3);
+ out[208] = __msa_copy_s_h(out0, 7);
+ out[224] = __msa_copy_s_h(out1, 3);
+ out[240] = __msa_copy_s_h(out1, 7);
+}
+
+static void TransformDC(const int16_t* in, uint8_t* dst) {
+ const int DC = (in[0] + 4) >> 3;
+ const v8i16 tmp0 = __msa_fill_h(DC);
+ ADDBLK_ST4x4_UB(tmp0, tmp0, tmp0, tmp0, dst, BPS);
+}
+
+static void TransformAC3(const int16_t* in, uint8_t* dst) {
+ const int a = in[0] + 4;
+ const int c4 = MULT2(in[4]);
+ const int d4 = MULT1(in[4]);
+ const int in2 = MULT2(in[1]);
+ const int in3 = MULT1(in[1]);
+ v4i32 tmp0 = { 0 };
+ v4i32 out0 = __msa_fill_w(a + d4);
+ v4i32 out1 = __msa_fill_w(a + c4);
+ v4i32 out2 = __msa_fill_w(a - c4);
+ v4i32 out3 = __msa_fill_w(a - d4);
+ v4i32 res0, res1, res2, res3;
+ const v4i32 zero = { 0 };
+ v16u8 dest0, dest1, dest2, dest3;
+
+ INSERT_W4_SW(in3, in2, -in2, -in3, tmp0);
+ ADD4(out0, tmp0, out1, tmp0, out2, tmp0, out3, tmp0,
+ out0, out1, out2, out3);
+ SRAI_W4_SW(out0, out1, out2, out3, 3);
+ LD_UB4(dst, BPS, dest0, dest1, dest2, dest3);
+ ILVR_B4_SW(zero, dest0, zero, dest1, zero, dest2, zero, dest3,
+ res0, res1, res2, res3);
+ ILVR_H4_SW(zero, res0, zero, res1, zero, res2, zero, res3,
+ res0, res1, res2, res3);
+ ADD4(res0, out0, res1, out1, res2, out2, res3, out3, res0, res1, res2, res3);
+ CLIP_SW4_0_255(res0, res1, res2, res3);
+ PCKEV_B2_SW(res0, res1, res2, res3, out0, out1);
+ res0 = (v4i32)__msa_pckev_b((v16i8)out0, (v16i8)out1);
+ ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS);
+}
+
+//------------------------------------------------------------------------------
+// Edge filtering functions
+
+#define FLIP_SIGN2(in0, in1, out0, out1) { \
+ out0 = (v16i8)__msa_xori_b(in0, 0x80); \
+ out1 = (v16i8)__msa_xori_b(in1, 0x80); \
+}
+
+#define FLIP_SIGN4(in0, in1, in2, in3, out0, out1, out2, out3) { \
+ FLIP_SIGN2(in0, in1, out0, out1); \
+ FLIP_SIGN2(in2, in3, out2, out3); \
+}
+
+#define FILT_VAL(q0_m, p0_m, mask, filt) do { \
+ v16i8 q0_sub_p0; \
+ q0_sub_p0 = __msa_subs_s_b(q0_m, p0_m); \
+ filt = __msa_adds_s_b(filt, q0_sub_p0); \
+ filt = __msa_adds_s_b(filt, q0_sub_p0); \
+ filt = __msa_adds_s_b(filt, q0_sub_p0); \
+ filt = filt & mask; \
+} while (0)
+
+#define FILT2(q_m, p_m, q, p) do { \
+ u_r = SRAI_H(temp1, 7); \
+ u_r = __msa_sat_s_h(u_r, 7); \
+ u_l = SRAI_H(temp3, 7); \
+ u_l = __msa_sat_s_h(u_l, 7); \
+ u = __msa_pckev_b((v16i8)u_l, (v16i8)u_r); \
+ q_m = __msa_subs_s_b(q_m, u); \
+ p_m = __msa_adds_s_b(p_m, u); \
+ q = __msa_xori_b((v16u8)q_m, 0x80); \
+ p = __msa_xori_b((v16u8)p_m, 0x80); \
+} while (0)
+
+#define LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev) do { \
+ v16i8 p1_m, p0_m, q0_m, q1_m; \
+ v16i8 filt, t1, t2; \
+ const v16i8 cnst4b = __msa_ldi_b(4); \
+ const v16i8 cnst3b = __msa_ldi_b(3); \
+ \
+ FLIP_SIGN4(p1, p0, q0, q1, p1_m, p0_m, q0_m, q1_m); \
+ filt = __msa_subs_s_b(p1_m, q1_m); \
+ filt = filt & hev; \
+ FILT_VAL(q0_m, p0_m, mask, filt); \
+ t1 = __msa_adds_s_b(filt, cnst4b); \
+ t1 = SRAI_B(t1, 3); \
+ t2 = __msa_adds_s_b(filt, cnst3b); \
+ t2 = SRAI_B(t2, 3); \
+ q0_m = __msa_subs_s_b(q0_m, t1); \
+ q0 = __msa_xori_b((v16u8)q0_m, 0x80); \
+ p0_m = __msa_adds_s_b(p0_m, t2); \
+ p0 = __msa_xori_b((v16u8)p0_m, 0x80); \
+ filt = __msa_srari_b(t1, 1); \
+ hev = __msa_xori_b(hev, 0xff); \
+ filt = filt & hev; \
+ q1_m = __msa_subs_s_b(q1_m, filt); \
+ q1 = __msa_xori_b((v16u8)q1_m, 0x80); \
+ p1_m = __msa_adds_s_b(p1_m, filt); \
+ p1 = __msa_xori_b((v16u8)p1_m, 0x80); \
+} while (0)
+
+#define LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev) do { \
+ v16i8 p2_m, p1_m, p0_m, q2_m, q1_m, q0_m; \
+ v16i8 u, filt, t1, t2, filt_sign; \
+ v8i16 filt_r, filt_l, u_r, u_l; \
+ v8i16 temp0, temp1, temp2, temp3; \
+ const v16i8 cnst4b = __msa_ldi_b(4); \
+ const v16i8 cnst3b = __msa_ldi_b(3); \
+ const v8i16 cnst9h = __msa_ldi_h(9); \
+ const v8i16 cnst63h = __msa_ldi_h(63); \
+ \
+ FLIP_SIGN4(p1, p0, q0, q1, p1_m, p0_m, q0_m, q1_m); \
+ filt = __msa_subs_s_b(p1_m, q1_m); \
+ FILT_VAL(q0_m, p0_m, mask, filt); \
+ FLIP_SIGN2(p2, q2, p2_m, q2_m); \
+ t2 = filt & hev; \
+ /* filt_val &= ~hev */ \
+ hev = __msa_xori_b(hev, 0xff); \
+ filt = filt & hev; \
+ t1 = __msa_adds_s_b(t2, cnst4b); \
+ t1 = SRAI_B(t1, 3); \
+ t2 = __msa_adds_s_b(t2, cnst3b); \
+ t2 = SRAI_B(t2, 3); \
+ q0_m = __msa_subs_s_b(q0_m, t1); \
+ p0_m = __msa_adds_s_b(p0_m, t2); \
+ filt_sign = __msa_clti_s_b(filt, 0); \
+ ILVRL_B2_SH(filt_sign, filt, filt_r, filt_l); \
+ /* update q2/p2 */ \
+ temp0 = filt_r * cnst9h; \
+ temp1 = temp0 + cnst63h; \
+ temp2 = filt_l * cnst9h; \
+ temp3 = temp2 + cnst63h; \
+ FILT2(q2_m, p2_m, q2, p2); \
+ /* update q1/p1 */ \
+ temp1 = temp1 + temp0; \
+ temp3 = temp3 + temp2; \
+ FILT2(q1_m, p1_m, q1, p1); \
+ /* update q0/p0 */ \
+ temp1 = temp1 + temp0; \
+ temp3 = temp3 + temp2; \
+ FILT2(q0_m, p0_m, q0, p0); \
+} while (0)
+
+#define LPF_MASK_HEV(p3_in, p2_in, p1_in, p0_in, \
+ q0_in, q1_in, q2_in, q3_in, \
+ limit_in, b_limit_in, thresh_in, \
+ hev_out, mask_out) do { \
+ v16u8 p3_asub_p2_m, p2_asub_p1_m, p1_asub_p0_m, q1_asub_q0_m; \
+ v16u8 p1_asub_q1_m, p0_asub_q0_m, q3_asub_q2_m, q2_asub_q1_m; \
+ v16u8 flat_out; \
+ \
+ /* absolute subtraction of pixel values */ \
+ p3_asub_p2_m = __msa_asub_u_b(p3_in, p2_in); \
+ p2_asub_p1_m = __msa_asub_u_b(p2_in, p1_in); \
+ p1_asub_p0_m = __msa_asub_u_b(p1_in, p0_in); \
+ q1_asub_q0_m = __msa_asub_u_b(q1_in, q0_in); \
+ q2_asub_q1_m = __msa_asub_u_b(q2_in, q1_in); \
+ q3_asub_q2_m = __msa_asub_u_b(q3_in, q2_in); \
+ p0_asub_q0_m = __msa_asub_u_b(p0_in, q0_in); \
+ p1_asub_q1_m = __msa_asub_u_b(p1_in, q1_in); \
+ /* calculation of hev */ \
+ flat_out = __msa_max_u_b(p1_asub_p0_m, q1_asub_q0_m); \
+ hev_out = (thresh_in < flat_out); \
+ /* calculation of mask */ \
+ p0_asub_q0_m = __msa_adds_u_b(p0_asub_q0_m, p0_asub_q0_m); \
+ p1_asub_q1_m = SRAI_B(p1_asub_q1_m, 1); \
+ p0_asub_q0_m = __msa_adds_u_b(p0_asub_q0_m, p1_asub_q1_m); \
+ mask_out = (b_limit_in < p0_asub_q0_m); \
+ mask_out = __msa_max_u_b(flat_out, mask_out); \
+ p3_asub_p2_m = __msa_max_u_b(p3_asub_p2_m, p2_asub_p1_m); \
+ mask_out = __msa_max_u_b(p3_asub_p2_m, mask_out); \
+ q2_asub_q1_m = __msa_max_u_b(q2_asub_q1_m, q3_asub_q2_m); \
+ mask_out = __msa_max_u_b(q2_asub_q1_m, mask_out); \
+ mask_out = (limit_in < mask_out); \
+ mask_out = __msa_xori_b(mask_out, 0xff); \
+} while (0)
+
+#define ST6x1_UB(in0, in0_idx, in1, in1_idx, pdst, stride) do { \
+ const uint16_t tmp0_h = __msa_copy_s_h((v8i16)in1, in1_idx); \
+ const uint32_t tmp0_w = __msa_copy_s_w((v4i32)in0, in0_idx); \
+ SW(tmp0_w, pdst); \
+ SH(tmp0_h, pdst + stride); \
+} while (0)
+
+#define ST6x4_UB(in0, start_in0_idx, in1, start_in1_idx, pdst, stride) do { \
+ uint8_t* ptmp1 = (uint8_t*)pdst; \
+ ST6x1_UB(in0, start_in0_idx, in1, start_in1_idx, ptmp1, 4); \
+ ptmp1 += stride; \
+ ST6x1_UB(in0, start_in0_idx + 1, in1, start_in1_idx + 1, ptmp1, 4); \
+ ptmp1 += stride; \
+ ST6x1_UB(in0, start_in0_idx + 2, in1, start_in1_idx + 2, ptmp1, 4); \
+ ptmp1 += stride; \
+ ST6x1_UB(in0, start_in0_idx + 3, in1, start_in1_idx + 3, ptmp1, 4); \
+} while (0)
+
+#define LPF_SIMPLE_FILT(p1_in, p0_in, q0_in, q1_in, mask) do { \
+ v16i8 p1_m, p0_m, q0_m, q1_m, filt, filt1, filt2; \
+ const v16i8 cnst4b = __msa_ldi_b(4); \
+ const v16i8 cnst3b = __msa_ldi_b(3); \
+ \
+ FLIP_SIGN4(p1_in, p0_in, q0_in, q1_in, p1_m, p0_m, q0_m, q1_m); \
+ filt = __msa_subs_s_b(p1_m, q1_m); \
+ FILT_VAL(q0_m, p0_m, mask, filt); \
+ filt1 = __msa_adds_s_b(filt, cnst4b); \
+ filt1 = SRAI_B(filt1, 3); \
+ filt2 = __msa_adds_s_b(filt, cnst3b); \
+ filt2 = SRAI_B(filt2, 3); \
+ q0_m = __msa_subs_s_b(q0_m, filt1); \
+ p0_m = __msa_adds_s_b(p0_m, filt2); \
+ q0_in = __msa_xori_b((v16u8)q0_m, 0x80); \
+ p0_in = __msa_xori_b((v16u8)p0_m, 0x80); \
+} while (0)
+
+#define LPF_SIMPLE_MASK(p1, p0, q0, q1, b_limit, mask) do { \
+ v16u8 p1_a_sub_q1, p0_a_sub_q0; \
+ \
+ p0_a_sub_q0 = __msa_asub_u_b(p0, q0); \
+ p1_a_sub_q1 = __msa_asub_u_b(p1, q1); \
+ p1_a_sub_q1 = (v16u8)__msa_srli_b((v16i8)p1_a_sub_q1, 1); \
+ p0_a_sub_q0 = __msa_adds_u_b(p0_a_sub_q0, p0_a_sub_q0); \
+ mask = __msa_adds_u_b(p0_a_sub_q0, p1_a_sub_q1); \
+ mask = (mask <= b_limit); \
+} while (0)
+
+static void VFilter16(uint8_t* src, int stride,
+ int b_limit_in, int limit_in, int thresh_in) {
+ uint8_t* ptemp = src - 4 * stride;
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0;
+ v16u8 mask, hev;
+ const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in);
+ const v16u8 limit = (v16u8)__msa_fill_b(limit_in);
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+
+ LD_UB8(ptemp, stride, p3, p2, p1, p0, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh,
+ hev, mask);
+ LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev);
+ ptemp = src - 3 * stride;
+ ST_UB4(p2, p1, p0, q0, ptemp, stride);
+ ptemp += (4 * stride);
+ ST_UB2(q1, q2, ptemp, stride);
+}
+
+static void HFilter16(uint8_t* src, int stride,
+ int b_limit_in, int limit_in, int thresh_in) {
+ uint8_t* ptmp = src - 4;
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0;
+ v16u8 mask, hev;
+ v16u8 row0, row1, row2, row3, row4, row5, row6, row7, row8;
+ v16u8 row9, row10, row11, row12, row13, row14, row15;
+ v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+ const v16u8 limit = (v16u8)__msa_fill_b(limit_in);
+ const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in);
+
+ LD_UB8(ptmp, stride, row0, row1, row2, row3, row4, row5, row6, row7);
+ ptmp += (8 * stride);
+ LD_UB8(ptmp, stride, row8, row9, row10, row11, row12, row13, row14, row15);
+ TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7,
+ row8, row9, row10, row11, row12, row13, row14, row15,
+ p3, p2, p1, p0, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh,
+ hev, mask);
+ LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev);
+ ILVR_B2_SH(p1, p2, q0, p0, tmp0, tmp1);
+ ILVRL_H2_SH(tmp1, tmp0, tmp3, tmp4);
+ ILVL_B2_SH(p1, p2, q0, p0, tmp0, tmp1);
+ ILVRL_H2_SH(tmp1, tmp0, tmp6, tmp7);
+ ILVRL_B2_SH(q2, q1, tmp2, tmp5);
+ ptmp = src - 3;
+ ST6x1_UB(tmp3, 0, tmp2, 0, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp3, 1, tmp2, 1, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp3, 2, tmp2, 2, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp3, 3, tmp2, 3, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp4, 0, tmp2, 4, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp4, 1, tmp2, 5, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp4, 2, tmp2, 6, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp4, 3, tmp2, 7, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp6, 0, tmp5, 0, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp6, 1, tmp5, 1, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp6, 2, tmp5, 2, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp6, 3, tmp5, 3, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp7, 0, tmp5, 4, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp7, 1, tmp5, 5, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp7, 2, tmp5, 6, ptmp, 4);
+ ptmp += stride;
+ ST6x1_UB(tmp7, 3, tmp5, 7, ptmp, 4);
+}
+
+// on three inner edges
+static void VFilterHorEdge16i(uint8_t* src, int stride,
+ int b_limit, int limit, int thresh) {
+ v16u8 mask, hev;
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0;
+ const v16u8 thresh0 = (v16u8)__msa_fill_b(thresh);
+ const v16u8 b_limit0 = (v16u8)__msa_fill_b(b_limit);
+ const v16u8 limit0 = (v16u8)__msa_fill_b(limit);
+
+ LD_UB8((src - 4 * stride), stride, p3, p2, p1, p0, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit0, b_limit0, thresh0,
+ hev, mask);
+ LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev);
+ ST_UB4(p1, p0, q0, q1, (src - 2 * stride), stride);
+}
+
+static void VFilter16i(uint8_t* src_y, int stride,
+ int b_limit, int limit, int thresh) {
+ VFilterHorEdge16i(src_y + 4 * stride, stride, b_limit, limit, thresh);
+ VFilterHorEdge16i(src_y + 8 * stride, stride, b_limit, limit, thresh);
+ VFilterHorEdge16i(src_y + 12 * stride, stride, b_limit, limit, thresh);
+}
+
+static void HFilterVertEdge16i(uint8_t* src, int stride,
+ int b_limit, int limit, int thresh) {
+ v16u8 mask, hev;
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0;
+ v16u8 row0, row1, row2, row3, row4, row5, row6, row7;
+ v16u8 row8, row9, row10, row11, row12, row13, row14, row15;
+ v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5;
+ const v16u8 thresh0 = (v16u8)__msa_fill_b(thresh);
+ const v16u8 b_limit0 = (v16u8)__msa_fill_b(b_limit);
+ const v16u8 limit0 = (v16u8)__msa_fill_b(limit);
+
+ LD_UB8(src - 4, stride, row0, row1, row2, row3, row4, row5, row6, row7);
+ LD_UB8(src - 4 + (8 * stride), stride,
+ row8, row9, row10, row11, row12, row13, row14, row15);
+ TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7,
+ row8, row9, row10, row11, row12, row13, row14, row15,
+ p3, p2, p1, p0, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit0, b_limit0, thresh0,
+ hev, mask);
+ LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev);
+ ILVR_B2_SH(p0, p1, q1, q0, tmp0, tmp1);
+ ILVRL_H2_SH(tmp1, tmp0, tmp2, tmp3);
+ ILVL_B2_SH(p0, p1, q1, q0, tmp0, tmp1);
+ ILVRL_H2_SH(tmp1, tmp0, tmp4, tmp5);
+ src -= 2;
+ ST4x8_UB(tmp2, tmp3, src, stride);
+ src += (8 * stride);
+ ST4x8_UB(tmp4, tmp5, src, stride);
+}
+
+static void HFilter16i(uint8_t* src_y, int stride,
+ int b_limit, int limit, int thresh) {
+ HFilterVertEdge16i(src_y + 4, stride, b_limit, limit, thresh);
+ HFilterVertEdge16i(src_y + 8, stride, b_limit, limit, thresh);
+ HFilterVertEdge16i(src_y + 12, stride, b_limit, limit, thresh);
+}
+
+// 8-pixels wide variants, for chroma filtering
+static void VFilter8(uint8_t* src_u, uint8_t* src_v, int stride,
+ int b_limit_in, int limit_in, int thresh_in) {
+ uint8_t* ptmp_src_u = src_u - 4 * stride;
+ uint8_t* ptmp_src_v = src_v - 4 * stride;
+ uint64_t p2_d, p1_d, p0_d, q0_d, q1_d, q2_d;
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev;
+ v16u8 p3_u, p2_u, p1_u, p0_u, q3_u, q2_u, q1_u, q0_u;
+ v16u8 p3_v, p2_v, p1_v, p0_v, q3_v, q2_v, q1_v, q0_v;
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+ const v16u8 limit = (v16u8)__msa_fill_b(limit_in);
+ const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in);
+
+ LD_UB8(ptmp_src_u, stride, p3_u, p2_u, p1_u, p0_u, q0_u, q1_u, q2_u, q3_u);
+ LD_UB8(ptmp_src_v, stride, p3_v, p2_v, p1_v, p0_v, q0_v, q1_v, q2_v, q3_v);
+ ILVR_D4_UB(p3_v, p3_u, p2_v, p2_u, p1_v, p1_u, p0_v, p0_u, p3, p2, p1, p0);
+ ILVR_D4_UB(q0_v, q0_u, q1_v, q1_u, q2_v, q2_u, q3_v, q3_u, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh,
+ hev, mask);
+ LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev);
+ p2_d = __msa_copy_s_d((v2i64)p2, 0);
+ p1_d = __msa_copy_s_d((v2i64)p1, 0);
+ p0_d = __msa_copy_s_d((v2i64)p0, 0);
+ q0_d = __msa_copy_s_d((v2i64)q0, 0);
+ q1_d = __msa_copy_s_d((v2i64)q1, 0);
+ q2_d = __msa_copy_s_d((v2i64)q2, 0);
+ ptmp_src_u += stride;
+ SD4(p2_d, p1_d, p0_d, q0_d, ptmp_src_u, stride);
+ ptmp_src_u += (4 * stride);
+ SD(q1_d, ptmp_src_u);
+ ptmp_src_u += stride;
+ SD(q2_d, ptmp_src_u);
+ p2_d = __msa_copy_s_d((v2i64)p2, 1);
+ p1_d = __msa_copy_s_d((v2i64)p1, 1);
+ p0_d = __msa_copy_s_d((v2i64)p0, 1);
+ q0_d = __msa_copy_s_d((v2i64)q0, 1);
+ q1_d = __msa_copy_s_d((v2i64)q1, 1);
+ q2_d = __msa_copy_s_d((v2i64)q2, 1);
+ ptmp_src_v += stride;
+ SD4(p2_d, p1_d, p0_d, q0_d, ptmp_src_v, stride);
+ ptmp_src_v += (4 * stride);
+ SD(q1_d, ptmp_src_v);
+ ptmp_src_v += stride;
+ SD(q2_d, ptmp_src_v);
+}
+
+static void HFilter8(uint8_t* src_u, uint8_t* src_v, int stride,
+ int b_limit_in, int limit_in, int thresh_in) {
+ uint8_t* ptmp_src_u = src_u - 4;
+ uint8_t* ptmp_src_v = src_v - 4;
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev;
+ v16u8 row0, row1, row2, row3, row4, row5, row6, row7, row8;
+ v16u8 row9, row10, row11, row12, row13, row14, row15;
+ v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+ const v16u8 limit = (v16u8)__msa_fill_b(limit_in);
+ const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in);
+
+ LD_UB8(ptmp_src_u, stride, row0, row1, row2, row3, row4, row5, row6, row7);
+ LD_UB8(ptmp_src_v, stride,
+ row8, row9, row10, row11, row12, row13, row14, row15);
+ TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7,
+ row8, row9, row10, row11, row12, row13, row14, row15,
+ p3, p2, p1, p0, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh,
+ hev, mask);
+ LPF_MBFILTER(p2, p1, p0, q0, q1, q2, mask, hev);
+ ILVR_B2_SH(p1, p2, q0, p0, tmp0, tmp1);
+ ILVRL_H2_SH(tmp1, tmp0, tmp3, tmp4);
+ ILVL_B2_SH(p1, p2, q0, p0, tmp0, tmp1);
+ ILVRL_H2_SH(tmp1, tmp0, tmp6, tmp7);
+ ILVRL_B2_SH(q2, q1, tmp2, tmp5);
+ ptmp_src_u += 1;
+ ST6x4_UB(tmp3, 0, tmp2, 0, ptmp_src_u, stride);
+ ptmp_src_u += 4 * stride;
+ ST6x4_UB(tmp4, 0, tmp2, 4, ptmp_src_u, stride);
+ ptmp_src_v += 1;
+ ST6x4_UB(tmp6, 0, tmp5, 0, ptmp_src_v, stride);
+ ptmp_src_v += 4 * stride;
+ ST6x4_UB(tmp7, 0, tmp5, 4, ptmp_src_v, stride);
+}
+
+static void VFilter8i(uint8_t* src_u, uint8_t* src_v, int stride,
+ int b_limit_in, int limit_in, int thresh_in) {
+ uint64_t p1_d, p0_d, q0_d, q1_d;
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev;
+ v16u8 p3_u, p2_u, p1_u, p0_u, q3_u, q2_u, q1_u, q0_u;
+ v16u8 p3_v, p2_v, p1_v, p0_v, q3_v, q2_v, q1_v, q0_v;
+ const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in);
+ const v16u8 limit = (v16u8)__msa_fill_b(limit_in);
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+
+ LD_UB8(src_u, stride, p3_u, p2_u, p1_u, p0_u, q0_u, q1_u, q2_u, q3_u);
+ src_u += (5 * stride);
+ LD_UB8(src_v, stride, p3_v, p2_v, p1_v, p0_v, q0_v, q1_v, q2_v, q3_v);
+ src_v += (5 * stride);
+ ILVR_D4_UB(p3_v, p3_u, p2_v, p2_u, p1_v, p1_u, p0_v, p0_u, p3, p2, p1, p0);
+ ILVR_D4_UB(q0_v, q0_u, q1_v, q1_u, q2_v, q2_u, q3_v, q3_u, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh,
+ hev, mask);
+ LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev);
+ p1_d = __msa_copy_s_d((v2i64)p1, 0);
+ p0_d = __msa_copy_s_d((v2i64)p0, 0);
+ q0_d = __msa_copy_s_d((v2i64)q0, 0);
+ q1_d = __msa_copy_s_d((v2i64)q1, 0);
+ SD4(q1_d, q0_d, p0_d, p1_d, src_u, -stride);
+ p1_d = __msa_copy_s_d((v2i64)p1, 1);
+ p0_d = __msa_copy_s_d((v2i64)p0, 1);
+ q0_d = __msa_copy_s_d((v2i64)q0, 1);
+ q1_d = __msa_copy_s_d((v2i64)q1, 1);
+ SD4(q1_d, q0_d, p0_d, p1_d, src_v, -stride);
+}
+
+static void HFilter8i(uint8_t* src_u, uint8_t* src_v, int stride,
+ int b_limit_in, int limit_in, int thresh_in) {
+ v16u8 p3, p2, p1, p0, q3, q2, q1, q0, mask, hev;
+ v16u8 row0, row1, row2, row3, row4, row5, row6, row7, row8;
+ v16u8 row9, row10, row11, row12, row13, row14, row15;
+ v4i32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5;
+ const v16u8 thresh = (v16u8)__msa_fill_b(thresh_in);
+ const v16u8 limit = (v16u8)__msa_fill_b(limit_in);
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+
+ LD_UB8(src_u, stride, row0, row1, row2, row3, row4, row5, row6, row7);
+ LD_UB8(src_v, stride,
+ row8, row9, row10, row11, row12, row13, row14, row15);
+ TRANSPOSE16x8_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7,
+ row8, row9, row10, row11, row12, row13, row14, row15,
+ p3, p2, p1, p0, q0, q1, q2, q3);
+ LPF_MASK_HEV(p3, p2, p1, p0, q0, q1, q2, q3, limit, b_limit, thresh,
+ hev, mask);
+ LPF_FILTER4_4W(p1, p0, q0, q1, mask, hev);
+ ILVR_B2_SW(p0, p1, q1, q0, tmp0, tmp1);
+ ILVRL_H2_SW(tmp1, tmp0, tmp2, tmp3);
+ ILVL_B2_SW(p0, p1, q1, q0, tmp0, tmp1);
+ ILVRL_H2_SW(tmp1, tmp0, tmp4, tmp5);
+ src_u += 2;
+ ST4x4_UB(tmp2, tmp2, 0, 1, 2, 3, src_u, stride);
+ src_u += 4 * stride;
+ ST4x4_UB(tmp3, tmp3, 0, 1, 2, 3, src_u, stride);
+ src_v += 2;
+ ST4x4_UB(tmp4, tmp4, 0, 1, 2, 3, src_v, stride);
+ src_v += 4 * stride;
+ ST4x4_UB(tmp5, tmp5, 0, 1, 2, 3, src_v, stride);
+}
+
+static void SimpleVFilter16(uint8_t* src, int stride, int b_limit_in) {
+ v16u8 p1, p0, q1, q0, mask;
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+
+ LD_UB4(src - 2 * stride, stride, p1, p0, q0, q1);
+ LPF_SIMPLE_MASK(p1, p0, q0, q1, b_limit, mask);
+ LPF_SIMPLE_FILT(p1, p0, q0, q1, mask);
+ ST_UB2(p0, q0, src - stride, stride);
+}
+
+static void SimpleHFilter16(uint8_t* src, int stride, int b_limit_in) {
+ v16u8 p1, p0, q1, q0, mask, row0, row1, row2, row3, row4, row5, row6, row7;
+ v16u8 row8, row9, row10, row11, row12, row13, row14, row15;
+ v8i16 tmp0, tmp1;
+ const v16u8 b_limit = (v16u8)__msa_fill_b(b_limit_in);
+ uint8_t* ptemp_src = src - 2;
+
+ LD_UB8(ptemp_src, stride, row0, row1, row2, row3, row4, row5, row6, row7);
+ LD_UB8(ptemp_src + 8 * stride, stride,
+ row8, row9, row10, row11, row12, row13, row14, row15);
+ TRANSPOSE16x4_UB_UB(row0, row1, row2, row3, row4, row5, row6, row7,
+ row8, row9, row10, row11, row12, row13, row14, row15,
+ p1, p0, q0, q1);
+ LPF_SIMPLE_MASK(p1, p0, q0, q1, b_limit, mask);
+ LPF_SIMPLE_FILT(p1, p0, q0, q1, mask);
+ ILVRL_B2_SH(q0, p0, tmp1, tmp0);
+ ptemp_src += 1;
+ ST2x4_UB(tmp1, 0, ptemp_src, stride);
+ ptemp_src += 4 * stride;
+ ST2x4_UB(tmp1, 4, ptemp_src, stride);
+ ptemp_src += 4 * stride;
+ ST2x4_UB(tmp0, 0, ptemp_src, stride);
+ ptemp_src += 4 * stride;
+ ST2x4_UB(tmp0, 4, ptemp_src, stride);
+ ptemp_src += 4 * stride;
+}
+
+static void SimpleVFilter16i(uint8_t* src_y, int stride, int b_limit_in) {
+ SimpleVFilter16(src_y + 4 * stride, stride, b_limit_in);
+ SimpleVFilter16(src_y + 8 * stride, stride, b_limit_in);
+ SimpleVFilter16(src_y + 12 * stride, stride, b_limit_in);
+}
+
+static void SimpleHFilter16i(uint8_t* src_y, int stride, int b_limit_in) {
+ SimpleHFilter16(src_y + 4, stride, b_limit_in);
+ SimpleHFilter16(src_y + 8, stride, b_limit_in);
+ SimpleHFilter16(src_y + 12, stride, b_limit_in);
+}
+
+//------------------------------------------------------------------------------
+// Intra predictions
+//------------------------------------------------------------------------------
+
+// 4x4
+
+static void DC4(uint8_t* dst) { // DC
+ uint32_t dc = 4;
+ int i;
+ for (i = 0; i < 4; ++i) dc += dst[i - BPS] + dst[-1 + i * BPS];
+ dc >>= 3;
+ dc = dc | (dc << 8) | (dc << 16) | (dc << 24);
+ SW4(dc, dc, dc, dc, dst, BPS);
+}
+
+static void TM4(uint8_t* dst) {
+ const uint8_t* const ptemp = dst - BPS - 1;
+ v8i16 T, d, r0, r1, r2, r3;
+ const v16i8 zero = { 0 };
+ const v8i16 TL = (v8i16)__msa_fill_h(ptemp[0 * BPS]);
+ const v8i16 L0 = (v8i16)__msa_fill_h(ptemp[1 * BPS]);
+ const v8i16 L1 = (v8i16)__msa_fill_h(ptemp[2 * BPS]);
+ const v8i16 L2 = (v8i16)__msa_fill_h(ptemp[3 * BPS]);
+ const v8i16 L3 = (v8i16)__msa_fill_h(ptemp[4 * BPS]);
+ const v16u8 T1 = LD_UB(ptemp + 1);
+
+ T = (v8i16)__msa_ilvr_b(zero, (v16i8)T1);
+ d = T - TL;
+ ADD4(d, L0, d, L1, d, L2, d, L3, r0, r1, r2, r3);
+ CLIP_SH4_0_255(r0, r1, r2, r3);
+ PCKEV_ST4x4_UB(r0, r1, r2, r3, dst, BPS);
+}
+
+static void VE4(uint8_t* dst) { // vertical
+ const uint8_t* const ptop = dst - BPS - 1;
+ const uint32_t val0 = LW(ptop + 0);
+ const uint32_t val1 = LW(ptop + 4);
+ uint32_t out;
+ v16u8 A = { 0 }, B, C, AC, B2, R;
+
+ INSERT_W2_UB(val0, val1, A);
+ B = SLDI_UB(A, A, 1);
+ C = SLDI_UB(A, A, 2);
+ AC = __msa_ave_u_b(A, C);
+ B2 = __msa_ave_u_b(B, B);
+ R = __msa_aver_u_b(AC, B2);
+ out = __msa_copy_s_w((v4i32)R, 0);
+ SW4(out, out, out, out, dst, BPS);
+}
+
+static void RD4(uint8_t* dst) { // Down-right
+ const uint8_t* const ptop = dst - 1 - BPS;
+ uint32_t val0 = LW(ptop + 0);
+ uint32_t val1 = LW(ptop + 4);
+ uint32_t val2, val3;
+ v16u8 A, B, C, AC, B2, R, A1 = { 0 };
+
+ INSERT_W2_UB(val0, val1, A1);
+ A = SLDI_UB(A1, A1, 12);
+ A = (v16u8)__msa_insert_b((v16i8)A, 3, ptop[1 * BPS]);
+ A = (v16u8)__msa_insert_b((v16i8)A, 2, ptop[2 * BPS]);
+ A = (v16u8)__msa_insert_b((v16i8)A, 1, ptop[3 * BPS]);
+ A = (v16u8)__msa_insert_b((v16i8)A, 0, ptop[4 * BPS]);
+ B = SLDI_UB(A, A, 1);
+ C = SLDI_UB(A, A, 2);
+ AC = __msa_ave_u_b(A, C);
+ B2 = __msa_ave_u_b(B, B);
+ R = __msa_aver_u_b(AC, B2);
+ val3 = __msa_copy_s_w((v4i32)R, 0);
+ R = SLDI_UB(R, R, 1);
+ val2 = __msa_copy_s_w((v4i32)R, 0);
+ R = SLDI_UB(R, R, 1);
+ val1 = __msa_copy_s_w((v4i32)R, 0);
+ R = SLDI_UB(R, R, 1);
+ val0 = __msa_copy_s_w((v4i32)R, 0);
+ SW4(val0, val1, val2, val3, dst, BPS);
+}
+
+static void LD4(uint8_t* dst) { // Down-Left
+ const uint8_t* const ptop = dst - BPS;
+ uint32_t val0 = LW(ptop + 0);
+ uint32_t val1 = LW(ptop + 4);
+ uint32_t val2, val3;
+ v16u8 A = { 0 }, B, C, AC, B2, R;
+
+ INSERT_W2_UB(val0, val1, A);
+ B = SLDI_UB(A, A, 1);
+ C = SLDI_UB(A, A, 2);
+ C = (v16u8)__msa_insert_b((v16i8)C, 6, ptop[7]);
+ AC = __msa_ave_u_b(A, C);
+ B2 = __msa_ave_u_b(B, B);
+ R = __msa_aver_u_b(AC, B2);
+ val0 = __msa_copy_s_w((v4i32)R, 0);
+ R = SLDI_UB(R, R, 1);
+ val1 = __msa_copy_s_w((v4i32)R, 0);
+ R = SLDI_UB(R, R, 1);
+ val2 = __msa_copy_s_w((v4i32)R, 0);
+ R = SLDI_UB(R, R, 1);
+ val3 = __msa_copy_s_w((v4i32)R, 0);
+ SW4(val0, val1, val2, val3, dst, BPS);
+}
+
+// 16x16
+
+static void DC16(uint8_t* dst) { // DC
+ uint32_t dc = 16;
+ int i;
+ const v16u8 rtop = LD_UB(dst - BPS);
+ const v8u16 dctop = __msa_hadd_u_h(rtop, rtop);
+ v16u8 out;
+
+ for (i = 0; i < 16; ++i) {
+ dc += dst[-1 + i * BPS];
+ }
+ dc += HADD_UH_U32(dctop);
+ out = (v16u8)__msa_fill_b(dc >> 5);
+ ST_UB8(out, out, out, out, out, out, out, out, dst, BPS);
+ ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS);
+}
+
+static void TM16(uint8_t* dst) {
+ int j;
+ v8i16 d1, d2;
+ const v16i8 zero = { 0 };
+ const v8i16 TL = (v8i16)__msa_fill_h(dst[-1 - BPS]);
+ const v16i8 T = LD_SB(dst - BPS);
+
+ ILVRL_B2_SH(zero, T, d1, d2);
+ SUB2(d1, TL, d2, TL, d1, d2);
+ for (j = 0; j < 16; j += 4) {
+ v16i8 t0, t1, t2, t3;
+ v8i16 r0, r1, r2, r3, r4, r5, r6, r7;
+ const v8i16 L0 = (v8i16)__msa_fill_h(dst[-1 + 0 * BPS]);
+ const v8i16 L1 = (v8i16)__msa_fill_h(dst[-1 + 1 * BPS]);
+ const v8i16 L2 = (v8i16)__msa_fill_h(dst[-1 + 2 * BPS]);
+ const v8i16 L3 = (v8i16)__msa_fill_h(dst[-1 + 3 * BPS]);
+ ADD4(d1, L0, d1, L1, d1, L2, d1, L3, r0, r1, r2, r3);
+ ADD4(d2, L0, d2, L1, d2, L2, d2, L3, r4, r5, r6, r7);
+ CLIP_SH4_0_255(r0, r1, r2, r3);
+ CLIP_SH4_0_255(r4, r5, r6, r7);
+ PCKEV_B4_SB(r4, r0, r5, r1, r6, r2, r7, r3, t0, t1, t2, t3);
+ ST_SB4(t0, t1, t2, t3, dst, BPS);
+ dst += 4 * BPS;
+ }
+}
+
+static void VE16(uint8_t* dst) { // vertical
+ const v16u8 rtop = LD_UB(dst - BPS);
+ ST_UB8(rtop, rtop, rtop, rtop, rtop, rtop, rtop, rtop, dst, BPS);
+ ST_UB8(rtop, rtop, rtop, rtop, rtop, rtop, rtop, rtop, dst + 8 * BPS, BPS);
+}
+
+static void HE16(uint8_t* dst) { // horizontal
+ int j;
+ for (j = 16; j > 0; j -= 4) {
+ const v16u8 L0 = (v16u8)__msa_fill_b(dst[-1 + 0 * BPS]);
+ const v16u8 L1 = (v16u8)__msa_fill_b(dst[-1 + 1 * BPS]);
+ const v16u8 L2 = (v16u8)__msa_fill_b(dst[-1 + 2 * BPS]);
+ const v16u8 L3 = (v16u8)__msa_fill_b(dst[-1 + 3 * BPS]);
+ ST_UB4(L0, L1, L2, L3, dst, BPS);
+ dst += 4 * BPS;
+ }
+}
+
+static void DC16NoTop(uint8_t* dst) { // DC with top samples not available
+ int j;
+ uint32_t dc = 8;
+ v16u8 out;
+
+ for (j = 0; j < 16; ++j) {
+ dc += dst[-1 + j * BPS];
+ }
+ out = (v16u8)__msa_fill_b(dc >> 4);
+ ST_UB8(out, out, out, out, out, out, out, out, dst, BPS);
+ ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS);
+}
+
+static void DC16NoLeft(uint8_t* dst) { // DC with left samples not available
+ uint32_t dc = 8;
+ const v16u8 rtop = LD_UB(dst - BPS);
+ const v8u16 dctop = __msa_hadd_u_h(rtop, rtop);
+ v16u8 out;
+
+ dc += HADD_UH_U32(dctop);
+ out = (v16u8)__msa_fill_b(dc >> 4);
+ ST_UB8(out, out, out, out, out, out, out, out, dst, BPS);
+ ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS);
+}
+
+static void DC16NoTopLeft(uint8_t* dst) { // DC with nothing
+ const v16u8 out = (v16u8)__msa_fill_b(0x80);
+ ST_UB8(out, out, out, out, out, out, out, out, dst, BPS);
+ ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS);
+}
+
+// Chroma
+
+#define STORE8x8(out, dst) do { \
+ SD4(out, out, out, out, dst + 0 * BPS, BPS); \
+ SD4(out, out, out, out, dst + 4 * BPS, BPS); \
+} while (0)
+
+static void DC8uv(uint8_t* dst) { // DC
+ uint32_t dc = 8;
+ int i;
+ uint64_t out;
+ const v16u8 rtop = LD_UB(dst - BPS);
+ const v8u16 temp0 = __msa_hadd_u_h(rtop, rtop);
+ const v4u32 temp1 = __msa_hadd_u_w(temp0, temp0);
+ const v2u64 temp2 = __msa_hadd_u_d(temp1, temp1);
+ v16u8 dctemp;
+
+ for (i = 0; i < 8; ++i) {
+ dc += dst[-1 + i * BPS];
+ }
+ dc += __msa_copy_s_w((v4i32)temp2, 0);
+ dctemp = (v16u8)__msa_fill_b(dc >> 4);
+ out = __msa_copy_s_d((v2i64)dctemp, 0);
+ STORE8x8(out, dst);
+}
+
+static void TM8uv(uint8_t* dst) {
+ int j;
+ const v16i8 T1 = LD_SB(dst - BPS);
+ const v16i8 zero = { 0 };
+ const v8i16 T = (v8i16)__msa_ilvr_b(zero, T1);
+ const v8i16 TL = (v8i16)__msa_fill_h(dst[-1 - BPS]);
+ const v8i16 d = T - TL;
+
+ for (j = 0; j < 8; j += 4) {
+ v16i8 t0, t1;
+ v8i16 r0 = (v8i16)__msa_fill_h(dst[-1 + 0 * BPS]);
+ v8i16 r1 = (v8i16)__msa_fill_h(dst[-1 + 1 * BPS]);
+ v8i16 r2 = (v8i16)__msa_fill_h(dst[-1 + 2 * BPS]);
+ v8i16 r3 = (v8i16)__msa_fill_h(dst[-1 + 3 * BPS]);
+ ADD4(d, r0, d, r1, d, r2, d, r3, r0, r1, r2, r3);
+ CLIP_SH4_0_255(r0, r1, r2, r3);
+ PCKEV_B2_SB(r1, r0, r3, r2, t0, t1);
+ ST4x4_UB(t0, t1, 0, 2, 0, 2, dst, BPS);
+ ST4x4_UB(t0, t1, 1, 3, 1, 3, dst + 4, BPS);
+ dst += 4 * BPS;
+ }
+}
+
+static void VE8uv(uint8_t* dst) { // vertical
+ const v16u8 rtop = LD_UB(dst - BPS);
+ const uint64_t out = __msa_copy_s_d((v2i64)rtop, 0);
+ STORE8x8(out, dst);
+}
+
+static void HE8uv(uint8_t* dst) { // horizontal
+ int j;
+ for (j = 0; j < 8; j += 4) {
+ const v16u8 L0 = (v16u8)__msa_fill_b(dst[-1 + 0 * BPS]);
+ const v16u8 L1 = (v16u8)__msa_fill_b(dst[-1 + 1 * BPS]);
+ const v16u8 L2 = (v16u8)__msa_fill_b(dst[-1 + 2 * BPS]);
+ const v16u8 L3 = (v16u8)__msa_fill_b(dst[-1 + 3 * BPS]);
+ const uint64_t out0 = __msa_copy_s_d((v2i64)L0, 0);
+ const uint64_t out1 = __msa_copy_s_d((v2i64)L1, 0);
+ const uint64_t out2 = __msa_copy_s_d((v2i64)L2, 0);
+ const uint64_t out3 = __msa_copy_s_d((v2i64)L3, 0);
+ SD4(out0, out1, out2, out3, dst, BPS);
+ dst += 4 * BPS;
+ }
+}
+
+static void DC8uvNoLeft(uint8_t* dst) { // DC with no left samples
+ const uint32_t dc = 4;
+ const v16u8 rtop = LD_UB(dst - BPS);
+ const v8u16 temp0 = __msa_hadd_u_h(rtop, rtop);
+ const v4u32 temp1 = __msa_hadd_u_w(temp0, temp0);
+ const v2u64 temp2 = __msa_hadd_u_d(temp1, temp1);
+ const uint32_t sum_m = __msa_copy_s_w((v4i32)temp2, 0);
+ const v16u8 dcval = (v16u8)__msa_fill_b((dc + sum_m) >> 3);
+ const uint64_t out = __msa_copy_s_d((v2i64)dcval, 0);
+ STORE8x8(out, dst);
+}
+
+static void DC8uvNoTop(uint8_t* dst) { // DC with no top samples
+ uint32_t dc = 4;
+ int i;
+ uint64_t out;
+ v16u8 dctemp;
+
+ for (i = 0; i < 8; ++i) {
+ dc += dst[-1 + i * BPS];
+ }
+ dctemp = (v16u8)__msa_fill_b(dc >> 3);
+ out = __msa_copy_s_d((v2i64)dctemp, 0);
+ STORE8x8(out, dst);
+}
+
+static void DC8uvNoTopLeft(uint8_t* dst) { // DC with nothing
+ const uint64_t out = 0x8080808080808080ULL;
+ STORE8x8(out, dst);
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8DspInitMSA(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMSA(void) {
+ VP8TransformWHT = TransformWHT;
+ VP8Transform = TransformTwo;
+ VP8TransformDC = TransformDC;
+ VP8TransformAC3 = TransformAC3;
+
+ VP8VFilter16 = VFilter16;
+ VP8HFilter16 = HFilter16;
+ VP8VFilter16i = VFilter16i;
+ VP8HFilter16i = HFilter16i;
+ VP8VFilter8 = VFilter8;
+ VP8HFilter8 = HFilter8;
+ VP8VFilter8i = VFilter8i;
+ VP8HFilter8i = HFilter8i;
+ VP8SimpleVFilter16 = SimpleVFilter16;
+ VP8SimpleHFilter16 = SimpleHFilter16;
+ VP8SimpleVFilter16i = SimpleVFilter16i;
+ VP8SimpleHFilter16i = SimpleHFilter16i;
+
+ VP8PredLuma4[0] = DC4;
+ VP8PredLuma4[1] = TM4;
+ VP8PredLuma4[2] = VE4;
+ VP8PredLuma4[4] = RD4;
+ VP8PredLuma4[6] = LD4;
+ VP8PredLuma16[0] = DC16;
+ VP8PredLuma16[1] = TM16;
+ VP8PredLuma16[2] = VE16;
+ VP8PredLuma16[3] = HE16;
+ VP8PredLuma16[4] = DC16NoTop;
+ VP8PredLuma16[5] = DC16NoLeft;
+ VP8PredLuma16[6] = DC16NoTopLeft;
+ VP8PredChroma8[0] = DC8uv;
+ VP8PredChroma8[1] = TM8uv;
+ VP8PredChroma8[2] = VE8uv;
+ VP8PredChroma8[3] = HE8uv;
+ VP8PredChroma8[4] = DC8uvNoTop;
+ VP8PredChroma8[5] = DC8uvNoLeft;
+ VP8PredChroma8[6] = DC8uvNoTopLeft;
+}
+
+#else // !WEBP_USE_MSA
+
+WEBP_DSP_INIT_STUB(VP8DspInitMSA)
+
+#endif // WEBP_USE_MSA
diff --git a/src/third_party/libwebp/src/dsp/dec_neon.c b/src/third_party/libwebp/src/dsp/dec_neon.c
new file mode 100644
index 0000000..ffa697f
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec_neon.c
@@ -0,0 +1,1652 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// ARM NEON version of dsp functions and loop filtering.
+//
+// Authors: Somnath Banerjee (somnath@google.com)
+// Johann Koenig (johannkoenig@google.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON)
+
+#include "src/dsp/neon.h"
+#include "src/dec/vp8i_dec.h"
+
+//------------------------------------------------------------------------------
+// NxM Loading functions
+
+#if !defined(WORK_AROUND_GCC)
+
+// This intrinsics version makes gcc-4.6.3 crash during Load4x??() compilation
+// (register alloc, probably). The variants somewhat mitigate the problem, but
+// not quite. HFilter16i() remains problematic.
+static WEBP_INLINE uint8x8x4_t Load4x8_NEON(const uint8_t* const src,
+ int stride) {
+ const uint8x8_t zero = vdup_n_u8(0);
+ uint8x8x4_t out;
+ INIT_VECTOR4(out, zero, zero, zero, zero);
+ out = vld4_lane_u8(src + 0 * stride, out, 0);
+ out = vld4_lane_u8(src + 1 * stride, out, 1);
+ out = vld4_lane_u8(src + 2 * stride, out, 2);
+ out = vld4_lane_u8(src + 3 * stride, out, 3);
+ out = vld4_lane_u8(src + 4 * stride, out, 4);
+ out = vld4_lane_u8(src + 5 * stride, out, 5);
+ out = vld4_lane_u8(src + 6 * stride, out, 6);
+ out = vld4_lane_u8(src + 7 * stride, out, 7);
+ return out;
+}
+
+static WEBP_INLINE void Load4x16_NEON(const uint8_t* const src, int stride,
+ uint8x16_t* const p1,
+ uint8x16_t* const p0,
+ uint8x16_t* const q0,
+ uint8x16_t* const q1) {
+ // row0 = p1[0..7]|p0[0..7]|q0[0..7]|q1[0..7]
+ // row8 = p1[8..15]|p0[8..15]|q0[8..15]|q1[8..15]
+ const uint8x8x4_t row0 = Load4x8_NEON(src - 2 + 0 * stride, stride);
+ const uint8x8x4_t row8 = Load4x8_NEON(src - 2 + 8 * stride, stride);
+ *p1 = vcombine_u8(row0.val[0], row8.val[0]);
+ *p0 = vcombine_u8(row0.val[1], row8.val[1]);
+ *q0 = vcombine_u8(row0.val[2], row8.val[2]);
+ *q1 = vcombine_u8(row0.val[3], row8.val[3]);
+}
+
+#else // WORK_AROUND_GCC
+
+#define LOADQ_LANE_32b(VALUE, LANE) do { \
+ (VALUE) = vld1q_lane_u32((const uint32_t*)src, (VALUE), (LANE)); \
+ src += stride; \
+} while (0)
+
+static WEBP_INLINE void Load4x16_NEON(const uint8_t* src, int stride,
+ uint8x16_t* const p1,
+ uint8x16_t* const p0,
+ uint8x16_t* const q0,
+ uint8x16_t* const q1) {
+ const uint32x4_t zero = vdupq_n_u32(0);
+ uint32x4x4_t in;
+ INIT_VECTOR4(in, zero, zero, zero, zero);
+ src -= 2;
+ LOADQ_LANE_32b(in.val[0], 0);
+ LOADQ_LANE_32b(in.val[1], 0);
+ LOADQ_LANE_32b(in.val[2], 0);
+ LOADQ_LANE_32b(in.val[3], 0);
+ LOADQ_LANE_32b(in.val[0], 1);
+ LOADQ_LANE_32b(in.val[1], 1);
+ LOADQ_LANE_32b(in.val[2], 1);
+ LOADQ_LANE_32b(in.val[3], 1);
+ LOADQ_LANE_32b(in.val[0], 2);
+ LOADQ_LANE_32b(in.val[1], 2);
+ LOADQ_LANE_32b(in.val[2], 2);
+ LOADQ_LANE_32b(in.val[3], 2);
+ LOADQ_LANE_32b(in.val[0], 3);
+ LOADQ_LANE_32b(in.val[1], 3);
+ LOADQ_LANE_32b(in.val[2], 3);
+ LOADQ_LANE_32b(in.val[3], 3);
+ // Transpose four 4x4 parts:
+ {
+ const uint8x16x2_t row01 = vtrnq_u8(vreinterpretq_u8_u32(in.val[0]),
+ vreinterpretq_u8_u32(in.val[1]));
+ const uint8x16x2_t row23 = vtrnq_u8(vreinterpretq_u8_u32(in.val[2]),
+ vreinterpretq_u8_u32(in.val[3]));
+ const uint16x8x2_t row02 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[0]),
+ vreinterpretq_u16_u8(row23.val[0]));
+ const uint16x8x2_t row13 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[1]),
+ vreinterpretq_u16_u8(row23.val[1]));
+ *p1 = vreinterpretq_u8_u16(row02.val[0]);
+ *p0 = vreinterpretq_u8_u16(row13.val[0]);
+ *q0 = vreinterpretq_u8_u16(row02.val[1]);
+ *q1 = vreinterpretq_u8_u16(row13.val[1]);
+ }
+}
+#undef LOADQ_LANE_32b
+
+#endif // !WORK_AROUND_GCC
+
+static WEBP_INLINE void Load8x16_NEON(
+ const uint8_t* const src, int stride,
+ uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1,
+ uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1,
+ uint8x16_t* const q2, uint8x16_t* const q3) {
+ Load4x16_NEON(src - 2, stride, p3, p2, p1, p0);
+ Load4x16_NEON(src + 2, stride, q0, q1, q2, q3);
+}
+
+static WEBP_INLINE void Load16x4_NEON(const uint8_t* const src, int stride,
+ uint8x16_t* const p1,
+ uint8x16_t* const p0,
+ uint8x16_t* const q0,
+ uint8x16_t* const q1) {
+ *p1 = vld1q_u8(src - 2 * stride);
+ *p0 = vld1q_u8(src - 1 * stride);
+ *q0 = vld1q_u8(src + 0 * stride);
+ *q1 = vld1q_u8(src + 1 * stride);
+}
+
+static WEBP_INLINE void Load16x8_NEON(
+ const uint8_t* const src, int stride,
+ uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1,
+ uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1,
+ uint8x16_t* const q2, uint8x16_t* const q3) {
+ Load16x4_NEON(src - 2 * stride, stride, p3, p2, p1, p0);
+ Load16x4_NEON(src + 2 * stride, stride, q0, q1, q2, q3);
+}
+
+static WEBP_INLINE void Load8x8x2_NEON(
+ const uint8_t* const u, const uint8_t* const v, int stride,
+ uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1,
+ uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1,
+ uint8x16_t* const q2, uint8x16_t* const q3) {
+ // We pack the 8x8 u-samples in the lower half of the uint8x16_t destination
+ // and the v-samples on the higher half.
+ *p3 = vcombine_u8(vld1_u8(u - 4 * stride), vld1_u8(v - 4 * stride));
+ *p2 = vcombine_u8(vld1_u8(u - 3 * stride), vld1_u8(v - 3 * stride));
+ *p1 = vcombine_u8(vld1_u8(u - 2 * stride), vld1_u8(v - 2 * stride));
+ *p0 = vcombine_u8(vld1_u8(u - 1 * stride), vld1_u8(v - 1 * stride));
+ *q0 = vcombine_u8(vld1_u8(u + 0 * stride), vld1_u8(v + 0 * stride));
+ *q1 = vcombine_u8(vld1_u8(u + 1 * stride), vld1_u8(v + 1 * stride));
+ *q2 = vcombine_u8(vld1_u8(u + 2 * stride), vld1_u8(v + 2 * stride));
+ *q3 = vcombine_u8(vld1_u8(u + 3 * stride), vld1_u8(v + 3 * stride));
+}
+
+#if !defined(WORK_AROUND_GCC)
+
+#define LOAD_UV_8(ROW) \
+ vcombine_u8(vld1_u8(u - 4 + (ROW) * stride), vld1_u8(v - 4 + (ROW) * stride))
+
+static WEBP_INLINE void Load8x8x2T_NEON(
+ const uint8_t* const u, const uint8_t* const v, int stride,
+ uint8x16_t* const p3, uint8x16_t* const p2, uint8x16_t* const p1,
+ uint8x16_t* const p0, uint8x16_t* const q0, uint8x16_t* const q1,
+ uint8x16_t* const q2, uint8x16_t* const q3) {
+ // We pack the 8x8 u-samples in the lower half of the uint8x16_t destination
+ // and the v-samples on the higher half.
+ const uint8x16_t row0 = LOAD_UV_8(0);
+ const uint8x16_t row1 = LOAD_UV_8(1);
+ const uint8x16_t row2 = LOAD_UV_8(2);
+ const uint8x16_t row3 = LOAD_UV_8(3);
+ const uint8x16_t row4 = LOAD_UV_8(4);
+ const uint8x16_t row5 = LOAD_UV_8(5);
+ const uint8x16_t row6 = LOAD_UV_8(6);
+ const uint8x16_t row7 = LOAD_UV_8(7);
+ // Perform two side-by-side 8x8 transposes
+ // u00 u01 u02 u03 u04 u05 u06 u07 | v00 v01 v02 v03 v04 v05 v06 v07
+ // u10 u11 u12 u13 u14 u15 u16 u17 | v10 v11 v12 ...
+ // u20 u21 u22 u23 u24 u25 u26 u27 | v20 v21 ...
+ // u30 u31 u32 u33 u34 u35 u36 u37 | ...
+ // u40 u41 u42 u43 u44 u45 u46 u47 | ...
+ // u50 u51 u52 u53 u54 u55 u56 u57 | ...
+ // u60 u61 u62 u63 u64 u65 u66 u67 | v60 ...
+ // u70 u71 u72 u73 u74 u75 u76 u77 | v70 v71 v72 ...
+ const uint8x16x2_t row01 = vtrnq_u8(row0, row1); // u00 u10 u02 u12 ...
+ // u01 u11 u03 u13 ...
+ const uint8x16x2_t row23 = vtrnq_u8(row2, row3); // u20 u30 u22 u32 ...
+ // u21 u31 u23 u33 ...
+ const uint8x16x2_t row45 = vtrnq_u8(row4, row5); // ...
+ const uint8x16x2_t row67 = vtrnq_u8(row6, row7); // ...
+ const uint16x8x2_t row02 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[0]),
+ vreinterpretq_u16_u8(row23.val[0]));
+ const uint16x8x2_t row13 = vtrnq_u16(vreinterpretq_u16_u8(row01.val[1]),
+ vreinterpretq_u16_u8(row23.val[1]));
+ const uint16x8x2_t row46 = vtrnq_u16(vreinterpretq_u16_u8(row45.val[0]),
+ vreinterpretq_u16_u8(row67.val[0]));
+ const uint16x8x2_t row57 = vtrnq_u16(vreinterpretq_u16_u8(row45.val[1]),
+ vreinterpretq_u16_u8(row67.val[1]));
+ const uint32x4x2_t row04 = vtrnq_u32(vreinterpretq_u32_u16(row02.val[0]),
+ vreinterpretq_u32_u16(row46.val[0]));
+ const uint32x4x2_t row26 = vtrnq_u32(vreinterpretq_u32_u16(row02.val[1]),
+ vreinterpretq_u32_u16(row46.val[1]));
+ const uint32x4x2_t row15 = vtrnq_u32(vreinterpretq_u32_u16(row13.val[0]),
+ vreinterpretq_u32_u16(row57.val[0]));
+ const uint32x4x2_t row37 = vtrnq_u32(vreinterpretq_u32_u16(row13.val[1]),
+ vreinterpretq_u32_u16(row57.val[1]));
+ *p3 = vreinterpretq_u8_u32(row04.val[0]);
+ *p2 = vreinterpretq_u8_u32(row15.val[0]);
+ *p1 = vreinterpretq_u8_u32(row26.val[0]);
+ *p0 = vreinterpretq_u8_u32(row37.val[0]);
+ *q0 = vreinterpretq_u8_u32(row04.val[1]);
+ *q1 = vreinterpretq_u8_u32(row15.val[1]);
+ *q2 = vreinterpretq_u8_u32(row26.val[1]);
+ *q3 = vreinterpretq_u8_u32(row37.val[1]);
+}
+#undef LOAD_UV_8
+
+#endif // !WORK_AROUND_GCC
+
+static WEBP_INLINE void Store2x8_NEON(const uint8x8x2_t v,
+ uint8_t* const dst, int stride) {
+ vst2_lane_u8(dst + 0 * stride, v, 0);
+ vst2_lane_u8(dst + 1 * stride, v, 1);
+ vst2_lane_u8(dst + 2 * stride, v, 2);
+ vst2_lane_u8(dst + 3 * stride, v, 3);
+ vst2_lane_u8(dst + 4 * stride, v, 4);
+ vst2_lane_u8(dst + 5 * stride, v, 5);
+ vst2_lane_u8(dst + 6 * stride, v, 6);
+ vst2_lane_u8(dst + 7 * stride, v, 7);
+}
+
+static WEBP_INLINE void Store2x16_NEON(const uint8x16_t p0, const uint8x16_t q0,
+ uint8_t* const dst, int stride) {
+ uint8x8x2_t lo, hi;
+ lo.val[0] = vget_low_u8(p0);
+ lo.val[1] = vget_low_u8(q0);
+ hi.val[0] = vget_high_u8(p0);
+ hi.val[1] = vget_high_u8(q0);
+ Store2x8_NEON(lo, dst - 1 + 0 * stride, stride);
+ Store2x8_NEON(hi, dst - 1 + 8 * stride, stride);
+}
+
+#if !defined(WORK_AROUND_GCC)
+static WEBP_INLINE void Store4x8_NEON(const uint8x8x4_t v,
+ uint8_t* const dst, int stride) {
+ vst4_lane_u8(dst + 0 * stride, v, 0);
+ vst4_lane_u8(dst + 1 * stride, v, 1);
+ vst4_lane_u8(dst + 2 * stride, v, 2);
+ vst4_lane_u8(dst + 3 * stride, v, 3);
+ vst4_lane_u8(dst + 4 * stride, v, 4);
+ vst4_lane_u8(dst + 5 * stride, v, 5);
+ vst4_lane_u8(dst + 6 * stride, v, 6);
+ vst4_lane_u8(dst + 7 * stride, v, 7);
+}
+
+static WEBP_INLINE void Store4x16_NEON(const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1,
+ uint8_t* const dst, int stride) {
+ uint8x8x4_t lo, hi;
+ INIT_VECTOR4(lo,
+ vget_low_u8(p1), vget_low_u8(p0),
+ vget_low_u8(q0), vget_low_u8(q1));
+ INIT_VECTOR4(hi,
+ vget_high_u8(p1), vget_high_u8(p0),
+ vget_high_u8(q0), vget_high_u8(q1));
+ Store4x8_NEON(lo, dst - 2 + 0 * stride, stride);
+ Store4x8_NEON(hi, dst - 2 + 8 * stride, stride);
+}
+#endif // !WORK_AROUND_GCC
+
+static WEBP_INLINE void Store16x2_NEON(const uint8x16_t p0, const uint8x16_t q0,
+ uint8_t* const dst, int stride) {
+ vst1q_u8(dst - stride, p0);
+ vst1q_u8(dst, q0);
+}
+
+static WEBP_INLINE void Store16x4_NEON(const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1,
+ uint8_t* const dst, int stride) {
+ Store16x2_NEON(p1, p0, dst - stride, stride);
+ Store16x2_NEON(q0, q1, dst + stride, stride);
+}
+
+static WEBP_INLINE void Store8x2x2_NEON(const uint8x16_t p0,
+ const uint8x16_t q0,
+ uint8_t* const u, uint8_t* const v,
+ int stride) {
+ // p0 and q0 contain the u+v samples packed in low/high halves.
+ vst1_u8(u - stride, vget_low_u8(p0));
+ vst1_u8(u, vget_low_u8(q0));
+ vst1_u8(v - stride, vget_high_u8(p0));
+ vst1_u8(v, vget_high_u8(q0));
+}
+
+static WEBP_INLINE void Store8x4x2_NEON(const uint8x16_t p1,
+ const uint8x16_t p0,
+ const uint8x16_t q0,
+ const uint8x16_t q1,
+ uint8_t* const u, uint8_t* const v,
+ int stride) {
+ // The p1...q1 registers contain the u+v samples packed in low/high halves.
+ Store8x2x2_NEON(p1, p0, u - stride, v - stride, stride);
+ Store8x2x2_NEON(q0, q1, u + stride, v + stride, stride);
+}
+
+#if !defined(WORK_AROUND_GCC)
+
+#define STORE6_LANE(DST, VAL0, VAL1, LANE) do { \
+ vst3_lane_u8((DST) - 3, (VAL0), (LANE)); \
+ vst3_lane_u8((DST) + 0, (VAL1), (LANE)); \
+ (DST) += stride; \
+} while (0)
+
+static WEBP_INLINE void Store6x8x2_NEON(
+ const uint8x16_t p2, const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1, const uint8x16_t q2,
+ uint8_t* u, uint8_t* v, int stride) {
+ uint8x8x3_t u0, u1, v0, v1;
+ INIT_VECTOR3(u0, vget_low_u8(p2), vget_low_u8(p1), vget_low_u8(p0));
+ INIT_VECTOR3(u1, vget_low_u8(q0), vget_low_u8(q1), vget_low_u8(q2));
+ INIT_VECTOR3(v0, vget_high_u8(p2), vget_high_u8(p1), vget_high_u8(p0));
+ INIT_VECTOR3(v1, vget_high_u8(q0), vget_high_u8(q1), vget_high_u8(q2));
+ STORE6_LANE(u, u0, u1, 0);
+ STORE6_LANE(u, u0, u1, 1);
+ STORE6_LANE(u, u0, u1, 2);
+ STORE6_LANE(u, u0, u1, 3);
+ STORE6_LANE(u, u0, u1, 4);
+ STORE6_LANE(u, u0, u1, 5);
+ STORE6_LANE(u, u0, u1, 6);
+ STORE6_LANE(u, u0, u1, 7);
+ STORE6_LANE(v, v0, v1, 0);
+ STORE6_LANE(v, v0, v1, 1);
+ STORE6_LANE(v, v0, v1, 2);
+ STORE6_LANE(v, v0, v1, 3);
+ STORE6_LANE(v, v0, v1, 4);
+ STORE6_LANE(v, v0, v1, 5);
+ STORE6_LANE(v, v0, v1, 6);
+ STORE6_LANE(v, v0, v1, 7);
+}
+#undef STORE6_LANE
+
+static WEBP_INLINE void Store4x8x2_NEON(const uint8x16_t p1,
+ const uint8x16_t p0,
+ const uint8x16_t q0,
+ const uint8x16_t q1,
+ uint8_t* const u, uint8_t* const v,
+ int stride) {
+ uint8x8x4_t u0, v0;
+ INIT_VECTOR4(u0,
+ vget_low_u8(p1), vget_low_u8(p0),
+ vget_low_u8(q0), vget_low_u8(q1));
+ INIT_VECTOR4(v0,
+ vget_high_u8(p1), vget_high_u8(p0),
+ vget_high_u8(q0), vget_high_u8(q1));
+ vst4_lane_u8(u - 2 + 0 * stride, u0, 0);
+ vst4_lane_u8(u - 2 + 1 * stride, u0, 1);
+ vst4_lane_u8(u - 2 + 2 * stride, u0, 2);
+ vst4_lane_u8(u - 2 + 3 * stride, u0, 3);
+ vst4_lane_u8(u - 2 + 4 * stride, u0, 4);
+ vst4_lane_u8(u - 2 + 5 * stride, u0, 5);
+ vst4_lane_u8(u - 2 + 6 * stride, u0, 6);
+ vst4_lane_u8(u - 2 + 7 * stride, u0, 7);
+ vst4_lane_u8(v - 2 + 0 * stride, v0, 0);
+ vst4_lane_u8(v - 2 + 1 * stride, v0, 1);
+ vst4_lane_u8(v - 2 + 2 * stride, v0, 2);
+ vst4_lane_u8(v - 2 + 3 * stride, v0, 3);
+ vst4_lane_u8(v - 2 + 4 * stride, v0, 4);
+ vst4_lane_u8(v - 2 + 5 * stride, v0, 5);
+ vst4_lane_u8(v - 2 + 6 * stride, v0, 6);
+ vst4_lane_u8(v - 2 + 7 * stride, v0, 7);
+}
+
+#endif // !WORK_AROUND_GCC
+
+// Zero extend 'v' to an int16x8_t.
+static WEBP_INLINE int16x8_t ConvertU8ToS16_NEON(uint8x8_t v) {
+ return vreinterpretq_s16_u16(vmovl_u8(v));
+}
+
+// Performs unsigned 8b saturation on 'dst01' and 'dst23' storing the result
+// to the corresponding rows of 'dst'.
+static WEBP_INLINE void SaturateAndStore4x4_NEON(uint8_t* const dst,
+ const int16x8_t dst01,
+ const int16x8_t dst23) {
+ // Unsigned saturate to 8b.
+ const uint8x8_t dst01_u8 = vqmovun_s16(dst01);
+ const uint8x8_t dst23_u8 = vqmovun_s16(dst23);
+
+ // Store the results.
+ vst1_lane_u32((uint32_t*)(dst + 0 * BPS), vreinterpret_u32_u8(dst01_u8), 0);
+ vst1_lane_u32((uint32_t*)(dst + 1 * BPS), vreinterpret_u32_u8(dst01_u8), 1);
+ vst1_lane_u32((uint32_t*)(dst + 2 * BPS), vreinterpret_u32_u8(dst23_u8), 0);
+ vst1_lane_u32((uint32_t*)(dst + 3 * BPS), vreinterpret_u32_u8(dst23_u8), 1);
+}
+
+static WEBP_INLINE void Add4x4_NEON(const int16x8_t row01,
+ const int16x8_t row23,
+ uint8_t* const dst) {
+ uint32x2_t dst01 = vdup_n_u32(0);
+ uint32x2_t dst23 = vdup_n_u32(0);
+
+ // Load the source pixels.
+ dst01 = vld1_lane_u32((uint32_t*)(dst + 0 * BPS), dst01, 0);
+ dst23 = vld1_lane_u32((uint32_t*)(dst + 2 * BPS), dst23, 0);
+ dst01 = vld1_lane_u32((uint32_t*)(dst + 1 * BPS), dst01, 1);
+ dst23 = vld1_lane_u32((uint32_t*)(dst + 3 * BPS), dst23, 1);
+
+ {
+ // Convert to 16b.
+ const int16x8_t dst01_s16 = ConvertU8ToS16_NEON(vreinterpret_u8_u32(dst01));
+ const int16x8_t dst23_s16 = ConvertU8ToS16_NEON(vreinterpret_u8_u32(dst23));
+
+ // Descale with rounding.
+ const int16x8_t out01 = vrsraq_n_s16(dst01_s16, row01, 3);
+ const int16x8_t out23 = vrsraq_n_s16(dst23_s16, row23, 3);
+ // Add the inverse transform.
+ SaturateAndStore4x4_NEON(dst, out01, out23);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Simple In-loop filtering (Paragraph 15.2)
+
+static uint8x16_t NeedsFilter_NEON(const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1,
+ int thresh) {
+ const uint8x16_t thresh_v = vdupq_n_u8((uint8_t)thresh);
+ const uint8x16_t a_p0_q0 = vabdq_u8(p0, q0); // abs(p0-q0)
+ const uint8x16_t a_p1_q1 = vabdq_u8(p1, q1); // abs(p1-q1)
+ const uint8x16_t a_p0_q0_2 = vqaddq_u8(a_p0_q0, a_p0_q0); // 2 * abs(p0-q0)
+ const uint8x16_t a_p1_q1_2 = vshrq_n_u8(a_p1_q1, 1); // abs(p1-q1) / 2
+ const uint8x16_t sum = vqaddq_u8(a_p0_q0_2, a_p1_q1_2);
+ const uint8x16_t mask = vcgeq_u8(thresh_v, sum);
+ return mask;
+}
+
+static int8x16_t FlipSign_NEON(const uint8x16_t v) {
+ const uint8x16_t sign_bit = vdupq_n_u8(0x80);
+ return vreinterpretq_s8_u8(veorq_u8(v, sign_bit));
+}
+
+static uint8x16_t FlipSignBack_NEON(const int8x16_t v) {
+ const int8x16_t sign_bit = vdupq_n_s8(0x80);
+ return vreinterpretq_u8_s8(veorq_s8(v, sign_bit));
+}
+
+static int8x16_t GetBaseDelta_NEON(const int8x16_t p1, const int8x16_t p0,
+ const int8x16_t q0, const int8x16_t q1) {
+ const int8x16_t q0_p0 = vqsubq_s8(q0, p0); // (q0-p0)
+ const int8x16_t p1_q1 = vqsubq_s8(p1, q1); // (p1-q1)
+ const int8x16_t s1 = vqaddq_s8(p1_q1, q0_p0); // (p1-q1) + 1 * (q0 - p0)
+ const int8x16_t s2 = vqaddq_s8(q0_p0, s1); // (p1-q1) + 2 * (q0 - p0)
+ const int8x16_t s3 = vqaddq_s8(q0_p0, s2); // (p1-q1) + 3 * (q0 - p0)
+ return s3;
+}
+
+static int8x16_t GetBaseDelta0_NEON(const int8x16_t p0, const int8x16_t q0) {
+ const int8x16_t q0_p0 = vqsubq_s8(q0, p0); // (q0-p0)
+ const int8x16_t s1 = vqaddq_s8(q0_p0, q0_p0); // 2 * (q0 - p0)
+ const int8x16_t s2 = vqaddq_s8(q0_p0, s1); // 3 * (q0 - p0)
+ return s2;
+}
+
+//------------------------------------------------------------------------------
+
+static void ApplyFilter2NoFlip_NEON(const int8x16_t p0s, const int8x16_t q0s,
+ const int8x16_t delta,
+ int8x16_t* const op0,
+ int8x16_t* const oq0) {
+ const int8x16_t kCst3 = vdupq_n_s8(0x03);
+ const int8x16_t kCst4 = vdupq_n_s8(0x04);
+ const int8x16_t delta_p3 = vqaddq_s8(delta, kCst3);
+ const int8x16_t delta_p4 = vqaddq_s8(delta, kCst4);
+ const int8x16_t delta3 = vshrq_n_s8(delta_p3, 3);
+ const int8x16_t delta4 = vshrq_n_s8(delta_p4, 3);
+ *op0 = vqaddq_s8(p0s, delta3);
+ *oq0 = vqsubq_s8(q0s, delta4);
+}
+
+#if defined(WEBP_USE_INTRINSICS)
+
+static void ApplyFilter2_NEON(const int8x16_t p0s, const int8x16_t q0s,
+ const int8x16_t delta,
+ uint8x16_t* const op0, uint8x16_t* const oq0) {
+ const int8x16_t kCst3 = vdupq_n_s8(0x03);
+ const int8x16_t kCst4 = vdupq_n_s8(0x04);
+ const int8x16_t delta_p3 = vqaddq_s8(delta, kCst3);
+ const int8x16_t delta_p4 = vqaddq_s8(delta, kCst4);
+ const int8x16_t delta3 = vshrq_n_s8(delta_p3, 3);
+ const int8x16_t delta4 = vshrq_n_s8(delta_p4, 3);
+ const int8x16_t sp0 = vqaddq_s8(p0s, delta3);
+ const int8x16_t sq0 = vqsubq_s8(q0s, delta4);
+ *op0 = FlipSignBack_NEON(sp0);
+ *oq0 = FlipSignBack_NEON(sq0);
+}
+
+static void DoFilter2_NEON(const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1,
+ const uint8x16_t mask,
+ uint8x16_t* const op0, uint8x16_t* const oq0) {
+ const int8x16_t p1s = FlipSign_NEON(p1);
+ const int8x16_t p0s = FlipSign_NEON(p0);
+ const int8x16_t q0s = FlipSign_NEON(q0);
+ const int8x16_t q1s = FlipSign_NEON(q1);
+ const int8x16_t delta0 = GetBaseDelta_NEON(p1s, p0s, q0s, q1s);
+ const int8x16_t delta1 = vandq_s8(delta0, vreinterpretq_s8_u8(mask));
+ ApplyFilter2_NEON(p0s, q0s, delta1, op0, oq0);
+}
+
+static void SimpleVFilter16_NEON(uint8_t* p, int stride, int thresh) {
+ uint8x16_t p1, p0, q0, q1, op0, oq0;
+ Load16x4_NEON(p, stride, &p1, &p0, &q0, &q1);
+ {
+ const uint8x16_t mask = NeedsFilter_NEON(p1, p0, q0, q1, thresh);
+ DoFilter2_NEON(p1, p0, q0, q1, mask, &op0, &oq0);
+ }
+ Store16x2_NEON(op0, oq0, p, stride);
+}
+
+static void SimpleHFilter16_NEON(uint8_t* p, int stride, int thresh) {
+ uint8x16_t p1, p0, q0, q1, oq0, op0;
+ Load4x16_NEON(p, stride, &p1, &p0, &q0, &q1);
+ {
+ const uint8x16_t mask = NeedsFilter_NEON(p1, p0, q0, q1, thresh);
+ DoFilter2_NEON(p1, p0, q0, q1, mask, &op0, &oq0);
+ }
+ Store2x16_NEON(op0, oq0, p, stride);
+}
+
+#else
+
+// Load/Store vertical edge
+#define LOAD8x4(c1, c2, c3, c4, b1, b2, stride) \
+ "vld4.8 {" #c1 "[0]," #c2 "[0]," #c3 "[0]," #c4 "[0]}," #b1 "," #stride "\n" \
+ "vld4.8 {" #c1 "[1]," #c2 "[1]," #c3 "[1]," #c4 "[1]}," #b2 "," #stride "\n" \
+ "vld4.8 {" #c1 "[2]," #c2 "[2]," #c3 "[2]," #c4 "[2]}," #b1 "," #stride "\n" \
+ "vld4.8 {" #c1 "[3]," #c2 "[3]," #c3 "[3]," #c4 "[3]}," #b2 "," #stride "\n" \
+ "vld4.8 {" #c1 "[4]," #c2 "[4]," #c3 "[4]," #c4 "[4]}," #b1 "," #stride "\n" \
+ "vld4.8 {" #c1 "[5]," #c2 "[5]," #c3 "[5]," #c4 "[5]}," #b2 "," #stride "\n" \
+ "vld4.8 {" #c1 "[6]," #c2 "[6]," #c3 "[6]," #c4 "[6]}," #b1 "," #stride "\n" \
+ "vld4.8 {" #c1 "[7]," #c2 "[7]," #c3 "[7]," #c4 "[7]}," #b2 "," #stride "\n"
+
+#define STORE8x2(c1, c2, p, stride) \
+ "vst2.8 {" #c1 "[0], " #c2 "[0]}," #p "," #stride " \n" \
+ "vst2.8 {" #c1 "[1], " #c2 "[1]}," #p "," #stride " \n" \
+ "vst2.8 {" #c1 "[2], " #c2 "[2]}," #p "," #stride " \n" \
+ "vst2.8 {" #c1 "[3], " #c2 "[3]}," #p "," #stride " \n" \
+ "vst2.8 {" #c1 "[4], " #c2 "[4]}," #p "," #stride " \n" \
+ "vst2.8 {" #c1 "[5], " #c2 "[5]}," #p "," #stride " \n" \
+ "vst2.8 {" #c1 "[6], " #c2 "[6]}," #p "," #stride " \n" \
+ "vst2.8 {" #c1 "[7], " #c2 "[7]}," #p "," #stride " \n"
+
+#define QRegs "q0", "q1", "q2", "q3", \
+ "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
+
+#define FLIP_SIGN_BIT2(a, b, s) \
+ "veor " #a "," #a "," #s " \n" \
+ "veor " #b "," #b "," #s " \n" \
+
+#define FLIP_SIGN_BIT4(a, b, c, d, s) \
+ FLIP_SIGN_BIT2(a, b, s) \
+ FLIP_SIGN_BIT2(c, d, s) \
+
+#define NEEDS_FILTER(p1, p0, q0, q1, thresh, mask) \
+ "vabd.u8 q15," #p0 "," #q0 " \n" /* abs(p0 - q0) */ \
+ "vabd.u8 q14," #p1 "," #q1 " \n" /* abs(p1 - q1) */ \
+ "vqadd.u8 q15, q15, q15 \n" /* abs(p0 - q0) * 2 */ \
+ "vshr.u8 q14, q14, #1 \n" /* abs(p1 - q1) / 2 */ \
+ "vqadd.u8 q15, q15, q14 \n" /* abs(p0 - q0) * 2 + abs(p1 - q1) / 2 */ \
+ "vdup.8 q14, " #thresh " \n" \
+ "vcge.u8 " #mask ", q14, q15 \n" /* mask <= thresh */
+
+#define GET_BASE_DELTA(p1, p0, q0, q1, o) \
+ "vqsub.s8 q15," #q0 "," #p0 " \n" /* (q0 - p0) */ \
+ "vqsub.s8 " #o "," #p1 "," #q1 " \n" /* (p1 - q1) */ \
+ "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 1 * (p0 - q0) */ \
+ "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 2 * (p0 - q0) */ \
+ "vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 3 * (p0 - q0) */
+
+#define DO_SIMPLE_FILTER(p0, q0, fl) \
+ "vmov.i8 q15, #0x03 \n" \
+ "vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 3 */ \
+ "vshr.s8 q15, q15, #3 \n" /* filter1 >> 3 */ \
+ "vqadd.s8 " #p0 "," #p0 ", q15 \n" /* p0 += filter1 */ \
+ \
+ "vmov.i8 q15, #0x04 \n" \
+ "vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 4 */ \
+ "vshr.s8 q15, q15, #3 \n" /* filter2 >> 3 */ \
+ "vqsub.s8 " #q0 "," #q0 ", q15 \n" /* q0 -= filter2 */
+
+// Applies filter on 2 pixels (p0 and q0)
+#define DO_FILTER2(p1, p0, q0, q1, thresh) \
+ NEEDS_FILTER(p1, p0, q0, q1, thresh, q9) /* filter mask in q9 */ \
+ "vmov.i8 q10, #0x80 \n" /* sign bit */ \
+ FLIP_SIGN_BIT4(p1, p0, q0, q1, q10) /* convert to signed value */ \
+ GET_BASE_DELTA(p1, p0, q0, q1, q11) /* get filter level */ \
+ "vand q9, q9, q11 \n" /* apply filter mask */ \
+ DO_SIMPLE_FILTER(p0, q0, q9) /* apply filter */ \
+ FLIP_SIGN_BIT2(p0, q0, q10)
+
+static void SimpleVFilter16_NEON(uint8_t* p, int stride, int thresh) {
+ __asm__ volatile (
+ "sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride
+
+ "vld1.u8 {q1}, [%[p]], %[stride] \n" // p1
+ "vld1.u8 {q2}, [%[p]], %[stride] \n" // p0
+ "vld1.u8 {q3}, [%[p]], %[stride] \n" // q0
+ "vld1.u8 {q12}, [%[p]] \n" // q1
+
+ DO_FILTER2(q1, q2, q3, q12, %[thresh])
+
+ "sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride
+
+ "vst1.u8 {q2}, [%[p]], %[stride] \n" // store op0
+ "vst1.u8 {q3}, [%[p]] \n" // store oq0
+ : [p] "+r"(p)
+ : [stride] "r"(stride), [thresh] "r"(thresh)
+ : "memory", QRegs
+ );
+}
+
+static void SimpleHFilter16_NEON(uint8_t* p, int stride, int thresh) {
+ __asm__ volatile (
+ "sub r4, %[p], #2 \n" // base1 = p - 2
+ "lsl r6, %[stride], #1 \n" // r6 = 2 * stride
+ "add r5, r4, %[stride] \n" // base2 = base1 + stride
+
+ LOAD8x4(d2, d3, d4, d5, [r4], [r5], r6)
+ LOAD8x4(d24, d25, d26, d27, [r4], [r5], r6)
+ "vswp d3, d24 \n" // p1:q1 p0:q3
+ "vswp d5, d26 \n" // q0:q2 q1:q4
+ "vswp q2, q12 \n" // p1:q1 p0:q2 q0:q3 q1:q4
+
+ DO_FILTER2(q1, q2, q12, q13, %[thresh])
+
+ "sub %[p], %[p], #1 \n" // p - 1
+
+ "vswp d5, d24 \n"
+ STORE8x2(d4, d5, [%[p]], %[stride])
+ STORE8x2(d24, d25, [%[p]], %[stride])
+
+ : [p] "+r"(p)
+ : [stride] "r"(stride), [thresh] "r"(thresh)
+ : "memory", "r4", "r5", "r6", QRegs
+ );
+}
+
+#undef LOAD8x4
+#undef STORE8x2
+
+#endif // WEBP_USE_INTRINSICS
+
+static void SimpleVFilter16i_NEON(uint8_t* p, int stride, int thresh) {
+ uint32_t k;
+ for (k = 3; k != 0; --k) {
+ p += 4 * stride;
+ SimpleVFilter16_NEON(p, stride, thresh);
+ }
+}
+
+static void SimpleHFilter16i_NEON(uint8_t* p, int stride, int thresh) {
+ uint32_t k;
+ for (k = 3; k != 0; --k) {
+ p += 4;
+ SimpleHFilter16_NEON(p, stride, thresh);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Complex In-loop filtering (Paragraph 15.3)
+
+static uint8x16_t NeedsHev_NEON(const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1,
+ int hev_thresh) {
+ const uint8x16_t hev_thresh_v = vdupq_n_u8((uint8_t)hev_thresh);
+ const uint8x16_t a_p1_p0 = vabdq_u8(p1, p0); // abs(p1 - p0)
+ const uint8x16_t a_q1_q0 = vabdq_u8(q1, q0); // abs(q1 - q0)
+ const uint8x16_t a_max = vmaxq_u8(a_p1_p0, a_q1_q0);
+ const uint8x16_t mask = vcgtq_u8(a_max, hev_thresh_v);
+ return mask;
+}
+
+static uint8x16_t NeedsFilter2_NEON(const uint8x16_t p3, const uint8x16_t p2,
+ const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1,
+ const uint8x16_t q2, const uint8x16_t q3,
+ int ithresh, int thresh) {
+ const uint8x16_t ithresh_v = vdupq_n_u8((uint8_t)ithresh);
+ const uint8x16_t a_p3_p2 = vabdq_u8(p3, p2); // abs(p3 - p2)
+ const uint8x16_t a_p2_p1 = vabdq_u8(p2, p1); // abs(p2 - p1)
+ const uint8x16_t a_p1_p0 = vabdq_u8(p1, p0); // abs(p1 - p0)
+ const uint8x16_t a_q3_q2 = vabdq_u8(q3, q2); // abs(q3 - q2)
+ const uint8x16_t a_q2_q1 = vabdq_u8(q2, q1); // abs(q2 - q1)
+ const uint8x16_t a_q1_q0 = vabdq_u8(q1, q0); // abs(q1 - q0)
+ const uint8x16_t max1 = vmaxq_u8(a_p3_p2, a_p2_p1);
+ const uint8x16_t max2 = vmaxq_u8(a_p1_p0, a_q3_q2);
+ const uint8x16_t max3 = vmaxq_u8(a_q2_q1, a_q1_q0);
+ const uint8x16_t max12 = vmaxq_u8(max1, max2);
+ const uint8x16_t max123 = vmaxq_u8(max12, max3);
+ const uint8x16_t mask2 = vcgeq_u8(ithresh_v, max123);
+ const uint8x16_t mask1 = NeedsFilter_NEON(p1, p0, q0, q1, thresh);
+ const uint8x16_t mask = vandq_u8(mask1, mask2);
+ return mask;
+}
+
+// 4-points filter
+
+static void ApplyFilter4_NEON(
+ const int8x16_t p1, const int8x16_t p0,
+ const int8x16_t q0, const int8x16_t q1,
+ const int8x16_t delta0,
+ uint8x16_t* const op1, uint8x16_t* const op0,
+ uint8x16_t* const oq0, uint8x16_t* const oq1) {
+ const int8x16_t kCst3 = vdupq_n_s8(0x03);
+ const int8x16_t kCst4 = vdupq_n_s8(0x04);
+ const int8x16_t delta1 = vqaddq_s8(delta0, kCst4);
+ const int8x16_t delta2 = vqaddq_s8(delta0, kCst3);
+ const int8x16_t a1 = vshrq_n_s8(delta1, 3);
+ const int8x16_t a2 = vshrq_n_s8(delta2, 3);
+ const int8x16_t a3 = vrshrq_n_s8(a1, 1); // a3 = (a1 + 1) >> 1
+ *op0 = FlipSignBack_NEON(vqaddq_s8(p0, a2)); // clip(p0 + a2)
+ *oq0 = FlipSignBack_NEON(vqsubq_s8(q0, a1)); // clip(q0 - a1)
+ *op1 = FlipSignBack_NEON(vqaddq_s8(p1, a3)); // clip(p1 + a3)
+ *oq1 = FlipSignBack_NEON(vqsubq_s8(q1, a3)); // clip(q1 - a3)
+}
+
+static void DoFilter4_NEON(
+ const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1,
+ const uint8x16_t mask, const uint8x16_t hev_mask,
+ uint8x16_t* const op1, uint8x16_t* const op0,
+ uint8x16_t* const oq0, uint8x16_t* const oq1) {
+ // This is a fused version of DoFilter2() calling ApplyFilter2 directly
+ const int8x16_t p1s = FlipSign_NEON(p1);
+ int8x16_t p0s = FlipSign_NEON(p0);
+ int8x16_t q0s = FlipSign_NEON(q0);
+ const int8x16_t q1s = FlipSign_NEON(q1);
+ const uint8x16_t simple_lf_mask = vandq_u8(mask, hev_mask);
+
+ // do_filter2 part (simple loopfilter on pixels with hev)
+ {
+ const int8x16_t delta = GetBaseDelta_NEON(p1s, p0s, q0s, q1s);
+ const int8x16_t simple_lf_delta =
+ vandq_s8(delta, vreinterpretq_s8_u8(simple_lf_mask));
+ ApplyFilter2NoFlip_NEON(p0s, q0s, simple_lf_delta, &p0s, &q0s);
+ }
+
+ // do_filter4 part (complex loopfilter on pixels without hev)
+ {
+ const int8x16_t delta0 = GetBaseDelta0_NEON(p0s, q0s);
+ // we use: (mask & hev_mask) ^ mask = mask & !hev_mask
+ const uint8x16_t complex_lf_mask = veorq_u8(simple_lf_mask, mask);
+ const int8x16_t complex_lf_delta =
+ vandq_s8(delta0, vreinterpretq_s8_u8(complex_lf_mask));
+ ApplyFilter4_NEON(p1s, p0s, q0s, q1s, complex_lf_delta, op1, op0, oq0, oq1);
+ }
+}
+
+// 6-points filter
+
+static void ApplyFilter6_NEON(
+ const int8x16_t p2, const int8x16_t p1, const int8x16_t p0,
+ const int8x16_t q0, const int8x16_t q1, const int8x16_t q2,
+ const int8x16_t delta,
+ uint8x16_t* const op2, uint8x16_t* const op1, uint8x16_t* const op0,
+ uint8x16_t* const oq0, uint8x16_t* const oq1, uint8x16_t* const oq2) {
+ // We have to compute: X = (9*a+63) >> 7, Y = (18*a+63)>>7, Z = (27*a+63) >> 7
+ // Turns out, there's a common sub-expression S=9 * a - 1 that can be used
+ // with the special vqrshrn_n_s16 rounding-shift-and-narrow instruction:
+ // X = (S + 64) >> 7, Y = (S + 32) >> 6, Z = (18 * a + S + 64) >> 7
+ const int8x8_t delta_lo = vget_low_s8(delta);
+ const int8x8_t delta_hi = vget_high_s8(delta);
+ const int8x8_t kCst9 = vdup_n_s8(9);
+ const int16x8_t kCstm1 = vdupq_n_s16(-1);
+ const int8x8_t kCst18 = vdup_n_s8(18);
+ const int16x8_t S_lo = vmlal_s8(kCstm1, kCst9, delta_lo); // S = 9 * a - 1
+ const int16x8_t S_hi = vmlal_s8(kCstm1, kCst9, delta_hi);
+ const int16x8_t Z_lo = vmlal_s8(S_lo, kCst18, delta_lo); // S + 18 * a
+ const int16x8_t Z_hi = vmlal_s8(S_hi, kCst18, delta_hi);
+ const int8x8_t a3_lo = vqrshrn_n_s16(S_lo, 7); // (9 * a + 63) >> 7
+ const int8x8_t a3_hi = vqrshrn_n_s16(S_hi, 7);
+ const int8x8_t a2_lo = vqrshrn_n_s16(S_lo, 6); // (9 * a + 31) >> 6
+ const int8x8_t a2_hi = vqrshrn_n_s16(S_hi, 6);
+ const int8x8_t a1_lo = vqrshrn_n_s16(Z_lo, 7); // (27 * a + 63) >> 7
+ const int8x8_t a1_hi = vqrshrn_n_s16(Z_hi, 7);
+ const int8x16_t a1 = vcombine_s8(a1_lo, a1_hi);
+ const int8x16_t a2 = vcombine_s8(a2_lo, a2_hi);
+ const int8x16_t a3 = vcombine_s8(a3_lo, a3_hi);
+
+ *op0 = FlipSignBack_NEON(vqaddq_s8(p0, a1)); // clip(p0 + a1)
+ *oq0 = FlipSignBack_NEON(vqsubq_s8(q0, a1)); // clip(q0 - q1)
+ *oq1 = FlipSignBack_NEON(vqsubq_s8(q1, a2)); // clip(q1 - a2)
+ *op1 = FlipSignBack_NEON(vqaddq_s8(p1, a2)); // clip(p1 + a2)
+ *oq2 = FlipSignBack_NEON(vqsubq_s8(q2, a3)); // clip(q2 - a3)
+ *op2 = FlipSignBack_NEON(vqaddq_s8(p2, a3)); // clip(p2 + a3)
+}
+
+static void DoFilter6_NEON(
+ const uint8x16_t p2, const uint8x16_t p1, const uint8x16_t p0,
+ const uint8x16_t q0, const uint8x16_t q1, const uint8x16_t q2,
+ const uint8x16_t mask, const uint8x16_t hev_mask,
+ uint8x16_t* const op2, uint8x16_t* const op1, uint8x16_t* const op0,
+ uint8x16_t* const oq0, uint8x16_t* const oq1, uint8x16_t* const oq2) {
+ // This is a fused version of DoFilter2() calling ApplyFilter2 directly
+ const int8x16_t p2s = FlipSign_NEON(p2);
+ const int8x16_t p1s = FlipSign_NEON(p1);
+ int8x16_t p0s = FlipSign_NEON(p0);
+ int8x16_t q0s = FlipSign_NEON(q0);
+ const int8x16_t q1s = FlipSign_NEON(q1);
+ const int8x16_t q2s = FlipSign_NEON(q2);
+ const uint8x16_t simple_lf_mask = vandq_u8(mask, hev_mask);
+ const int8x16_t delta0 = GetBaseDelta_NEON(p1s, p0s, q0s, q1s);
+
+ // do_filter2 part (simple loopfilter on pixels with hev)
+ {
+ const int8x16_t simple_lf_delta =
+ vandq_s8(delta0, vreinterpretq_s8_u8(simple_lf_mask));
+ ApplyFilter2NoFlip_NEON(p0s, q0s, simple_lf_delta, &p0s, &q0s);
+ }
+
+ // do_filter6 part (complex loopfilter on pixels without hev)
+ {
+ // we use: (mask & hev_mask) ^ mask = mask & !hev_mask
+ const uint8x16_t complex_lf_mask = veorq_u8(simple_lf_mask, mask);
+ const int8x16_t complex_lf_delta =
+ vandq_s8(delta0, vreinterpretq_s8_u8(complex_lf_mask));
+ ApplyFilter6_NEON(p2s, p1s, p0s, q0s, q1s, q2s, complex_lf_delta,
+ op2, op1, op0, oq0, oq1, oq2);
+ }
+}
+
+// on macroblock edges
+
+static void VFilter16_NEON(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3;
+ Load16x8_NEON(p, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3,
+ ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ uint8x16_t op2, op1, op0, oq0, oq1, oq2;
+ DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask,
+ &op2, &op1, &op0, &oq0, &oq1, &oq2);
+ Store16x2_NEON(op2, op1, p - 2 * stride, stride);
+ Store16x2_NEON(op0, oq0, p + 0 * stride, stride);
+ Store16x2_NEON(oq1, oq2, p + 2 * stride, stride);
+ }
+}
+
+static void HFilter16_NEON(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3;
+ Load8x16_NEON(p, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3,
+ ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ uint8x16_t op2, op1, op0, oq0, oq1, oq2;
+ DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask,
+ &op2, &op1, &op0, &oq0, &oq1, &oq2);
+ Store2x16_NEON(op2, op1, p - 2, stride);
+ Store2x16_NEON(op0, oq0, p + 0, stride);
+ Store2x16_NEON(oq1, oq2, p + 2, stride);
+ }
+}
+
+// on three inner edges
+static void VFilter16i_NEON(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint32_t k;
+ uint8x16_t p3, p2, p1, p0;
+ Load16x4_NEON(p + 2 * stride, stride, &p3, &p2, &p1, &p0);
+ for (k = 3; k != 0; --k) {
+ uint8x16_t q0, q1, q2, q3;
+ p += 4 * stride;
+ Load16x4_NEON(p + 2 * stride, stride, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask =
+ NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ // p3 and p2 are not just temporary variables here: they will be
+ // re-used for next span. And q2/q3 will become p1/p0 accordingly.
+ DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &p1, &p0, &p3, &p2);
+ Store16x4_NEON(p1, p0, p3, p2, p, stride);
+ p1 = q2;
+ p0 = q3;
+ }
+ }
+}
+
+#if !defined(WORK_AROUND_GCC)
+static void HFilter16i_NEON(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint32_t k;
+ uint8x16_t p3, p2, p1, p0;
+ Load4x16_NEON(p + 2, stride, &p3, &p2, &p1, &p0);
+ for (k = 3; k != 0; --k) {
+ uint8x16_t q0, q1, q2, q3;
+ p += 4;
+ Load4x16_NEON(p + 2, stride, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask =
+ NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3, ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &p1, &p0, &p3, &p2);
+ Store4x16_NEON(p1, p0, p3, p2, p, stride);
+ p1 = q2;
+ p0 = q3;
+ }
+ }
+}
+#endif // !WORK_AROUND_GCC
+
+// 8-pixels wide variant, for chroma filtering
+static void VFilter8_NEON(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3;
+ Load8x8x2_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3,
+ ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ uint8x16_t op2, op1, op0, oq0, oq1, oq2;
+ DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask,
+ &op2, &op1, &op0, &oq0, &oq1, &oq2);
+ Store8x2x2_NEON(op2, op1, u - 2 * stride, v - 2 * stride, stride);
+ Store8x2x2_NEON(op0, oq0, u + 0 * stride, v + 0 * stride, stride);
+ Store8x2x2_NEON(oq1, oq2, u + 2 * stride, v + 2 * stride, stride);
+ }
+}
+static void VFilter8i_NEON(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3;
+ u += 4 * stride;
+ v += 4 * stride;
+ Load8x8x2_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3,
+ ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ uint8x16_t op1, op0, oq0, oq1;
+ DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &op1, &op0, &oq0, &oq1);
+ Store8x4x2_NEON(op1, op0, oq0, oq1, u, v, stride);
+ }
+}
+
+#if !defined(WORK_AROUND_GCC)
+static void HFilter8_NEON(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3;
+ Load8x8x2T_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3,
+ ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ uint8x16_t op2, op1, op0, oq0, oq1, oq2;
+ DoFilter6_NEON(p2, p1, p0, q0, q1, q2, mask, hev_mask,
+ &op2, &op1, &op0, &oq0, &oq1, &oq2);
+ Store6x8x2_NEON(op2, op1, op0, oq0, oq1, oq2, u, v, stride);
+ }
+}
+
+static void HFilter8i_NEON(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ uint8x16_t p3, p2, p1, p0, q0, q1, q2, q3;
+ u += 4;
+ v += 4;
+ Load8x8x2T_NEON(u, v, stride, &p3, &p2, &p1, &p0, &q0, &q1, &q2, &q3);
+ {
+ const uint8x16_t mask = NeedsFilter2_NEON(p3, p2, p1, p0, q0, q1, q2, q3,
+ ithresh, thresh);
+ const uint8x16_t hev_mask = NeedsHev_NEON(p1, p0, q0, q1, hev_thresh);
+ uint8x16_t op1, op0, oq0, oq1;
+ DoFilter4_NEON(p1, p0, q0, q1, mask, hev_mask, &op1, &op0, &oq0, &oq1);
+ Store4x8x2_NEON(op1, op0, oq0, oq1, u, v, stride);
+ }
+}
+#endif // !WORK_AROUND_GCC
+
+//-----------------------------------------------------------------------------
+// Inverse transforms (Paragraph 14.4)
+
+// Technically these are unsigned but vqdmulh is only available in signed.
+// vqdmulh returns high half (effectively >> 16) but also doubles the value,
+// changing the >> 16 to >> 15 and requiring an additional >> 1.
+// We use this to our advantage with kC2. The canonical value is 35468.
+// However, the high bit is set so treating it as signed will give incorrect
+// results. We avoid this by down shifting by 1 here to clear the highest bit.
+// Combined with the doubling effect of vqdmulh we get >> 16.
+// This can not be applied to kC1 because the lowest bit is set. Down shifting
+// the constant would reduce precision.
+
+// libwebp uses a trick to avoid some extra addition that libvpx does.
+// Instead of:
+// temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1) >> 16);
+// libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the
+// same issue with kC1 and vqdmulh that we work around by down shifting kC2
+
+static const int16_t kC1 = 20091;
+static const int16_t kC2 = 17734; // half of kC2, actually. See comment above.
+
+#if defined(WEBP_USE_INTRINSICS)
+static WEBP_INLINE void Transpose8x2_NEON(const int16x8_t in0,
+ const int16x8_t in1,
+ int16x8x2_t* const out) {
+ // a0 a1 a2 a3 | b0 b1 b2 b3 => a0 b0 c0 d0 | a1 b1 c1 d1
+ // c0 c1 c2 c3 | d0 d1 d2 d3 a2 b2 c2 d2 | a3 b3 c3 d3
+ const int16x8x2_t tmp0 = vzipq_s16(in0, in1); // a0 c0 a1 c1 a2 c2 ...
+ // b0 d0 b1 d1 b2 d2 ...
+ *out = vzipq_s16(tmp0.val[0], tmp0.val[1]);
+}
+
+static WEBP_INLINE void TransformPass_NEON(int16x8x2_t* const rows) {
+ // {rows} = in0 | in4
+ // in8 | in12
+ // B1 = in4 | in12
+ const int16x8_t B1 =
+ vcombine_s16(vget_high_s16(rows->val[0]), vget_high_s16(rows->val[1]));
+ // C0 = kC1 * in4 | kC1 * in12
+ // C1 = kC2 * in4 | kC2 * in12
+ const int16x8_t C0 = vsraq_n_s16(B1, vqdmulhq_n_s16(B1, kC1), 1);
+ const int16x8_t C1 = vqdmulhq_n_s16(B1, kC2);
+ const int16x4_t a = vqadd_s16(vget_low_s16(rows->val[0]),
+ vget_low_s16(rows->val[1])); // in0 + in8
+ const int16x4_t b = vqsub_s16(vget_low_s16(rows->val[0]),
+ vget_low_s16(rows->val[1])); // in0 - in8
+ // c = kC2 * in4 - kC1 * in12
+ // d = kC1 * in4 + kC2 * in12
+ const int16x4_t c = vqsub_s16(vget_low_s16(C1), vget_high_s16(C0));
+ const int16x4_t d = vqadd_s16(vget_low_s16(C0), vget_high_s16(C1));
+ const int16x8_t D0 = vcombine_s16(a, b); // D0 = a | b
+ const int16x8_t D1 = vcombine_s16(d, c); // D1 = d | c
+ const int16x8_t E0 = vqaddq_s16(D0, D1); // a+d | b+c
+ const int16x8_t E_tmp = vqsubq_s16(D0, D1); // a-d | b-c
+ const int16x8_t E1 = vcombine_s16(vget_high_s16(E_tmp), vget_low_s16(E_tmp));
+ Transpose8x2_NEON(E0, E1, rows);
+}
+
+static void TransformOne_NEON(const int16_t* in, uint8_t* dst) {
+ int16x8x2_t rows;
+ INIT_VECTOR2(rows, vld1q_s16(in + 0), vld1q_s16(in + 8));
+ TransformPass_NEON(&rows);
+ TransformPass_NEON(&rows);
+ Add4x4_NEON(rows.val[0], rows.val[1], dst);
+}
+
+#else
+
+static void TransformOne_NEON(const int16_t* in, uint8_t* dst) {
+ const int kBPS = BPS;
+ // kC1, kC2. Padded because vld1.16 loads 8 bytes
+ const int16_t constants[4] = { kC1, kC2, 0, 0 };
+ /* Adapted from libvpx: vp8/common/arm/neon/shortidct4x4llm_neon.asm */
+ __asm__ volatile (
+ "vld1.16 {q1, q2}, [%[in]] \n"
+ "vld1.16 {d0}, [%[constants]] \n"
+
+ /* d2: in[0]
+ * d3: in[8]
+ * d4: in[4]
+ * d5: in[12]
+ */
+ "vswp d3, d4 \n"
+
+ /* q8 = {in[4], in[12]} * kC1 * 2 >> 16
+ * q9 = {in[4], in[12]} * kC2 >> 16
+ */
+ "vqdmulh.s16 q8, q2, d0[0] \n"
+ "vqdmulh.s16 q9, q2, d0[1] \n"
+
+ /* d22 = a = in[0] + in[8]
+ * d23 = b = in[0] - in[8]
+ */
+ "vqadd.s16 d22, d2, d3 \n"
+ "vqsub.s16 d23, d2, d3 \n"
+
+ /* The multiplication should be x * kC1 >> 16
+ * However, with vqdmulh we get x * kC1 * 2 >> 16
+ * (multiply, double, return high half)
+ * We avoided this in kC2 by pre-shifting the constant.
+ * q8 = in[4]/[12] * kC1 >> 16
+ */
+ "vshr.s16 q8, q8, #1 \n"
+
+ /* Add {in[4], in[12]} back after the multiplication. This is handled by
+ * adding 1 << 16 to kC1 in the libwebp C code.
+ */
+ "vqadd.s16 q8, q2, q8 \n"
+
+ /* d20 = c = in[4]*kC2 - in[12]*kC1
+ * d21 = d = in[4]*kC1 + in[12]*kC2
+ */
+ "vqsub.s16 d20, d18, d17 \n"
+ "vqadd.s16 d21, d19, d16 \n"
+
+ /* d2 = tmp[0] = a + d
+ * d3 = tmp[1] = b + c
+ * d4 = tmp[2] = b - c
+ * d5 = tmp[3] = a - d
+ */
+ "vqadd.s16 d2, d22, d21 \n"
+ "vqadd.s16 d3, d23, d20 \n"
+ "vqsub.s16 d4, d23, d20 \n"
+ "vqsub.s16 d5, d22, d21 \n"
+
+ "vzip.16 q1, q2 \n"
+ "vzip.16 q1, q2 \n"
+
+ "vswp d3, d4 \n"
+
+ /* q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16
+ * q9 = {tmp[4], tmp[12]} * kC2 >> 16
+ */
+ "vqdmulh.s16 q8, q2, d0[0] \n"
+ "vqdmulh.s16 q9, q2, d0[1] \n"
+
+ /* d22 = a = tmp[0] + tmp[8]
+ * d23 = b = tmp[0] - tmp[8]
+ */
+ "vqadd.s16 d22, d2, d3 \n"
+ "vqsub.s16 d23, d2, d3 \n"
+
+ /* See long winded explanations prior */
+ "vshr.s16 q8, q8, #1 \n"
+ "vqadd.s16 q8, q2, q8 \n"
+
+ /* d20 = c = in[4]*kC2 - in[12]*kC1
+ * d21 = d = in[4]*kC1 + in[12]*kC2
+ */
+ "vqsub.s16 d20, d18, d17 \n"
+ "vqadd.s16 d21, d19, d16 \n"
+
+ /* d2 = tmp[0] = a + d
+ * d3 = tmp[1] = b + c
+ * d4 = tmp[2] = b - c
+ * d5 = tmp[3] = a - d
+ */
+ "vqadd.s16 d2, d22, d21 \n"
+ "vqadd.s16 d3, d23, d20 \n"
+ "vqsub.s16 d4, d23, d20 \n"
+ "vqsub.s16 d5, d22, d21 \n"
+
+ "vld1.32 d6[0], [%[dst]], %[kBPS] \n"
+ "vld1.32 d6[1], [%[dst]], %[kBPS] \n"
+ "vld1.32 d7[0], [%[dst]], %[kBPS] \n"
+ "vld1.32 d7[1], [%[dst]], %[kBPS] \n"
+
+ "sub %[dst], %[dst], %[kBPS], lsl #2 \n"
+
+ /* (val) + 4 >> 3 */
+ "vrshr.s16 d2, d2, #3 \n"
+ "vrshr.s16 d3, d3, #3 \n"
+ "vrshr.s16 d4, d4, #3 \n"
+ "vrshr.s16 d5, d5, #3 \n"
+
+ "vzip.16 q1, q2 \n"
+ "vzip.16 q1, q2 \n"
+
+ /* Must accumulate before saturating */
+ "vmovl.u8 q8, d6 \n"
+ "vmovl.u8 q9, d7 \n"
+
+ "vqadd.s16 q1, q1, q8 \n"
+ "vqadd.s16 q2, q2, q9 \n"
+
+ "vqmovun.s16 d0, q1 \n"
+ "vqmovun.s16 d1, q2 \n"
+
+ "vst1.32 d0[0], [%[dst]], %[kBPS] \n"
+ "vst1.32 d0[1], [%[dst]], %[kBPS] \n"
+ "vst1.32 d1[0], [%[dst]], %[kBPS] \n"
+ "vst1.32 d1[1], [%[dst]] \n"
+
+ : [in] "+r"(in), [dst] "+r"(dst) /* modified registers */
+ : [kBPS] "r"(kBPS), [constants] "r"(constants) /* constants */
+ : "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" /* clobbered */
+ );
+}
+
+#endif // WEBP_USE_INTRINSICS
+
+static void TransformTwo_NEON(const int16_t* in, uint8_t* dst, int do_two) {
+ TransformOne_NEON(in, dst);
+ if (do_two) {
+ TransformOne_NEON(in + 16, dst + 4);
+ }
+}
+
+static void TransformDC_NEON(const int16_t* in, uint8_t* dst) {
+ const int16x8_t DC = vdupq_n_s16(in[0]);
+ Add4x4_NEON(DC, DC, dst);
+}
+
+//------------------------------------------------------------------------------
+
+#define STORE_WHT(dst, col, rows) do { \
+ *dst = vgetq_lane_s32(rows.val[0], col); (dst) += 16; \
+ *dst = vgetq_lane_s32(rows.val[1], col); (dst) += 16; \
+ *dst = vgetq_lane_s32(rows.val[2], col); (dst) += 16; \
+ *dst = vgetq_lane_s32(rows.val[3], col); (dst) += 16; \
+} while (0)
+
+static void TransformWHT_NEON(const int16_t* in, int16_t* out) {
+ int32x4x4_t tmp;
+
+ {
+ // Load the source.
+ const int16x4_t in00_03 = vld1_s16(in + 0);
+ const int16x4_t in04_07 = vld1_s16(in + 4);
+ const int16x4_t in08_11 = vld1_s16(in + 8);
+ const int16x4_t in12_15 = vld1_s16(in + 12);
+ const int32x4_t a0 = vaddl_s16(in00_03, in12_15); // in[0..3] + in[12..15]
+ const int32x4_t a1 = vaddl_s16(in04_07, in08_11); // in[4..7] + in[8..11]
+ const int32x4_t a2 = vsubl_s16(in04_07, in08_11); // in[4..7] - in[8..11]
+ const int32x4_t a3 = vsubl_s16(in00_03, in12_15); // in[0..3] - in[12..15]
+ tmp.val[0] = vaddq_s32(a0, a1);
+ tmp.val[1] = vaddq_s32(a3, a2);
+ tmp.val[2] = vsubq_s32(a0, a1);
+ tmp.val[3] = vsubq_s32(a3, a2);
+ // Arrange the temporary results column-wise.
+ tmp = Transpose4x4_NEON(tmp);
+ }
+
+ {
+ const int32x4_t kCst3 = vdupq_n_s32(3);
+ const int32x4_t dc = vaddq_s32(tmp.val[0], kCst3); // add rounder
+ const int32x4_t a0 = vaddq_s32(dc, tmp.val[3]);
+ const int32x4_t a1 = vaddq_s32(tmp.val[1], tmp.val[2]);
+ const int32x4_t a2 = vsubq_s32(tmp.val[1], tmp.val[2]);
+ const int32x4_t a3 = vsubq_s32(dc, tmp.val[3]);
+
+ tmp.val[0] = vaddq_s32(a0, a1);
+ tmp.val[1] = vaddq_s32(a3, a2);
+ tmp.val[2] = vsubq_s32(a0, a1);
+ tmp.val[3] = vsubq_s32(a3, a2);
+
+ // right shift the results by 3.
+ tmp.val[0] = vshrq_n_s32(tmp.val[0], 3);
+ tmp.val[1] = vshrq_n_s32(tmp.val[1], 3);
+ tmp.val[2] = vshrq_n_s32(tmp.val[2], 3);
+ tmp.val[3] = vshrq_n_s32(tmp.val[3], 3);
+
+ STORE_WHT(out, 0, tmp);
+ STORE_WHT(out, 1, tmp);
+ STORE_WHT(out, 2, tmp);
+ STORE_WHT(out, 3, tmp);
+ }
+}
+
+#undef STORE_WHT
+
+//------------------------------------------------------------------------------
+
+#define MUL(a, b) (((a) * (b)) >> 16)
+static void TransformAC3_NEON(const int16_t* in, uint8_t* dst) {
+ static const int kC1_full = 20091 + (1 << 16);
+ static const int kC2_full = 35468;
+ const int16x4_t A = vld1_dup_s16(in);
+ const int16x4_t c4 = vdup_n_s16(MUL(in[4], kC2_full));
+ const int16x4_t d4 = vdup_n_s16(MUL(in[4], kC1_full));
+ const int c1 = MUL(in[1], kC2_full);
+ const int d1 = MUL(in[1], kC1_full);
+ const uint64_t cd = (uint64_t)( d1 & 0xffff) << 0 |
+ (uint64_t)( c1 & 0xffff) << 16 |
+ (uint64_t)(-c1 & 0xffff) << 32 |
+ (uint64_t)(-d1 & 0xffff) << 48;
+ const int16x4_t CD = vcreate_s16(cd);
+ const int16x4_t B = vqadd_s16(A, CD);
+ const int16x8_t m0_m1 = vcombine_s16(vqadd_s16(B, d4), vqadd_s16(B, c4));
+ const int16x8_t m2_m3 = vcombine_s16(vqsub_s16(B, c4), vqsub_s16(B, d4));
+ Add4x4_NEON(m0_m1, m2_m3, dst);
+}
+#undef MUL
+
+//------------------------------------------------------------------------------
+// 4x4
+
+static void DC4_NEON(uint8_t* dst) { // DC
+ const uint8x8_t A = vld1_u8(dst - BPS); // top row
+ const uint16x4_t p0 = vpaddl_u8(A); // cascading summation of the top
+ const uint16x4_t p1 = vpadd_u16(p0, p0);
+ const uint16x8_t L0 = vmovl_u8(vld1_u8(dst + 0 * BPS - 1));
+ const uint16x8_t L1 = vmovl_u8(vld1_u8(dst + 1 * BPS - 1));
+ const uint16x8_t L2 = vmovl_u8(vld1_u8(dst + 2 * BPS - 1));
+ const uint16x8_t L3 = vmovl_u8(vld1_u8(dst + 3 * BPS - 1));
+ const uint16x8_t s0 = vaddq_u16(L0, L1);
+ const uint16x8_t s1 = vaddq_u16(L2, L3);
+ const uint16x8_t s01 = vaddq_u16(s0, s1);
+ const uint16x8_t sum = vaddq_u16(s01, vcombine_u16(p1, p1));
+ const uint8x8_t dc0 = vrshrn_n_u16(sum, 3); // (sum + 4) >> 3
+ const uint8x8_t dc = vdup_lane_u8(dc0, 0);
+ int i;
+ for (i = 0; i < 4; ++i) {
+ vst1_lane_u32((uint32_t*)(dst + i * BPS), vreinterpret_u32_u8(dc), 0);
+ }
+}
+
+// TrueMotion (4x4 + 8x8)
+static WEBP_INLINE void TrueMotion_NEON(uint8_t* dst, int size) {
+ const uint8x8_t TL = vld1_dup_u8(dst - BPS - 1); // top-left pixel 'A[-1]'
+ const uint8x8_t T = vld1_u8(dst - BPS); // top row 'A[0..3]'
+ const int16x8_t d = vreinterpretq_s16_u16(vsubl_u8(T, TL)); // A[c] - A[-1]
+ int y;
+ for (y = 0; y < size; y += 4) {
+ // left edge
+ const int16x8_t L0 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 0 * BPS - 1));
+ const int16x8_t L1 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 1 * BPS - 1));
+ const int16x8_t L2 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 2 * BPS - 1));
+ const int16x8_t L3 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 3 * BPS - 1));
+ const int16x8_t r0 = vaddq_s16(L0, d); // L[r] + A[c] - A[-1]
+ const int16x8_t r1 = vaddq_s16(L1, d);
+ const int16x8_t r2 = vaddq_s16(L2, d);
+ const int16x8_t r3 = vaddq_s16(L3, d);
+ // Saturate and store the result.
+ const uint32x2_t r0_u32 = vreinterpret_u32_u8(vqmovun_s16(r0));
+ const uint32x2_t r1_u32 = vreinterpret_u32_u8(vqmovun_s16(r1));
+ const uint32x2_t r2_u32 = vreinterpret_u32_u8(vqmovun_s16(r2));
+ const uint32x2_t r3_u32 = vreinterpret_u32_u8(vqmovun_s16(r3));
+ if (size == 4) {
+ vst1_lane_u32((uint32_t*)(dst + 0 * BPS), r0_u32, 0);
+ vst1_lane_u32((uint32_t*)(dst + 1 * BPS), r1_u32, 0);
+ vst1_lane_u32((uint32_t*)(dst + 2 * BPS), r2_u32, 0);
+ vst1_lane_u32((uint32_t*)(dst + 3 * BPS), r3_u32, 0);
+ } else {
+ vst1_u32((uint32_t*)(dst + 0 * BPS), r0_u32);
+ vst1_u32((uint32_t*)(dst + 1 * BPS), r1_u32);
+ vst1_u32((uint32_t*)(dst + 2 * BPS), r2_u32);
+ vst1_u32((uint32_t*)(dst + 3 * BPS), r3_u32);
+ }
+ dst += 4 * BPS;
+ }
+}
+
+static void TM4_NEON(uint8_t* dst) { TrueMotion_NEON(dst, 4); }
+
+static void VE4_NEON(uint8_t* dst) { // vertical
+ // NB: avoid vld1_u64 here as an alignment hint may be added -> SIGBUS.
+ const uint64x1_t A0 = vreinterpret_u64_u8(vld1_u8(dst - BPS - 1)); // top row
+ const uint64x1_t A1 = vshr_n_u64(A0, 8);
+ const uint64x1_t A2 = vshr_n_u64(A0, 16);
+ const uint8x8_t ABCDEFGH = vreinterpret_u8_u64(A0);
+ const uint8x8_t BCDEFGH0 = vreinterpret_u8_u64(A1);
+ const uint8x8_t CDEFGH00 = vreinterpret_u8_u64(A2);
+ const uint8x8_t b = vhadd_u8(ABCDEFGH, CDEFGH00);
+ const uint8x8_t avg = vrhadd_u8(b, BCDEFGH0);
+ int i;
+ for (i = 0; i < 4; ++i) {
+ vst1_lane_u32((uint32_t*)(dst + i * BPS), vreinterpret_u32_u8(avg), 0);
+ }
+}
+
+static void RD4_NEON(uint8_t* dst) { // Down-right
+ const uint8x8_t XABCD_u8 = vld1_u8(dst - BPS - 1);
+ const uint64x1_t XABCD = vreinterpret_u64_u8(XABCD_u8);
+ const uint64x1_t ____XABC = vshl_n_u64(XABCD, 32);
+ const uint32_t I = dst[-1 + 0 * BPS];
+ const uint32_t J = dst[-1 + 1 * BPS];
+ const uint32_t K = dst[-1 + 2 * BPS];
+ const uint32_t L = dst[-1 + 3 * BPS];
+ const uint64x1_t LKJI____ = vcreate_u64(L | (K << 8) | (J << 16) | (I << 24));
+ const uint64x1_t LKJIXABC = vorr_u64(LKJI____, ____XABC);
+ const uint8x8_t KJIXABC_ = vreinterpret_u8_u64(vshr_n_u64(LKJIXABC, 8));
+ const uint8x8_t JIXABC__ = vreinterpret_u8_u64(vshr_n_u64(LKJIXABC, 16));
+ const uint8_t D = vget_lane_u8(XABCD_u8, 4);
+ const uint8x8_t JIXABCD_ = vset_lane_u8(D, JIXABC__, 6);
+ const uint8x8_t LKJIXABC_u8 = vreinterpret_u8_u64(LKJIXABC);
+ const uint8x8_t avg1 = vhadd_u8(JIXABCD_, LKJIXABC_u8);
+ const uint8x8_t avg2 = vrhadd_u8(avg1, KJIXABC_);
+ const uint64x1_t avg2_u64 = vreinterpret_u64_u8(avg2);
+ const uint32x2_t r3 = vreinterpret_u32_u8(avg2);
+ const uint32x2_t r2 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 8));
+ const uint32x2_t r1 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 16));
+ const uint32x2_t r0 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 24));
+ vst1_lane_u32((uint32_t*)(dst + 0 * BPS), r0, 0);
+ vst1_lane_u32((uint32_t*)(dst + 1 * BPS), r1, 0);
+ vst1_lane_u32((uint32_t*)(dst + 2 * BPS), r2, 0);
+ vst1_lane_u32((uint32_t*)(dst + 3 * BPS), r3, 0);
+}
+
+static void LD4_NEON(uint8_t* dst) { // Down-left
+ // Note using the same shift trick as VE4() is slower here.
+ const uint8x8_t ABCDEFGH = vld1_u8(dst - BPS + 0);
+ const uint8x8_t BCDEFGH0 = vld1_u8(dst - BPS + 1);
+ const uint8x8_t CDEFGH00 = vld1_u8(dst - BPS + 2);
+ const uint8x8_t CDEFGHH0 = vset_lane_u8(dst[-BPS + 7], CDEFGH00, 6);
+ const uint8x8_t avg1 = vhadd_u8(ABCDEFGH, CDEFGHH0);
+ const uint8x8_t avg2 = vrhadd_u8(avg1, BCDEFGH0);
+ const uint64x1_t avg2_u64 = vreinterpret_u64_u8(avg2);
+ const uint32x2_t r0 = vreinterpret_u32_u8(avg2);
+ const uint32x2_t r1 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 8));
+ const uint32x2_t r2 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 16));
+ const uint32x2_t r3 = vreinterpret_u32_u64(vshr_n_u64(avg2_u64, 24));
+ vst1_lane_u32((uint32_t*)(dst + 0 * BPS), r0, 0);
+ vst1_lane_u32((uint32_t*)(dst + 1 * BPS), r1, 0);
+ vst1_lane_u32((uint32_t*)(dst + 2 * BPS), r2, 0);
+ vst1_lane_u32((uint32_t*)(dst + 3 * BPS), r3, 0);
+}
+
+//------------------------------------------------------------------------------
+// Chroma
+
+static void VE8uv_NEON(uint8_t* dst) { // vertical
+ const uint8x8_t top = vld1_u8(dst - BPS);
+ int j;
+ for (j = 0; j < 8; ++j) {
+ vst1_u8(dst + j * BPS, top);
+ }
+}
+
+static void HE8uv_NEON(uint8_t* dst) { // horizontal
+ int j;
+ for (j = 0; j < 8; ++j) {
+ const uint8x8_t left = vld1_dup_u8(dst - 1);
+ vst1_u8(dst, left);
+ dst += BPS;
+ }
+}
+
+static WEBP_INLINE void DC8_NEON(uint8_t* dst, int do_top, int do_left) {
+ uint16x8_t sum_top;
+ uint16x8_t sum_left;
+ uint8x8_t dc0;
+
+ if (do_top) {
+ const uint8x8_t A = vld1_u8(dst - BPS); // top row
+ const uint16x4_t p0 = vpaddl_u8(A); // cascading summation of the top
+ const uint16x4_t p1 = vpadd_u16(p0, p0);
+ const uint16x4_t p2 = vpadd_u16(p1, p1);
+ sum_top = vcombine_u16(p2, p2);
+ }
+
+ if (do_left) {
+ const uint16x8_t L0 = vmovl_u8(vld1_u8(dst + 0 * BPS - 1));
+ const uint16x8_t L1 = vmovl_u8(vld1_u8(dst + 1 * BPS - 1));
+ const uint16x8_t L2 = vmovl_u8(vld1_u8(dst + 2 * BPS - 1));
+ const uint16x8_t L3 = vmovl_u8(vld1_u8(dst + 3 * BPS - 1));
+ const uint16x8_t L4 = vmovl_u8(vld1_u8(dst + 4 * BPS - 1));
+ const uint16x8_t L5 = vmovl_u8(vld1_u8(dst + 5 * BPS - 1));
+ const uint16x8_t L6 = vmovl_u8(vld1_u8(dst + 6 * BPS - 1));
+ const uint16x8_t L7 = vmovl_u8(vld1_u8(dst + 7 * BPS - 1));
+ const uint16x8_t s0 = vaddq_u16(L0, L1);
+ const uint16x8_t s1 = vaddq_u16(L2, L3);
+ const uint16x8_t s2 = vaddq_u16(L4, L5);
+ const uint16x8_t s3 = vaddq_u16(L6, L7);
+ const uint16x8_t s01 = vaddq_u16(s0, s1);
+ const uint16x8_t s23 = vaddq_u16(s2, s3);
+ sum_left = vaddq_u16(s01, s23);
+ }
+
+ if (do_top && do_left) {
+ const uint16x8_t sum = vaddq_u16(sum_left, sum_top);
+ dc0 = vrshrn_n_u16(sum, 4);
+ } else if (do_top) {
+ dc0 = vrshrn_n_u16(sum_top, 3);
+ } else if (do_left) {
+ dc0 = vrshrn_n_u16(sum_left, 3);
+ } else {
+ dc0 = vdup_n_u8(0x80);
+ }
+
+ {
+ const uint8x8_t dc = vdup_lane_u8(dc0, 0);
+ int i;
+ for (i = 0; i < 8; ++i) {
+ vst1_u32((uint32_t*)(dst + i * BPS), vreinterpret_u32_u8(dc));
+ }
+ }
+}
+
+static void DC8uv_NEON(uint8_t* dst) { DC8_NEON(dst, 1, 1); }
+static void DC8uvNoTop_NEON(uint8_t* dst) { DC8_NEON(dst, 0, 1); }
+static void DC8uvNoLeft_NEON(uint8_t* dst) { DC8_NEON(dst, 1, 0); }
+static void DC8uvNoTopLeft_NEON(uint8_t* dst) { DC8_NEON(dst, 0, 0); }
+
+static void TM8uv_NEON(uint8_t* dst) { TrueMotion_NEON(dst, 8); }
+
+//------------------------------------------------------------------------------
+// 16x16
+
+static void VE16_NEON(uint8_t* dst) { // vertical
+ const uint8x16_t top = vld1q_u8(dst - BPS);
+ int j;
+ for (j = 0; j < 16; ++j) {
+ vst1q_u8(dst + j * BPS, top);
+ }
+}
+
+static void HE16_NEON(uint8_t* dst) { // horizontal
+ int j;
+ for (j = 0; j < 16; ++j) {
+ const uint8x16_t left = vld1q_dup_u8(dst - 1);
+ vst1q_u8(dst, left);
+ dst += BPS;
+ }
+}
+
+static WEBP_INLINE void DC16_NEON(uint8_t* dst, int do_top, int do_left) {
+ uint16x8_t sum_top;
+ uint16x8_t sum_left;
+ uint8x8_t dc0;
+
+ if (do_top) {
+ const uint8x16_t A = vld1q_u8(dst - BPS); // top row
+ const uint16x8_t p0 = vpaddlq_u8(A); // cascading summation of the top
+ const uint16x4_t p1 = vadd_u16(vget_low_u16(p0), vget_high_u16(p0));
+ const uint16x4_t p2 = vpadd_u16(p1, p1);
+ const uint16x4_t p3 = vpadd_u16(p2, p2);
+ sum_top = vcombine_u16(p3, p3);
+ }
+
+ if (do_left) {
+ int i;
+ sum_left = vdupq_n_u16(0);
+ for (i = 0; i < 16; i += 8) {
+ const uint16x8_t L0 = vmovl_u8(vld1_u8(dst + (i + 0) * BPS - 1));
+ const uint16x8_t L1 = vmovl_u8(vld1_u8(dst + (i + 1) * BPS - 1));
+ const uint16x8_t L2 = vmovl_u8(vld1_u8(dst + (i + 2) * BPS - 1));
+ const uint16x8_t L3 = vmovl_u8(vld1_u8(dst + (i + 3) * BPS - 1));
+ const uint16x8_t L4 = vmovl_u8(vld1_u8(dst + (i + 4) * BPS - 1));
+ const uint16x8_t L5 = vmovl_u8(vld1_u8(dst + (i + 5) * BPS - 1));
+ const uint16x8_t L6 = vmovl_u8(vld1_u8(dst + (i + 6) * BPS - 1));
+ const uint16x8_t L7 = vmovl_u8(vld1_u8(dst + (i + 7) * BPS - 1));
+ const uint16x8_t s0 = vaddq_u16(L0, L1);
+ const uint16x8_t s1 = vaddq_u16(L2, L3);
+ const uint16x8_t s2 = vaddq_u16(L4, L5);
+ const uint16x8_t s3 = vaddq_u16(L6, L7);
+ const uint16x8_t s01 = vaddq_u16(s0, s1);
+ const uint16x8_t s23 = vaddq_u16(s2, s3);
+ const uint16x8_t sum = vaddq_u16(s01, s23);
+ sum_left = vaddq_u16(sum_left, sum);
+ }
+ }
+
+ if (do_top && do_left) {
+ const uint16x8_t sum = vaddq_u16(sum_left, sum_top);
+ dc0 = vrshrn_n_u16(sum, 5);
+ } else if (do_top) {
+ dc0 = vrshrn_n_u16(sum_top, 4);
+ } else if (do_left) {
+ dc0 = vrshrn_n_u16(sum_left, 4);
+ } else {
+ dc0 = vdup_n_u8(0x80);
+ }
+
+ {
+ const uint8x16_t dc = vdupq_lane_u8(dc0, 0);
+ int i;
+ for (i = 0; i < 16; ++i) {
+ vst1q_u8(dst + i * BPS, dc);
+ }
+ }
+}
+
+static void DC16TopLeft_NEON(uint8_t* dst) { DC16_NEON(dst, 1, 1); }
+static void DC16NoTop_NEON(uint8_t* dst) { DC16_NEON(dst, 0, 1); }
+static void DC16NoLeft_NEON(uint8_t* dst) { DC16_NEON(dst, 1, 0); }
+static void DC16NoTopLeft_NEON(uint8_t* dst) { DC16_NEON(dst, 0, 0); }
+
+static void TM16_NEON(uint8_t* dst) {
+ const uint8x8_t TL = vld1_dup_u8(dst - BPS - 1); // top-left pixel 'A[-1]'
+ const uint8x16_t T = vld1q_u8(dst - BPS); // top row 'A[0..15]'
+ // A[c] - A[-1]
+ const int16x8_t d_lo = vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(T), TL));
+ const int16x8_t d_hi = vreinterpretq_s16_u16(vsubl_u8(vget_high_u8(T), TL));
+ int y;
+ for (y = 0; y < 16; y += 4) {
+ // left edge
+ const int16x8_t L0 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 0 * BPS - 1));
+ const int16x8_t L1 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 1 * BPS - 1));
+ const int16x8_t L2 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 2 * BPS - 1));
+ const int16x8_t L3 = ConvertU8ToS16_NEON(vld1_dup_u8(dst + 3 * BPS - 1));
+ const int16x8_t r0_lo = vaddq_s16(L0, d_lo); // L[r] + A[c] - A[-1]
+ const int16x8_t r1_lo = vaddq_s16(L1, d_lo);
+ const int16x8_t r2_lo = vaddq_s16(L2, d_lo);
+ const int16x8_t r3_lo = vaddq_s16(L3, d_lo);
+ const int16x8_t r0_hi = vaddq_s16(L0, d_hi);
+ const int16x8_t r1_hi = vaddq_s16(L1, d_hi);
+ const int16x8_t r2_hi = vaddq_s16(L2, d_hi);
+ const int16x8_t r3_hi = vaddq_s16(L3, d_hi);
+ // Saturate and store the result.
+ const uint8x16_t row0 = vcombine_u8(vqmovun_s16(r0_lo), vqmovun_s16(r0_hi));
+ const uint8x16_t row1 = vcombine_u8(vqmovun_s16(r1_lo), vqmovun_s16(r1_hi));
+ const uint8x16_t row2 = vcombine_u8(vqmovun_s16(r2_lo), vqmovun_s16(r2_hi));
+ const uint8x16_t row3 = vcombine_u8(vqmovun_s16(r3_lo), vqmovun_s16(r3_hi));
+ vst1q_u8(dst + 0 * BPS, row0);
+ vst1q_u8(dst + 1 * BPS, row1);
+ vst1q_u8(dst + 2 * BPS, row2);
+ vst1q_u8(dst + 3 * BPS, row3);
+ dst += 4 * BPS;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8DspInitNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitNEON(void) {
+ VP8Transform = TransformTwo_NEON;
+ VP8TransformAC3 = TransformAC3_NEON;
+ VP8TransformDC = TransformDC_NEON;
+ VP8TransformWHT = TransformWHT_NEON;
+
+ VP8VFilter16 = VFilter16_NEON;
+ VP8VFilter16i = VFilter16i_NEON;
+ VP8HFilter16 = HFilter16_NEON;
+#if !defined(WORK_AROUND_GCC)
+ VP8HFilter16i = HFilter16i_NEON;
+#endif
+ VP8VFilter8 = VFilter8_NEON;
+ VP8VFilter8i = VFilter8i_NEON;
+#if !defined(WORK_AROUND_GCC)
+ VP8HFilter8 = HFilter8_NEON;
+ VP8HFilter8i = HFilter8i_NEON;
+#endif
+ VP8SimpleVFilter16 = SimpleVFilter16_NEON;
+ VP8SimpleHFilter16 = SimpleHFilter16_NEON;
+ VP8SimpleVFilter16i = SimpleVFilter16i_NEON;
+ VP8SimpleHFilter16i = SimpleHFilter16i_NEON;
+
+ VP8PredLuma4[0] = DC4_NEON;
+ VP8PredLuma4[1] = TM4_NEON;
+ VP8PredLuma4[2] = VE4_NEON;
+ VP8PredLuma4[4] = RD4_NEON;
+ VP8PredLuma4[6] = LD4_NEON;
+
+ VP8PredLuma16[0] = DC16TopLeft_NEON;
+ VP8PredLuma16[1] = TM16_NEON;
+ VP8PredLuma16[2] = VE16_NEON;
+ VP8PredLuma16[3] = HE16_NEON;
+ VP8PredLuma16[4] = DC16NoTop_NEON;
+ VP8PredLuma16[5] = DC16NoLeft_NEON;
+ VP8PredLuma16[6] = DC16NoTopLeft_NEON;
+
+ VP8PredChroma8[0] = DC8uv_NEON;
+ VP8PredChroma8[1] = TM8uv_NEON;
+ VP8PredChroma8[2] = VE8uv_NEON;
+ VP8PredChroma8[3] = HE8uv_NEON;
+ VP8PredChroma8[4] = DC8uvNoTop_NEON;
+ VP8PredChroma8[5] = DC8uvNoLeft_NEON;
+ VP8PredChroma8[6] = DC8uvNoTopLeft_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(VP8DspInitNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/dec_sse2.c b/src/third_party/libwebp/src/dsp/dec_sse2.c
new file mode 100644
index 0000000..b3840fa
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec_sse2.c
@@ -0,0 +1,1227 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 version of some decoding functions (idct, loop filtering).
+//
+// Author: somnath@google.com (Somnath Banerjee)
+// cduvivier@google.com (Christian Duvivier)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+
+// The 3-coeff sparse transform in SSE2 is not really faster than the plain-C
+// one it seems => disable it by default. Uncomment the following to enable:
+#if !defined(USE_TRANSFORM_AC3)
+#define USE_TRANSFORM_AC3 0 // ALTERNATE_CODE
+#endif
+
+#include <emmintrin.h>
+#include "src/dsp/common_sse2.h"
+#include "src/dec/vp8i_dec.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// Transforms (Paragraph 14.4)
+
+static void Transform_SSE2(const int16_t* in, uint8_t* dst, int do_two) {
+ // This implementation makes use of 16-bit fixed point versions of two
+ // multiply constants:
+ // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
+ // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
+ //
+ // To be able to use signed 16-bit integers, we use the following trick to
+ // have constants within range:
+ // - Associated constants are obtained by subtracting the 16-bit fixed point
+ // version of one:
+ // k = K - (1 << 16) => K = k + (1 << 16)
+ // K1 = 85267 => k1 = 20091
+ // K2 = 35468 => k2 = -30068
+ // - The multiplication of a variable by a constant become the sum of the
+ // variable and the multiplication of that variable by the associated
+ // constant:
+ // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
+ const __m128i k1 = _mm_set1_epi16(20091);
+ const __m128i k2 = _mm_set1_epi16(-30068);
+ __m128i T0, T1, T2, T3;
+
+ // Load and concatenate the transform coefficients (we'll do two transforms
+ // in parallel). In the case of only one transform, the second half of the
+ // vectors will just contain random value we'll never use nor store.
+ __m128i in0, in1, in2, in3;
+ {
+ in0 = _mm_loadl_epi64((const __m128i*)&in[0]);
+ in1 = _mm_loadl_epi64((const __m128i*)&in[4]);
+ in2 = _mm_loadl_epi64((const __m128i*)&in[8]);
+ in3 = _mm_loadl_epi64((const __m128i*)&in[12]);
+ // a00 a10 a20 a30 x x x x
+ // a01 a11 a21 a31 x x x x
+ // a02 a12 a22 a32 x x x x
+ // a03 a13 a23 a33 x x x x
+ if (do_two) {
+ const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]);
+ const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]);
+ const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]);
+ const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]);
+ in0 = _mm_unpacklo_epi64(in0, inB0);
+ in1 = _mm_unpacklo_epi64(in1, inB1);
+ in2 = _mm_unpacklo_epi64(in2, inB2);
+ in3 = _mm_unpacklo_epi64(in3, inB3);
+ // a00 a10 a20 a30 b00 b10 b20 b30
+ // a01 a11 a21 a31 b01 b11 b21 b31
+ // a02 a12 a22 a32 b02 b12 b22 b32
+ // a03 a13 a23 a33 b03 b13 b23 b33
+ }
+ }
+
+ // Vertical pass and subsequent transpose.
+ {
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ const __m128i a = _mm_add_epi16(in0, in2);
+ const __m128i b = _mm_sub_epi16(in0, in2);
+ // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
+ const __m128i c1 = _mm_mulhi_epi16(in1, k2);
+ const __m128i c2 = _mm_mulhi_epi16(in3, k1);
+ const __m128i c3 = _mm_sub_epi16(in1, in3);
+ const __m128i c4 = _mm_sub_epi16(c1, c2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
+ const __m128i d1 = _mm_mulhi_epi16(in1, k1);
+ const __m128i d2 = _mm_mulhi_epi16(in3, k2);
+ const __m128i d3 = _mm_add_epi16(in1, in3);
+ const __m128i d4 = _mm_add_epi16(d1, d2);
+ const __m128i d = _mm_add_epi16(d3, d4);
+
+ // Second pass.
+ const __m128i tmp0 = _mm_add_epi16(a, d);
+ const __m128i tmp1 = _mm_add_epi16(b, c);
+ const __m128i tmp2 = _mm_sub_epi16(b, c);
+ const __m128i tmp3 = _mm_sub_epi16(a, d);
+
+ // Transpose the two 4x4.
+ VP8Transpose_2_4x4_16b(&tmp0, &tmp1, &tmp2, &tmp3, &T0, &T1, &T2, &T3);
+ }
+
+ // Horizontal pass and subsequent transpose.
+ {
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ const __m128i four = _mm_set1_epi16(4);
+ const __m128i dc = _mm_add_epi16(T0, four);
+ const __m128i a = _mm_add_epi16(dc, T2);
+ const __m128i b = _mm_sub_epi16(dc, T2);
+ // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
+ const __m128i c1 = _mm_mulhi_epi16(T1, k2);
+ const __m128i c2 = _mm_mulhi_epi16(T3, k1);
+ const __m128i c3 = _mm_sub_epi16(T1, T3);
+ const __m128i c4 = _mm_sub_epi16(c1, c2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
+ const __m128i d1 = _mm_mulhi_epi16(T1, k1);
+ const __m128i d2 = _mm_mulhi_epi16(T3, k2);
+ const __m128i d3 = _mm_add_epi16(T1, T3);
+ const __m128i d4 = _mm_add_epi16(d1, d2);
+ const __m128i d = _mm_add_epi16(d3, d4);
+
+ // Second pass.
+ const __m128i tmp0 = _mm_add_epi16(a, d);
+ const __m128i tmp1 = _mm_add_epi16(b, c);
+ const __m128i tmp2 = _mm_sub_epi16(b, c);
+ const __m128i tmp3 = _mm_sub_epi16(a, d);
+ const __m128i shifted0 = _mm_srai_epi16(tmp0, 3);
+ const __m128i shifted1 = _mm_srai_epi16(tmp1, 3);
+ const __m128i shifted2 = _mm_srai_epi16(tmp2, 3);
+ const __m128i shifted3 = _mm_srai_epi16(tmp3, 3);
+
+ // Transpose the two 4x4.
+ VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1,
+ &T2, &T3);
+ }
+
+ // Add inverse transform to 'dst' and store.
+ {
+ const __m128i zero = _mm_setzero_si128();
+ // Load the reference(s).
+ __m128i dst0, dst1, dst2, dst3;
+ if (do_two) {
+ // Load eight bytes/pixels per line.
+ dst0 = _mm_loadl_epi64((__m128i*)(dst + 0 * BPS));
+ dst1 = _mm_loadl_epi64((__m128i*)(dst + 1 * BPS));
+ dst2 = _mm_loadl_epi64((__m128i*)(dst + 2 * BPS));
+ dst3 = _mm_loadl_epi64((__m128i*)(dst + 3 * BPS));
+ } else {
+ // Load four bytes/pixels per line.
+ dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS));
+ dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS));
+ dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS));
+ dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS));
+ }
+ // Convert to 16b.
+ dst0 = _mm_unpacklo_epi8(dst0, zero);
+ dst1 = _mm_unpacklo_epi8(dst1, zero);
+ dst2 = _mm_unpacklo_epi8(dst2, zero);
+ dst3 = _mm_unpacklo_epi8(dst3, zero);
+ // Add the inverse transform(s).
+ dst0 = _mm_add_epi16(dst0, T0);
+ dst1 = _mm_add_epi16(dst1, T1);
+ dst2 = _mm_add_epi16(dst2, T2);
+ dst3 = _mm_add_epi16(dst3, T3);
+ // Unsigned saturate to 8b.
+ dst0 = _mm_packus_epi16(dst0, dst0);
+ dst1 = _mm_packus_epi16(dst1, dst1);
+ dst2 = _mm_packus_epi16(dst2, dst2);
+ dst3 = _mm_packus_epi16(dst3, dst3);
+ // Store the results.
+ if (do_two) {
+ // Store eight bytes/pixels per line.
+ _mm_storel_epi64((__m128i*)(dst + 0 * BPS), dst0);
+ _mm_storel_epi64((__m128i*)(dst + 1 * BPS), dst1);
+ _mm_storel_epi64((__m128i*)(dst + 2 * BPS), dst2);
+ _mm_storel_epi64((__m128i*)(dst + 3 * BPS), dst3);
+ } else {
+ // Store four bytes/pixels per line.
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
+ }
+ }
+}
+
+#if (USE_TRANSFORM_AC3 == 1)
+#define MUL(a, b) (((a) * (b)) >> 16)
+static void TransformAC3(const int16_t* in, uint8_t* dst) {
+ static const int kC1 = 20091 + (1 << 16);
+ static const int kC2 = 35468;
+ const __m128i A = _mm_set1_epi16(in[0] + 4);
+ const __m128i c4 = _mm_set1_epi16(MUL(in[4], kC2));
+ const __m128i d4 = _mm_set1_epi16(MUL(in[4], kC1));
+ const int c1 = MUL(in[1], kC2);
+ const int d1 = MUL(in[1], kC1);
+ const __m128i CD = _mm_set_epi16(0, 0, 0, 0, -d1, -c1, c1, d1);
+ const __m128i B = _mm_adds_epi16(A, CD);
+ const __m128i m0 = _mm_adds_epi16(B, d4);
+ const __m128i m1 = _mm_adds_epi16(B, c4);
+ const __m128i m2 = _mm_subs_epi16(B, c4);
+ const __m128i m3 = _mm_subs_epi16(B, d4);
+ const __m128i zero = _mm_setzero_si128();
+ // Load the source pixels.
+ __m128i dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS));
+ __m128i dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS));
+ __m128i dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS));
+ __m128i dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS));
+ // Convert to 16b.
+ dst0 = _mm_unpacklo_epi8(dst0, zero);
+ dst1 = _mm_unpacklo_epi8(dst1, zero);
+ dst2 = _mm_unpacklo_epi8(dst2, zero);
+ dst3 = _mm_unpacklo_epi8(dst3, zero);
+ // Add the inverse transform.
+ dst0 = _mm_adds_epi16(dst0, _mm_srai_epi16(m0, 3));
+ dst1 = _mm_adds_epi16(dst1, _mm_srai_epi16(m1, 3));
+ dst2 = _mm_adds_epi16(dst2, _mm_srai_epi16(m2, 3));
+ dst3 = _mm_adds_epi16(dst3, _mm_srai_epi16(m3, 3));
+ // Unsigned saturate to 8b.
+ dst0 = _mm_packus_epi16(dst0, dst0);
+ dst1 = _mm_packus_epi16(dst1, dst1);
+ dst2 = _mm_packus_epi16(dst2, dst2);
+ dst3 = _mm_packus_epi16(dst3, dst3);
+ // Store the results.
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
+}
+#undef MUL
+#endif // USE_TRANSFORM_AC3
+
+//------------------------------------------------------------------------------
+// Loop Filter (Paragraph 15)
+
+// Compute abs(p - q) = subs(p - q) OR subs(q - p)
+#define MM_ABS(p, q) _mm_or_si128( \
+ _mm_subs_epu8((q), (p)), \
+ _mm_subs_epu8((p), (q)))
+
+// Shift each byte of "x" by 3 bits while preserving by the sign bit.
+static WEBP_INLINE void SignedShift8b_SSE2(__m128i* const x) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i lo_0 = _mm_unpacklo_epi8(zero, *x);
+ const __m128i hi_0 = _mm_unpackhi_epi8(zero, *x);
+ const __m128i lo_1 = _mm_srai_epi16(lo_0, 3 + 8);
+ const __m128i hi_1 = _mm_srai_epi16(hi_0, 3 + 8);
+ *x = _mm_packs_epi16(lo_1, hi_1);
+}
+
+#define FLIP_SIGN_BIT2(a, b) { \
+ (a) = _mm_xor_si128(a, sign_bit); \
+ (b) = _mm_xor_si128(b, sign_bit); \
+}
+
+#define FLIP_SIGN_BIT4(a, b, c, d) { \
+ FLIP_SIGN_BIT2(a, b); \
+ FLIP_SIGN_BIT2(c, d); \
+}
+
+// input/output is uint8_t
+static WEBP_INLINE void GetNotHEV_SSE2(const __m128i* const p1,
+ const __m128i* const p0,
+ const __m128i* const q0,
+ const __m128i* const q1,
+ int hev_thresh, __m128i* const not_hev) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i t_1 = MM_ABS(*p1, *p0);
+ const __m128i t_2 = MM_ABS(*q1, *q0);
+
+ const __m128i h = _mm_set1_epi8(hev_thresh);
+ const __m128i t_max = _mm_max_epu8(t_1, t_2);
+
+ const __m128i t_max_h = _mm_subs_epu8(t_max, h);
+ *not_hev = _mm_cmpeq_epi8(t_max_h, zero); // not_hev <= t1 && not_hev <= t2
+}
+
+// input pixels are int8_t
+static WEBP_INLINE void GetBaseDelta_SSE2(const __m128i* const p1,
+ const __m128i* const p0,
+ const __m128i* const q0,
+ const __m128i* const q1,
+ __m128i* const delta) {
+ // beware of addition order, for saturation!
+ const __m128i p1_q1 = _mm_subs_epi8(*p1, *q1); // p1 - q1
+ const __m128i q0_p0 = _mm_subs_epi8(*q0, *p0); // q0 - p0
+ const __m128i s1 = _mm_adds_epi8(p1_q1, q0_p0); // p1 - q1 + 1 * (q0 - p0)
+ const __m128i s2 = _mm_adds_epi8(q0_p0, s1); // p1 - q1 + 2 * (q0 - p0)
+ const __m128i s3 = _mm_adds_epi8(q0_p0, s2); // p1 - q1 + 3 * (q0 - p0)
+ *delta = s3;
+}
+
+// input and output are int8_t
+static WEBP_INLINE void DoSimpleFilter_SSE2(__m128i* const p0,
+ __m128i* const q0,
+ const __m128i* const fl) {
+ const __m128i k3 = _mm_set1_epi8(3);
+ const __m128i k4 = _mm_set1_epi8(4);
+ __m128i v3 = _mm_adds_epi8(*fl, k3);
+ __m128i v4 = _mm_adds_epi8(*fl, k4);
+
+ SignedShift8b_SSE2(&v4); // v4 >> 3
+ SignedShift8b_SSE2(&v3); // v3 >> 3
+ *q0 = _mm_subs_epi8(*q0, v4); // q0 -= v4
+ *p0 = _mm_adds_epi8(*p0, v3); // p0 += v3
+}
+
+// Updates values of 2 pixels at MB edge during complex filtering.
+// Update operations:
+// q = q - delta and p = p + delta; where delta = [(a_hi >> 7), (a_lo >> 7)]
+// Pixels 'pi' and 'qi' are int8_t on input, uint8_t on output (sign flip).
+static WEBP_INLINE void Update2Pixels_SSE2(__m128i* const pi, __m128i* const qi,
+ const __m128i* const a0_lo,
+ const __m128i* const a0_hi) {
+ const __m128i a1_lo = _mm_srai_epi16(*a0_lo, 7);
+ const __m128i a1_hi = _mm_srai_epi16(*a0_hi, 7);
+ const __m128i delta = _mm_packs_epi16(a1_lo, a1_hi);
+ const __m128i sign_bit = _mm_set1_epi8(0x80);
+ *pi = _mm_adds_epi8(*pi, delta);
+ *qi = _mm_subs_epi8(*qi, delta);
+ FLIP_SIGN_BIT2(*pi, *qi);
+}
+
+// input pixels are uint8_t
+static WEBP_INLINE void NeedsFilter_SSE2(const __m128i* const p1,
+ const __m128i* const p0,
+ const __m128i* const q0,
+ const __m128i* const q1,
+ int thresh, __m128i* const mask) {
+ const __m128i m_thresh = _mm_set1_epi8(thresh);
+ const __m128i t1 = MM_ABS(*p1, *q1); // abs(p1 - q1)
+ const __m128i kFE = _mm_set1_epi8(0xFE);
+ const __m128i t2 = _mm_and_si128(t1, kFE); // set lsb of each byte to zero
+ const __m128i t3 = _mm_srli_epi16(t2, 1); // abs(p1 - q1) / 2
+
+ const __m128i t4 = MM_ABS(*p0, *q0); // abs(p0 - q0)
+ const __m128i t5 = _mm_adds_epu8(t4, t4); // abs(p0 - q0) * 2
+ const __m128i t6 = _mm_adds_epu8(t5, t3); // abs(p0-q0)*2 + abs(p1-q1)/2
+
+ const __m128i t7 = _mm_subs_epu8(t6, m_thresh); // mask <= m_thresh
+ *mask = _mm_cmpeq_epi8(t7, _mm_setzero_si128());
+}
+
+//------------------------------------------------------------------------------
+// Edge filtering functions
+
+// Applies filter on 2 pixels (p0 and q0)
+static WEBP_INLINE void DoFilter2_SSE2(__m128i* const p1, __m128i* const p0,
+ __m128i* const q0, __m128i* const q1,
+ int thresh) {
+ __m128i a, mask;
+ const __m128i sign_bit = _mm_set1_epi8(0x80);
+ // convert p1/q1 to int8_t (for GetBaseDelta_SSE2)
+ const __m128i p1s = _mm_xor_si128(*p1, sign_bit);
+ const __m128i q1s = _mm_xor_si128(*q1, sign_bit);
+
+ NeedsFilter_SSE2(p1, p0, q0, q1, thresh, &mask);
+
+ FLIP_SIGN_BIT2(*p0, *q0);
+ GetBaseDelta_SSE2(&p1s, p0, q0, &q1s, &a);
+ a = _mm_and_si128(a, mask); // mask filter values we don't care about
+ DoSimpleFilter_SSE2(p0, q0, &a);
+ FLIP_SIGN_BIT2(*p0, *q0);
+}
+
+// Applies filter on 4 pixels (p1, p0, q0 and q1)
+static WEBP_INLINE void DoFilter4_SSE2(__m128i* const p1, __m128i* const p0,
+ __m128i* const q0, __m128i* const q1,
+ const __m128i* const mask,
+ int hev_thresh) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i sign_bit = _mm_set1_epi8(0x80);
+ const __m128i k64 = _mm_set1_epi8(64);
+ const __m128i k3 = _mm_set1_epi8(3);
+ const __m128i k4 = _mm_set1_epi8(4);
+ __m128i not_hev;
+ __m128i t1, t2, t3;
+
+ // compute hev mask
+ GetNotHEV_SSE2(p1, p0, q0, q1, hev_thresh, ¬_hev);
+
+ // convert to signed values
+ FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
+
+ t1 = _mm_subs_epi8(*p1, *q1); // p1 - q1
+ t1 = _mm_andnot_si128(not_hev, t1); // hev(p1 - q1)
+ t2 = _mm_subs_epi8(*q0, *p0); // q0 - p0
+ t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 1 * (q0 - p0)
+ t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 2 * (q0 - p0)
+ t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 3 * (q0 - p0)
+ t1 = _mm_and_si128(t1, *mask); // mask filter values we don't care about
+
+ t2 = _mm_adds_epi8(t1, k3); // 3 * (q0 - p0) + hev(p1 - q1) + 3
+ t3 = _mm_adds_epi8(t1, k4); // 3 * (q0 - p0) + hev(p1 - q1) + 4
+ SignedShift8b_SSE2(&t2); // (3 * (q0 - p0) + hev(p1 - q1) + 3) >> 3
+ SignedShift8b_SSE2(&t3); // (3 * (q0 - p0) + hev(p1 - q1) + 4) >> 3
+ *p0 = _mm_adds_epi8(*p0, t2); // p0 += t2
+ *q0 = _mm_subs_epi8(*q0, t3); // q0 -= t3
+ FLIP_SIGN_BIT2(*p0, *q0);
+
+ // this is equivalent to signed (a + 1) >> 1 calculation
+ t2 = _mm_add_epi8(t3, sign_bit);
+ t3 = _mm_avg_epu8(t2, zero);
+ t3 = _mm_sub_epi8(t3, k64);
+
+ t3 = _mm_and_si128(not_hev, t3); // if !hev
+ *q1 = _mm_subs_epi8(*q1, t3); // q1 -= t3
+ *p1 = _mm_adds_epi8(*p1, t3); // p1 += t3
+ FLIP_SIGN_BIT2(*p1, *q1);
+}
+
+// Applies filter on 6 pixels (p2, p1, p0, q0, q1 and q2)
+static WEBP_INLINE void DoFilter6_SSE2(__m128i* const p2, __m128i* const p1,
+ __m128i* const p0, __m128i* const q0,
+ __m128i* const q1, __m128i* const q2,
+ const __m128i* const mask,
+ int hev_thresh) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i sign_bit = _mm_set1_epi8(0x80);
+ __m128i a, not_hev;
+
+ // compute hev mask
+ GetNotHEV_SSE2(p1, p0, q0, q1, hev_thresh, ¬_hev);
+
+ FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
+ FLIP_SIGN_BIT2(*p2, *q2);
+ GetBaseDelta_SSE2(p1, p0, q0, q1, &a);
+
+ { // do simple filter on pixels with hev
+ const __m128i m = _mm_andnot_si128(not_hev, *mask);
+ const __m128i f = _mm_and_si128(a, m);
+ DoSimpleFilter_SSE2(p0, q0, &f);
+ }
+
+ { // do strong filter on pixels with not hev
+ const __m128i k9 = _mm_set1_epi16(0x0900);
+ const __m128i k63 = _mm_set1_epi16(63);
+
+ const __m128i m = _mm_and_si128(not_hev, *mask);
+ const __m128i f = _mm_and_si128(a, m);
+
+ const __m128i f_lo = _mm_unpacklo_epi8(zero, f);
+ const __m128i f_hi = _mm_unpackhi_epi8(zero, f);
+
+ const __m128i f9_lo = _mm_mulhi_epi16(f_lo, k9); // Filter (lo) * 9
+ const __m128i f9_hi = _mm_mulhi_epi16(f_hi, k9); // Filter (hi) * 9
+
+ const __m128i a2_lo = _mm_add_epi16(f9_lo, k63); // Filter * 9 + 63
+ const __m128i a2_hi = _mm_add_epi16(f9_hi, k63); // Filter * 9 + 63
+
+ const __m128i a1_lo = _mm_add_epi16(a2_lo, f9_lo); // Filter * 18 + 63
+ const __m128i a1_hi = _mm_add_epi16(a2_hi, f9_hi); // Filter * 18 + 63
+
+ const __m128i a0_lo = _mm_add_epi16(a1_lo, f9_lo); // Filter * 27 + 63
+ const __m128i a0_hi = _mm_add_epi16(a1_hi, f9_hi); // Filter * 27 + 63
+
+ Update2Pixels_SSE2(p2, q2, &a2_lo, &a2_hi);
+ Update2Pixels_SSE2(p1, q1, &a1_lo, &a1_hi);
+ Update2Pixels_SSE2(p0, q0, &a0_lo, &a0_hi);
+ }
+}
+
+// reads 8 rows across a vertical edge.
+static WEBP_INLINE void Load8x4_SSE2(const uint8_t* const b, int stride,
+ __m128i* const p, __m128i* const q) {
+ // A0 = 63 62 61 60 23 22 21 20 43 42 41 40 03 02 01 00
+ // A1 = 73 72 71 70 33 32 31 30 53 52 51 50 13 12 11 10
+ const __m128i A0 = _mm_set_epi32(
+ WebPMemToUint32(&b[6 * stride]), WebPMemToUint32(&b[2 * stride]),
+ WebPMemToUint32(&b[4 * stride]), WebPMemToUint32(&b[0 * stride]));
+ const __m128i A1 = _mm_set_epi32(
+ WebPMemToUint32(&b[7 * stride]), WebPMemToUint32(&b[3 * stride]),
+ WebPMemToUint32(&b[5 * stride]), WebPMemToUint32(&b[1 * stride]));
+
+ // B0 = 53 43 52 42 51 41 50 40 13 03 12 02 11 01 10 00
+ // B1 = 73 63 72 62 71 61 70 60 33 23 32 22 31 21 30 20
+ const __m128i B0 = _mm_unpacklo_epi8(A0, A1);
+ const __m128i B1 = _mm_unpackhi_epi8(A0, A1);
+
+ // C0 = 33 23 13 03 32 22 12 02 31 21 11 01 30 20 10 00
+ // C1 = 73 63 53 43 72 62 52 42 71 61 51 41 70 60 50 40
+ const __m128i C0 = _mm_unpacklo_epi16(B0, B1);
+ const __m128i C1 = _mm_unpackhi_epi16(B0, B1);
+
+ // *p = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00
+ // *q = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02
+ *p = _mm_unpacklo_epi32(C0, C1);
+ *q = _mm_unpackhi_epi32(C0, C1);
+}
+
+static WEBP_INLINE void Load16x4_SSE2(const uint8_t* const r0,
+ const uint8_t* const r8,
+ int stride,
+ __m128i* const p1, __m128i* const p0,
+ __m128i* const q0, __m128i* const q1) {
+ // Assume the pixels around the edge (|) are numbered as follows
+ // 00 01 | 02 03
+ // 10 11 | 12 13
+ // ... | ...
+ // e0 e1 | e2 e3
+ // f0 f1 | f2 f3
+ //
+ // r0 is pointing to the 0th row (00)
+ // r8 is pointing to the 8th row (80)
+
+ // Load
+ // p1 = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00
+ // q0 = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02
+ // p0 = f1 e1 d1 c1 b1 a1 91 81 f0 e0 d0 c0 b0 a0 90 80
+ // q1 = f3 e3 d3 c3 b3 a3 93 83 f2 e2 d2 c2 b2 a2 92 82
+ Load8x4_SSE2(r0, stride, p1, q0);
+ Load8x4_SSE2(r8, stride, p0, q1);
+
+ {
+ // p1 = f0 e0 d0 c0 b0 a0 90 80 70 60 50 40 30 20 10 00
+ // p0 = f1 e1 d1 c1 b1 a1 91 81 71 61 51 41 31 21 11 01
+ // q0 = f2 e2 d2 c2 b2 a2 92 82 72 62 52 42 32 22 12 02
+ // q1 = f3 e3 d3 c3 b3 a3 93 83 73 63 53 43 33 23 13 03
+ const __m128i t1 = *p1;
+ const __m128i t2 = *q0;
+ *p1 = _mm_unpacklo_epi64(t1, *p0);
+ *p0 = _mm_unpackhi_epi64(t1, *p0);
+ *q0 = _mm_unpacklo_epi64(t2, *q1);
+ *q1 = _mm_unpackhi_epi64(t2, *q1);
+ }
+}
+
+static WEBP_INLINE void Store4x4_SSE2(__m128i* const x,
+ uint8_t* dst, int stride) {
+ int i;
+ for (i = 0; i < 4; ++i, dst += stride) {
+ WebPUint32ToMem(dst, _mm_cvtsi128_si32(*x));
+ *x = _mm_srli_si128(*x, 4);
+ }
+}
+
+// Transpose back and store
+static WEBP_INLINE void Store16x4_SSE2(const __m128i* const p1,
+ const __m128i* const p0,
+ const __m128i* const q0,
+ const __m128i* const q1,
+ uint8_t* r0, uint8_t* r8,
+ int stride) {
+ __m128i t1, p1_s, p0_s, q0_s, q1_s;
+
+ // p0 = 71 70 61 60 51 50 41 40 31 30 21 20 11 10 01 00
+ // p1 = f1 f0 e1 e0 d1 d0 c1 c0 b1 b0 a1 a0 91 90 81 80
+ t1 = *p0;
+ p0_s = _mm_unpacklo_epi8(*p1, t1);
+ p1_s = _mm_unpackhi_epi8(*p1, t1);
+
+ // q0 = 73 72 63 62 53 52 43 42 33 32 23 22 13 12 03 02
+ // q1 = f3 f2 e3 e2 d3 d2 c3 c2 b3 b2 a3 a2 93 92 83 82
+ t1 = *q0;
+ q0_s = _mm_unpacklo_epi8(t1, *q1);
+ q1_s = _mm_unpackhi_epi8(t1, *q1);
+
+ // p0 = 33 32 31 30 23 22 21 20 13 12 11 10 03 02 01 00
+ // q0 = 73 72 71 70 63 62 61 60 53 52 51 50 43 42 41 40
+ t1 = p0_s;
+ p0_s = _mm_unpacklo_epi16(t1, q0_s);
+ q0_s = _mm_unpackhi_epi16(t1, q0_s);
+
+ // p1 = b3 b2 b1 b0 a3 a2 a1 a0 93 92 91 90 83 82 81 80
+ // q1 = f3 f2 f1 f0 e3 e2 e1 e0 d3 d2 d1 d0 c3 c2 c1 c0
+ t1 = p1_s;
+ p1_s = _mm_unpacklo_epi16(t1, q1_s);
+ q1_s = _mm_unpackhi_epi16(t1, q1_s);
+
+ Store4x4_SSE2(&p0_s, r0, stride);
+ r0 += 4 * stride;
+ Store4x4_SSE2(&q0_s, r0, stride);
+
+ Store4x4_SSE2(&p1_s, r8, stride);
+ r8 += 4 * stride;
+ Store4x4_SSE2(&q1_s, r8, stride);
+}
+
+//------------------------------------------------------------------------------
+// Simple In-loop filtering (Paragraph 15.2)
+
+static void SimpleVFilter16_SSE2(uint8_t* p, int stride, int thresh) {
+ // Load
+ __m128i p1 = _mm_loadu_si128((__m128i*)&p[-2 * stride]);
+ __m128i p0 = _mm_loadu_si128((__m128i*)&p[-stride]);
+ __m128i q0 = _mm_loadu_si128((__m128i*)&p[0]);
+ __m128i q1 = _mm_loadu_si128((__m128i*)&p[stride]);
+
+ DoFilter2_SSE2(&p1, &p0, &q0, &q1, thresh);
+
+ // Store
+ _mm_storeu_si128((__m128i*)&p[-stride], p0);
+ _mm_storeu_si128((__m128i*)&p[0], q0);
+}
+
+static void SimpleHFilter16_SSE2(uint8_t* p, int stride, int thresh) {
+ __m128i p1, p0, q0, q1;
+
+ p -= 2; // beginning of p1
+
+ Load16x4_SSE2(p, p + 8 * stride, stride, &p1, &p0, &q0, &q1);
+ DoFilter2_SSE2(&p1, &p0, &q0, &q1, thresh);
+ Store16x4_SSE2(&p1, &p0, &q0, &q1, p, p + 8 * stride, stride);
+}
+
+static void SimpleVFilter16i_SSE2(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4 * stride;
+ SimpleVFilter16_SSE2(p, stride, thresh);
+ }
+}
+
+static void SimpleHFilter16i_SSE2(uint8_t* p, int stride, int thresh) {
+ int k;
+ for (k = 3; k > 0; --k) {
+ p += 4;
+ SimpleHFilter16_SSE2(p, stride, thresh);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Complex In-loop filtering (Paragraph 15.3)
+
+#define MAX_DIFF1(p3, p2, p1, p0, m) do { \
+ (m) = MM_ABS(p1, p0); \
+ (m) = _mm_max_epu8(m, MM_ABS(p3, p2)); \
+ (m) = _mm_max_epu8(m, MM_ABS(p2, p1)); \
+} while (0)
+
+#define MAX_DIFF2(p3, p2, p1, p0, m) do { \
+ (m) = _mm_max_epu8(m, MM_ABS(p1, p0)); \
+ (m) = _mm_max_epu8(m, MM_ABS(p3, p2)); \
+ (m) = _mm_max_epu8(m, MM_ABS(p2, p1)); \
+} while (0)
+
+#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) { \
+ (e1) = _mm_loadu_si128((__m128i*)&(p)[0 * (stride)]); \
+ (e2) = _mm_loadu_si128((__m128i*)&(p)[1 * (stride)]); \
+ (e3) = _mm_loadu_si128((__m128i*)&(p)[2 * (stride)]); \
+ (e4) = _mm_loadu_si128((__m128i*)&(p)[3 * (stride)]); \
+}
+
+#define LOADUV_H_EDGE(p, u, v, stride) do { \
+ const __m128i U = _mm_loadl_epi64((__m128i*)&(u)[(stride)]); \
+ const __m128i V = _mm_loadl_epi64((__m128i*)&(v)[(stride)]); \
+ (p) = _mm_unpacklo_epi64(U, V); \
+} while (0)
+
+#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) { \
+ LOADUV_H_EDGE(e1, u, v, 0 * (stride)); \
+ LOADUV_H_EDGE(e2, u, v, 1 * (stride)); \
+ LOADUV_H_EDGE(e3, u, v, 2 * (stride)); \
+ LOADUV_H_EDGE(e4, u, v, 3 * (stride)); \
+}
+
+#define STOREUV(p, u, v, stride) { \
+ _mm_storel_epi64((__m128i*)&(u)[(stride)], p); \
+ (p) = _mm_srli_si128(p, 8); \
+ _mm_storel_epi64((__m128i*)&(v)[(stride)], p); \
+}
+
+static WEBP_INLINE void ComplexMask_SSE2(const __m128i* const p1,
+ const __m128i* const p0,
+ const __m128i* const q0,
+ const __m128i* const q1,
+ int thresh, int ithresh,
+ __m128i* const mask) {
+ const __m128i it = _mm_set1_epi8(ithresh);
+ const __m128i diff = _mm_subs_epu8(*mask, it);
+ const __m128i thresh_mask = _mm_cmpeq_epi8(diff, _mm_setzero_si128());
+ __m128i filter_mask;
+ NeedsFilter_SSE2(p1, p0, q0, q1, thresh, &filter_mask);
+ *mask = _mm_and_si128(thresh_mask, filter_mask);
+}
+
+// on macroblock edges
+static void VFilter16_SSE2(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ __m128i t1;
+ __m128i mask;
+ __m128i p2, p1, p0, q0, q1, q2;
+
+ // Load p3, p2, p1, p0
+ LOAD_H_EDGES4(p - 4 * stride, stride, t1, p2, p1, p0);
+ MAX_DIFF1(t1, p2, p1, p0, mask);
+
+ // Load q0, q1, q2, q3
+ LOAD_H_EDGES4(p, stride, q0, q1, q2, t1);
+ MAX_DIFF2(t1, q2, q1, q0, mask);
+
+ ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask);
+ DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
+
+ // Store
+ _mm_storeu_si128((__m128i*)&p[-3 * stride], p2);
+ _mm_storeu_si128((__m128i*)&p[-2 * stride], p1);
+ _mm_storeu_si128((__m128i*)&p[-1 * stride], p0);
+ _mm_storeu_si128((__m128i*)&p[+0 * stride], q0);
+ _mm_storeu_si128((__m128i*)&p[+1 * stride], q1);
+ _mm_storeu_si128((__m128i*)&p[+2 * stride], q2);
+}
+
+static void HFilter16_SSE2(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ __m128i mask;
+ __m128i p3, p2, p1, p0, q0, q1, q2, q3;
+
+ uint8_t* const b = p - 4;
+ Load16x4_SSE2(b, b + 8 * stride, stride, &p3, &p2, &p1, &p0);
+ MAX_DIFF1(p3, p2, p1, p0, mask);
+
+ Load16x4_SSE2(p, p + 8 * stride, stride, &q0, &q1, &q2, &q3);
+ MAX_DIFF2(q3, q2, q1, q0, mask);
+
+ ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask);
+ DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
+
+ Store16x4_SSE2(&p3, &p2, &p1, &p0, b, b + 8 * stride, stride);
+ Store16x4_SSE2(&q0, &q1, &q2, &q3, p, p + 8 * stride, stride);
+}
+
+// on three inner edges
+static void VFilter16i_SSE2(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ __m128i p3, p2, p1, p0; // loop invariants
+
+ LOAD_H_EDGES4(p, stride, p3, p2, p1, p0); // prologue
+
+ for (k = 3; k > 0; --k) {
+ __m128i mask, tmp1, tmp2;
+ uint8_t* const b = p + 2 * stride; // beginning of p1
+ p += 4 * stride;
+
+ MAX_DIFF1(p3, p2, p1, p0, mask); // compute partial mask
+ LOAD_H_EDGES4(p, stride, p3, p2, tmp1, tmp2);
+ MAX_DIFF2(p3, p2, tmp1, tmp2, mask);
+
+ // p3 and p2 are not just temporary variables here: they will be
+ // re-used for next span. And q2/q3 will become p1/p0 accordingly.
+ ComplexMask_SSE2(&p1, &p0, &p3, &p2, thresh, ithresh, &mask);
+ DoFilter4_SSE2(&p1, &p0, &p3, &p2, &mask, hev_thresh);
+
+ // Store
+ _mm_storeu_si128((__m128i*)&b[0 * stride], p1);
+ _mm_storeu_si128((__m128i*)&b[1 * stride], p0);
+ _mm_storeu_si128((__m128i*)&b[2 * stride], p3);
+ _mm_storeu_si128((__m128i*)&b[3 * stride], p2);
+
+ // rotate samples
+ p1 = tmp1;
+ p0 = tmp2;
+ }
+}
+
+static void HFilter16i_SSE2(uint8_t* p, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ int k;
+ __m128i p3, p2, p1, p0; // loop invariants
+
+ Load16x4_SSE2(p, p + 8 * stride, stride, &p3, &p2, &p1, &p0); // prologue
+
+ for (k = 3; k > 0; --k) {
+ __m128i mask, tmp1, tmp2;
+ uint8_t* const b = p + 2; // beginning of p1
+
+ p += 4; // beginning of q0 (and next span)
+
+ MAX_DIFF1(p3, p2, p1, p0, mask); // compute partial mask
+ Load16x4_SSE2(p, p + 8 * stride, stride, &p3, &p2, &tmp1, &tmp2);
+ MAX_DIFF2(p3, p2, tmp1, tmp2, mask);
+
+ ComplexMask_SSE2(&p1, &p0, &p3, &p2, thresh, ithresh, &mask);
+ DoFilter4_SSE2(&p1, &p0, &p3, &p2, &mask, hev_thresh);
+
+ Store16x4_SSE2(&p1, &p0, &p3, &p2, b, b + 8 * stride, stride);
+
+ // rotate samples
+ p1 = tmp1;
+ p0 = tmp2;
+ }
+}
+
+// 8-pixels wide variant, for chroma filtering
+static void VFilter8_SSE2(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ __m128i mask;
+ __m128i t1, p2, p1, p0, q0, q1, q2;
+
+ // Load p3, p2, p1, p0
+ LOADUV_H_EDGES4(u - 4 * stride, v - 4 * stride, stride, t1, p2, p1, p0);
+ MAX_DIFF1(t1, p2, p1, p0, mask);
+
+ // Load q0, q1, q2, q3
+ LOADUV_H_EDGES4(u, v, stride, q0, q1, q2, t1);
+ MAX_DIFF2(t1, q2, q1, q0, mask);
+
+ ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask);
+ DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
+
+ // Store
+ STOREUV(p2, u, v, -3 * stride);
+ STOREUV(p1, u, v, -2 * stride);
+ STOREUV(p0, u, v, -1 * stride);
+ STOREUV(q0, u, v, 0 * stride);
+ STOREUV(q1, u, v, 1 * stride);
+ STOREUV(q2, u, v, 2 * stride);
+}
+
+static void HFilter8_SSE2(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ __m128i mask;
+ __m128i p3, p2, p1, p0, q0, q1, q2, q3;
+
+ uint8_t* const tu = u - 4;
+ uint8_t* const tv = v - 4;
+ Load16x4_SSE2(tu, tv, stride, &p3, &p2, &p1, &p0);
+ MAX_DIFF1(p3, p2, p1, p0, mask);
+
+ Load16x4_SSE2(u, v, stride, &q0, &q1, &q2, &q3);
+ MAX_DIFF2(q3, q2, q1, q0, mask);
+
+ ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask);
+ DoFilter6_SSE2(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
+
+ Store16x4_SSE2(&p3, &p2, &p1, &p0, tu, tv, stride);
+ Store16x4_SSE2(&q0, &q1, &q2, &q3, u, v, stride);
+}
+
+static void VFilter8i_SSE2(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ __m128i mask;
+ __m128i t1, t2, p1, p0, q0, q1;
+
+ // Load p3, p2, p1, p0
+ LOADUV_H_EDGES4(u, v, stride, t2, t1, p1, p0);
+ MAX_DIFF1(t2, t1, p1, p0, mask);
+
+ u += 4 * stride;
+ v += 4 * stride;
+
+ // Load q0, q1, q2, q3
+ LOADUV_H_EDGES4(u, v, stride, q0, q1, t1, t2);
+ MAX_DIFF2(t2, t1, q1, q0, mask);
+
+ ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask);
+ DoFilter4_SSE2(&p1, &p0, &q0, &q1, &mask, hev_thresh);
+
+ // Store
+ STOREUV(p1, u, v, -2 * stride);
+ STOREUV(p0, u, v, -1 * stride);
+ STOREUV(q0, u, v, 0 * stride);
+ STOREUV(q1, u, v, 1 * stride);
+}
+
+static void HFilter8i_SSE2(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_thresh) {
+ __m128i mask;
+ __m128i t1, t2, p1, p0, q0, q1;
+ Load16x4_SSE2(u, v, stride, &t2, &t1, &p1, &p0); // p3, p2, p1, p0
+ MAX_DIFF1(t2, t1, p1, p0, mask);
+
+ u += 4; // beginning of q0
+ v += 4;
+ Load16x4_SSE2(u, v, stride, &q0, &q1, &t1, &t2); // q0, q1, q2, q3
+ MAX_DIFF2(t2, t1, q1, q0, mask);
+
+ ComplexMask_SSE2(&p1, &p0, &q0, &q1, thresh, ithresh, &mask);
+ DoFilter4_SSE2(&p1, &p0, &q0, &q1, &mask, hev_thresh);
+
+ u -= 2; // beginning of p1
+ v -= 2;
+ Store16x4_SSE2(&p1, &p0, &q0, &q1, u, v, stride);
+}
+
+//------------------------------------------------------------------------------
+// 4x4 predictions
+
+#define DST(x, y) dst[(x) + (y) * BPS]
+#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
+
+// We use the following 8b-arithmetic tricks:
+// (a + 2 * b + c + 2) >> 2 = (AC + b + 1) >> 1
+// where: AC = (a + c) >> 1 = [(a + c + 1) >> 1] - [(a^c) & 1]
+// and:
+// (a + 2 * b + c + 2) >> 2 = (AB + BC + 1) >> 1 - (ab|bc)&lsb
+// where: AC = (a + b + 1) >> 1, BC = (b + c + 1) >> 1
+// and ab = a ^ b, bc = b ^ c, lsb = (AC^BC)&1
+
+static void VE4_SSE2(uint8_t* dst) { // vertical
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(dst - BPS - 1));
+ const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1);
+ const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2);
+ const __m128i a = _mm_avg_epu8(ABCDEFGH, CDEFGH00);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one);
+ const __m128i b = _mm_subs_epu8(a, lsb);
+ const __m128i avg = _mm_avg_epu8(b, BCDEFGH0);
+ const uint32_t vals = _mm_cvtsi128_si32(avg);
+ int i;
+ for (i = 0; i < 4; ++i) {
+ WebPUint32ToMem(dst + i * BPS, vals);
+ }
+}
+
+static void LD4_SSE2(uint8_t* dst) { // Down-Left
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(dst - BPS));
+ const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1);
+ const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2);
+ const __m128i CDEFGHH0 = _mm_insert_epi16(CDEFGH00, dst[-BPS + 7], 3);
+ const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, CDEFGHH0);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one);
+ const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
+ const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0);
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+}
+
+static void VR4_SSE2(uint8_t* dst) { // Vertical-Right
+ const __m128i one = _mm_set1_epi8(1);
+ const int I = dst[-1 + 0 * BPS];
+ const int J = dst[-1 + 1 * BPS];
+ const int K = dst[-1 + 2 * BPS];
+ const int X = dst[-1 - BPS];
+ const __m128i XABCD = _mm_loadl_epi64((__m128i*)(dst - BPS - 1));
+ const __m128i ABCD0 = _mm_srli_si128(XABCD, 1);
+ const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0);
+ const __m128i _XABCD = _mm_slli_si128(XABCD, 1);
+ const __m128i IXABCD = _mm_insert_epi16(_XABCD, I | (X << 8), 0);
+ const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
+ const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
+ const __m128i efgh = _mm_avg_epu8(avg2, XABCD);
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd ));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh ));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
+
+ // these two are hard to implement in SSE2, so we keep the C-version:
+ DST(0, 2) = AVG3(J, I, X);
+ DST(0, 3) = AVG3(K, J, I);
+}
+
+static void VL4_SSE2(uint8_t* dst) { // Vertical-Left
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(dst - BPS));
+ const __m128i BCDEFGH_ = _mm_srli_si128(ABCDEFGH, 1);
+ const __m128i CDEFGH__ = _mm_srli_si128(ABCDEFGH, 2);
+ const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, BCDEFGH_);
+ const __m128i avg2 = _mm_avg_epu8(CDEFGH__, BCDEFGH_);
+ const __m128i avg3 = _mm_avg_epu8(avg1, avg2);
+ const __m128i lsb1 = _mm_and_si128(_mm_xor_si128(avg1, avg2), one);
+ const __m128i ab = _mm_xor_si128(ABCDEFGH, BCDEFGH_);
+ const __m128i bc = _mm_xor_si128(CDEFGH__, BCDEFGH_);
+ const __m128i abbc = _mm_or_si128(ab, bc);
+ const __m128i lsb2 = _mm_and_si128(abbc, lsb1);
+ const __m128i avg4 = _mm_subs_epu8(avg3, lsb2);
+ const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 ));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 ));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
+
+ // these two are hard to get and irregular
+ DST(3, 2) = (extra_out >> 0) & 0xff;
+ DST(3, 3) = (extra_out >> 8) & 0xff;
+}
+
+static void RD4_SSE2(uint8_t* dst) { // Down-right
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i XABCD = _mm_loadl_epi64((__m128i*)(dst - BPS - 1));
+ const __m128i ____XABCD = _mm_slli_si128(XABCD, 4);
+ const uint32_t I = dst[-1 + 0 * BPS];
+ const uint32_t J = dst[-1 + 1 * BPS];
+ const uint32_t K = dst[-1 + 2 * BPS];
+ const uint32_t L = dst[-1 + 3 * BPS];
+ const __m128i LKJI_____ =
+ _mm_cvtsi32_si128(L | (K << 8) | (J << 16) | (I << 24));
+ const __m128i LKJIXABCD = _mm_or_si128(LKJI_____, ____XABCD);
+ const __m128i KJIXABCD_ = _mm_srli_si128(LKJIXABCD, 1);
+ const __m128i JIXABCD__ = _mm_srli_si128(LKJIXABCD, 2);
+ const __m128i avg1 = _mm_avg_epu8(JIXABCD__, LKJIXABCD);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one);
+ const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
+ const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_);
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+}
+
+#undef DST
+#undef AVG3
+
+//------------------------------------------------------------------------------
+// Luma 16x16
+
+static WEBP_INLINE void TrueMotion_SSE2(uint8_t* dst, int size) {
+ const uint8_t* top = dst - BPS;
+ const __m128i zero = _mm_setzero_si128();
+ int y;
+ if (size == 4) {
+ const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top));
+ const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
+ for (y = 0; y < 4; ++y, dst += BPS) {
+ const int val = dst[-1] - top[-1];
+ const __m128i base = _mm_set1_epi16(val);
+ const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
+ WebPUint32ToMem(dst, _mm_cvtsi128_si32(out));
+ }
+ } else if (size == 8) {
+ const __m128i top_values = _mm_loadl_epi64((const __m128i*)top);
+ const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
+ for (y = 0; y < 8; ++y, dst += BPS) {
+ const int val = dst[-1] - top[-1];
+ const __m128i base = _mm_set1_epi16(val);
+ const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
+ _mm_storel_epi64((__m128i*)dst, out);
+ }
+ } else {
+ const __m128i top_values = _mm_loadu_si128((const __m128i*)top);
+ const __m128i top_base_0 = _mm_unpacklo_epi8(top_values, zero);
+ const __m128i top_base_1 = _mm_unpackhi_epi8(top_values, zero);
+ for (y = 0; y < 16; ++y, dst += BPS) {
+ const int val = dst[-1] - top[-1];
+ const __m128i base = _mm_set1_epi16(val);
+ const __m128i out_0 = _mm_add_epi16(base, top_base_0);
+ const __m128i out_1 = _mm_add_epi16(base, top_base_1);
+ const __m128i out = _mm_packus_epi16(out_0, out_1);
+ _mm_storeu_si128((__m128i*)dst, out);
+ }
+ }
+}
+
+static void TM4_SSE2(uint8_t* dst) { TrueMotion_SSE2(dst, 4); }
+static void TM8uv_SSE2(uint8_t* dst) { TrueMotion_SSE2(dst, 8); }
+static void TM16_SSE2(uint8_t* dst) { TrueMotion_SSE2(dst, 16); }
+
+static void VE16_SSE2(uint8_t* dst) {
+ const __m128i top = _mm_loadu_si128((const __m128i*)(dst - BPS));
+ int j;
+ for (j = 0; j < 16; ++j) {
+ _mm_storeu_si128((__m128i*)(dst + j * BPS), top);
+ }
+}
+
+static void HE16_SSE2(uint8_t* dst) { // horizontal
+ int j;
+ for (j = 16; j > 0; --j) {
+ const __m128i values = _mm_set1_epi8(dst[-1]);
+ _mm_storeu_si128((__m128i*)dst, values);
+ dst += BPS;
+ }
+}
+
+static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) {
+ int j;
+ const __m128i values = _mm_set1_epi8(v);
+ for (j = 0; j < 16; ++j) {
+ _mm_storeu_si128((__m128i*)(dst + j * BPS), values);
+ }
+}
+
+static void DC16_SSE2(uint8_t* dst) { // DC
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i top = _mm_loadu_si128((const __m128i*)(dst - BPS));
+ const __m128i sad8x2 = _mm_sad_epu8(top, zero);
+ // sum the two sads: sad8x2[0:1] + sad8x2[8:9]
+ const __m128i sum = _mm_add_epi16(sad8x2, _mm_shuffle_epi32(sad8x2, 2));
+ int left = 0;
+ int j;
+ for (j = 0; j < 16; ++j) {
+ left += dst[-1 + j * BPS];
+ }
+ {
+ const int DC = _mm_cvtsi128_si32(sum) + left + 16;
+ Put16_SSE2(DC >> 5, dst);
+ }
+}
+
+static void DC16NoTop_SSE2(uint8_t* dst) { // DC with top samples unavailable
+ int DC = 8;
+ int j;
+ for (j = 0; j < 16; ++j) {
+ DC += dst[-1 + j * BPS];
+ }
+ Put16_SSE2(DC >> 4, dst);
+}
+
+static void DC16NoLeft_SSE2(uint8_t* dst) { // DC with left samples unavailable
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i top = _mm_loadu_si128((const __m128i*)(dst - BPS));
+ const __m128i sad8x2 = _mm_sad_epu8(top, zero);
+ // sum the two sads: sad8x2[0:1] + sad8x2[8:9]
+ const __m128i sum = _mm_add_epi16(sad8x2, _mm_shuffle_epi32(sad8x2, 2));
+ const int DC = _mm_cvtsi128_si32(sum) + 8;
+ Put16_SSE2(DC >> 4, dst);
+}
+
+static void DC16NoTopLeft_SSE2(uint8_t* dst) { // DC with no top & left samples
+ Put16_SSE2(0x80, dst);
+}
+
+//------------------------------------------------------------------------------
+// Chroma
+
+static void VE8uv_SSE2(uint8_t* dst) { // vertical
+ int j;
+ const __m128i top = _mm_loadl_epi64((const __m128i*)(dst - BPS));
+ for (j = 0; j < 8; ++j) {
+ _mm_storel_epi64((__m128i*)(dst + j * BPS), top);
+ }
+}
+
+// helper for chroma-DC predictions
+static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) {
+ int j;
+ const __m128i values = _mm_set1_epi8(v);
+ for (j = 0; j < 8; ++j) {
+ _mm_storel_epi64((__m128i*)(dst + j * BPS), values);
+ }
+}
+
+static void DC8uv_SSE2(uint8_t* dst) { // DC
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i top = _mm_loadl_epi64((const __m128i*)(dst - BPS));
+ const __m128i sum = _mm_sad_epu8(top, zero);
+ int left = 0;
+ int j;
+ for (j = 0; j < 8; ++j) {
+ left += dst[-1 + j * BPS];
+ }
+ {
+ const int DC = _mm_cvtsi128_si32(sum) + left + 8;
+ Put8x8uv_SSE2(DC >> 4, dst);
+ }
+}
+
+static void DC8uvNoLeft_SSE2(uint8_t* dst) { // DC with no left samples
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i top = _mm_loadl_epi64((const __m128i*)(dst - BPS));
+ const __m128i sum = _mm_sad_epu8(top, zero);
+ const int DC = _mm_cvtsi128_si32(sum) + 4;
+ Put8x8uv_SSE2(DC >> 3, dst);
+}
+
+static void DC8uvNoTop_SSE2(uint8_t* dst) { // DC with no top samples
+ int dc0 = 4;
+ int i;
+ for (i = 0; i < 8; ++i) {
+ dc0 += dst[-1 + i * BPS];
+ }
+ Put8x8uv_SSE2(dc0 >> 3, dst);
+}
+
+static void DC8uvNoTopLeft_SSE2(uint8_t* dst) { // DC with nothing
+ Put8x8uv_SSE2(0x80, dst);
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8DspInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitSSE2(void) {
+ VP8Transform = Transform_SSE2;
+#if (USE_TRANSFORM_AC3 == 1)
+ VP8TransformAC3 = TransformAC3_SSE2;
+#endif
+
+ VP8VFilter16 = VFilter16_SSE2;
+ VP8HFilter16 = HFilter16_SSE2;
+ VP8VFilter8 = VFilter8_SSE2;
+ VP8HFilter8 = HFilter8_SSE2;
+ VP8VFilter16i = VFilter16i_SSE2;
+ VP8HFilter16i = HFilter16i_SSE2;
+ VP8VFilter8i = VFilter8i_SSE2;
+ VP8HFilter8i = HFilter8i_SSE2;
+
+ VP8SimpleVFilter16 = SimpleVFilter16_SSE2;
+ VP8SimpleHFilter16 = SimpleHFilter16_SSE2;
+ VP8SimpleVFilter16i = SimpleVFilter16i_SSE2;
+ VP8SimpleHFilter16i = SimpleHFilter16i_SSE2;
+
+ VP8PredLuma4[1] = TM4_SSE2;
+ VP8PredLuma4[2] = VE4_SSE2;
+ VP8PredLuma4[4] = RD4_SSE2;
+ VP8PredLuma4[5] = VR4_SSE2;
+ VP8PredLuma4[6] = LD4_SSE2;
+ VP8PredLuma4[7] = VL4_SSE2;
+
+ VP8PredLuma16[0] = DC16_SSE2;
+ VP8PredLuma16[1] = TM16_SSE2;
+ VP8PredLuma16[2] = VE16_SSE2;
+ VP8PredLuma16[3] = HE16_SSE2;
+ VP8PredLuma16[4] = DC16NoTop_SSE2;
+ VP8PredLuma16[5] = DC16NoLeft_SSE2;
+ VP8PredLuma16[6] = DC16NoTopLeft_SSE2;
+
+ VP8PredChroma8[0] = DC8uv_SSE2;
+ VP8PredChroma8[1] = TM8uv_SSE2;
+ VP8PredChroma8[2] = VE8uv_SSE2;
+ VP8PredChroma8[4] = DC8uvNoTop_SSE2;
+ VP8PredChroma8[5] = DC8uvNoLeft_SSE2;
+ VP8PredChroma8[6] = DC8uvNoTopLeft_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(VP8DspInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/dec_sse41.c b/src/third_party/libwebp/src/dsp/dec_sse41.c
new file mode 100644
index 0000000..8f18506
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dec_sse41.c
@@ -0,0 +1,46 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE4 version of some decoding functions.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE41)
+
+#include <smmintrin.h>
+#include "src/dec/vp8i_dec.h"
+#include "src/utils/utils.h"
+
+static void HE16_SSE41(uint8_t* dst) { // horizontal
+ int j;
+ const __m128i kShuffle3 = _mm_set1_epi8(3);
+ for (j = 16; j > 0; --j) {
+ const __m128i in = _mm_cvtsi32_si128(WebPMemToUint32(dst - 4));
+ const __m128i values = _mm_shuffle_epi8(in, kShuffle3);
+ _mm_storeu_si128((__m128i*)dst, values);
+ dst += BPS;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8DspInitSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitSSE41(void) {
+ VP8PredLuma16[3] = HE16_SSE41;
+}
+
+#else // !WEBP_USE_SSE41
+
+WEBP_DSP_INIT_STUB(VP8DspInitSSE41)
+
+#endif // WEBP_USE_SSE41
diff --git a/src/third_party/libwebp/src/dsp/dsp.h b/src/third_party/libwebp/src/dsp/dsp.h
new file mode 100644
index 0000000..7f9bb53
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/dsp.h
@@ -0,0 +1,693 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_DSP_DSP_H_
+#define WEBP_DSP_DSP_H_
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BPS 32 // this is the common stride for enc/dec
+
+//------------------------------------------------------------------------------
+// CPU detection
+
+#if defined(__GNUC__)
+# define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__)
+# define LOCAL_GCC_PREREQ(maj, min) \
+ (LOCAL_GCC_VERSION >= (((maj) << 8) | (min)))
+#else
+# define LOCAL_GCC_VERSION 0
+# define LOCAL_GCC_PREREQ(maj, min) 0
+#endif
+
+#if defined(__clang__)
+# define LOCAL_CLANG_VERSION ((__clang_major__ << 8) | __clang_minor__)
+# define LOCAL_CLANG_PREREQ(maj, min) \
+ (LOCAL_CLANG_VERSION >= (((maj) << 8) | (min)))
+#else
+# define LOCAL_CLANG_VERSION 0
+# define LOCAL_CLANG_PREREQ(maj, min) 0
+#endif
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+// for now, none of the optimizations below are available in emscripten
+#if !defined(EMSCRIPTEN)
+
+#if defined(_MSC_VER) && _MSC_VER > 1310 && \
+ (defined(_M_X64) || defined(_M_IX86))
+#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 && \
+ (defined(_M_X64) || defined(_M_IX86))
+#define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets
+#endif
+
+// WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp
+// files without intrinsics, allowing the corresponding Init() to be called.
+// Files containing intrinsics will need to be built targeting the instruction
+// set so should succeed on one of the earlier tests.
+#if defined(__SSE2__) || defined(WEBP_MSC_SSE2) || defined(WEBP_HAVE_SSE2)
+#define WEBP_USE_SSE2
+#endif
+
+#if defined(__SSE4_1__) || defined(WEBP_MSC_SSE41) || defined(WEBP_HAVE_SSE41)
+#define WEBP_USE_SSE41
+#endif
+
+#if defined(__AVX2__) || defined(WEBP_HAVE_AVX2)
+#define WEBP_USE_AVX2
+#endif
+
+// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
+// inline assembly would need to be modified for use with Native Client.
+#if (defined(__ARM_NEON__) || \
+ defined(__aarch64__) || defined(WEBP_HAVE_NEON)) && \
+ !defined(__native_client__)
+#define WEBP_USE_NEON
+#endif
+
+#if !defined(WEBP_USE_NEON) && defined(__ANDROID__) && \
+ defined(__ARM_ARCH_7A__) && defined(HAVE_CPU_FEATURES_H)
+#define WEBP_ANDROID_NEON // Android targets that may have NEON
+#define WEBP_USE_NEON
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#define WEBP_USE_NEON
+#define WEBP_USE_INTRINSICS
+#endif
+
+#if defined(__mips__) && !defined(__mips64) && \
+ defined(__mips_isa_rev) && (__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
+#define WEBP_USE_MIPS32
+#if (__mips_isa_rev >= 2)
+#define WEBP_USE_MIPS32_R2
+#if defined(__mips_dspr2) || (defined(__mips_dsp_rev) && __mips_dsp_rev >= 2)
+#define WEBP_USE_MIPS_DSP_R2
+#endif
+#endif
+#endif
+
+#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
+#define WEBP_USE_MSA
+#endif
+
+#endif /* EMSCRIPTEN */
+
+#ifndef WEBP_DSP_OMIT_C_CODE
+#define WEBP_DSP_OMIT_C_CODE 1
+#endif
+
+#if (defined(__aarch64__) || defined(__ARM_NEON__)) && WEBP_DSP_OMIT_C_CODE
+#define WEBP_NEON_OMIT_C_CODE 1
+#else
+#define WEBP_NEON_OMIT_C_CODE 0
+#endif
+
+#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__))
+#define WEBP_NEON_WORK_AROUND_GCC 1
+#else
+#define WEBP_NEON_WORK_AROUND_GCC 0
+#endif
+
+// This macro prevents thread_sanitizer from reporting known concurrent writes.
+#define WEBP_TSAN_IGNORE_FUNCTION
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#undef WEBP_TSAN_IGNORE_FUNCTION
+#define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread))
+#endif
+#endif
+
+#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
+#if !defined(STARBOARD)
+// TODO: If not including this is a problem on a Starboard platform, then
+// we should implement this functionality with Starboard threading
+// primitives instead.
+#include <pthread.h> // NOLINT
+#endif
+
+#define WEBP_DSP_INIT(func) do { \
+ static volatile VP8CPUInfo func ## _last_cpuinfo_used = \
+ (VP8CPUInfo)&func ## _last_cpuinfo_used; \
+ static pthread_mutex_t func ## _lock = PTHREAD_MUTEX_INITIALIZER; \
+ if (pthread_mutex_lock(&func ## _lock)) break; \
+ if (func ## _last_cpuinfo_used != VP8GetCPUInfo) func(); \
+ func ## _last_cpuinfo_used = VP8GetCPUInfo; \
+ (void)pthread_mutex_unlock(&func ## _lock); \
+} while (0)
+#else // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
+#define WEBP_DSP_INIT(func) do { \
+ static volatile VP8CPUInfo func ## _last_cpuinfo_used = \
+ (VP8CPUInfo)&func ## _last_cpuinfo_used; \
+ if (func ## _last_cpuinfo_used == VP8GetCPUInfo) break; \
+ func(); \
+ func ## _last_cpuinfo_used = VP8GetCPUInfo; \
+} while (0)
+#endif // defined(WEBP_USE_THREAD) && !defined(_WIN32)
+
+// Defines an Init + helper function that control multiple initialization of
+// function pointers / tables.
+/* Usage:
+ WEBP_DSP_INIT_FUNC(InitFunc) {
+ ...function body
+ }
+*/
+#define WEBP_DSP_INIT_FUNC(name) \
+ static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void); \
+ WEBP_TSAN_IGNORE_FUNCTION void name(void) { \
+ WEBP_DSP_INIT(name ## _body); \
+ } \
+ static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void)
+
+#define WEBP_UBSAN_IGNORE_UNDEF
+#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+#if defined(__clang__) && defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+// This macro prevents the undefined behavior sanitizer from reporting
+// failures. This is only meant to silence unaligned loads on platforms that
+// are known to support them.
+#undef WEBP_UBSAN_IGNORE_UNDEF
+#define WEBP_UBSAN_IGNORE_UNDEF \
+ __attribute__((no_sanitize("undefined")))
+
+// This macro prevents the undefined behavior sanitizer from reporting
+// failures related to unsigned integer overflows. This is only meant to
+// silence cases where this well defined behavior is expected.
+#undef WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW \
+ __attribute__((no_sanitize("unsigned-integer-overflow")))
+#endif
+#endif
+
+// Regularize the definition of WEBP_SWAP_16BIT_CSP (backward compatibility)
+#if !defined(WEBP_SWAP_16BIT_CSP)
+#define WEBP_SWAP_16BIT_CSP 0
+#endif
+
+// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__)
+#if defined(STARBOARD)
+#if SB_IS(BIG_ENDIAN)
+#define WORDS_BIGENDIAN
+#endif
+#else
+#if !defined(WORDS_BIGENDIAN) && \
+ (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \
+ (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
+#define WORDS_BIGENDIAN
+#endif
+#endif // defined(STARBOARD)
+
+typedef enum {
+ kSSE2,
+ kSSE3,
+ kSlowSSSE3, // special feature for slow SSSE3 architectures
+ kSSE4_1,
+ kAVX,
+ kAVX2,
+ kNEON,
+ kMIPS32,
+ kMIPSdspR2,
+ kMSA
+} CPUFeature;
+// returns true if the CPU supports the feature.
+typedef int (*VP8CPUInfo)(CPUFeature feature);
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
+
+//------------------------------------------------------------------------------
+// Init stub generator
+
+// Defines an init function stub to ensure each module exposes a symbol,
+// avoiding a compiler warning.
+#define WEBP_DSP_INIT_STUB(func) \
+ extern void func(void); \
+ void func(void) {}
+
+//------------------------------------------------------------------------------
+// Encoding
+
+// Transforms
+// VP8Idct: Does one of two inverse transforms. If do_two is set, the transforms
+// will be done for (ref, in, dst) and (ref + 4, in + 16, dst + 4).
+typedef void (*VP8Idct)(const uint8_t* ref, const int16_t* in, uint8_t* dst,
+ int do_two);
+typedef void (*VP8Fdct)(const uint8_t* src, const uint8_t* ref, int16_t* out);
+typedef void (*VP8WHT)(const int16_t* in, int16_t* out);
+extern VP8Idct VP8ITransform;
+extern VP8Fdct VP8FTransform;
+extern VP8Fdct VP8FTransform2; // performs two transforms at a time
+extern VP8WHT VP8FTransformWHT;
+// Predictions
+// *dst is the destination block. *top and *left can be NULL.
+typedef void (*VP8IntraPreds)(uint8_t *dst, const uint8_t* left,
+ const uint8_t* top);
+typedef void (*VP8Intra4Preds)(uint8_t *dst, const uint8_t* top);
+extern VP8Intra4Preds VP8EncPredLuma4;
+extern VP8IntraPreds VP8EncPredLuma16;
+extern VP8IntraPreds VP8EncPredChroma8;
+
+typedef int (*VP8Metric)(const uint8_t* pix, const uint8_t* ref);
+extern VP8Metric VP8SSE16x16, VP8SSE16x8, VP8SSE8x8, VP8SSE4x4;
+typedef int (*VP8WMetric)(const uint8_t* pix, const uint8_t* ref,
+ const uint16_t* const weights);
+// The weights for VP8TDisto4x4 and VP8TDisto16x16 contain a row-major
+// 4 by 4 symmetric matrix.
+extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16;
+
+// Compute the average (DC) of four 4x4 blocks.
+// Each sub-4x4 block #i sum is stored in dc[i].
+typedef void (*VP8MeanMetric)(const uint8_t* ref, uint32_t dc[4]);
+extern VP8MeanMetric VP8Mean16x4;
+
+typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
+extern VP8BlockCopy VP8Copy4x4;
+extern VP8BlockCopy VP8Copy16x8;
+// Quantization
+struct VP8Matrix; // forward declaration
+typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16],
+ const struct VP8Matrix* const mtx);
+// Same as VP8QuantizeBlock, but quantizes two consecutive blocks.
+typedef int (*VP8Quantize2Blocks)(int16_t in[32], int16_t out[32],
+ const struct VP8Matrix* const mtx);
+
+extern VP8QuantizeBlock VP8EncQuantizeBlock;
+extern VP8Quantize2Blocks VP8EncQuantize2Blocks;
+
+// specific to 2nd transform:
+typedef int (*VP8QuantizeBlockWHT)(int16_t in[16], int16_t out[16],
+ const struct VP8Matrix* const mtx);
+extern VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT;
+
+extern const int VP8DspScan[16 + 4 + 4];
+
+// Collect histogram for susceptibility calculation.
+#define MAX_COEFF_THRESH 31 // size of histogram used by CollectHistogram.
+typedef struct {
+ // We only need to store max_value and last_non_zero, not the distribution.
+ int max_value;
+ int last_non_zero;
+} VP8Histogram;
+typedef void (*VP8CHisto)(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block,
+ VP8Histogram* const histo);
+extern VP8CHisto VP8CollectHistogram;
+// General-purpose util function to help VP8CollectHistogram().
+void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1],
+ VP8Histogram* const histo);
+
+// must be called before using any of the above
+void VP8EncDspInit(void);
+
+//------------------------------------------------------------------------------
+// cost functions (encoding)
+
+extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p)
+// approximate cost per level:
+extern const uint16_t VP8LevelFixedCosts[2047 /*MAX_LEVEL*/ + 1];
+extern const uint8_t VP8EncBands[16 + 1];
+
+struct VP8Residual;
+typedef void (*VP8SetResidualCoeffsFunc)(const int16_t* const coeffs,
+ struct VP8Residual* const res);
+extern VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
+
+// Cost calculation function.
+typedef int (*VP8GetResidualCostFunc)(int ctx0,
+ const struct VP8Residual* const res);
+extern VP8GetResidualCostFunc VP8GetResidualCost;
+
+// must be called before anything using the above
+void VP8EncDspCostInit(void);
+
+//------------------------------------------------------------------------------
+// SSIM / PSNR utils
+
+// struct for accumulating statistical moments
+typedef struct {
+ uint32_t w; // sum(w_i) : sum of weights
+ uint32_t xm, ym; // sum(w_i * x_i), sum(w_i * y_i)
+ uint32_t xxm, xym, yym; // sum(w_i * x_i * x_i), etc.
+} VP8DistoStats;
+
+// Compute the final SSIM value
+// The non-clipped version assumes stats->w = (2 * VP8_SSIM_KERNEL + 1)^2.
+double VP8SSIMFromStats(const VP8DistoStats* const stats);
+double VP8SSIMFromStatsClipped(const VP8DistoStats* const stats);
+
+#define VP8_SSIM_KERNEL 3 // total size of the kernel: 2 * VP8_SSIM_KERNEL + 1
+typedef double (*VP8SSIMGetClippedFunc)(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int xo, int yo, // center position
+ int W, int H); // plane dimension
+
+#if !defined(WEBP_REDUCE_SIZE)
+// This version is called with the guarantee that you can load 8 bytes and
+// 8 rows at offset src1 and src2
+typedef double (*VP8SSIMGetFunc)(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2);
+
+extern VP8SSIMGetFunc VP8SSIMGet; // unclipped / unchecked
+extern VP8SSIMGetClippedFunc VP8SSIMGetClipped; // with clipping
+#endif
+
+#if !defined(WEBP_DISABLE_STATS)
+typedef uint32_t (*VP8AccumulateSSEFunc)(const uint8_t* src1,
+ const uint8_t* src2, int len);
+extern VP8AccumulateSSEFunc VP8AccumulateSSE;
+#endif
+
+// must be called before using any of the above directly
+void VP8SSIMDspInit(void);
+
+//------------------------------------------------------------------------------
+// Decoding
+
+typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst);
+// when doing two transforms, coeffs is actually int16_t[2][16].
+typedef void (*VP8DecIdct2)(const int16_t* coeffs, uint8_t* dst, int do_two);
+extern VP8DecIdct2 VP8Transform;
+extern VP8DecIdct VP8TransformAC3;
+extern VP8DecIdct VP8TransformUV;
+extern VP8DecIdct VP8TransformDC;
+extern VP8DecIdct VP8TransformDCUV;
+extern VP8WHT VP8TransformWHT;
+
+// *dst is the destination block, with stride BPS. Boundary samples are
+// assumed accessible when needed.
+typedef void (*VP8PredFunc)(uint8_t* dst);
+extern VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
+extern VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
+extern VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
+
+// clipping tables (for filtering)
+extern const int8_t* const VP8ksclip1; // clips [-1020, 1020] to [-128, 127]
+extern const int8_t* const VP8ksclip2; // clips [-112, 112] to [-16, 15]
+extern const uint8_t* const VP8kclip1; // clips [-255,511] to [0,255]
+extern const uint8_t* const VP8kabs0; // abs(x) for x in [-255,255]
+// must be called first
+void VP8InitClipTables(void);
+
+// simple filter (only for luma)
+typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh);
+extern VP8SimpleFilterFunc VP8SimpleVFilter16;
+extern VP8SimpleFilterFunc VP8SimpleHFilter16;
+extern VP8SimpleFilterFunc VP8SimpleVFilter16i; // filter 3 inner edges
+extern VP8SimpleFilterFunc VP8SimpleHFilter16i;
+
+// regular filter (on both macroblock edges and inner edges)
+typedef void (*VP8LumaFilterFunc)(uint8_t* luma, int stride,
+ int thresh, int ithresh, int hev_t);
+typedef void (*VP8ChromaFilterFunc)(uint8_t* u, uint8_t* v, int stride,
+ int thresh, int ithresh, int hev_t);
+// on outer edge
+extern VP8LumaFilterFunc VP8VFilter16;
+extern VP8LumaFilterFunc VP8HFilter16;
+extern VP8ChromaFilterFunc VP8VFilter8;
+extern VP8ChromaFilterFunc VP8HFilter8;
+
+// on inner edge
+extern VP8LumaFilterFunc VP8VFilter16i; // filtering 3 inner edges altogether
+extern VP8LumaFilterFunc VP8HFilter16i;
+extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether
+extern VP8ChromaFilterFunc VP8HFilter8i;
+
+// Dithering. Combines dithering values (centered around 128) with dst[],
+// according to: dst[] = clip(dst[] + (((dither[]-128) + 8) >> 4)
+#define VP8_DITHER_DESCALE 4
+#define VP8_DITHER_DESCALE_ROUNDER (1 << (VP8_DITHER_DESCALE - 1))
+#define VP8_DITHER_AMP_BITS 7
+#define VP8_DITHER_AMP_CENTER (1 << VP8_DITHER_AMP_BITS)
+extern void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst,
+ int dst_stride);
+
+// must be called before anything using the above
+void VP8DspInit(void);
+
+//------------------------------------------------------------------------------
+// WebP I/O
+
+#define FANCY_UPSAMPLING // undefined to remove fancy upsampling support
+
+// Convert a pair of y/u/v lines together to the output rgb/a colorspace.
+// bottom_y can be NULL if only one line of output is needed (at top/bottom).
+typedef void (*WebPUpsampleLinePairFunc)(
+ const uint8_t* top_y, const uint8_t* bottom_y,
+ const uint8_t* top_u, const uint8_t* top_v,
+ const uint8_t* cur_u, const uint8_t* cur_v,
+ uint8_t* top_dst, uint8_t* bottom_dst, int len);
+
+#ifdef FANCY_UPSAMPLING
+
+// Fancy upsampling functions to convert YUV to RGB(A) modes
+extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
+
+#endif // FANCY_UPSAMPLING
+
+// Per-row point-sampling methods.
+typedef void (*WebPSamplerRowFunc)(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len);
+// Generic function to apply 'WebPSamplerRowFunc' to the whole plane:
+void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
+ const uint8_t* u, const uint8_t* v, int uv_stride,
+ uint8_t* dst, int dst_stride,
+ int width, int height, WebPSamplerRowFunc func);
+
+// Sampling functions to convert rows of YUV to RGB(A)
+extern WebPSamplerRowFunc WebPSamplers[/* MODE_LAST */];
+
+// General function for converting two lines of ARGB or RGBA.
+// 'alpha_is_last' should be true if 0xff000000 is stored in memory as
+// as 0x00, 0x00, 0x00, 0xff (little endian).
+WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last);
+
+// YUV444->RGB converters
+typedef void (*WebPYUV444Converter)(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len);
+
+extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
+
+// Must be called before using the WebPUpsamplers[] (and for premultiplied
+// colorspaces like rgbA, rgbA4444, etc)
+void WebPInitUpsamplers(void);
+// Must be called before using WebPSamplers[]
+void WebPInitSamplers(void);
+// Must be called before using WebPYUV444Converters[]
+void WebPInitYUV444Converters(void);
+
+//------------------------------------------------------------------------------
+// ARGB -> YUV converters
+
+// Convert ARGB samples to luma Y.
+extern void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width);
+// Convert ARGB samples to U/V with downsampling. do_store should be '1' for
+// even lines and '0' for odd ones. 'src_width' is the original width, not
+// the U/V one.
+extern void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v,
+ int src_width, int do_store);
+
+// Convert a row of accumulated (four-values) of rgba32 toward U/V
+extern void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width);
+
+// Convert RGB or BGR to Y
+extern void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width);
+extern void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width);
+
+// used for plain-C fallback.
+extern void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v,
+ int src_width, int do_store);
+extern void WebPConvertRGBA32ToUV_C(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width);
+
+// utilities for accurate RGB->YUV conversion
+extern uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* src, const uint16_t* ref,
+ uint16_t* dst, int len);
+extern void (*WebPSharpYUVUpdateRGB)(const int16_t* src, const int16_t* ref,
+ int16_t* dst, int len);
+extern void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B,
+ int len,
+ const uint16_t* best_y, uint16_t* out);
+
+// Must be called before using the above.
+void WebPInitConvertARGBToYUV(void);
+
+//------------------------------------------------------------------------------
+// Rescaler
+
+struct WebPRescaler;
+
+// Import a row of data and save its contribution in the rescaler.
+// 'channel' denotes the channel number to be imported. 'Expand' corresponds to
+// the wrk->x_expand case. Otherwise, 'Shrink' is to be used.
+typedef void (*WebPRescalerImportRowFunc)(struct WebPRescaler* const wrk,
+ const uint8_t* src);
+
+extern WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
+extern WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
+
+// Export one row (starting at x_out position) from rescaler.
+// 'Expand' corresponds to the wrk->y_expand case.
+// Otherwise 'Shrink' is to be used
+typedef void (*WebPRescalerExportRowFunc)(struct WebPRescaler* const wrk);
+extern WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
+extern WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
+
+// Plain-C implementation, as fall-back.
+extern void WebPRescalerImportRowExpand_C(struct WebPRescaler* const wrk,
+ const uint8_t* src);
+extern void WebPRescalerImportRowShrink_C(struct WebPRescaler* const wrk,
+ const uint8_t* src);
+extern void WebPRescalerExportRowExpand_C(struct WebPRescaler* const wrk);
+extern void WebPRescalerExportRowShrink_C(struct WebPRescaler* const wrk);
+
+// Main entry calls:
+extern void WebPRescalerImportRow(struct WebPRescaler* const wrk,
+ const uint8_t* src);
+// Export one row (starting at x_out position) from rescaler.
+extern void WebPRescalerExportRow(struct WebPRescaler* const wrk);
+
+// Must be called first before using the above.
+void WebPRescalerDspInit(void);
+
+//------------------------------------------------------------------------------
+// Utilities for processing transparent channel.
+
+// Apply alpha pre-multiply on an rgba, bgra or argb plane of size w * h.
+// alpha_first should be 0 for argb, 1 for rgba or bgra (where alpha is last).
+extern void (*WebPApplyAlphaMultiply)(
+ uint8_t* rgba, int alpha_first, int w, int h, int stride);
+
+// Same, buf specifically for RGBA4444 format
+extern void (*WebPApplyAlphaMultiply4444)(
+ uint8_t* rgba4444, int w, int h, int stride);
+
+// Dispatch the values from alpha[] plane to the ARGB destination 'dst'.
+// Returns true if alpha[] plane has non-trivial values different from 0xff.
+extern int (*WebPDispatchAlpha)(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint8_t* dst, int dst_stride);
+
+// Transfer packed 8b alpha[] values to green channel in dst[], zero'ing the
+// A/R/B values. 'dst_stride' is the stride for dst[] in uint32_t units.
+extern void (*WebPDispatchAlphaToGreen)(const uint8_t* alpha, int alpha_stride,
+ int width, int height,
+ uint32_t* dst, int dst_stride);
+
+// Extract the alpha values from 32b values in argb[] and pack them into alpha[]
+// (this is the opposite of WebPDispatchAlpha).
+// Returns true if there's only trivial 0xff alpha values.
+extern int (*WebPExtractAlpha)(const uint8_t* argb, int argb_stride,
+ int width, int height,
+ uint8_t* alpha, int alpha_stride);
+
+// Extract the green values from 32b values in argb[] and pack them into alpha[]
+// (this is the opposite of WebPDispatchAlphaToGreen).
+extern void (*WebPExtractGreen)(const uint32_t* argb, uint8_t* alpha, int size);
+
+// Pre-Multiply operation transforms x into x * A / 255 (where x=Y,R,G or B).
+// Un-Multiply operation transforms x into x * 255 / A.
+
+// Pre-Multiply or Un-Multiply (if 'inverse' is true) argb values in a row.
+extern void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse);
+
+// Same a WebPMultARGBRow(), but for several rows.
+void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows,
+ int inverse);
+
+// Same for a row of single values, with side alpha values.
+extern void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha,
+ int width, int inverse);
+
+// Same a WebPMultRow(), but for several 'num_rows' rows.
+void WebPMultRows(uint8_t* ptr, int stride,
+ const uint8_t* alpha, int alpha_stride,
+ int width, int num_rows, int inverse);
+
+// Plain-C versions, used as fallback by some implementations.
+void WebPMultRow_C(uint8_t* const ptr, const uint8_t* const alpha,
+ int width, int inverse);
+void WebPMultARGBRow_C(uint32_t* const ptr, int width, int inverse);
+
+#ifdef WORDS_BIGENDIAN
+// ARGB packing function: a/r/g/b input is rgba or bgra order.
+extern void (*WebPPackARGB)(const uint8_t* a, const uint8_t* r,
+ const uint8_t* g, const uint8_t* b, int len,
+ uint32_t* out);
+#endif
+
+// RGB packing function. 'step' can be 3 or 4. r/g/b input is rgb or bgr order.
+extern void (*WebPPackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
+ int len, int step, uint32_t* out);
+
+// This function returns true if src[i] contains a value different from 0xff.
+extern int (*WebPHasAlpha8b)(const uint8_t* src, int length);
+// This function returns true if src[4*i] contains a value different from 0xff.
+extern int (*WebPHasAlpha32b)(const uint8_t* src, int length);
+
+// To be called first before using the above.
+void WebPInitAlphaProcessing(void);
+
+//------------------------------------------------------------------------------
+// Filter functions
+
+typedef enum { // Filter types.
+ WEBP_FILTER_NONE = 0,
+ WEBP_FILTER_HORIZONTAL,
+ WEBP_FILTER_VERTICAL,
+ WEBP_FILTER_GRADIENT,
+ WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
+ WEBP_FILTER_BEST, // meta-types
+ WEBP_FILTER_FAST
+} WEBP_FILTER_TYPE;
+
+typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
+ int stride, uint8_t* out);
+// In-place un-filtering.
+// Warning! 'prev_line' pointer can be equal to 'cur_line' or 'preds'.
+typedef void (*WebPUnfilterFunc)(const uint8_t* prev_line, const uint8_t* preds,
+ uint8_t* cur_line, int width);
+
+// Filter the given data using the given predictor.
+// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
+// in raster order.
+// 'stride' is number of bytes per scan line (with possible padding).
+// 'out' should be pre-allocated.
+extern WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
+
+// In-place reconstruct the original data from the given filtered data.
+// The reconstruction will be done for 'num_rows' rows starting from 'row'
+// (assuming rows upto 'row - 1' are already reconstructed).
+extern WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
+
+// To be called first before using the above.
+void VP8FiltersInit(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_DSP_DSP_H_ */
diff --git a/src/third_party/libwebp/src/dsp/enc.c b/src/third_party/libwebp/src/dsp/enc.c
new file mode 100644
index 0000000..f96e19c
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc.c
@@ -0,0 +1,842 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical encoding functions.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h> // for abs()
+#endif
+
+#include "src/dsp/dsp.h"
+#include "src/enc/vp8i_enc.h"
+
+static WEBP_INLINE uint8_t clip_8b(int v) {
+ return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static WEBP_INLINE int clip_max(int v, int max) {
+ return (v > max) ? max : v;
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//------------------------------------------------------------------------------
+// Compute susceptibility based on DCT-coeff histograms:
+// the higher, the "easier" the macroblock is to compress.
+
+const int VP8DspScan[16 + 4 + 4] = {
+ // Luma
+ 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
+ 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
+ 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
+ 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS,
+
+ 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U
+ 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V
+};
+
+// general-purpose util function
+void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1],
+ VP8Histogram* const histo) {
+ int max_value = 0, last_non_zero = 1;
+ int k;
+ for (k = 0; k <= MAX_COEFF_THRESH; ++k) {
+ const int value = distribution[k];
+ if (value > 0) {
+ if (value > max_value) max_value = value;
+ last_non_zero = k;
+ }
+ }
+ histo->max_value = max_value;
+ histo->last_non_zero = last_non_zero;
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void CollectHistogram_C(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block,
+ VP8Histogram* const histo) {
+ int j;
+ int distribution[MAX_COEFF_THRESH + 1] = { 0 };
+ for (j = start_block; j < end_block; ++j) {
+ int k;
+ int16_t out[16];
+
+ VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
+
+ // Convert coefficients to bin.
+ for (k = 0; k < 16; ++k) {
+ const int v = abs(out[k]) >> 3;
+ const int clipped_value = clip_max(v, MAX_COEFF_THRESH);
+ ++distribution[clipped_value];
+ }
+ }
+ VP8SetHistogramData(distribution, histo);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//------------------------------------------------------------------------------
+// run-time tables (~4k)
+
+static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
+
+// We declare this variable 'volatile' to prevent instruction reordering
+// and make sure it's set to true _last_ (so as to be thread-safe)
+static volatile int tables_ok = 0;
+
+static WEBP_TSAN_IGNORE_FUNCTION void InitTables(void) {
+ if (!tables_ok) {
+ int i;
+ for (i = -255; i <= 255 + 255; ++i) {
+ clip1[255 + i] = clip_8b(i);
+ }
+ tables_ok = 1;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Transforms (Paragraph 14.4)
+
+#if !WEBP_NEON_OMIT_C_CODE
+
+#define STORE(x, y, v) \
+ dst[(x) + (y) * BPS] = clip_8b(ref[(x) + (y) * BPS] + ((v) >> 3))
+
+static const int kC1 = 20091 + (1 << 16);
+static const int kC2 = 35468;
+#define MUL(a, b) (((a) * (b)) >> 16)
+
+static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst) {
+ int C[4 * 4], *tmp;
+ int i;
+ tmp = C;
+ for (i = 0; i < 4; ++i) { // vertical pass
+ const int a = in[0] + in[8];
+ const int b = in[0] - in[8];
+ const int c = MUL(in[4], kC2) - MUL(in[12], kC1);
+ const int d = MUL(in[4], kC1) + MUL(in[12], kC2);
+ tmp[0] = a + d;
+ tmp[1] = b + c;
+ tmp[2] = b - c;
+ tmp[3] = a - d;
+ tmp += 4;
+ in++;
+ }
+
+ tmp = C;
+ for (i = 0; i < 4; ++i) { // horizontal pass
+ const int dc = tmp[0] + 4;
+ const int a = dc + tmp[8];
+ const int b = dc - tmp[8];
+ const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
+ const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
+ STORE(0, i, a + d);
+ STORE(1, i, b + c);
+ STORE(2, i, b - c);
+ STORE(3, i, a - d);
+ tmp++;
+ }
+}
+
+static void ITransform_C(const uint8_t* ref, const int16_t* in, uint8_t* dst,
+ int do_two) {
+ ITransformOne(ref, in, dst);
+ if (do_two) {
+ ITransformOne(ref + 4, in + 16, dst + 4);
+ }
+}
+
+static void FTransform_C(const uint8_t* src, const uint8_t* ref, int16_t* out) {
+ int i;
+ int tmp[16];
+ for (i = 0; i < 4; ++i, src += BPS, ref += BPS) {
+ const int d0 = src[0] - ref[0]; // 9bit dynamic range ([-255,255])
+ const int d1 = src[1] - ref[1];
+ const int d2 = src[2] - ref[2];
+ const int d3 = src[3] - ref[3];
+ const int a0 = (d0 + d3); // 10b [-510,510]
+ const int a1 = (d1 + d2);
+ const int a2 = (d1 - d2);
+ const int a3 = (d0 - d3);
+ tmp[0 + i * 4] = (a0 + a1) * 8; // 14b [-8160,8160]
+ tmp[1 + i * 4] = (a2 * 2217 + a3 * 5352 + 1812) >> 9; // [-7536,7542]
+ tmp[2 + i * 4] = (a0 - a1) * 8;
+ tmp[3 + i * 4] = (a3 * 2217 - a2 * 5352 + 937) >> 9;
+ }
+ for (i = 0; i < 4; ++i) {
+ const int a0 = (tmp[0 + i] + tmp[12 + i]); // 15b
+ const int a1 = (tmp[4 + i] + tmp[ 8 + i]);
+ const int a2 = (tmp[4 + i] - tmp[ 8 + i]);
+ const int a3 = (tmp[0 + i] - tmp[12 + i]);
+ out[0 + i] = (a0 + a1 + 7) >> 4; // 12b
+ out[4 + i] = ((a2 * 2217 + a3 * 5352 + 12000) >> 16) + (a3 != 0);
+ out[8 + i] = (a0 - a1 + 7) >> 4;
+ out[12+ i] = ((a3 * 2217 - a2 * 5352 + 51000) >> 16);
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+static void FTransform2_C(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ VP8FTransform(src, ref, out);
+ VP8FTransform(src + 4, ref + 4, out + 16);
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void FTransformWHT_C(const int16_t* in, int16_t* out) {
+ // input is 12b signed
+ int32_t tmp[16];
+ int i;
+ for (i = 0; i < 4; ++i, in += 64) {
+ const int a0 = (in[0 * 16] + in[2 * 16]); // 13b
+ const int a1 = (in[1 * 16] + in[3 * 16]);
+ const int a2 = (in[1 * 16] - in[3 * 16]);
+ const int a3 = (in[0 * 16] - in[2 * 16]);
+ tmp[0 + i * 4] = a0 + a1; // 14b
+ tmp[1 + i * 4] = a3 + a2;
+ tmp[2 + i * 4] = a3 - a2;
+ tmp[3 + i * 4] = a0 - a1;
+ }
+ for (i = 0; i < 4; ++i) {
+ const int a0 = (tmp[0 + i] + tmp[8 + i]); // 15b
+ const int a1 = (tmp[4 + i] + tmp[12+ i]);
+ const int a2 = (tmp[4 + i] - tmp[12+ i]);
+ const int a3 = (tmp[0 + i] - tmp[8 + i]);
+ const int b0 = a0 + a1; // 16b
+ const int b1 = a3 + a2;
+ const int b2 = a3 - a2;
+ const int b3 = a0 - a1;
+ out[ 0 + i] = b0 >> 1; // 15b
+ out[ 4 + i] = b1 >> 1;
+ out[ 8 + i] = b2 >> 1;
+ out[12 + i] = b3 >> 1;
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+#undef MUL
+#undef STORE
+
+//------------------------------------------------------------------------------
+// Intra predictions
+
+static WEBP_INLINE void Fill(uint8_t* dst, int value, int size) {
+ int j;
+ for (j = 0; j < size; ++j) {
+ memset(dst + j * BPS, value, size);
+ }
+}
+
+static WEBP_INLINE void VerticalPred(uint8_t* dst,
+ const uint8_t* top, int size) {
+ int j;
+ if (top != NULL) {
+ for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size);
+ } else {
+ Fill(dst, 127, size);
+ }
+}
+
+static WEBP_INLINE void HorizontalPred(uint8_t* dst,
+ const uint8_t* left, int size) {
+ if (left != NULL) {
+ int j;
+ for (j = 0; j < size; ++j) {
+ memset(dst + j * BPS, left[j], size);
+ }
+ } else {
+ Fill(dst, 129, size);
+ }
+}
+
+static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top, int size) {
+ int y;
+ if (left != NULL) {
+ if (top != NULL) {
+ const uint8_t* const clip = clip1 + 255 - left[-1];
+ for (y = 0; y < size; ++y) {
+ const uint8_t* const clip_table = clip + left[y];
+ int x;
+ for (x = 0; x < size; ++x) {
+ dst[x] = clip_table[top[x]];
+ }
+ dst += BPS;
+ }
+ } else {
+ HorizontalPred(dst, left, size);
+ }
+ } else {
+ // true motion without left samples (hence: with default 129 value)
+ // is equivalent to VE prediction where you just copy the top samples.
+ // Note that if top samples are not available, the default value is
+ // then 129, and not 127 as in the VerticalPred case.
+ if (top != NULL) {
+ VerticalPred(dst, top, size);
+ } else {
+ Fill(dst, 129, size);
+ }
+ }
+}
+
+static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top,
+ int size, int round, int shift) {
+ int DC = 0;
+ int j;
+ if (top != NULL) {
+ for (j = 0; j < size; ++j) DC += top[j];
+ if (left != NULL) { // top and left present
+ for (j = 0; j < size; ++j) DC += left[j];
+ } else { // top, but no left
+ DC += DC;
+ }
+ DC = (DC + round) >> shift;
+ } else if (left != NULL) { // left but no top
+ for (j = 0; j < size; ++j) DC += left[j];
+ DC += DC;
+ DC = (DC + round) >> shift;
+ } else { // no top, no left, nothing.
+ DC = 0x80;
+ }
+ Fill(dst, DC, size);
+}
+
+//------------------------------------------------------------------------------
+// Chroma 8x8 prediction (paragraph 12.2)
+
+static void IntraChromaPreds_C(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ // U block
+ DCMode(C8DC8 + dst, left, top, 8, 8, 4);
+ VerticalPred(C8VE8 + dst, top, 8);
+ HorizontalPred(C8HE8 + dst, left, 8);
+ TrueMotion(C8TM8 + dst, left, top, 8);
+ // V block
+ dst += 8;
+ if (top != NULL) top += 8;
+ if (left != NULL) left += 16;
+ DCMode(C8DC8 + dst, left, top, 8, 8, 4);
+ VerticalPred(C8VE8 + dst, top, 8);
+ HorizontalPred(C8HE8 + dst, left, 8);
+ TrueMotion(C8TM8 + dst, left, top, 8);
+}
+
+//------------------------------------------------------------------------------
+// luma 16x16 prediction (paragraph 12.3)
+
+static void Intra16Preds_C(uint8_t* dst,
+ const uint8_t* left, const uint8_t* top) {
+ DCMode(I16DC16 + dst, left, top, 16, 16, 5);
+ VerticalPred(I16VE16 + dst, top, 16);
+ HorizontalPred(I16HE16 + dst, left, 16);
+ TrueMotion(I16TM16 + dst, left, top, 16);
+}
+
+//------------------------------------------------------------------------------
+// luma 4x4 prediction
+
+#define DST(x, y) dst[(x) + (y) * BPS]
+#define AVG3(a, b, c) ((uint8_t)(((a) + 2 * (b) + (c) + 2) >> 2))
+#define AVG2(a, b) (((a) + (b) + 1) >> 1)
+
+static void VE4(uint8_t* dst, const uint8_t* top) { // vertical
+ const uint8_t vals[4] = {
+ AVG3(top[-1], top[0], top[1]),
+ AVG3(top[ 0], top[1], top[2]),
+ AVG3(top[ 1], top[2], top[3]),
+ AVG3(top[ 2], top[3], top[4])
+ };
+ int i;
+ for (i = 0; i < 4; ++i) {
+ memcpy(dst + i * BPS, vals, 4);
+ }
+}
+
+static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(X, I, J));
+ WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(I, J, K));
+ WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(J, K, L));
+ WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L));
+}
+
+static void DC4(uint8_t* dst, const uint8_t* top) {
+ uint32_t dc = 4;
+ int i;
+ for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i];
+ Fill(dst, dc >> 3, 4);
+}
+
+static void RD4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ DST(0, 3) = AVG3(J, K, L);
+ DST(0, 2) = DST(1, 3) = AVG3(I, J, K);
+ DST(0, 1) = DST(1, 2) = DST(2, 3) = AVG3(X, I, J);
+ DST(0, 0) = DST(1, 1) = DST(2, 2) = DST(3, 3) = AVG3(A, X, I);
+ DST(1, 0) = DST(2, 1) = DST(3, 2) = AVG3(B, A, X);
+ DST(2, 0) = DST(3, 1) = AVG3(C, B, A);
+ DST(3, 0) = AVG3(D, C, B);
+}
+
+static void LD4(uint8_t* dst, const uint8_t* top) {
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ const int E = top[4];
+ const int F = top[5];
+ const int G = top[6];
+ const int H = top[7];
+ DST(0, 0) = AVG3(A, B, C);
+ DST(1, 0) = DST(0, 1) = AVG3(B, C, D);
+ DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E);
+ DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F);
+ DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G);
+ DST(3, 2) = DST(2, 3) = AVG3(F, G, H);
+ DST(3, 3) = AVG3(G, H, H);
+}
+
+static void VR4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ DST(0, 0) = DST(1, 2) = AVG2(X, A);
+ DST(1, 0) = DST(2, 2) = AVG2(A, B);
+ DST(2, 0) = DST(3, 2) = AVG2(B, C);
+ DST(3, 0) = AVG2(C, D);
+
+ DST(0, 3) = AVG3(K, J, I);
+ DST(0, 2) = AVG3(J, I, X);
+ DST(0, 1) = DST(1, 3) = AVG3(I, X, A);
+ DST(1, 1) = DST(2, 3) = AVG3(X, A, B);
+ DST(2, 1) = DST(3, 3) = AVG3(A, B, C);
+ DST(3, 1) = AVG3(B, C, D);
+}
+
+static void VL4(uint8_t* dst, const uint8_t* top) {
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ const int E = top[4];
+ const int F = top[5];
+ const int G = top[6];
+ const int H = top[7];
+ DST(0, 0) = AVG2(A, B);
+ DST(1, 0) = DST(0, 2) = AVG2(B, C);
+ DST(2, 0) = DST(1, 2) = AVG2(C, D);
+ DST(3, 0) = DST(2, 2) = AVG2(D, E);
+
+ DST(0, 1) = AVG3(A, B, C);
+ DST(1, 1) = DST(0, 3) = AVG3(B, C, D);
+ DST(2, 1) = DST(1, 3) = AVG3(C, D, E);
+ DST(3, 1) = DST(2, 3) = AVG3(D, E, F);
+ DST(3, 2) = AVG3(E, F, G);
+ DST(3, 3) = AVG3(F, G, H);
+}
+
+static void HU4(uint8_t* dst, const uint8_t* top) {
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ DST(0, 0) = AVG2(I, J);
+ DST(2, 0) = DST(0, 1) = AVG2(J, K);
+ DST(2, 1) = DST(0, 2) = AVG2(K, L);
+ DST(1, 0) = AVG3(I, J, K);
+ DST(3, 0) = DST(1, 1) = AVG3(J, K, L);
+ DST(3, 1) = DST(1, 2) = AVG3(K, L, L);
+ DST(3, 2) = DST(2, 2) =
+ DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
+}
+
+static void HD4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+
+ DST(0, 0) = DST(2, 1) = AVG2(I, X);
+ DST(0, 1) = DST(2, 2) = AVG2(J, I);
+ DST(0, 2) = DST(2, 3) = AVG2(K, J);
+ DST(0, 3) = AVG2(L, K);
+
+ DST(3, 0) = AVG3(A, B, C);
+ DST(2, 0) = AVG3(X, A, B);
+ DST(1, 0) = DST(3, 1) = AVG3(I, X, A);
+ DST(1, 1) = DST(3, 2) = AVG3(J, I, X);
+ DST(1, 2) = DST(3, 3) = AVG3(K, J, I);
+ DST(1, 3) = AVG3(L, K, J);
+}
+
+static void TM4(uint8_t* dst, const uint8_t* top) {
+ int x, y;
+ const uint8_t* const clip = clip1 + 255 - top[-1];
+ for (y = 0; y < 4; ++y) {
+ const uint8_t* const clip_table = clip + top[-2 - y];
+ for (x = 0; x < 4; ++x) {
+ dst[x] = clip_table[top[x]];
+ }
+ dst += BPS;
+ }
+}
+
+#undef DST
+#undef AVG3
+#undef AVG2
+
+// Left samples are top[-5 .. -2], top_left is top[-1], top are
+// located at top[0..3], and top right is top[4..7]
+static void Intra4Preds_C(uint8_t* dst, const uint8_t* top) {
+ DC4(I4DC4 + dst, top);
+ TM4(I4TM4 + dst, top);
+ VE4(I4VE4 + dst, top);
+ HE4(I4HE4 + dst, top);
+ RD4(I4RD4 + dst, top);
+ VR4(I4VR4 + dst, top);
+ LD4(I4LD4 + dst, top);
+ VL4(I4VL4 + dst, top);
+ HD4(I4HD4 + dst, top);
+ HU4(I4HU4 + dst, top);
+}
+
+//------------------------------------------------------------------------------
+// Metric
+
+#if !WEBP_NEON_OMIT_C_CODE
+static WEBP_INLINE int GetSSE(const uint8_t* a, const uint8_t* b,
+ int w, int h) {
+ int count = 0;
+ int y, x;
+ for (y = 0; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ const int diff = (int)a[x] - b[x];
+ count += diff * diff;
+ }
+ a += BPS;
+ b += BPS;
+ }
+ return count;
+}
+
+static int SSE16x16_C(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 16, 16);
+}
+static int SSE16x8_C(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 16, 8);
+}
+static int SSE8x8_C(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 8, 8);
+}
+static int SSE4x4_C(const uint8_t* a, const uint8_t* b) {
+ return GetSSE(a, b, 4, 4);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+static void Mean16x4_C(const uint8_t* ref, uint32_t dc[4]) {
+ int k, x, y;
+ for (k = 0; k < 4; ++k) {
+ uint32_t avg = 0;
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ avg += ref[x + y * BPS];
+ }
+ }
+ dc[k] = avg;
+ ref += 4; // go to next 4x4 block.
+ }
+}
+
+//------------------------------------------------------------------------------
+// Texture distortion
+//
+// We try to match the spectral content (weighted) between source and
+// reconstructed samples.
+
+#if !WEBP_NEON_OMIT_C_CODE
+// Hadamard transform
+// Returns the weighted sum of the absolute value of transformed coefficients.
+// w[] contains a row-major 4 by 4 symmetric matrix.
+static int TTransform(const uint8_t* in, const uint16_t* w) {
+ int sum = 0;
+ int tmp[16];
+ int i;
+ // horizontal pass
+ for (i = 0; i < 4; ++i, in += BPS) {
+ const int a0 = in[0] + in[2];
+ const int a1 = in[1] + in[3];
+ const int a2 = in[1] - in[3];
+ const int a3 = in[0] - in[2];
+ tmp[0 + i * 4] = a0 + a1;
+ tmp[1 + i * 4] = a3 + a2;
+ tmp[2 + i * 4] = a3 - a2;
+ tmp[3 + i * 4] = a0 - a1;
+ }
+ // vertical pass
+ for (i = 0; i < 4; ++i, ++w) {
+ const int a0 = tmp[0 + i] + tmp[8 + i];
+ const int a1 = tmp[4 + i] + tmp[12+ i];
+ const int a2 = tmp[4 + i] - tmp[12+ i];
+ const int a3 = tmp[0 + i] - tmp[8 + i];
+ const int b0 = a0 + a1;
+ const int b1 = a3 + a2;
+ const int b2 = a3 - a2;
+ const int b3 = a0 - a1;
+
+ sum += w[ 0] * abs(b0);
+ sum += w[ 4] * abs(b1);
+ sum += w[ 8] * abs(b2);
+ sum += w[12] * abs(b3);
+ }
+ return sum;
+}
+
+static int Disto4x4_C(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ const int sum1 = TTransform(a, w);
+ const int sum2 = TTransform(b, w);
+ return abs(sum2 - sum1) >> 5;
+}
+
+static int Disto16x16_C(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4_C(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//------------------------------------------------------------------------------
+// Quantization
+//
+
+static const uint8_t kZigzag[16] = {
+ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
+};
+
+// Simple quantization
+static int QuantizeBlock_C(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ int last = -1;
+ int n;
+ for (n = 0; n < 16; ++n) {
+ const int j = kZigzag[n];
+ const int sign = (in[j] < 0);
+ const uint32_t coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
+ if (coeff > mtx->zthresh_[j]) {
+ const uint32_t Q = mtx->q_[j];
+ const uint32_t iQ = mtx->iq_[j];
+ const uint32_t B = mtx->bias_[j];
+ int level = QUANTDIV(coeff, iQ, B);
+ if (level > MAX_LEVEL) level = MAX_LEVEL;
+ if (sign) level = -level;
+ in[j] = level * (int)Q;
+ out[n] = level;
+ if (level) last = n;
+ } else {
+ out[n] = 0;
+ in[j] = 0;
+ }
+ }
+ return (last >= 0);
+}
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+static int Quantize2Blocks_C(int16_t in[32], int16_t out[32],
+ const VP8Matrix* const mtx) {
+ int nz;
+ nz = VP8EncQuantizeBlock(in + 0 * 16, out + 0 * 16, mtx) << 0;
+ nz |= VP8EncQuantizeBlock(in + 1 * 16, out + 1 * 16, mtx) << 1;
+ return nz;
+}
+#endif // !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+
+//------------------------------------------------------------------------------
+// Block copy
+
+static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int w, int h) {
+ int y;
+ for (y = 0; y < h; ++y) {
+ memcpy(dst, src, w);
+ src += BPS;
+ dst += BPS;
+ }
+}
+
+static void Copy4x4_C(const uint8_t* src, uint8_t* dst) {
+ Copy(src, dst, 4, 4);
+}
+
+static void Copy16x8_C(const uint8_t* src, uint8_t* dst) {
+ Copy(src, dst, 16, 8);
+}
+
+//------------------------------------------------------------------------------
+// Initialization
+
+// Speed-critical function pointers. We have to initialize them to the default
+// implementations within VP8EncDspInit().
+VP8CHisto VP8CollectHistogram;
+VP8Idct VP8ITransform;
+VP8Fdct VP8FTransform;
+VP8Fdct VP8FTransform2;
+VP8WHT VP8FTransformWHT;
+VP8Intra4Preds VP8EncPredLuma4;
+VP8IntraPreds VP8EncPredLuma16;
+VP8IntraPreds VP8EncPredChroma8;
+VP8Metric VP8SSE16x16;
+VP8Metric VP8SSE8x8;
+VP8Metric VP8SSE16x8;
+VP8Metric VP8SSE4x4;
+VP8WMetric VP8TDisto4x4;
+VP8WMetric VP8TDisto16x16;
+VP8MeanMetric VP8Mean16x4;
+VP8QuantizeBlock VP8EncQuantizeBlock;
+VP8Quantize2Blocks VP8EncQuantize2Blocks;
+VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT;
+VP8BlockCopy VP8Copy4x4;
+VP8BlockCopy VP8Copy16x8;
+
+extern void VP8EncDspInitSSE2(void);
+extern void VP8EncDspInitSSE41(void);
+extern void VP8EncDspInitAVX2(void);
+extern void VP8EncDspInitNEON(void);
+extern void VP8EncDspInitMIPS32(void);
+extern void VP8EncDspInitMIPSdspR2(void);
+extern void VP8EncDspInitMSA(void);
+
+WEBP_DSP_INIT_FUNC(VP8EncDspInit) {
+ VP8DspInit(); // common inverse transforms
+ InitTables();
+
+ // default C implementations
+#if !WEBP_NEON_OMIT_C_CODE
+ VP8ITransform = ITransform_C;
+ VP8FTransform = FTransform_C;
+ VP8FTransformWHT = FTransformWHT_C;
+ VP8TDisto4x4 = Disto4x4_C;
+ VP8TDisto16x16 = Disto16x16_C;
+ VP8CollectHistogram = CollectHistogram_C;
+ VP8SSE16x16 = SSE16x16_C;
+ VP8SSE16x8 = SSE16x8_C;
+ VP8SSE8x8 = SSE8x8_C;
+ VP8SSE4x4 = SSE4x4_C;
+#endif
+
+#if !WEBP_NEON_OMIT_C_CODE || WEBP_NEON_WORK_AROUND_GCC
+ VP8EncQuantizeBlock = QuantizeBlock_C;
+ VP8EncQuantize2Blocks = Quantize2Blocks_C;
+#endif
+
+ VP8FTransform2 = FTransform2_C;
+ VP8EncPredLuma4 = Intra4Preds_C;
+ VP8EncPredLuma16 = Intra16Preds_C;
+ VP8EncPredChroma8 = IntraChromaPreds_C;
+ VP8Mean16x4 = Mean16x4_C;
+ VP8EncQuantizeBlockWHT = QuantizeBlock_C;
+ VP8Copy4x4 = Copy4x4_C;
+ VP8Copy16x8 = Copy16x8_C;
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ VP8EncDspInitSSE2();
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ VP8EncDspInitSSE41();
+ }
+#endif
+ }
+#endif
+#if defined(WEBP_USE_AVX2)
+ if (VP8GetCPUInfo(kAVX2)) {
+ VP8EncDspInitAVX2();
+ }
+#endif
+#if defined(WEBP_USE_MIPS32)
+ if (VP8GetCPUInfo(kMIPS32)) {
+ VP8EncDspInitMIPS32();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ VP8EncDspInitMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_MSA)
+ if (VP8GetCPUInfo(kMSA)) {
+ VP8EncDspInitMSA();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ VP8EncDspInitNEON();
+ }
+#endif
+
+ assert(VP8ITransform != NULL);
+ assert(VP8FTransform != NULL);
+ assert(VP8FTransformWHT != NULL);
+ assert(VP8TDisto4x4 != NULL);
+ assert(VP8TDisto16x16 != NULL);
+ assert(VP8CollectHistogram != NULL);
+ assert(VP8SSE16x16 != NULL);
+ assert(VP8SSE16x8 != NULL);
+ assert(VP8SSE8x8 != NULL);
+ assert(VP8SSE4x4 != NULL);
+ assert(VP8EncQuantizeBlock != NULL);
+ assert(VP8EncQuantize2Blocks != NULL);
+ assert(VP8FTransform2 != NULL);
+ assert(VP8EncPredLuma4 != NULL);
+ assert(VP8EncPredLuma16 != NULL);
+ assert(VP8EncPredChroma8 != NULL);
+ assert(VP8Mean16x4 != NULL);
+ assert(VP8EncQuantizeBlockWHT != NULL);
+ assert(VP8Copy4x4 != NULL);
+ assert(VP8Copy16x8 != NULL);
+}
diff --git a/src/third_party/libwebp/src/dsp/enc_avx2.c b/src/third_party/libwebp/src/dsp/enc_avx2.c
new file mode 100644
index 0000000..8bc5798
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc_avx2.c
@@ -0,0 +1,21 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// AVX2 version of speed-critical encoding functions.
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_AVX2)
+
+#endif // WEBP_USE_AVX2
+
+//------------------------------------------------------------------------------
+// Entry point
+
+WEBP_DSP_INIT_STUB(VP8EncDspInitAVX2)
diff --git a/src/third_party/libwebp/src/dsp/enc_mips32.c b/src/third_party/libwebp/src/dsp/enc_mips32.c
new file mode 100644
index 0000000..618f0fc
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc_mips32.c
@@ -0,0 +1,677 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of speed-critical encoding functions.
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+// Jovan Zelincevic (jovan.zelincevic@imgtec.com)
+// Slobodan Prijic (slobodan.prijic@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS32)
+
+#include "src/dsp/mips_macro.h"
+#include "src/enc/vp8i_enc.h"
+#include "src/enc/cost_enc.h"
+
+static const int kC1 = 20091 + (1 << 16);
+static const int kC2 = 35468;
+
+// macro for one vertical pass in ITransformOne
+// MUL macro inlined
+// temp0..temp15 holds tmp[0]..tmp[15]
+// A..D - offsets in bytes to load from in buffer
+// TEMP0..TEMP3 - registers for corresponding tmp elements
+// TEMP4..TEMP5 - temporary registers
+#define VERTICAL_PASS(A, B, C, D, TEMP4, TEMP0, TEMP1, TEMP2, TEMP3) \
+ "lh %[temp16], " #A "(%[temp20]) \n\t" \
+ "lh %[temp18], " #B "(%[temp20]) \n\t" \
+ "lh %[temp17], " #C "(%[temp20]) \n\t" \
+ "lh %[temp19], " #D "(%[temp20]) \n\t" \
+ "addu %[" #TEMP4 "], %[temp16], %[temp18] \n\t" \
+ "subu %[temp16], %[temp16], %[temp18] \n\t" \
+ "mul %[" #TEMP0 "], %[temp17], %[kC2] \n\t" \
+ "mul %[temp18], %[temp19], %[kC1] \n\t" \
+ "mul %[temp17], %[temp17], %[kC1] \n\t" \
+ "mul %[temp19], %[temp19], %[kC2] \n\t" \
+ "sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\n" \
+ "sra %[temp18], %[temp18], 16 \n\n" \
+ "sra %[temp17], %[temp17], 16 \n\n" \
+ "sra %[temp19], %[temp19], 16 \n\n" \
+ "subu %[" #TEMP2 "], %[" #TEMP0 "], %[temp18] \n\t" \
+ "addu %[" #TEMP3 "], %[temp17], %[temp19] \n\t" \
+ "addu %[" #TEMP0 "], %[" #TEMP4 "], %[" #TEMP3 "] \n\t" \
+ "addu %[" #TEMP1 "], %[temp16], %[" #TEMP2 "] \n\t" \
+ "subu %[" #TEMP2 "], %[temp16], %[" #TEMP2 "] \n\t" \
+ "subu %[" #TEMP3 "], %[" #TEMP4 "], %[" #TEMP3 "] \n\t"
+
+// macro for one horizontal pass in ITransformOne
+// MUL and STORE macros inlined
+// a = clip_8b(a) is replaced with: a = max(a, 0); a = min(a, 255)
+// temp0..temp15 holds tmp[0]..tmp[15]
+// A - offset in bytes to load from ref and store to dst buffer
+// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements
+#define HORIZONTAL_PASS(A, TEMP0, TEMP4, TEMP8, TEMP12) \
+ "addiu %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \
+ "addu %[temp16], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
+ "subu %[temp17], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
+ "mul %[" #TEMP0 "], %[" #TEMP4 "], %[kC2] \n\t" \
+ "mul %[" #TEMP8 "], %[" #TEMP12 "], %[kC1] \n\t" \
+ "mul %[" #TEMP4 "], %[" #TEMP4 "], %[kC1] \n\t" \
+ "mul %[" #TEMP12 "], %[" #TEMP12 "], %[kC2] \n\t" \
+ "sra %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\t" \
+ "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \
+ "sra %[" #TEMP4 "], %[" #TEMP4 "], 16 \n\t" \
+ "sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \
+ "subu %[temp18], %[" #TEMP0 "], %[" #TEMP8 "] \n\t" \
+ "addu %[temp19], %[" #TEMP4 "], %[" #TEMP12 "] \n\t" \
+ "addu %[" #TEMP0 "], %[temp16], %[temp19] \n\t" \
+ "addu %[" #TEMP4 "], %[temp17], %[temp18] \n\t" \
+ "subu %[" #TEMP8 "], %[temp17], %[temp18] \n\t" \
+ "subu %[" #TEMP12 "], %[temp16], %[temp19] \n\t" \
+ "lw %[temp20], 0(%[args]) \n\t" \
+ "sra %[" #TEMP0 "], %[" #TEMP0 "], 3 \n\t" \
+ "sra %[" #TEMP4 "], %[" #TEMP4 "], 3 \n\t" \
+ "sra %[" #TEMP8 "], %[" #TEMP8 "], 3 \n\t" \
+ "sra %[" #TEMP12 "], %[" #TEMP12 "], 3 \n\t" \
+ "lbu %[temp16], 0+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \
+ "lbu %[temp17], 1+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \
+ "lbu %[temp18], 2+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \
+ "lbu %[temp19], 3+" XSTR(BPS) "*" #A "(%[temp20]) \n\t" \
+ "addu %[" #TEMP0 "], %[temp16], %[" #TEMP0 "] \n\t" \
+ "addu %[" #TEMP4 "], %[temp17], %[" #TEMP4 "] \n\t" \
+ "addu %[" #TEMP8 "], %[temp18], %[" #TEMP8 "] \n\t" \
+ "addu %[" #TEMP12 "], %[temp19], %[" #TEMP12 "] \n\t" \
+ "slt %[temp16], %[" #TEMP0 "], $zero \n\t" \
+ "slt %[temp17], %[" #TEMP4 "], $zero \n\t" \
+ "slt %[temp18], %[" #TEMP8 "], $zero \n\t" \
+ "slt %[temp19], %[" #TEMP12 "], $zero \n\t" \
+ "movn %[" #TEMP0 "], $zero, %[temp16] \n\t" \
+ "movn %[" #TEMP4 "], $zero, %[temp17] \n\t" \
+ "movn %[" #TEMP8 "], $zero, %[temp18] \n\t" \
+ "movn %[" #TEMP12 "], $zero, %[temp19] \n\t" \
+ "addiu %[temp20], $zero, 255 \n\t" \
+ "slt %[temp16], %[" #TEMP0 "], %[temp20] \n\t" \
+ "slt %[temp17], %[" #TEMP4 "], %[temp20] \n\t" \
+ "slt %[temp18], %[" #TEMP8 "], %[temp20] \n\t" \
+ "slt %[temp19], %[" #TEMP12 "], %[temp20] \n\t" \
+ "movz %[" #TEMP0 "], %[temp20], %[temp16] \n\t" \
+ "movz %[" #TEMP4 "], %[temp20], %[temp17] \n\t" \
+ "lw %[temp16], 8(%[args]) \n\t" \
+ "movz %[" #TEMP8 "], %[temp20], %[temp18] \n\t" \
+ "movz %[" #TEMP12 "], %[temp20], %[temp19] \n\t" \
+ "sb %[" #TEMP0 "], 0+" XSTR(BPS) "*" #A "(%[temp16]) \n\t" \
+ "sb %[" #TEMP4 "], 1+" XSTR(BPS) "*" #A "(%[temp16]) \n\t" \
+ "sb %[" #TEMP8 "], 2+" XSTR(BPS) "*" #A "(%[temp16]) \n\t" \
+ "sb %[" #TEMP12 "], 3+" XSTR(BPS) "*" #A "(%[temp16]) \n\t"
+
+// Does one or two inverse transforms.
+static WEBP_INLINE void ITransformOne_MIPS32(const uint8_t* ref,
+ const int16_t* in,
+ uint8_t* dst) {
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6;
+ int temp7, temp8, temp9, temp10, temp11, temp12, temp13;
+ int temp14, temp15, temp16, temp17, temp18, temp19, temp20;
+ const int* args[3] = {(const int*)ref, (const int*)in, (const int*)dst};
+
+ __asm__ volatile(
+ "lw %[temp20], 4(%[args]) \n\t"
+ VERTICAL_PASS(0, 16, 8, 24, temp4, temp0, temp1, temp2, temp3)
+ VERTICAL_PASS(2, 18, 10, 26, temp8, temp4, temp5, temp6, temp7)
+ VERTICAL_PASS(4, 20, 12, 28, temp12, temp8, temp9, temp10, temp11)
+ VERTICAL_PASS(6, 22, 14, 30, temp20, temp12, temp13, temp14, temp15)
+
+ HORIZONTAL_PASS(0, temp0, temp4, temp8, temp12)
+ HORIZONTAL_PASS(1, temp1, temp5, temp9, temp13)
+ HORIZONTAL_PASS(2, temp2, temp6, temp10, temp14)
+ HORIZONTAL_PASS(3, temp3, temp7, temp11, temp15)
+
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11),
+ [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14),
+ [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17),
+ [temp18]"=&r"(temp18), [temp19]"=&r"(temp19), [temp20]"=&r"(temp20)
+ : [args]"r"(args), [kC1]"r"(kC1), [kC2]"r"(kC2)
+ : "memory", "hi", "lo"
+ );
+}
+
+static void ITransform_MIPS32(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst, int do_two) {
+ ITransformOne_MIPS32(ref, in, dst);
+ if (do_two) {
+ ITransformOne_MIPS32(ref + 4, in + 16, dst + 4);
+ }
+}
+
+#undef VERTICAL_PASS
+#undef HORIZONTAL_PASS
+
+// macro for one pass through for loop in QuantizeBlock
+// QUANTDIV macro inlined
+// J - offset in bytes (kZigzag[n] * 2)
+// K - offset in bytes (kZigzag[n] * 4)
+// N - offset in bytes (n * 2)
+#define QUANTIZE_ONE(J, K, N) \
+ "lh %[temp0], " #J "(%[ppin]) \n\t" \
+ "lhu %[temp1], " #J "(%[ppsharpen]) \n\t" \
+ "lw %[temp2], " #K "(%[ppzthresh]) \n\t" \
+ "sra %[sign], %[temp0], 15 \n\t" \
+ "xor %[coeff], %[temp0], %[sign] \n\t" \
+ "subu %[coeff], %[coeff], %[sign] \n\t" \
+ "addu %[coeff], %[coeff], %[temp1] \n\t" \
+ "slt %[temp4], %[temp2], %[coeff] \n\t" \
+ "addiu %[temp5], $zero, 0 \n\t" \
+ "addiu %[level], $zero, 0 \n\t" \
+ "beqz %[temp4], 2f \n\t" \
+ "lhu %[temp1], " #J "(%[ppiq]) \n\t" \
+ "lw %[temp2], " #K "(%[ppbias]) \n\t" \
+ "lhu %[temp3], " #J "(%[ppq]) \n\t" \
+ "mul %[level], %[coeff], %[temp1] \n\t" \
+ "addu %[level], %[level], %[temp2] \n\t" \
+ "sra %[level], %[level], 17 \n\t" \
+ "slt %[temp4], %[max_level], %[level] \n\t" \
+ "movn %[level], %[max_level], %[temp4] \n\t" \
+ "xor %[level], %[level], %[sign] \n\t" \
+ "subu %[level], %[level], %[sign] \n\t" \
+ "mul %[temp5], %[level], %[temp3] \n\t" \
+"2: \n\t" \
+ "sh %[temp5], " #J "(%[ppin]) \n\t" \
+ "sh %[level], " #N "(%[pout]) \n\t"
+
+static int QuantizeBlock_MIPS32(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ int sign, coeff, level, i;
+ int max_level = MAX_LEVEL;
+
+ int16_t* ppin = &in[0];
+ int16_t* pout = &out[0];
+ const uint16_t* ppsharpen = &mtx->sharpen_[0];
+ const uint32_t* ppzthresh = &mtx->zthresh_[0];
+ const uint16_t* ppq = &mtx->q_[0];
+ const uint16_t* ppiq = &mtx->iq_[0];
+ const uint32_t* ppbias = &mtx->bias_[0];
+
+ __asm__ volatile(
+ QUANTIZE_ONE( 0, 0, 0)
+ QUANTIZE_ONE( 2, 4, 2)
+ QUANTIZE_ONE( 8, 16, 4)
+ QUANTIZE_ONE(16, 32, 6)
+ QUANTIZE_ONE(10, 20, 8)
+ QUANTIZE_ONE( 4, 8, 10)
+ QUANTIZE_ONE( 6, 12, 12)
+ QUANTIZE_ONE(12, 24, 14)
+ QUANTIZE_ONE(18, 36, 16)
+ QUANTIZE_ONE(24, 48, 18)
+ QUANTIZE_ONE(26, 52, 20)
+ QUANTIZE_ONE(20, 40, 22)
+ QUANTIZE_ONE(14, 28, 24)
+ QUANTIZE_ONE(22, 44, 26)
+ QUANTIZE_ONE(28, 56, 28)
+ QUANTIZE_ONE(30, 60, 30)
+
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [sign]"=&r"(sign), [coeff]"=&r"(coeff),
+ [level]"=&r"(level)
+ : [pout]"r"(pout), [ppin]"r"(ppin),
+ [ppiq]"r"(ppiq), [max_level]"r"(max_level),
+ [ppbias]"r"(ppbias), [ppzthresh]"r"(ppzthresh),
+ [ppsharpen]"r"(ppsharpen), [ppq]"r"(ppq)
+ : "memory", "hi", "lo"
+ );
+
+ // moved out from macro to increase possibility for earlier breaking
+ for (i = 15; i >= 0; i--) {
+ if (out[i]) return 1;
+ }
+ return 0;
+}
+
+static int Quantize2Blocks_MIPS32(int16_t in[32], int16_t out[32],
+ const VP8Matrix* const mtx) {
+ int nz;
+ nz = QuantizeBlock_MIPS32(in + 0 * 16, out + 0 * 16, mtx) << 0;
+ nz |= QuantizeBlock_MIPS32(in + 1 * 16, out + 1 * 16, mtx) << 1;
+ return nz;
+}
+
+#undef QUANTIZE_ONE
+
+// macro for one horizontal pass in Disto4x4 (TTransform)
+// two calls of function TTransform are merged into single one
+// A - offset in bytes to load from a and b buffers
+// E..H - offsets in bytes to store first results to tmp buffer
+// E1..H1 - offsets in bytes to store second results to tmp buffer
+#define HORIZONTAL_PASS(A, E, F, G, H, E1, F1, G1, H1) \
+ "lbu %[temp0], 0+" XSTR(BPS) "*" #A "(%[a]) \n\t" \
+ "lbu %[temp1], 1+" XSTR(BPS) "*" #A "(%[a]) \n\t" \
+ "lbu %[temp2], 2+" XSTR(BPS) "*" #A "(%[a]) \n\t" \
+ "lbu %[temp3], 3+" XSTR(BPS) "*" #A "(%[a]) \n\t" \
+ "lbu %[temp4], 0+" XSTR(BPS) "*" #A "(%[b]) \n\t" \
+ "lbu %[temp5], 1+" XSTR(BPS) "*" #A "(%[b]) \n\t" \
+ "lbu %[temp6], 2+" XSTR(BPS) "*" #A "(%[b]) \n\t" \
+ "lbu %[temp7], 3+" XSTR(BPS) "*" #A "(%[b]) \n\t" \
+ "addu %[temp8], %[temp0], %[temp2] \n\t" \
+ "subu %[temp0], %[temp0], %[temp2] \n\t" \
+ "addu %[temp2], %[temp1], %[temp3] \n\t" \
+ "subu %[temp1], %[temp1], %[temp3] \n\t" \
+ "addu %[temp3], %[temp4], %[temp6] \n\t" \
+ "subu %[temp4], %[temp4], %[temp6] \n\t" \
+ "addu %[temp6], %[temp5], %[temp7] \n\t" \
+ "subu %[temp5], %[temp5], %[temp7] \n\t" \
+ "addu %[temp7], %[temp8], %[temp2] \n\t" \
+ "subu %[temp2], %[temp8], %[temp2] \n\t" \
+ "addu %[temp8], %[temp0], %[temp1] \n\t" \
+ "subu %[temp0], %[temp0], %[temp1] \n\t" \
+ "addu %[temp1], %[temp3], %[temp6] \n\t" \
+ "subu %[temp3], %[temp3], %[temp6] \n\t" \
+ "addu %[temp6], %[temp4], %[temp5] \n\t" \
+ "subu %[temp4], %[temp4], %[temp5] \n\t" \
+ "sw %[temp7], " #E "(%[tmp]) \n\t" \
+ "sw %[temp2], " #H "(%[tmp]) \n\t" \
+ "sw %[temp8], " #F "(%[tmp]) \n\t" \
+ "sw %[temp0], " #G "(%[tmp]) \n\t" \
+ "sw %[temp1], " #E1 "(%[tmp]) \n\t" \
+ "sw %[temp3], " #H1 "(%[tmp]) \n\t" \
+ "sw %[temp6], " #F1 "(%[tmp]) \n\t" \
+ "sw %[temp4], " #G1 "(%[tmp]) \n\t"
+
+// macro for one vertical pass in Disto4x4 (TTransform)
+// two calls of function TTransform are merged into single one
+// since only one accu is available in mips32r1 instruction set
+// first is done second call of function TTransform and after
+// that first one.
+// const int sum1 = TTransform(a, w);
+// const int sum2 = TTransform(b, w);
+// return abs(sum2 - sum1) >> 5;
+// (sum2 - sum1) is calculated with madds (sub2) and msubs (sub1)
+// A..D - offsets in bytes to load first results from tmp buffer
+// A1..D1 - offsets in bytes to load second results from tmp buffer
+// E..H - offsets in bytes to load from w buffer
+#define VERTICAL_PASS(A, B, C, D, A1, B1, C1, D1, E, F, G, H) \
+ "lw %[temp0], " #A1 "(%[tmp]) \n\t" \
+ "lw %[temp1], " #C1 "(%[tmp]) \n\t" \
+ "lw %[temp2], " #B1 "(%[tmp]) \n\t" \
+ "lw %[temp3], " #D1 "(%[tmp]) \n\t" \
+ "addu %[temp8], %[temp0], %[temp1] \n\t" \
+ "subu %[temp0], %[temp0], %[temp1] \n\t" \
+ "addu %[temp1], %[temp2], %[temp3] \n\t" \
+ "subu %[temp2], %[temp2], %[temp3] \n\t" \
+ "addu %[temp3], %[temp8], %[temp1] \n\t" \
+ "subu %[temp8], %[temp8], %[temp1] \n\t" \
+ "addu %[temp1], %[temp0], %[temp2] \n\t" \
+ "subu %[temp0], %[temp0], %[temp2] \n\t" \
+ "sra %[temp4], %[temp3], 31 \n\t" \
+ "sra %[temp5], %[temp1], 31 \n\t" \
+ "sra %[temp6], %[temp0], 31 \n\t" \
+ "sra %[temp7], %[temp8], 31 \n\t" \
+ "xor %[temp3], %[temp3], %[temp4] \n\t" \
+ "xor %[temp1], %[temp1], %[temp5] \n\t" \
+ "xor %[temp0], %[temp0], %[temp6] \n\t" \
+ "xor %[temp8], %[temp8], %[temp7] \n\t" \
+ "subu %[temp3], %[temp3], %[temp4] \n\t" \
+ "subu %[temp1], %[temp1], %[temp5] \n\t" \
+ "subu %[temp0], %[temp0], %[temp6] \n\t" \
+ "subu %[temp8], %[temp8], %[temp7] \n\t" \
+ "lhu %[temp4], " #E "(%[w]) \n\t" \
+ "lhu %[temp5], " #F "(%[w]) \n\t" \
+ "lhu %[temp6], " #G "(%[w]) \n\t" \
+ "lhu %[temp7], " #H "(%[w]) \n\t" \
+ "madd %[temp4], %[temp3] \n\t" \
+ "madd %[temp5], %[temp1] \n\t" \
+ "madd %[temp6], %[temp0] \n\t" \
+ "madd %[temp7], %[temp8] \n\t" \
+ "lw %[temp0], " #A "(%[tmp]) \n\t" \
+ "lw %[temp1], " #C "(%[tmp]) \n\t" \
+ "lw %[temp2], " #B "(%[tmp]) \n\t" \
+ "lw %[temp3], " #D "(%[tmp]) \n\t" \
+ "addu %[temp8], %[temp0], %[temp1] \n\t" \
+ "subu %[temp0], %[temp0], %[temp1] \n\t" \
+ "addu %[temp1], %[temp2], %[temp3] \n\t" \
+ "subu %[temp2], %[temp2], %[temp3] \n\t" \
+ "addu %[temp3], %[temp8], %[temp1] \n\t" \
+ "subu %[temp1], %[temp8], %[temp1] \n\t" \
+ "addu %[temp8], %[temp0], %[temp2] \n\t" \
+ "subu %[temp0], %[temp0], %[temp2] \n\t" \
+ "sra %[temp2], %[temp3], 31 \n\t" \
+ "xor %[temp3], %[temp3], %[temp2] \n\t" \
+ "subu %[temp3], %[temp3], %[temp2] \n\t" \
+ "msub %[temp4], %[temp3] \n\t" \
+ "sra %[temp2], %[temp8], 31 \n\t" \
+ "sra %[temp3], %[temp0], 31 \n\t" \
+ "sra %[temp4], %[temp1], 31 \n\t" \
+ "xor %[temp8], %[temp8], %[temp2] \n\t" \
+ "xor %[temp0], %[temp0], %[temp3] \n\t" \
+ "xor %[temp1], %[temp1], %[temp4] \n\t" \
+ "subu %[temp8], %[temp8], %[temp2] \n\t" \
+ "subu %[temp0], %[temp0], %[temp3] \n\t" \
+ "subu %[temp1], %[temp1], %[temp4] \n\t" \
+ "msub %[temp5], %[temp8] \n\t" \
+ "msub %[temp6], %[temp0] \n\t" \
+ "msub %[temp7], %[temp1] \n\t"
+
+static int Disto4x4_MIPS32(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int tmp[32];
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8;
+
+ __asm__ volatile(
+ HORIZONTAL_PASS(0, 0, 4, 8, 12, 64, 68, 72, 76)
+ HORIZONTAL_PASS(1, 16, 20, 24, 28, 80, 84, 88, 92)
+ HORIZONTAL_PASS(2, 32, 36, 40, 44, 96, 100, 104, 108)
+ HORIZONTAL_PASS(3, 48, 52, 56, 60, 112, 116, 120, 124)
+ "mthi $zero \n\t"
+ "mtlo $zero \n\t"
+ VERTICAL_PASS( 0, 16, 32, 48, 64, 80, 96, 112, 0, 8, 16, 24)
+ VERTICAL_PASS( 4, 20, 36, 52, 68, 84, 100, 116, 2, 10, 18, 26)
+ VERTICAL_PASS( 8, 24, 40, 56, 72, 88, 104, 120, 4, 12, 20, 28)
+ VERTICAL_PASS(12, 28, 44, 60, 76, 92, 108, 124, 6, 14, 22, 30)
+ "mflo %[temp0] \n\t"
+ "sra %[temp1], %[temp0], 31 \n\t"
+ "xor %[temp0], %[temp0], %[temp1] \n\t"
+ "subu %[temp0], %[temp0], %[temp1] \n\t"
+ "sra %[temp0], %[temp0], 5 \n\t"
+
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8)
+ : [a]"r"(a), [b]"r"(b), [w]"r"(w), [tmp]"r"(tmp)
+ : "memory", "hi", "lo"
+ );
+
+ return temp0;
+}
+
+#undef VERTICAL_PASS
+#undef HORIZONTAL_PASS
+
+static int Disto16x16_MIPS32(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4_MIPS32(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+
+// macro for one horizontal pass in FTransform
+// temp0..temp15 holds tmp[0]..tmp[15]
+// A - offset in bytes to load from src and ref buffers
+// TEMP0..TEMP3 - registers for corresponding tmp elements
+#define HORIZONTAL_PASS(A, TEMP0, TEMP1, TEMP2, TEMP3) \
+ "lw %[" #TEMP1 "], 0(%[args]) \n\t" \
+ "lw %[" #TEMP2 "], 4(%[args]) \n\t" \
+ "lbu %[temp16], 0+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \
+ "lbu %[temp17], 0+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \
+ "lbu %[temp18], 1+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \
+ "lbu %[temp19], 1+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \
+ "subu %[temp20], %[temp16], %[temp17] \n\t" \
+ "lbu %[temp16], 2+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \
+ "lbu %[temp17], 2+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \
+ "subu %[" #TEMP0 "], %[temp18], %[temp19] \n\t" \
+ "lbu %[temp18], 3+" XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \
+ "lbu %[temp19], 3+" XSTR(BPS) "*" #A "(%[" #TEMP2 "]) \n\t" \
+ "subu %[" #TEMP1 "], %[temp16], %[temp17] \n\t" \
+ "subu %[" #TEMP2 "], %[temp18], %[temp19] \n\t" \
+ "addu %[" #TEMP3 "], %[temp20], %[" #TEMP2 "] \n\t" \
+ "subu %[" #TEMP2 "], %[temp20], %[" #TEMP2 "] \n\t" \
+ "addu %[temp20], %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \
+ "subu %[" #TEMP0 "], %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \
+ "mul %[temp16], %[" #TEMP2 "], %[c5352] \n\t" \
+ "mul %[temp17], %[" #TEMP2 "], %[c2217] \n\t" \
+ "mul %[temp18], %[" #TEMP0 "], %[c5352] \n\t" \
+ "mul %[temp19], %[" #TEMP0 "], %[c2217] \n\t" \
+ "addu %[" #TEMP1 "], %[" #TEMP3 "], %[temp20] \n\t" \
+ "subu %[temp20], %[" #TEMP3 "], %[temp20] \n\t" \
+ "sll %[" #TEMP0 "], %[" #TEMP1 "], 3 \n\t" \
+ "sll %[" #TEMP2 "], %[temp20], 3 \n\t" \
+ "addiu %[temp16], %[temp16], 1812 \n\t" \
+ "addiu %[temp17], %[temp17], 937 \n\t" \
+ "addu %[temp16], %[temp16], %[temp19] \n\t" \
+ "subu %[temp17], %[temp17], %[temp18] \n\t" \
+ "sra %[" #TEMP1 "], %[temp16], 9 \n\t" \
+ "sra %[" #TEMP3 "], %[temp17], 9 \n\t"
+
+// macro for one vertical pass in FTransform
+// temp0..temp15 holds tmp[0]..tmp[15]
+// A..D - offsets in bytes to store to out buffer
+// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements
+#define VERTICAL_PASS(A, B, C, D, TEMP0, TEMP4, TEMP8, TEMP12) \
+ "addu %[temp16], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \
+ "subu %[temp19], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \
+ "addu %[temp17], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \
+ "subu %[temp18], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \
+ "mul %[" #TEMP8 "], %[temp19], %[c2217] \n\t" \
+ "mul %[" #TEMP12 "], %[temp18], %[c2217] \n\t" \
+ "mul %[" #TEMP4 "], %[temp19], %[c5352] \n\t" \
+ "mul %[temp18], %[temp18], %[c5352] \n\t" \
+ "addiu %[temp16], %[temp16], 7 \n\t" \
+ "addu %[" #TEMP0 "], %[temp16], %[temp17] \n\t" \
+ "sra %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \
+ "addu %[" #TEMP12 "], %[" #TEMP12 "], %[" #TEMP4 "] \n\t" \
+ "subu %[" #TEMP4 "], %[temp16], %[temp17] \n\t" \
+ "sra %[" #TEMP4 "], %[" #TEMP4 "], 4 \n\t" \
+ "addiu %[" #TEMP8 "], %[" #TEMP8 "], 30000 \n\t" \
+ "addiu %[" #TEMP12 "], %[" #TEMP12 "], 12000 \n\t" \
+ "addiu %[" #TEMP8 "], %[" #TEMP8 "], 21000 \n\t" \
+ "subu %[" #TEMP8 "], %[" #TEMP8 "], %[temp18] \n\t" \
+ "sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \
+ "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \
+ "addiu %[temp16], %[" #TEMP12 "], 1 \n\t" \
+ "movn %[" #TEMP12 "], %[temp16], %[temp19] \n\t" \
+ "sh %[" #TEMP0 "], " #A "(%[temp20]) \n\t" \
+ "sh %[" #TEMP4 "], " #C "(%[temp20]) \n\t" \
+ "sh %[" #TEMP8 "], " #D "(%[temp20]) \n\t" \
+ "sh %[" #TEMP12 "], " #B "(%[temp20]) \n\t"
+
+static void FTransform_MIPS32(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8;
+ int temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16;
+ int temp17, temp18, temp19, temp20;
+ const int c2217 = 2217;
+ const int c5352 = 5352;
+ const int* const args[3] =
+ { (const int*)src, (const int*)ref, (const int*)out };
+
+ __asm__ volatile(
+ HORIZONTAL_PASS(0, temp0, temp1, temp2, temp3)
+ HORIZONTAL_PASS(1, temp4, temp5, temp6, temp7)
+ HORIZONTAL_PASS(2, temp8, temp9, temp10, temp11)
+ HORIZONTAL_PASS(3, temp12, temp13, temp14, temp15)
+ "lw %[temp20], 8(%[args]) \n\t"
+ VERTICAL_PASS(0, 8, 16, 24, temp0, temp4, temp8, temp12)
+ VERTICAL_PASS(2, 10, 18, 26, temp1, temp5, temp9, temp13)
+ VERTICAL_PASS(4, 12, 20, 28, temp2, temp6, temp10, temp14)
+ VERTICAL_PASS(6, 14, 22, 30, temp3, temp7, temp11, temp15)
+
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11),
+ [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), [temp14]"=&r"(temp14),
+ [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), [temp17]"=&r"(temp17),
+ [temp18]"=&r"(temp18), [temp19]"=&r"(temp19), [temp20]"=&r"(temp20)
+ : [args]"r"(args), [c2217]"r"(c2217), [c5352]"r"(c5352)
+ : "memory", "hi", "lo"
+ );
+}
+
+#undef VERTICAL_PASS
+#undef HORIZONTAL_PASS
+
+#if !defined(WORK_AROUND_GCC)
+
+#define GET_SSE_INNER(A, B, C, D) \
+ "lbu %[temp0], " #A "(%[a]) \n\t" \
+ "lbu %[temp1], " #A "(%[b]) \n\t" \
+ "lbu %[temp2], " #B "(%[a]) \n\t" \
+ "lbu %[temp3], " #B "(%[b]) \n\t" \
+ "lbu %[temp4], " #C "(%[a]) \n\t" \
+ "lbu %[temp5], " #C "(%[b]) \n\t" \
+ "lbu %[temp6], " #D "(%[a]) \n\t" \
+ "lbu %[temp7], " #D "(%[b]) \n\t" \
+ "subu %[temp0], %[temp0], %[temp1] \n\t" \
+ "subu %[temp2], %[temp2], %[temp3] \n\t" \
+ "subu %[temp4], %[temp4], %[temp5] \n\t" \
+ "subu %[temp6], %[temp6], %[temp7] \n\t" \
+ "madd %[temp0], %[temp0] \n\t" \
+ "madd %[temp2], %[temp2] \n\t" \
+ "madd %[temp4], %[temp4] \n\t" \
+ "madd %[temp6], %[temp6] \n\t"
+
+#define GET_SSE(A, B, C, D) \
+ GET_SSE_INNER(A, A + 1, A + 2, A + 3) \
+ GET_SSE_INNER(B, B + 1, B + 2, B + 3) \
+ GET_SSE_INNER(C, C + 1, C + 2, C + 3) \
+ GET_SSE_INNER(D, D + 1, D + 2, D + 3)
+
+static int SSE16x16_MIPS32(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+
+ __asm__ volatile(
+ "mult $zero, $zero \n\t"
+
+ GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS)
+ GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS)
+ GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS)
+ GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS)
+ GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS)
+ GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS)
+ GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS)
+ GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS)
+ GET_SSE( 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS)
+ GET_SSE( 9 * BPS, 4 + 9 * BPS, 8 + 9 * BPS, 12 + 9 * BPS)
+ GET_SSE(10 * BPS, 4 + 10 * BPS, 8 + 10 * BPS, 12 + 10 * BPS)
+ GET_SSE(11 * BPS, 4 + 11 * BPS, 8 + 11 * BPS, 12 + 11 * BPS)
+ GET_SSE(12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS)
+ GET_SSE(13 * BPS, 4 + 13 * BPS, 8 + 13 * BPS, 12 + 13 * BPS)
+ GET_SSE(14 * BPS, 4 + 14 * BPS, 8 + 14 * BPS, 12 + 14 * BPS)
+ GET_SSE(15 * BPS, 4 + 15 * BPS, 8 + 15 * BPS, 12 + 15 * BPS)
+
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+static int SSE16x8_MIPS32(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+
+ __asm__ volatile(
+ "mult $zero, $zero \n\t"
+
+ GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS)
+ GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS)
+ GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS)
+ GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS)
+ GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS)
+ GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS)
+ GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS)
+ GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS)
+
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+static int SSE8x8_MIPS32(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+
+ __asm__ volatile(
+ "mult $zero, $zero \n\t"
+
+ GET_SSE(0 * BPS, 4 + 0 * BPS, 1 * BPS, 4 + 1 * BPS)
+ GET_SSE(2 * BPS, 4 + 2 * BPS, 3 * BPS, 4 + 3 * BPS)
+ GET_SSE(4 * BPS, 4 + 4 * BPS, 5 * BPS, 4 + 5 * BPS)
+ GET_SSE(6 * BPS, 4 + 6 * BPS, 7 * BPS, 4 + 7 * BPS)
+
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+static int SSE4x4_MIPS32(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+
+ __asm__ volatile(
+ "mult $zero, $zero \n\t"
+
+ GET_SSE(0 * BPS, 1 * BPS, 2 * BPS, 3 * BPS)
+
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+#undef GET_SSE
+#undef GET_SSE_INNER
+
+#endif // !WORK_AROUND_GCC
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspInitMIPS32(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitMIPS32(void) {
+ VP8ITransform = ITransform_MIPS32;
+ VP8FTransform = FTransform_MIPS32;
+
+ VP8EncQuantizeBlock = QuantizeBlock_MIPS32;
+ VP8EncQuantize2Blocks = Quantize2Blocks_MIPS32;
+
+ VP8TDisto4x4 = Disto4x4_MIPS32;
+ VP8TDisto16x16 = Disto16x16_MIPS32;
+
+#if !defined(WORK_AROUND_GCC)
+ VP8SSE16x16 = SSE16x16_MIPS32;
+ VP8SSE8x8 = SSE8x8_MIPS32;
+ VP8SSE16x8 = SSE16x8_MIPS32;
+ VP8SSE4x4 = SSE4x4_MIPS32;
+#endif
+}
+
+#else // !WEBP_USE_MIPS32
+
+WEBP_DSP_INIT_STUB(VP8EncDspInitMIPS32)
+
+#endif // WEBP_USE_MIPS32
diff --git a/src/third_party/libwebp/src/dsp/enc_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/enc_mips_dsp_r2.c
new file mode 100644
index 0000000..dd2876b
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc_mips_dsp_r2.c
@@ -0,0 +1,1522 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of speed-critical encoding functions.
+//
+// Author(s): Darko Laus (darko.laus@imgtec.com)
+// Mirko Raus (mirko.raus@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#endif
+
+#include "src/dsp/mips_macro.h"
+#include "src/enc/cost_enc.h"
+#include "src/enc/vp8i_enc.h"
+
+static const int kC1 = 20091 + (1 << 16);
+static const int kC2 = 35468;
+
+// O - output
+// I - input (macro doesn't change it)
+#define ADD_SUB_HALVES_X4(O0, O1, O2, O3, O4, O5, O6, O7, \
+ I0, I1, I2, I3, I4, I5, I6, I7) \
+ "addq.ph %[" #O0 "], %[" #I0 "], %[" #I1 "] \n\t" \
+ "subq.ph %[" #O1 "], %[" #I0 "], %[" #I1 "] \n\t" \
+ "addq.ph %[" #O2 "], %[" #I2 "], %[" #I3 "] \n\t" \
+ "subq.ph %[" #O3 "], %[" #I2 "], %[" #I3 "] \n\t" \
+ "addq.ph %[" #O4 "], %[" #I4 "], %[" #I5 "] \n\t" \
+ "subq.ph %[" #O5 "], %[" #I4 "], %[" #I5 "] \n\t" \
+ "addq.ph %[" #O6 "], %[" #I6 "], %[" #I7 "] \n\t" \
+ "subq.ph %[" #O7 "], %[" #I6 "], %[" #I7 "] \n\t"
+
+// IO - input/output
+#define ABS_X8(IO0, IO1, IO2, IO3, IO4, IO5, IO6, IO7) \
+ "absq_s.ph %[" #IO0 "], %[" #IO0 "] \n\t" \
+ "absq_s.ph %[" #IO1 "], %[" #IO1 "] \n\t" \
+ "absq_s.ph %[" #IO2 "], %[" #IO2 "] \n\t" \
+ "absq_s.ph %[" #IO3 "], %[" #IO3 "] \n\t" \
+ "absq_s.ph %[" #IO4 "], %[" #IO4 "] \n\t" \
+ "absq_s.ph %[" #IO5 "], %[" #IO5 "] \n\t" \
+ "absq_s.ph %[" #IO6 "], %[" #IO6 "] \n\t" \
+ "absq_s.ph %[" #IO7 "], %[" #IO7 "] \n\t"
+
+// dpa.w.ph $ac0 temp0 ,temp1
+// $ac += temp0[31..16] * temp1[31..16] + temp0[15..0] * temp1[15..0]
+// dpax.w.ph $ac0 temp0 ,temp1
+// $ac += temp0[31..16] * temp1[15..0] + temp0[15..0] * temp1[31..16]
+// O - output
+// I - input (macro doesn't change it)
+#define MUL_HALF(O0, I0, I1, I2, I3, I4, I5, I6, I7, \
+ I8, I9, I10, I11, I12, I13, I14, I15) \
+ "mult $ac0, $zero, $zero \n\t" \
+ "dpa.w.ph $ac0, %[" #I2 "], %[" #I0 "] \n\t" \
+ "dpax.w.ph $ac0, %[" #I5 "], %[" #I6 "] \n\t" \
+ "dpa.w.ph $ac0, %[" #I8 "], %[" #I9 "] \n\t" \
+ "dpax.w.ph $ac0, %[" #I11 "], %[" #I4 "] \n\t" \
+ "dpa.w.ph $ac0, %[" #I12 "], %[" #I7 "] \n\t" \
+ "dpax.w.ph $ac0, %[" #I13 "], %[" #I1 "] \n\t" \
+ "dpa.w.ph $ac0, %[" #I14 "], %[" #I3 "] \n\t" \
+ "dpax.w.ph $ac0, %[" #I15 "], %[" #I10 "] \n\t" \
+ "mflo %[" #O0 "], $ac0 \n\t"
+
+#define OUTPUT_EARLY_CLOBBER_REGS_17() \
+ OUTPUT_EARLY_CLOBBER_REGS_10(), \
+ [temp11]"=&r"(temp11), [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), \
+ [temp14]"=&r"(temp14), [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), \
+ [temp17]"=&r"(temp17)
+
+// macro for one horizontal pass in FTransform
+// temp0..temp15 holds tmp[0]..tmp[15]
+// A - offset in bytes to load from src and ref buffers
+// TEMP0..TEMP3 - registers for corresponding tmp elements
+#define HORIZONTAL_PASS(A, TEMP0, TEMP1, TEMP2, TEMP3) \
+ "lw %[" #TEMP0 "], 0(%[args]) \n\t" \
+ "lw %[" #TEMP1 "], 4(%[args]) \n\t" \
+ "lw %[" #TEMP2 "], " XSTR(BPS) "*" #A "(%[" #TEMP0 "]) \n\t" \
+ "lw %[" #TEMP3 "], " XSTR(BPS) "*" #A "(%[" #TEMP1 "]) \n\t" \
+ "preceu.ph.qbl %[" #TEMP0 "], %[" #TEMP2 "] \n\t" \
+ "preceu.ph.qbl %[" #TEMP1 "], %[" #TEMP3 "] \n\t" \
+ "preceu.ph.qbr %[" #TEMP2 "], %[" #TEMP2 "] \n\t" \
+ "preceu.ph.qbr %[" #TEMP3 "], %[" #TEMP3 "] \n\t" \
+ "subq.ph %[" #TEMP0 "], %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \
+ "subq.ph %[" #TEMP2 "], %[" #TEMP2 "], %[" #TEMP3 "] \n\t" \
+ "rotr %[" #TEMP0 "], %[" #TEMP0 "], 16 \n\t" \
+ "addq.ph %[" #TEMP1 "], %[" #TEMP2 "], %[" #TEMP0 "] \n\t" \
+ "subq.ph %[" #TEMP3 "], %[" #TEMP2 "], %[" #TEMP0 "] \n\t" \
+ "seh %[" #TEMP0 "], %[" #TEMP1 "] \n\t" \
+ "sra %[temp16], %[" #TEMP1 "], 16 \n\t" \
+ "seh %[temp19], %[" #TEMP3 "] \n\t" \
+ "sra %[" #TEMP3 "], %[" #TEMP3 "], 16 \n\t" \
+ "subu %[" #TEMP2 "], %[" #TEMP0 "], %[temp16] \n\t" \
+ "addu %[" #TEMP0 "], %[" #TEMP0 "], %[temp16] \n\t" \
+ "mul %[temp17], %[temp19], %[c2217] \n\t" \
+ "mul %[temp18], %[" #TEMP3 "], %[c5352] \n\t" \
+ "mul %[" #TEMP1 "], %[temp19], %[c5352] \n\t" \
+ "mul %[temp16], %[" #TEMP3 "], %[c2217] \n\t" \
+ "sll %[" #TEMP2 "], %[" #TEMP2 "], 3 \n\t" \
+ "sll %[" #TEMP0 "], %[" #TEMP0 "], 3 \n\t" \
+ "subu %[" #TEMP3 "], %[temp17], %[temp18] \n\t" \
+ "addu %[" #TEMP1 "], %[temp16], %[" #TEMP1 "] \n\t" \
+ "addiu %[" #TEMP3 "], %[" #TEMP3 "], 937 \n\t" \
+ "addiu %[" #TEMP1 "], %[" #TEMP1 "], 1812 \n\t" \
+ "sra %[" #TEMP3 "], %[" #TEMP3 "], 9 \n\t" \
+ "sra %[" #TEMP1 "], %[" #TEMP1 "], 9 \n\t"
+
+// macro for one vertical pass in FTransform
+// temp0..temp15 holds tmp[0]..tmp[15]
+// A..D - offsets in bytes to store to out buffer
+// TEMP0, TEMP4, TEMP8 and TEMP12 - registers for corresponding tmp elements
+#define VERTICAL_PASS(A, B, C, D, TEMP0, TEMP4, TEMP8, TEMP12) \
+ "addu %[temp16], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \
+ "subu %[temp19], %[" #TEMP0 "], %[" #TEMP12 "] \n\t" \
+ "addu %[temp17], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \
+ "subu %[temp18], %[" #TEMP4 "], %[" #TEMP8 "] \n\t" \
+ "mul %[" #TEMP8 "], %[temp19], %[c2217] \n\t" \
+ "mul %[" #TEMP12 "], %[temp18], %[c2217] \n\t" \
+ "mul %[" #TEMP4 "], %[temp19], %[c5352] \n\t" \
+ "mul %[temp18], %[temp18], %[c5352] \n\t" \
+ "addiu %[temp16], %[temp16], 7 \n\t" \
+ "addu %[" #TEMP0 "], %[temp16], %[temp17] \n\t" \
+ "sra %[" #TEMP0 "], %[" #TEMP0 "], 4 \n\t" \
+ "addu %[" #TEMP12 "], %[" #TEMP12 "], %[" #TEMP4 "] \n\t" \
+ "subu %[" #TEMP4 "], %[temp16], %[temp17] \n\t" \
+ "sra %[" #TEMP4 "], %[" #TEMP4 "], 4 \n\t" \
+ "addiu %[" #TEMP8 "], %[" #TEMP8 "], 30000 \n\t" \
+ "addiu %[" #TEMP12 "], %[" #TEMP12 "], 12000 \n\t" \
+ "addiu %[" #TEMP8 "], %[" #TEMP8 "], 21000 \n\t" \
+ "subu %[" #TEMP8 "], %[" #TEMP8 "], %[temp18] \n\t" \
+ "sra %[" #TEMP12 "], %[" #TEMP12 "], 16 \n\t" \
+ "sra %[" #TEMP8 "], %[" #TEMP8 "], 16 \n\t" \
+ "addiu %[temp16], %[" #TEMP12 "], 1 \n\t" \
+ "movn %[" #TEMP12 "], %[temp16], %[temp19] \n\t" \
+ "sh %[" #TEMP0 "], " #A "(%[temp20]) \n\t" \
+ "sh %[" #TEMP4 "], " #C "(%[temp20]) \n\t" \
+ "sh %[" #TEMP8 "], " #D "(%[temp20]) \n\t" \
+ "sh %[" #TEMP12 "], " #B "(%[temp20]) \n\t"
+
+static void FTransform_MIPSdspR2(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ const int c2217 = 2217;
+ const int c5352 = 5352;
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8;
+ int temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16;
+ int temp17, temp18, temp19, temp20;
+ const int* const args[3] =
+ { (const int*)src, (const int*)ref, (const int*)out };
+
+ __asm__ volatile (
+ HORIZONTAL_PASS(0, temp0, temp1, temp2, temp3)
+ HORIZONTAL_PASS(1, temp4, temp5, temp6, temp7)
+ HORIZONTAL_PASS(2, temp8, temp9, temp10, temp11)
+ HORIZONTAL_PASS(3, temp12, temp13, temp14, temp15)
+ "lw %[temp20], 8(%[args]) \n\t"
+ VERTICAL_PASS(0, 8, 16, 24, temp0, temp4, temp8, temp12)
+ VERTICAL_PASS(2, 10, 18, 26, temp1, temp5, temp9, temp13)
+ VERTICAL_PASS(4, 12, 20, 28, temp2, temp6, temp10, temp14)
+ VERTICAL_PASS(6, 14, 22, 30, temp3, temp7, temp11, temp15)
+ OUTPUT_EARLY_CLOBBER_REGS_18(),
+ [temp0]"=&r"(temp0), [temp19]"=&r"(temp19), [temp20]"=&r"(temp20)
+ : [args]"r"(args), [c2217]"r"(c2217), [c5352]"r"(c5352)
+ : "memory", "hi", "lo"
+ );
+}
+
+#undef VERTICAL_PASS
+#undef HORIZONTAL_PASS
+
+static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst) {
+ int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;
+ int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17, temp18;
+
+ __asm__ volatile (
+ "ulw %[temp1], 0(%[in]) \n\t"
+ "ulw %[temp2], 16(%[in]) \n\t"
+ LOAD_IN_X2(temp5, temp6, 24, 26)
+ ADD_SUB_HALVES(temp3, temp4, temp1, temp2)
+ LOAD_IN_X2(temp1, temp2, 8, 10)
+ MUL_SHIFT_SUM(temp7, temp8, temp9, temp10, temp11, temp12, temp13, temp14,
+ temp10, temp8, temp9, temp7, temp1, temp2, temp5, temp6,
+ temp13, temp11, temp14, temp12)
+ INSERT_HALF_X2(temp8, temp7, temp10, temp9)
+ "ulw %[temp17], 4(%[in]) \n\t"
+ "ulw %[temp18], 20(%[in]) \n\t"
+ ADD_SUB_HALVES(temp1, temp2, temp3, temp8)
+ ADD_SUB_HALVES(temp5, temp6, temp4, temp7)
+ ADD_SUB_HALVES(temp7, temp8, temp17, temp18)
+ LOAD_IN_X2(temp17, temp18, 12, 14)
+ LOAD_IN_X2(temp9, temp10, 28, 30)
+ MUL_SHIFT_SUM(temp11, temp12, temp13, temp14, temp15, temp16, temp4, temp17,
+ temp12, temp14, temp11, temp13, temp17, temp18, temp9, temp10,
+ temp15, temp4, temp16, temp17)
+ INSERT_HALF_X2(temp11, temp12, temp13, temp14)
+ ADD_SUB_HALVES(temp17, temp8, temp8, temp11)
+ ADD_SUB_HALVES(temp3, temp4, temp7, temp12)
+
+ // horizontal
+ SRA_16(temp9, temp10, temp11, temp12, temp1, temp2, temp5, temp6)
+ INSERT_HALF_X2(temp1, temp6, temp5, temp2)
+ SRA_16(temp13, temp14, temp15, temp16, temp3, temp4, temp17, temp8)
+ "repl.ph %[temp2], 0x4 \n\t"
+ INSERT_HALF_X2(temp3, temp8, temp17, temp4)
+ "addq.ph %[temp1], %[temp1], %[temp2] \n\t"
+ "addq.ph %[temp6], %[temp6], %[temp2] \n\t"
+ ADD_SUB_HALVES(temp2, temp4, temp1, temp3)
+ ADD_SUB_HALVES(temp5, temp7, temp6, temp8)
+ MUL_SHIFT_SUM(temp1, temp3, temp6, temp8, temp9, temp13, temp17, temp18,
+ temp3, temp13, temp1, temp9, temp9, temp13, temp11, temp15,
+ temp6, temp17, temp8, temp18)
+ MUL_SHIFT_SUM(temp6, temp8, temp18, temp17, temp11, temp15, temp12, temp16,
+ temp8, temp15, temp6, temp11, temp12, temp16, temp10, temp14,
+ temp18, temp12, temp17, temp16)
+ INSERT_HALF_X2(temp1, temp3, temp9, temp13)
+ INSERT_HALF_X2(temp6, temp8, temp11, temp15)
+ SHIFT_R_SUM_X2(temp9, temp10, temp11, temp12, temp13, temp14, temp15,
+ temp16, temp2, temp4, temp5, temp7, temp3, temp1, temp8,
+ temp6)
+ PACK_2_HALVES_TO_WORD(temp1, temp2, temp3, temp4, temp9, temp12, temp13,
+ temp16, temp11, temp10, temp15, temp14)
+ LOAD_WITH_OFFSET_X4(temp10, temp11, temp14, temp15, ref,
+ 0, 0, 0, 0,
+ 0, 1, 2, 3,
+ BPS)
+ CONVERT_2_BYTES_TO_HALF(temp5, temp6, temp7, temp8, temp17, temp18, temp10,
+ temp11, temp10, temp11, temp14, temp15)
+ STORE_SAT_SUM_X2(temp5, temp6, temp7, temp8, temp17, temp18, temp10, temp11,
+ temp9, temp12, temp1, temp2, temp13, temp16, temp3, temp4,
+ dst, 0, 1, 2, 3, BPS)
+
+ OUTPUT_EARLY_CLOBBER_REGS_18()
+ : [dst]"r"(dst), [in]"r"(in), [kC1]"r"(kC1), [kC2]"r"(kC2), [ref]"r"(ref)
+ : "memory", "hi", "lo"
+ );
+}
+
+static void ITransform_MIPSdspR2(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst, int do_two) {
+ ITransformOne(ref, in, dst);
+ if (do_two) {
+ ITransformOne(ref + 4, in + 16, dst + 4);
+ }
+}
+
+static int Disto4x4_MIPSdspR2(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, temp9;
+ int temp10, temp11, temp12, temp13, temp14, temp15, temp16, temp17;
+
+ __asm__ volatile (
+ LOAD_WITH_OFFSET_X4(temp1, temp2, temp3, temp4, a,
+ 0, 0, 0, 0,
+ 0, 1, 2, 3,
+ BPS)
+ CONVERT_2_BYTES_TO_HALF(temp5, temp6, temp7, temp8, temp9,temp10, temp11,
+ temp12, temp1, temp2, temp3, temp4)
+ ADD_SUB_HALVES_X4(temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8,
+ temp5, temp6, temp7, temp8, temp9, temp10, temp11, temp12)
+ PACK_2_HALVES_TO_WORD(temp9, temp10, temp11, temp12, temp1, temp3, temp5,
+ temp7, temp2, temp4, temp6, temp8)
+ ADD_SUB_HALVES_X4(temp2, temp4, temp6, temp8, temp9, temp1, temp3, temp10,
+ temp1, temp9, temp3, temp10, temp5, temp11, temp7, temp12)
+ ADD_SUB_HALVES_X4(temp5, temp11, temp7, temp2, temp9, temp3, temp6, temp12,
+ temp2, temp9, temp6, temp3, temp4, temp1, temp8, temp10)
+ ADD_SUB_HALVES_X4(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2,
+ temp5, temp7, temp11, temp2, temp9, temp6, temp3, temp12)
+ ABS_X8(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2)
+ LOAD_WITH_OFFSET_X4(temp3, temp6, temp9, temp12, w,
+ 0, 4, 8, 12,
+ 0, 0, 0, 0,
+ 0)
+ LOAD_WITH_OFFSET_X4(temp13, temp14, temp15, temp16, w,
+ 0, 4, 8, 12,
+ 1, 1, 1, 1,
+ 16)
+ MUL_HALF(temp17, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8,
+ temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16)
+ LOAD_WITH_OFFSET_X4(temp1, temp2, temp3, temp4, b,
+ 0, 0, 0, 0,
+ 0, 1, 2, 3,
+ BPS)
+ CONVERT_2_BYTES_TO_HALF(temp5,temp6, temp7, temp8, temp9,temp10, temp11,
+ temp12, temp1, temp2, temp3, temp4)
+ ADD_SUB_HALVES_X4(temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8,
+ temp5, temp6, temp7, temp8, temp9, temp10, temp11, temp12)
+ PACK_2_HALVES_TO_WORD(temp9, temp10, temp11, temp12, temp1, temp3, temp5,
+ temp7, temp2, temp4, temp6, temp8)
+ ADD_SUB_HALVES_X4(temp2, temp4, temp6, temp8, temp9, temp1, temp3, temp10,
+ temp1, temp9, temp3, temp10, temp5, temp11, temp7, temp12)
+ ADD_SUB_HALVES_X4(temp5, temp11, temp7, temp2, temp9, temp3, temp6, temp12,
+ temp2, temp9, temp6, temp3, temp4, temp1, temp8, temp10)
+ ADD_SUB_HALVES_X4(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2,
+ temp5, temp7, temp11, temp2, temp9, temp6, temp3, temp12)
+ ABS_X8(temp1, temp4, temp10, temp8, temp7, temp11, temp5, temp2)
+ LOAD_WITH_OFFSET_X4(temp3, temp6, temp9, temp12, w,
+ 0, 4, 8, 12,
+ 0, 0, 0, 0,
+ 0)
+ LOAD_WITH_OFFSET_X4(temp13, temp14, temp15, temp16, w,
+ 0, 4, 8, 12,
+ 1, 1, 1, 1,
+ 16)
+ MUL_HALF(temp3, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8,
+ temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16)
+ OUTPUT_EARLY_CLOBBER_REGS_17()
+ : [a]"r"(a), [b]"r"(b), [w]"r"(w)
+ : "memory", "hi", "lo"
+ );
+ return abs(temp3 - temp17) >> 5;
+}
+
+static int Disto16x16_MIPSdspR2(const uint8_t* const a,
+ const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4_MIPSdspR2(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+
+//------------------------------------------------------------------------------
+// Intra predictions
+
+#define FILL_PART(J, SIZE) \
+ "usw %[value], 0+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \
+ "usw %[value], 4+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \
+ ".if " #SIZE " == 16 \n\t" \
+ "usw %[value], 8+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \
+ "usw %[value], 12+" #J "*" XSTR(BPS) "(%[dst]) \n\t" \
+ ".endif \n\t"
+
+#define FILL_8_OR_16(DST, VALUE, SIZE) do { \
+ int value = (VALUE); \
+ __asm__ volatile ( \
+ "replv.qb %[value], %[value] \n\t" \
+ FILL_PART( 0, SIZE) \
+ FILL_PART( 1, SIZE) \
+ FILL_PART( 2, SIZE) \
+ FILL_PART( 3, SIZE) \
+ FILL_PART( 4, SIZE) \
+ FILL_PART( 5, SIZE) \
+ FILL_PART( 6, SIZE) \
+ FILL_PART( 7, SIZE) \
+ ".if " #SIZE " == 16 \n\t" \
+ FILL_PART( 8, 16) \
+ FILL_PART( 9, 16) \
+ FILL_PART(10, 16) \
+ FILL_PART(11, 16) \
+ FILL_PART(12, 16) \
+ FILL_PART(13, 16) \
+ FILL_PART(14, 16) \
+ FILL_PART(15, 16) \
+ ".endif \n\t" \
+ : [value]"+&r"(value) \
+ : [dst]"r"((DST)) \
+ : "memory" \
+ ); \
+} while (0)
+
+#define VERTICAL_PRED(DST, TOP, SIZE) \
+static WEBP_INLINE void VerticalPred##SIZE(uint8_t* (DST), \
+ const uint8_t* (TOP)) { \
+ int j; \
+ if ((TOP)) { \
+ for (j = 0; j < (SIZE); ++j) memcpy((DST) + j * BPS, (TOP), (SIZE)); \
+ } else { \
+ FILL_8_OR_16((DST), 127, (SIZE)); \
+ } \
+}
+
+VERTICAL_PRED(dst, top, 8)
+VERTICAL_PRED(dst, top, 16)
+
+#undef VERTICAL_PRED
+
+#define HORIZONTAL_PRED(DST, LEFT, SIZE) \
+static WEBP_INLINE void HorizontalPred##SIZE(uint8_t* (DST), \
+ const uint8_t* (LEFT)) { \
+ if (LEFT) { \
+ int j; \
+ for (j = 0; j < (SIZE); ++j) { \
+ memset((DST) + j * BPS, (LEFT)[j], (SIZE)); \
+ } \
+ } else { \
+ FILL_8_OR_16((DST), 129, (SIZE)); \
+ } \
+}
+
+HORIZONTAL_PRED(dst, left, 8)
+HORIZONTAL_PRED(dst, left, 16)
+
+#undef HORIZONTAL_PRED
+
+#define CLIPPING() \
+ "preceu.ph.qbl %[temp2], %[temp0] \n\t" \
+ "preceu.ph.qbr %[temp0], %[temp0] \n\t" \
+ "preceu.ph.qbl %[temp3], %[temp1] \n\t" \
+ "preceu.ph.qbr %[temp1], %[temp1] \n\t" \
+ "addu.ph %[temp2], %[temp2], %[leftY_1] \n\t" \
+ "addu.ph %[temp0], %[temp0], %[leftY_1] \n\t" \
+ "addu.ph %[temp3], %[temp3], %[leftY_1] \n\t" \
+ "addu.ph %[temp1], %[temp1], %[leftY_1] \n\t" \
+ "shll_s.ph %[temp2], %[temp2], 7 \n\t" \
+ "shll_s.ph %[temp0], %[temp0], 7 \n\t" \
+ "shll_s.ph %[temp3], %[temp3], 7 \n\t" \
+ "shll_s.ph %[temp1], %[temp1], 7 \n\t" \
+ "precrqu_s.qb.ph %[temp0], %[temp2], %[temp0] \n\t" \
+ "precrqu_s.qb.ph %[temp1], %[temp3], %[temp1] \n\t"
+
+#define CLIP_8B_TO_DST(DST, LEFT, TOP, SIZE) do { \
+ int leftY_1 = ((int)(LEFT)[y] << 16) + (LEFT)[y]; \
+ int temp0, temp1, temp2, temp3; \
+ __asm__ volatile ( \
+ "replv.ph %[leftY_1], %[leftY_1] \n\t" \
+ "ulw %[temp0], 0(%[top]) \n\t" \
+ "ulw %[temp1], 4(%[top]) \n\t" \
+ "subu.ph %[leftY_1], %[leftY_1], %[left_1] \n\t" \
+ CLIPPING() \
+ "usw %[temp0], 0(%[dst]) \n\t" \
+ "usw %[temp1], 4(%[dst]) \n\t" \
+ ".if " #SIZE " == 16 \n\t" \
+ "ulw %[temp0], 8(%[top]) \n\t" \
+ "ulw %[temp1], 12(%[top]) \n\t" \
+ CLIPPING() \
+ "usw %[temp0], 8(%[dst]) \n\t" \
+ "usw %[temp1], 12(%[dst]) \n\t" \
+ ".endif \n\t" \
+ : [leftY_1]"+&r"(leftY_1), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), \
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \
+ : [left_1]"r"(left_1), [top]"r"((TOP)), [dst]"r"((DST)) \
+ : "memory" \
+ ); \
+} while (0)
+
+#define CLIP_TO_DST(DST, LEFT, TOP, SIZE) do { \
+ int y; \
+ const int left_1 = ((int)(LEFT)[-1] << 16) + (LEFT)[-1]; \
+ for (y = 0; y < (SIZE); ++y) { \
+ CLIP_8B_TO_DST((DST), (LEFT), (TOP), (SIZE)); \
+ (DST) += BPS; \
+ } \
+} while (0)
+
+#define TRUE_MOTION(DST, LEFT, TOP, SIZE) \
+static WEBP_INLINE void TrueMotion##SIZE(uint8_t* (DST), const uint8_t* (LEFT),\
+ const uint8_t* (TOP)) { \
+ if ((LEFT) != NULL) { \
+ if ((TOP) != NULL) { \
+ CLIP_TO_DST((DST), (LEFT), (TOP), (SIZE)); \
+ } else { \
+ HorizontalPred##SIZE((DST), (LEFT)); \
+ } \
+ } else { \
+ /* true motion without left samples (hence: with default 129 value) */ \
+ /* is equivalent to VE prediction where you just copy the top samples. */ \
+ /* Note that if top samples are not available, the default value is */ \
+ /* then 129, and not 127 as in the VerticalPred case. */ \
+ if ((TOP) != NULL) { \
+ VerticalPred##SIZE((DST), (TOP)); \
+ } else { \
+ FILL_8_OR_16((DST), 129, (SIZE)); \
+ } \
+ } \
+}
+
+TRUE_MOTION(dst, left, top, 8)
+TRUE_MOTION(dst, left, top, 16)
+
+#undef TRUE_MOTION
+#undef CLIP_TO_DST
+#undef CLIP_8B_TO_DST
+#undef CLIPPING
+
+static WEBP_INLINE void DCMode16(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ int DC, DC1;
+ int temp0, temp1, temp2, temp3;
+
+ __asm__ volatile(
+ "beqz %[top], 2f \n\t"
+ LOAD_WITH_OFFSET_X4(temp0, temp1, temp2, temp3, top,
+ 0, 4, 8, 12,
+ 0, 0, 0, 0,
+ 0)
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "raddu.w.qb %[temp2], %[temp2] \n\t"
+ "raddu.w.qb %[temp3], %[temp3] \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "addu %[temp2], %[temp2], %[temp3] \n\t"
+ "addu %[DC], %[temp0], %[temp2] \n\t"
+ "move %[DC1], %[DC] \n\t"
+ "beqz %[left], 1f \n\t"
+ LOAD_WITH_OFFSET_X4(temp0, temp1, temp2, temp3, left,
+ 0, 4, 8, 12,
+ 0, 0, 0, 0,
+ 0)
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "raddu.w.qb %[temp2], %[temp2] \n\t"
+ "raddu.w.qb %[temp3], %[temp3] \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "addu %[temp2], %[temp2], %[temp3] \n\t"
+ "addu %[DC1], %[temp0], %[temp2] \n\t"
+ "1: \n\t"
+ "addu %[DC], %[DC], %[DC1] \n\t"
+ "j 3f \n\t"
+ "2: \n\t"
+ "beqz %[left], 4f \n\t"
+ LOAD_WITH_OFFSET_X4(temp0, temp1, temp2, temp3, left,
+ 0, 4, 8, 12,
+ 0, 0, 0, 0,
+ 0)
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "raddu.w.qb %[temp2], %[temp2] \n\t"
+ "raddu.w.qb %[temp3], %[temp3] \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "addu %[temp2], %[temp2], %[temp3] \n\t"
+ "addu %[DC], %[temp0], %[temp2] \n\t"
+ "addu %[DC], %[DC], %[DC] \n\t"
+ "3: \n\t"
+ "shra_r.w %[DC], %[DC], 5 \n\t"
+ "j 5f \n\t"
+ "4: \n\t"
+ "li %[DC], 0x80 \n\t"
+ "5: \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [DC]"=&r"(DC),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [DC1]"=&r"(DC1)
+ : [left]"r"(left), [top]"r"(top)
+ : "memory"
+ );
+
+ FILL_8_OR_16(dst, DC, 16);
+}
+
+static WEBP_INLINE void DCMode8(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ int DC, DC1;
+ int temp0, temp1, temp2, temp3;
+
+ __asm__ volatile(
+ "beqz %[top], 2f \n\t"
+ "ulw %[temp0], 0(%[top]) \n\t"
+ "ulw %[temp1], 4(%[top]) \n\t"
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "addu %[DC], %[temp0], %[temp1] \n\t"
+ "move %[DC1], %[DC] \n\t"
+ "beqz %[left], 1f \n\t"
+ "ulw %[temp2], 0(%[left]) \n\t"
+ "ulw %[temp3], 4(%[left]) \n\t"
+ "raddu.w.qb %[temp2], %[temp2] \n\t"
+ "raddu.w.qb %[temp3], %[temp3] \n\t"
+ "addu %[DC1], %[temp2], %[temp3] \n\t"
+ "1: \n\t"
+ "addu %[DC], %[DC], %[DC1] \n\t"
+ "j 3f \n\t"
+ "2: \n\t"
+ "beqz %[left], 4f \n\t"
+ "ulw %[temp2], 0(%[left]) \n\t"
+ "ulw %[temp3], 4(%[left]) \n\t"
+ "raddu.w.qb %[temp2], %[temp2] \n\t"
+ "raddu.w.qb %[temp3], %[temp3] \n\t"
+ "addu %[DC], %[temp2], %[temp3] \n\t"
+ "addu %[DC], %[DC], %[DC] \n\t"
+ "3: \n\t"
+ "shra_r.w %[DC], %[DC], 4 \n\t"
+ "j 5f \n\t"
+ "4: \n\t"
+ "li %[DC], 0x80 \n\t"
+ "5: \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [DC]"=&r"(DC),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [DC1]"=&r"(DC1)
+ : [left]"r"(left), [top]"r"(top)
+ : "memory"
+ );
+
+ FILL_8_OR_16(dst, DC, 8);
+}
+
+static void DC4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1;
+ __asm__ volatile(
+ "ulw %[temp0], 0(%[top]) \n\t"
+ "ulw %[temp1], -5(%[top]) \n\t"
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "raddu.w.qb %[temp1], %[temp1] \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "addiu %[temp0], %[temp0], 4 \n\t"
+ "srl %[temp0], %[temp0], 3 \n\t"
+ "replv.qb %[temp0], %[temp0] \n\t"
+ "usw %[temp0], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp0], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp0], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp0], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void TM4(uint8_t* dst, const uint8_t* top) {
+ int a10, a32, temp0, temp1, temp2, temp3, temp4, temp5;
+ const int c35 = 0xff00ff;
+ __asm__ volatile (
+ "lbu %[temp1], 0(%[top]) \n\t"
+ "lbu %[a10], 1(%[top]) \n\t"
+ "lbu %[temp2], 2(%[top]) \n\t"
+ "lbu %[a32], 3(%[top]) \n\t"
+ "ulw %[temp0], -5(%[top]) \n\t"
+ "lbu %[temp4], -1(%[top]) \n\t"
+ "append %[a10], %[temp1], 16 \n\t"
+ "append %[a32], %[temp2], 16 \n\t"
+ "replv.ph %[temp4], %[temp4] \n\t"
+ "shrl.ph %[temp1], %[temp0], 8 \n\t"
+ "and %[temp0], %[temp0], %[c35] \n\t"
+ "subu.ph %[temp1], %[temp1], %[temp4] \n\t"
+ "subu.ph %[temp0], %[temp0], %[temp4] \n\t"
+ "srl %[temp2], %[temp1], 16 \n\t"
+ "srl %[temp3], %[temp0], 16 \n\t"
+ "replv.ph %[temp2], %[temp2] \n\t"
+ "replv.ph %[temp3], %[temp3] \n\t"
+ "replv.ph %[temp4], %[temp1] \n\t"
+ "replv.ph %[temp5], %[temp0] \n\t"
+ "addu.ph %[temp0], %[temp3], %[a10] \n\t"
+ "addu.ph %[temp1], %[temp3], %[a32] \n\t"
+ "addu.ph %[temp3], %[temp2], %[a10] \n\t"
+ "addu.ph %[temp2], %[temp2], %[a32] \n\t"
+ "shll_s.ph %[temp0], %[temp0], 7 \n\t"
+ "shll_s.ph %[temp1], %[temp1], 7 \n\t"
+ "shll_s.ph %[temp3], %[temp3], 7 \n\t"
+ "shll_s.ph %[temp2], %[temp2], 7 \n\t"
+ "precrqu_s.qb.ph %[temp0], %[temp1], %[temp0] \n\t"
+ "precrqu_s.qb.ph %[temp1], %[temp2], %[temp3] \n\t"
+ "addu.ph %[temp2], %[temp5], %[a10] \n\t"
+ "addu.ph %[temp3], %[temp5], %[a32] \n\t"
+ "addu.ph %[temp5], %[temp4], %[a10] \n\t"
+ "addu.ph %[temp4], %[temp4], %[a32] \n\t"
+ "shll_s.ph %[temp2], %[temp2], 7 \n\t"
+ "shll_s.ph %[temp3], %[temp3], 7 \n\t"
+ "shll_s.ph %[temp4], %[temp4], 7 \n\t"
+ "shll_s.ph %[temp5], %[temp5], 7 \n\t"
+ "precrqu_s.qb.ph %[temp2], %[temp3], %[temp2] \n\t"
+ "precrqu_s.qb.ph %[temp3], %[temp4], %[temp5] \n\t"
+ "usw %[temp1], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp0], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp3], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp2], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [a10]"=&r"(a10), [a32]"=&r"(a32)
+ : [c35]"r"(c35), [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void VE4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6;
+ __asm__ volatile(
+ "ulw %[temp0], -1(%[top]) \n\t"
+ "ulh %[temp1], 3(%[top]) \n\t"
+ "preceu.ph.qbr %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbl %[temp3], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp4], %[temp1] \n\t"
+ "packrl.ph %[temp5], %[temp3], %[temp2] \n\t"
+ "packrl.ph %[temp6], %[temp4], %[temp3] \n\t"
+ "shll.ph %[temp5], %[temp5], 1 \n\t"
+ "shll.ph %[temp6], %[temp6], 1 \n\t"
+ "addq.ph %[temp2], %[temp5], %[temp2] \n\t"
+ "addq.ph %[temp6], %[temp6], %[temp4] \n\t"
+ "addq.ph %[temp2], %[temp2], %[temp3] \n\t"
+ "addq.ph %[temp6], %[temp6], %[temp3] \n\t"
+ "shra_r.ph %[temp2], %[temp2], 2 \n\t"
+ "shra_r.ph %[temp6], %[temp6], 2 \n\t"
+ "precr.qb.ph %[temp4], %[temp6], %[temp2] \n\t"
+ "usw %[temp4], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp4], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp4], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp4], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void HE4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6;
+ __asm__ volatile(
+ "ulw %[temp0], -4(%[top]) \n\t"
+ "lbu %[temp1], -5(%[top]) \n\t"
+ "preceu.ph.qbr %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbl %[temp3], %[temp0] \n\t"
+ "replv.ph %[temp4], %[temp1] \n\t"
+ "packrl.ph %[temp5], %[temp3], %[temp2] \n\t"
+ "packrl.ph %[temp6], %[temp2], %[temp4] \n\t"
+ "shll.ph %[temp5], %[temp5], 1 \n\t"
+ "shll.ph %[temp6], %[temp6], 1 \n\t"
+ "addq.ph %[temp3], %[temp3], %[temp5] \n\t"
+ "addq.ph %[temp3], %[temp3], %[temp2] \n\t"
+ "addq.ph %[temp2], %[temp2], %[temp6] \n\t"
+ "addq.ph %[temp2], %[temp2], %[temp4] \n\t"
+ "shra_r.ph %[temp3], %[temp3], 2 \n\t"
+ "shra_r.ph %[temp2], %[temp2], 2 \n\t"
+ "replv.qb %[temp0], %[temp3] \n\t"
+ "replv.qb %[temp1], %[temp2] \n\t"
+ "srl %[temp3], %[temp3], 16 \n\t"
+ "srl %[temp2], %[temp2], 16 \n\t"
+ "replv.qb %[temp3], %[temp3] \n\t"
+ "replv.qb %[temp2], %[temp2] \n\t"
+ "usw %[temp3], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp0], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp2], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp1], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void RD4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ int temp6, temp7, temp8, temp9, temp10, temp11;
+ __asm__ volatile(
+ "ulw %[temp0], -5(%[top]) \n\t"
+ "ulw %[temp1], -1(%[top]) \n\t"
+ "preceu.ph.qbl %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp3], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp4], %[temp1] \n\t"
+ "preceu.ph.qbl %[temp5], %[temp1] \n\t"
+ "packrl.ph %[temp6], %[temp2], %[temp3] \n\t"
+ "packrl.ph %[temp7], %[temp4], %[temp2] \n\t"
+ "packrl.ph %[temp8], %[temp5], %[temp4] \n\t"
+ "shll.ph %[temp6], %[temp6], 1 \n\t"
+ "addq.ph %[temp9], %[temp2], %[temp6] \n\t"
+ "shll.ph %[temp7], %[temp7], 1 \n\t"
+ "addq.ph %[temp9], %[temp9], %[temp3] \n\t"
+ "shll.ph %[temp8], %[temp8], 1 \n\t"
+ "shra_r.ph %[temp9], %[temp9], 2 \n\t"
+ "addq.ph %[temp10], %[temp4], %[temp7] \n\t"
+ "addq.ph %[temp11], %[temp5], %[temp8] \n\t"
+ "addq.ph %[temp10], %[temp10], %[temp2] \n\t"
+ "addq.ph %[temp11], %[temp11], %[temp4] \n\t"
+ "shra_r.ph %[temp10], %[temp10], 2 \n\t"
+ "shra_r.ph %[temp11], %[temp11], 2 \n\t"
+ "lbu %[temp0], 3(%[top]) \n\t"
+ "lbu %[temp1], 2(%[top]) \n\t"
+ "lbu %[temp2], 1(%[top]) \n\t"
+ "sll %[temp1], %[temp1], 1 \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "addu %[temp0], %[temp0], %[temp2] \n\t"
+ "precr.qb.ph %[temp9], %[temp10], %[temp9] \n\t"
+ "shra_r.w %[temp0], %[temp0], 2 \n\t"
+ "precr.qb.ph %[temp10], %[temp11], %[temp10] \n\t"
+ "usw %[temp9], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp10], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "prepend %[temp9], %[temp11], 8 \n\t"
+ "prepend %[temp10], %[temp0], 8 \n\t"
+ "usw %[temp9], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp10], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void VR4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8, temp9;
+ __asm__ volatile (
+ "ulw %[temp0], -4(%[top]) \n\t"
+ "ulw %[temp1], 0(%[top]) \n\t"
+ "preceu.ph.qbl %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp0], %[temp0] \n\t"
+ "preceu.ph.qbla %[temp3], %[temp1] \n\t"
+ "preceu.ph.qbra %[temp1], %[temp1] \n\t"
+ "packrl.ph %[temp7], %[temp3], %[temp2] \n\t"
+ "addqh_r.ph %[temp4], %[temp1], %[temp3] \n\t"
+ "move %[temp6], %[temp1] \n\t"
+ "append %[temp1], %[temp2], 16 \n\t"
+ "shll.ph %[temp9], %[temp6], 1 \n\t"
+ "addqh_r.ph %[temp5], %[temp7], %[temp6] \n\t"
+ "shll.ph %[temp8], %[temp7], 1 \n\t"
+ "addu.ph %[temp3], %[temp7], %[temp3] \n\t"
+ "addu.ph %[temp1], %[temp1], %[temp6] \n\t"
+ "packrl.ph %[temp7], %[temp2], %[temp0] \n\t"
+ "addu.ph %[temp6], %[temp0], %[temp2] \n\t"
+ "addu.ph %[temp3], %[temp3], %[temp9] \n\t"
+ "addu.ph %[temp1], %[temp1], %[temp8] \n\t"
+ "shll.ph %[temp7], %[temp7], 1 \n\t"
+ "shra_r.ph %[temp3], %[temp3], 2 \n\t"
+ "shra_r.ph %[temp1], %[temp1], 2 \n\t"
+ "addu.ph %[temp6], %[temp6], %[temp7] \n\t"
+ "shra_r.ph %[temp6], %[temp6], 2 \n\t"
+ "precrq.ph.w %[temp8], %[temp4], %[temp5] \n\t"
+ "append %[temp4], %[temp5], 16 \n\t"
+ "precrq.ph.w %[temp2], %[temp3], %[temp1] \n\t"
+ "append %[temp3], %[temp1], 16 \n\t"
+ "precr.qb.ph %[temp8], %[temp8], %[temp4] \n\t"
+ "precr.qb.ph %[temp3], %[temp2], %[temp3] \n\t"
+ "usw %[temp8], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp3], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "append %[temp3], %[temp6], 8 \n\t"
+ "srl %[temp6], %[temp6], 16 \n\t"
+ "append %[temp8], %[temp6], 8 \n\t"
+ "usw %[temp3], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp8], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void LD4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ int temp6, temp7, temp8, temp9, temp10, temp11;
+ __asm__ volatile(
+ "ulw %[temp0], 0(%[top]) \n\t"
+ "ulw %[temp1], 4(%[top]) \n\t"
+ "preceu.ph.qbl %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp3], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp4], %[temp1] \n\t"
+ "preceu.ph.qbl %[temp5], %[temp1] \n\t"
+ "packrl.ph %[temp6], %[temp2], %[temp3] \n\t"
+ "packrl.ph %[temp7], %[temp4], %[temp2] \n\t"
+ "packrl.ph %[temp8], %[temp5], %[temp4] \n\t"
+ "shll.ph %[temp6], %[temp6], 1 \n\t"
+ "addq.ph %[temp9], %[temp2], %[temp6] \n\t"
+ "shll.ph %[temp7], %[temp7], 1 \n\t"
+ "addq.ph %[temp9], %[temp9], %[temp3] \n\t"
+ "shll.ph %[temp8], %[temp8], 1 \n\t"
+ "shra_r.ph %[temp9], %[temp9], 2 \n\t"
+ "addq.ph %[temp10], %[temp4], %[temp7] \n\t"
+ "addq.ph %[temp11], %[temp5], %[temp8] \n\t"
+ "addq.ph %[temp10], %[temp10], %[temp2] \n\t"
+ "addq.ph %[temp11], %[temp11], %[temp4] \n\t"
+ "shra_r.ph %[temp10], %[temp10], 2 \n\t"
+ "shra_r.ph %[temp11], %[temp11], 2 \n\t"
+ "srl %[temp1], %[temp1], 24 \n\t"
+ "sll %[temp1], %[temp1], 1 \n\t"
+ "raddu.w.qb %[temp5], %[temp5] \n\t"
+ "precr.qb.ph %[temp9], %[temp10], %[temp9] \n\t"
+ "precr.qb.ph %[temp10], %[temp11], %[temp10] \n\t"
+ "addu %[temp1], %[temp1], %[temp5] \n\t"
+ "shra_r.w %[temp1], %[temp1], 2 \n\t"
+ "usw %[temp9], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp10], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "prepend %[temp9], %[temp11], 8 \n\t"
+ "prepend %[temp10], %[temp1], 8 \n\t"
+ "usw %[temp9], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp10], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9), [temp10]"=&r"(temp10), [temp11]"=&r"(temp11)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void VL4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8, temp9;
+ __asm__ volatile (
+ "ulw %[temp0], 0(%[top]) \n\t"
+ "ulw %[temp1], 4(%[top]) \n\t"
+ "preceu.ph.qbla %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbra %[temp0], %[temp0] \n\t"
+ "preceu.ph.qbl %[temp3], %[temp1] \n\t"
+ "preceu.ph.qbr %[temp1], %[temp1] \n\t"
+ "addqh_r.ph %[temp4], %[temp0], %[temp2] \n\t"
+ "packrl.ph %[temp7], %[temp1], %[temp0] \n\t"
+ "precrq.ph.w %[temp6], %[temp1], %[temp2] \n\t"
+ "shll.ph %[temp9], %[temp2], 1 \n\t"
+ "addqh_r.ph %[temp5], %[temp7], %[temp2] \n\t"
+ "shll.ph %[temp8], %[temp7], 1 \n\t"
+ "addu.ph %[temp2], %[temp2], %[temp6] \n\t"
+ "addu.ph %[temp0], %[temp0], %[temp7] \n\t"
+ "packrl.ph %[temp7], %[temp3], %[temp1] \n\t"
+ "addu.ph %[temp6], %[temp1], %[temp3] \n\t"
+ "addu.ph %[temp2], %[temp2], %[temp8] \n\t"
+ "addu.ph %[temp0], %[temp0], %[temp9] \n\t"
+ "shll.ph %[temp7], %[temp7], 1 \n\t"
+ "shra_r.ph %[temp2], %[temp2], 2 \n\t"
+ "shra_r.ph %[temp0], %[temp0], 2 \n\t"
+ "addu.ph %[temp6], %[temp6], %[temp7] \n\t"
+ "shra_r.ph %[temp6], %[temp6], 2 \n\t"
+ "precrq.ph.w %[temp8], %[temp5], %[temp4] \n\t"
+ "append %[temp5], %[temp4], 16 \n\t"
+ "precrq.ph.w %[temp3], %[temp2], %[temp0] \n\t"
+ "append %[temp2], %[temp0], 16 \n\t"
+ "precr.qb.ph %[temp8], %[temp8], %[temp5] \n\t"
+ "precr.qb.ph %[temp3], %[temp3], %[temp2] \n\t"
+ "usw %[temp8], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "prepend %[temp8], %[temp6], 8 \n\t"
+ "usw %[temp3], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "srl %[temp6], %[temp6], 16 \n\t"
+ "prepend %[temp3], %[temp6], 8 \n\t"
+ "usw %[temp8], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp3], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void HD4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8, temp9;
+ __asm__ volatile (
+ "ulw %[temp0], -5(%[top]) \n\t"
+ "ulw %[temp1], -1(%[top]) \n\t"
+ "preceu.ph.qbla %[temp2], %[temp0] \n\t"
+ "preceu.ph.qbra %[temp0], %[temp0] \n\t"
+ "preceu.ph.qbl %[temp3], %[temp1] \n\t"
+ "preceu.ph.qbr %[temp1], %[temp1] \n\t"
+ "addqh_r.ph %[temp4], %[temp0], %[temp2] \n\t"
+ "packrl.ph %[temp7], %[temp1], %[temp0] \n\t"
+ "precrq.ph.w %[temp6], %[temp1], %[temp2] \n\t"
+ "shll.ph %[temp9], %[temp2], 1 \n\t"
+ "addqh_r.ph %[temp5], %[temp7], %[temp2] \n\t"
+ "shll.ph %[temp8], %[temp7], 1 \n\t"
+ "addu.ph %[temp2], %[temp2], %[temp6] \n\t"
+ "addu.ph %[temp0], %[temp0], %[temp7] \n\t"
+ "packrl.ph %[temp7], %[temp3], %[temp1] \n\t"
+ "addu.ph %[temp6], %[temp1], %[temp3] \n\t"
+ "addu.ph %[temp2], %[temp2], %[temp8] \n\t"
+ "addu.ph %[temp0], %[temp0], %[temp9] \n\t"
+ "shll.ph %[temp7], %[temp7], 1 \n\t"
+ "shra_r.ph %[temp2], %[temp2], 2 \n\t"
+ "shra_r.ph %[temp0], %[temp0], 2 \n\t"
+ "addu.ph %[temp6], %[temp6], %[temp7] \n\t"
+ "shra_r.ph %[temp6], %[temp6], 2 \n\t"
+ "precrq.ph.w %[temp1], %[temp2], %[temp5] \n\t"
+ "precrq.ph.w %[temp3], %[temp0], %[temp4] \n\t"
+ "precr.qb.ph %[temp7], %[temp6], %[temp1] \n\t"
+ "precr.qb.ph %[temp6], %[temp1], %[temp3] \n\t"
+ "usw %[temp7], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp6], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ "append %[temp2], %[temp5], 16 \n\t"
+ "append %[temp0], %[temp4], 16 \n\t"
+ "precr.qb.ph %[temp5], %[temp3], %[temp2] \n\t"
+ "precr.qb.ph %[temp4], %[temp2], %[temp0] \n\t"
+ "usw %[temp5], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp4], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+static void HU4(uint8_t* dst, const uint8_t* top) {
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+ __asm__ volatile (
+ "ulw %[temp0], -5(%[top]) \n\t"
+ "preceu.ph.qbl %[temp1], %[temp0] \n\t"
+ "preceu.ph.qbr %[temp2], %[temp0] \n\t"
+ "packrl.ph %[temp3], %[temp1], %[temp2] \n\t"
+ "replv.qb %[temp7], %[temp2] \n\t"
+ "addqh_r.ph %[temp4], %[temp1], %[temp3] \n\t"
+ "addqh_r.ph %[temp5], %[temp3], %[temp2] \n\t"
+ "shll.ph %[temp6], %[temp3], 1 \n\t"
+ "addu.ph %[temp3], %[temp2], %[temp3] \n\t"
+ "addu.ph %[temp6], %[temp1], %[temp6] \n\t"
+ "shll.ph %[temp0], %[temp2], 1 \n\t"
+ "addu.ph %[temp6], %[temp6], %[temp2] \n\t"
+ "addu.ph %[temp0], %[temp3], %[temp0] \n\t"
+ "shra_r.ph %[temp6], %[temp6], 2 \n\t"
+ "shra_r.ph %[temp0], %[temp0], 2 \n\t"
+ "packrl.ph %[temp3], %[temp6], %[temp5] \n\t"
+ "precrq.ph.w %[temp2], %[temp6], %[temp4] \n\t"
+ "append %[temp0], %[temp5], 16 \n\t"
+ "precr.qb.ph %[temp3], %[temp3], %[temp2] \n\t"
+ "usw %[temp3], 0*" XSTR(BPS) "(%[dst]) \n\t"
+ "precr.qb.ph %[temp1], %[temp7], %[temp0] \n\t"
+ "usw %[temp7], 3*" XSTR(BPS) "(%[dst]) \n\t"
+ "packrl.ph %[temp2], %[temp1], %[temp3] \n\t"
+ "usw %[temp1], 2*" XSTR(BPS) "(%[dst]) \n\t"
+ "usw %[temp2], 1*" XSTR(BPS) "(%[dst]) \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7)
+ : [top]"r"(top), [dst]"r"(dst)
+ : "memory"
+ );
+}
+
+//------------------------------------------------------------------------------
+// Chroma 8x8 prediction (paragraph 12.2)
+
+static void IntraChromaPreds_MIPSdspR2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ // U block
+ DCMode8(C8DC8 + dst, left, top);
+ VerticalPred8(C8VE8 + dst, top);
+ HorizontalPred8(C8HE8 + dst, left);
+ TrueMotion8(C8TM8 + dst, left, top);
+ // V block
+ dst += 8;
+ if (top) top += 8;
+ if (left) left += 16;
+ DCMode8(C8DC8 + dst, left, top);
+ VerticalPred8(C8VE8 + dst, top);
+ HorizontalPred8(C8HE8 + dst, left);
+ TrueMotion8(C8TM8 + dst, left, top);
+}
+
+//------------------------------------------------------------------------------
+// luma 16x16 prediction (paragraph 12.3)
+
+static void Intra16Preds_MIPSdspR2(uint8_t* dst,
+ const uint8_t* left, const uint8_t* top) {
+ DCMode16(I16DC16 + dst, left, top);
+ VerticalPred16(I16VE16 + dst, top);
+ HorizontalPred16(I16HE16 + dst, left);
+ TrueMotion16(I16TM16 + dst, left, top);
+}
+
+// Left samples are top[-5 .. -2], top_left is top[-1], top are
+// located at top[0..3], and top right is top[4..7]
+static void Intra4Preds_MIPSdspR2(uint8_t* dst, const uint8_t* top) {
+ DC4(I4DC4 + dst, top);
+ TM4(I4TM4 + dst, top);
+ VE4(I4VE4 + dst, top);
+ HE4(I4HE4 + dst, top);
+ RD4(I4RD4 + dst, top);
+ VR4(I4VR4 + dst, top);
+ LD4(I4LD4 + dst, top);
+ VL4(I4VL4 + dst, top);
+ HD4(I4HD4 + dst, top);
+ HU4(I4HU4 + dst, top);
+}
+
+//------------------------------------------------------------------------------
+// Metric
+
+#if !defined(WORK_AROUND_GCC)
+
+#define GET_SSE_INNER(A) \
+ "lw %[temp0], " #A "(%[a]) \n\t" \
+ "lw %[temp1], " #A "(%[b]) \n\t" \
+ "preceu.ph.qbr %[temp2], %[temp0] \n\t" \
+ "preceu.ph.qbl %[temp0], %[temp0] \n\t" \
+ "preceu.ph.qbr %[temp3], %[temp1] \n\t" \
+ "preceu.ph.qbl %[temp1], %[temp1] \n\t" \
+ "subq.ph %[temp2], %[temp2], %[temp3] \n\t" \
+ "subq.ph %[temp0], %[temp0], %[temp1] \n\t" \
+ "dpa.w.ph $ac0, %[temp2], %[temp2] \n\t" \
+ "dpa.w.ph $ac0, %[temp0], %[temp0] \n\t"
+
+#define GET_SSE(A, B, C, D) \
+ GET_SSE_INNER(A) \
+ GET_SSE_INNER(B) \
+ GET_SSE_INNER(C) \
+ GET_SSE_INNER(D)
+
+static int SSE16x16_MIPSdspR2(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3;
+ __asm__ volatile (
+ "mult $zero, $zero \n\t"
+ GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS)
+ GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS)
+ GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS)
+ GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS)
+ GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS)
+ GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS)
+ GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS)
+ GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS)
+ GET_SSE( 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS)
+ GET_SSE( 9 * BPS, 4 + 9 * BPS, 8 + 9 * BPS, 12 + 9 * BPS)
+ GET_SSE(10 * BPS, 4 + 10 * BPS, 8 + 10 * BPS, 12 + 10 * BPS)
+ GET_SSE(11 * BPS, 4 + 11 * BPS, 8 + 11 * BPS, 12 + 11 * BPS)
+ GET_SSE(12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS)
+ GET_SSE(13 * BPS, 4 + 13 * BPS, 8 + 13 * BPS, 12 + 13 * BPS)
+ GET_SSE(14 * BPS, 4 + 14 * BPS, 8 + 14 * BPS, 12 + 14 * BPS)
+ GET_SSE(15 * BPS, 4 + 15 * BPS, 8 + 15 * BPS, 12 + 15 * BPS)
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+static int SSE16x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3;
+ __asm__ volatile (
+ "mult $zero, $zero \n\t"
+ GET_SSE( 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS)
+ GET_SSE( 1 * BPS, 4 + 1 * BPS, 8 + 1 * BPS, 12 + 1 * BPS)
+ GET_SSE( 2 * BPS, 4 + 2 * BPS, 8 + 2 * BPS, 12 + 2 * BPS)
+ GET_SSE( 3 * BPS, 4 + 3 * BPS, 8 + 3 * BPS, 12 + 3 * BPS)
+ GET_SSE( 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS)
+ GET_SSE( 5 * BPS, 4 + 5 * BPS, 8 + 5 * BPS, 12 + 5 * BPS)
+ GET_SSE( 6 * BPS, 4 + 6 * BPS, 8 + 6 * BPS, 12 + 6 * BPS)
+ GET_SSE( 7 * BPS, 4 + 7 * BPS, 8 + 7 * BPS, 12 + 7 * BPS)
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+static int SSE8x8_MIPSdspR2(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3;
+ __asm__ volatile (
+ "mult $zero, $zero \n\t"
+ GET_SSE(0 * BPS, 4 + 0 * BPS, 1 * BPS, 4 + 1 * BPS)
+ GET_SSE(2 * BPS, 4 + 2 * BPS, 3 * BPS, 4 + 3 * BPS)
+ GET_SSE(4 * BPS, 4 + 4 * BPS, 5 * BPS, 4 + 5 * BPS)
+ GET_SSE(6 * BPS, 4 + 6 * BPS, 7 * BPS, 4 + 7 * BPS)
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+static int SSE4x4_MIPSdspR2(const uint8_t* a, const uint8_t* b) {
+ int count;
+ int temp0, temp1, temp2, temp3;
+ __asm__ volatile (
+ "mult $zero, $zero \n\t"
+ GET_SSE(0 * BPS, 1 * BPS, 2 * BPS, 3 * BPS)
+ "mflo %[count] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [count]"=&r"(count)
+ : [a]"r"(a), [b]"r"(b)
+ : "memory", "hi", "lo"
+ );
+ return count;
+}
+
+#undef GET_SSE
+#undef GET_SSE_INNER
+
+#endif // !WORK_AROUND_GCC
+
+#undef FILL_8_OR_16
+#undef FILL_PART
+#undef OUTPUT_EARLY_CLOBBER_REGS_17
+#undef MUL_HALF
+#undef ABS_X8
+#undef ADD_SUB_HALVES_X4
+
+//------------------------------------------------------------------------------
+// Quantization
+//
+
+// macro for one pass through for loop in QuantizeBlock reading 2 values at time
+// QUANTDIV macro inlined
+// J - offset in bytes (kZigzag[n] * 2)
+// K - offset in bytes (kZigzag[n] * 4)
+// N - offset in bytes (n * 2)
+// N1 - offset in bytes ((n + 1) * 2)
+#define QUANTIZE_ONE(J, K, N, N1) \
+ "ulw %[temp1], " #J "(%[ppin]) \n\t" \
+ "ulw %[temp2], " #J "(%[ppsharpen]) \n\t" \
+ "lhu %[temp3], " #K "(%[ppzthresh]) \n\t" \
+ "lhu %[temp6], " #K "+4(%[ppzthresh]) \n\t" \
+ "absq_s.ph %[temp4], %[temp1] \n\t" \
+ "ins %[temp3], %[temp6], 16, 16 \n\t" \
+ "addu.ph %[coeff], %[temp4], %[temp2] \n\t" \
+ "shra.ph %[sign], %[temp1], 15 \n\t" \
+ "li %[level], 0x10001 \n\t" \
+ "cmp.lt.ph %[temp3], %[coeff] \n\t" \
+ "lhu %[temp1], " #J "(%[ppiq]) \n\t" \
+ "pick.ph %[temp5], %[level], $0 \n\t" \
+ "lw %[temp2], " #K "(%[ppbias]) \n\t" \
+ "beqz %[temp5], 0f \n\t" \
+ "lhu %[temp3], " #J "(%[ppq]) \n\t" \
+ "beq %[temp5], %[level], 1f \n\t" \
+ "andi %[temp5], %[temp5], 0x1 \n\t" \
+ "andi %[temp4], %[coeff], 0xffff \n\t" \
+ "beqz %[temp5], 2f \n\t" \
+ "mul %[level], %[temp4], %[temp1] \n\t" \
+ "sh $0, " #J "+2(%[ppin]) \n\t" \
+ "sh $0, " #N1 "(%[pout]) \n\t" \
+ "addu %[level], %[level], %[temp2] \n\t" \
+ "sra %[level], %[level], 17 \n\t" \
+ "slt %[temp4], %[max_level], %[level] \n\t" \
+ "movn %[level], %[max_level], %[temp4] \n\t" \
+ "andi %[temp6], %[sign], 0xffff \n\t" \
+ "xor %[level], %[level], %[temp6] \n\t" \
+ "subu %[level], %[level], %[temp6] \n\t" \
+ "mul %[temp5], %[level], %[temp3] \n\t" \
+ "or %[ret], %[ret], %[level] \n\t" \
+ "sh %[level], " #N "(%[pout]) \n\t" \
+ "sh %[temp5], " #J "(%[ppin]) \n\t" \
+ "j 3f \n\t" \
+"2: \n\t" \
+ "lhu %[temp1], " #J "+2(%[ppiq]) \n\t" \
+ "srl %[temp5], %[coeff], 16 \n\t" \
+ "mul %[level], %[temp5], %[temp1] \n\t" \
+ "lw %[temp2], " #K "+4(%[ppbias]) \n\t" \
+ "lhu %[temp3], " #J "+2(%[ppq]) \n\t" \
+ "addu %[level], %[level], %[temp2] \n\t" \
+ "sra %[level], %[level], 17 \n\t" \
+ "srl %[temp6], %[sign], 16 \n\t" \
+ "slt %[temp4], %[max_level], %[level] \n\t" \
+ "movn %[level], %[max_level], %[temp4] \n\t" \
+ "xor %[level], %[level], %[temp6] \n\t" \
+ "subu %[level], %[level], %[temp6] \n\t" \
+ "mul %[temp5], %[level], %[temp3] \n\t" \
+ "sh $0, " #J "(%[ppin]) \n\t" \
+ "sh $0, " #N "(%[pout]) \n\t" \
+ "or %[ret], %[ret], %[level] \n\t" \
+ "sh %[temp5], " #J "+2(%[ppin]) \n\t" \
+ "sh %[level], " #N1 "(%[pout]) \n\t" \
+ "j 3f \n\t" \
+"1: \n\t" \
+ "lhu %[temp1], " #J "(%[ppiq]) \n\t" \
+ "lw %[temp2], " #K "(%[ppbias]) \n\t" \
+ "ulw %[temp3], " #J "(%[ppq]) \n\t" \
+ "andi %[temp5], %[coeff], 0xffff \n\t" \
+ "srl %[temp0], %[coeff], 16 \n\t" \
+ "lhu %[temp6], " #J "+2(%[ppiq]) \n\t" \
+ "lw %[coeff], " #K "+4(%[ppbias]) \n\t" \
+ "mul %[level], %[temp5], %[temp1] \n\t" \
+ "mul %[temp4], %[temp0], %[temp6] \n\t" \
+ "addu %[level], %[level], %[temp2] \n\t" \
+ "addu %[temp4], %[temp4], %[coeff] \n\t" \
+ "precrq.ph.w %[level], %[temp4], %[level] \n\t" \
+ "shra.ph %[level], %[level], 1 \n\t" \
+ "cmp.lt.ph %[max_level1],%[level] \n\t" \
+ "pick.ph %[level], %[max_level], %[level] \n\t" \
+ "xor %[level], %[level], %[sign] \n\t" \
+ "subu.ph %[level], %[level], %[sign] \n\t" \
+ "mul.ph %[temp3], %[level], %[temp3] \n\t" \
+ "or %[ret], %[ret], %[level] \n\t" \
+ "sh %[level], " #N "(%[pout]) \n\t" \
+ "srl %[level], %[level], 16 \n\t" \
+ "sh %[level], " #N1 "(%[pout]) \n\t" \
+ "usw %[temp3], " #J "(%[ppin]) \n\t" \
+ "j 3f \n\t" \
+"0: \n\t" \
+ "sh $0, " #N "(%[pout]) \n\t" \
+ "sh $0, " #N1 "(%[pout]) \n\t" \
+ "usw $0, " #J "(%[ppin]) \n\t" \
+"3: \n\t"
+
+static int QuantizeBlock_MIPSdspR2(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ int temp0, temp1, temp2, temp3, temp4, temp5,temp6;
+ int sign, coeff, level;
+ int max_level = MAX_LEVEL;
+ int max_level1 = max_level << 16 | max_level;
+ int ret = 0;
+
+ int16_t* ppin = &in[0];
+ int16_t* pout = &out[0];
+ const uint16_t* ppsharpen = &mtx->sharpen_[0];
+ const uint32_t* ppzthresh = &mtx->zthresh_[0];
+ const uint16_t* ppq = &mtx->q_[0];
+ const uint16_t* ppiq = &mtx->iq_[0];
+ const uint32_t* ppbias = &mtx->bias_[0];
+
+ __asm__ volatile (
+ QUANTIZE_ONE( 0, 0, 0, 2)
+ QUANTIZE_ONE( 4, 8, 10, 12)
+ QUANTIZE_ONE( 8, 16, 4, 8)
+ QUANTIZE_ONE(12, 24, 14, 24)
+ QUANTIZE_ONE(16, 32, 6, 16)
+ QUANTIZE_ONE(20, 40, 22, 26)
+ QUANTIZE_ONE(24, 48, 18, 20)
+ QUANTIZE_ONE(28, 56, 28, 30)
+
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [sign]"=&r"(sign), [coeff]"=&r"(coeff),
+ [level]"=&r"(level), [temp6]"=&r"(temp6), [ret]"+&r"(ret)
+ : [ppin]"r"(ppin), [pout]"r"(pout), [max_level1]"r"(max_level1),
+ [ppiq]"r"(ppiq), [max_level]"r"(max_level),
+ [ppbias]"r"(ppbias), [ppzthresh]"r"(ppzthresh),
+ [ppsharpen]"r"(ppsharpen), [ppq]"r"(ppq)
+ : "memory", "hi", "lo"
+ );
+
+ return (ret != 0);
+}
+
+static int Quantize2Blocks_MIPSdspR2(int16_t in[32], int16_t out[32],
+ const VP8Matrix* const mtx) {
+ int nz;
+ nz = QuantizeBlock_MIPSdspR2(in + 0 * 16, out + 0 * 16, mtx) << 0;
+ nz |= QuantizeBlock_MIPSdspR2(in + 1 * 16, out + 1 * 16, mtx) << 1;
+ return nz;
+}
+
+#undef QUANTIZE_ONE
+
+// macro for one horizontal pass in FTransformWHT
+// temp0..temp7 holds tmp[0]..tmp[15]
+// A, B, C, D - offset in bytes to load from in buffer
+// TEMP0, TEMP1 - registers for corresponding tmp elements
+#define HORIZONTAL_PASS_WHT(A, B, C, D, TEMP0, TEMP1) \
+ "lh %[" #TEMP0 "], " #A "(%[in]) \n\t" \
+ "lh %[" #TEMP1 "], " #B "(%[in]) \n\t" \
+ "lh %[temp8], " #C "(%[in]) \n\t" \
+ "lh %[temp9], " #D "(%[in]) \n\t" \
+ "ins %[" #TEMP1 "], %[" #TEMP0 "], 16, 16 \n\t" \
+ "ins %[temp9], %[temp8], 16, 16 \n\t" \
+ "subq.ph %[temp8], %[" #TEMP1 "], %[temp9] \n\t" \
+ "addq.ph %[temp9], %[" #TEMP1 "], %[temp9] \n\t" \
+ "precrq.ph.w %[" #TEMP0 "], %[temp8], %[temp9] \n\t" \
+ "append %[temp8], %[temp9], 16 \n\t" \
+ "subq.ph %[" #TEMP1 "], %[" #TEMP0 "], %[temp8] \n\t" \
+ "addq.ph %[" #TEMP0 "], %[" #TEMP0 "], %[temp8] \n\t" \
+ "rotr %[" #TEMP1 "], %[" #TEMP1 "], 16 \n\t"
+
+// macro for one vertical pass in FTransformWHT
+// temp0..temp7 holds tmp[0]..tmp[15]
+// A, B, C, D - offsets in bytes to store to out buffer
+// TEMP0, TEMP2, TEMP4 and TEMP6 - registers for corresponding tmp elements
+#define VERTICAL_PASS_WHT(A, B, C, D, TEMP0, TEMP2, TEMP4, TEMP6) \
+ "addq.ph %[temp8], %[" #TEMP0 "], %[" #TEMP4 "] \n\t" \
+ "addq.ph %[temp9], %[" #TEMP2 "], %[" #TEMP6 "] \n\t" \
+ "subq.ph %[" #TEMP2 "], %[" #TEMP2 "], %[" #TEMP6 "] \n\t" \
+ "subq.ph %[" #TEMP6 "], %[" #TEMP0 "], %[" #TEMP4 "] \n\t" \
+ "addqh.ph %[" #TEMP0 "], %[temp8], %[temp9] \n\t" \
+ "subqh.ph %[" #TEMP4 "], %[" #TEMP6 "], %[" #TEMP2 "] \n\t" \
+ "addqh.ph %[" #TEMP2 "], %[" #TEMP2 "], %[" #TEMP6 "] \n\t" \
+ "subqh.ph %[" #TEMP6 "], %[temp8], %[temp9] \n\t" \
+ "usw %[" #TEMP0 "], " #A "(%[out]) \n\t" \
+ "usw %[" #TEMP2 "], " #B "(%[out]) \n\t" \
+ "usw %[" #TEMP4 "], " #C "(%[out]) \n\t" \
+ "usw %[" #TEMP6 "], " #D "(%[out]) \n\t"
+
+static void FTransformWHT_MIPSdspR2(const int16_t* in, int16_t* out) {
+ int temp0, temp1, temp2, temp3, temp4;
+ int temp5, temp6, temp7, temp8, temp9;
+
+ __asm__ volatile (
+ HORIZONTAL_PASS_WHT( 0, 32, 64, 96, temp0, temp1)
+ HORIZONTAL_PASS_WHT(128, 160, 192, 224, temp2, temp3)
+ HORIZONTAL_PASS_WHT(256, 288, 320, 352, temp4, temp5)
+ HORIZONTAL_PASS_WHT(384, 416, 448, 480, temp6, temp7)
+ VERTICAL_PASS_WHT(0, 8, 16, 24, temp0, temp2, temp4, temp6)
+ VERTICAL_PASS_WHT(4, 12, 20, 28, temp1, temp3, temp5, temp7)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8),
+ [temp9]"=&r"(temp9)
+ : [in]"r"(in), [out]"r"(out)
+ : "memory"
+ );
+}
+
+#undef VERTICAL_PASS_WHT
+#undef HORIZONTAL_PASS_WHT
+
+// macro for converting coefficients to bin
+// convert 8 coeffs at time
+// A, B, C, D - offsets in bytes to load from out buffer
+#define CONVERT_COEFFS_TO_BIN(A, B, C, D) \
+ "ulw %[temp0], " #A "(%[out]) \n\t" \
+ "ulw %[temp1], " #B "(%[out]) \n\t" \
+ "ulw %[temp2], " #C "(%[out]) \n\t" \
+ "ulw %[temp3], " #D "(%[out]) \n\t" \
+ "absq_s.ph %[temp0], %[temp0] \n\t" \
+ "absq_s.ph %[temp1], %[temp1] \n\t" \
+ "absq_s.ph %[temp2], %[temp2] \n\t" \
+ "absq_s.ph %[temp3], %[temp3] \n\t" \
+ "shra.ph %[temp0], %[temp0], 3 \n\t" \
+ "shra.ph %[temp1], %[temp1], 3 \n\t" \
+ "shra.ph %[temp2], %[temp2], 3 \n\t" \
+ "shra.ph %[temp3], %[temp3], 3 \n\t" \
+ "shll_s.ph %[temp0], %[temp0], 10 \n\t" \
+ "shll_s.ph %[temp1], %[temp1], 10 \n\t" \
+ "shll_s.ph %[temp2], %[temp2], 10 \n\t" \
+ "shll_s.ph %[temp3], %[temp3], 10 \n\t" \
+ "shrl.ph %[temp0], %[temp0], 10 \n\t" \
+ "shrl.ph %[temp1], %[temp1], 10 \n\t" \
+ "shrl.ph %[temp2], %[temp2], 10 \n\t" \
+ "shrl.ph %[temp3], %[temp3], 10 \n\t" \
+ "shll.ph %[temp0], %[temp0], 2 \n\t" \
+ "shll.ph %[temp1], %[temp1], 2 \n\t" \
+ "shll.ph %[temp2], %[temp2], 2 \n\t" \
+ "shll.ph %[temp3], %[temp3], 2 \n\t" \
+ "ext %[temp4], %[temp0], 0, 16 \n\t" \
+ "ext %[temp0], %[temp0], 16, 16 \n\t" \
+ "addu %[temp4], %[temp4], %[dist] \n\t" \
+ "addu %[temp0], %[temp0], %[dist] \n\t" \
+ "ext %[temp5], %[temp1], 0, 16 \n\t" \
+ "lw %[temp8], 0(%[temp4]) \n\t" \
+ "ext %[temp1], %[temp1], 16, 16 \n\t" \
+ "addu %[temp5], %[temp5], %[dist] \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp4]) \n\t" \
+ "lw %[temp8], 0(%[temp0]) \n\t" \
+ "addu %[temp1], %[temp1], %[dist] \n\t" \
+ "ext %[temp6], %[temp2], 0, 16 \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp0]) \n\t" \
+ "lw %[temp8], 0(%[temp5]) \n\t" \
+ "ext %[temp2], %[temp2], 16, 16 \n\t" \
+ "addu %[temp6], %[temp6], %[dist] \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp5]) \n\t" \
+ "lw %[temp8], 0(%[temp1]) \n\t" \
+ "addu %[temp2], %[temp2], %[dist] \n\t" \
+ "ext %[temp7], %[temp3], 0, 16 \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp1]) \n\t" \
+ "lw %[temp8], 0(%[temp6]) \n\t" \
+ "ext %[temp3], %[temp3], 16, 16 \n\t" \
+ "addu %[temp7], %[temp7], %[dist] \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp6]) \n\t" \
+ "lw %[temp8], 0(%[temp2]) \n\t" \
+ "addu %[temp3], %[temp3], %[dist] \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp2]) \n\t" \
+ "lw %[temp8], 0(%[temp7]) \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp7]) \n\t" \
+ "lw %[temp8], 0(%[temp3]) \n\t" \
+ "addiu %[temp8], %[temp8], 1 \n\t" \
+ "sw %[temp8], 0(%[temp3]) \n\t"
+
+static void CollectHistogram_MIPSdspR2(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block,
+ VP8Histogram* const histo) {
+ int j;
+ int distribution[MAX_COEFF_THRESH + 1] = { 0 };
+ const int max_coeff = (MAX_COEFF_THRESH << 16) + MAX_COEFF_THRESH;
+ for (j = start_block; j < end_block; ++j) {
+ int16_t out[16];
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8;
+
+ VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
+
+ // Convert coefficients to bin.
+ __asm__ volatile (
+ CONVERT_COEFFS_TO_BIN( 0, 4, 8, 12)
+ CONVERT_COEFFS_TO_BIN(16, 20, 24, 28)
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [temp8]"=&r"(temp8)
+ : [dist]"r"(distribution), [out]"r"(out), [max_coeff]"r"(max_coeff)
+ : "memory"
+ );
+ }
+ VP8SetHistogramData(distribution, histo);
+}
+
+#undef CONVERT_COEFFS_TO_BIN
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspInitMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitMIPSdspR2(void) {
+ VP8FTransform = FTransform_MIPSdspR2;
+ VP8FTransformWHT = FTransformWHT_MIPSdspR2;
+ VP8ITransform = ITransform_MIPSdspR2;
+
+ VP8TDisto4x4 = Disto4x4_MIPSdspR2;
+ VP8TDisto16x16 = Disto16x16_MIPSdspR2;
+
+ VP8EncPredLuma16 = Intra16Preds_MIPSdspR2;
+ VP8EncPredChroma8 = IntraChromaPreds_MIPSdspR2;
+ VP8EncPredLuma4 = Intra4Preds_MIPSdspR2;
+
+#if !defined(WORK_AROUND_GCC)
+ VP8SSE16x16 = SSE16x16_MIPSdspR2;
+ VP8SSE8x8 = SSE8x8_MIPSdspR2;
+ VP8SSE16x8 = SSE16x8_MIPSdspR2;
+ VP8SSE4x4 = SSE4x4_MIPSdspR2;
+#endif
+
+ VP8EncQuantizeBlock = QuantizeBlock_MIPSdspR2;
+ VP8EncQuantize2Blocks = Quantize2Blocks_MIPSdspR2;
+
+ VP8CollectHistogram = CollectHistogram_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(VP8EncDspInitMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/enc_msa.c b/src/third_party/libwebp/src/dsp/enc_msa.c
new file mode 100644
index 0000000..6f85add
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc_msa.c
@@ -0,0 +1,896 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA version of encoder dsp functions.
+//
+// Author: Prashant Patil (prashant.patil@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA)
+
+#include <stdlib.h>
+#include "src/dsp/msa_macro.h"
+#include "src/enc/vp8i_enc.h"
+
+//------------------------------------------------------------------------------
+// Transforms
+
+#define IDCT_1D_W(in0, in1, in2, in3, out0, out1, out2, out3) do { \
+ v4i32 a1_m, b1_m, c1_m, d1_m; \
+ const v4i32 cospi8sqrt2minus1 = __msa_fill_w(20091); \
+ const v4i32 sinpi8sqrt2 = __msa_fill_w(35468); \
+ v4i32 c_tmp1_m = in1 * sinpi8sqrt2; \
+ v4i32 c_tmp2_m = in3 * cospi8sqrt2minus1; \
+ v4i32 d_tmp1_m = in1 * cospi8sqrt2minus1; \
+ v4i32 d_tmp2_m = in3 * sinpi8sqrt2; \
+ \
+ ADDSUB2(in0, in2, a1_m, b1_m); \
+ SRAI_W2_SW(c_tmp1_m, c_tmp2_m, 16); \
+ c_tmp2_m = c_tmp2_m + in3; \
+ c1_m = c_tmp1_m - c_tmp2_m; \
+ SRAI_W2_SW(d_tmp1_m, d_tmp2_m, 16); \
+ d_tmp1_m = d_tmp1_m + in1; \
+ d1_m = d_tmp1_m + d_tmp2_m; \
+ BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \
+} while (0)
+
+static WEBP_INLINE void ITransformOne(const uint8_t* ref, const int16_t* in,
+ uint8_t* dst) {
+ v8i16 input0, input1;
+ v4i32 in0, in1, in2, in3, hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3;
+ v4i32 res0, res1, res2, res3;
+ v16i8 dest0, dest1, dest2, dest3;
+ const v16i8 zero = { 0 };
+
+ LD_SH2(in, 8, input0, input1);
+ UNPCK_SH_SW(input0, in0, in1);
+ UNPCK_SH_SW(input1, in2, in3);
+ IDCT_1D_W(in0, in1, in2, in3, hz0, hz1, hz2, hz3);
+ TRANSPOSE4x4_SW_SW(hz0, hz1, hz2, hz3, hz0, hz1, hz2, hz3);
+ IDCT_1D_W(hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3);
+ SRARI_W4_SW(vt0, vt1, vt2, vt3, 3);
+ TRANSPOSE4x4_SW_SW(vt0, vt1, vt2, vt3, vt0, vt1, vt2, vt3);
+ LD_SB4(ref, BPS, dest0, dest1, dest2, dest3);
+ ILVR_B4_SW(zero, dest0, zero, dest1, zero, dest2, zero, dest3,
+ res0, res1, res2, res3);
+ ILVR_H4_SW(zero, res0, zero, res1, zero, res2, zero, res3,
+ res0, res1, res2, res3);
+ ADD4(res0, vt0, res1, vt1, res2, vt2, res3, vt3, res0, res1, res2, res3);
+ CLIP_SW4_0_255(res0, res1, res2, res3);
+ PCKEV_B2_SW(res0, res1, res2, res3, vt0, vt1);
+ res0 = (v4i32)__msa_pckev_b((v16i8)vt0, (v16i8)vt1);
+ ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS);
+}
+
+static void ITransform_MSA(const uint8_t* ref, const int16_t* in, uint8_t* dst,
+ int do_two) {
+ ITransformOne(ref, in, dst);
+ if (do_two) {
+ ITransformOne(ref + 4, in + 16, dst + 4);
+ }
+}
+
+static void FTransform_MSA(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ uint64_t out0, out1, out2, out3;
+ uint32_t in0, in1, in2, in3;
+ v4i32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5;
+ v8i16 t0, t1, t2, t3;
+ v16u8 srcl0, srcl1, src0 = { 0 }, src1 = { 0 };
+ const v8i16 mask0 = { 0, 4, 8, 12, 1, 5, 9, 13 };
+ const v8i16 mask1 = { 3, 7, 11, 15, 2, 6, 10, 14 };
+ const v8i16 mask2 = { 4, 0, 5, 1, 6, 2, 7, 3 };
+ const v8i16 mask3 = { 0, 4, 1, 5, 2, 6, 3, 7 };
+ const v8i16 cnst0 = { 2217, -5352, 2217, -5352, 2217, -5352, 2217, -5352 };
+ const v8i16 cnst1 = { 5352, 2217, 5352, 2217, 5352, 2217, 5352, 2217 };
+
+ LW4(src, BPS, in0, in1, in2, in3);
+ INSERT_W4_UB(in0, in1, in2, in3, src0);
+ LW4(ref, BPS, in0, in1, in2, in3);
+ INSERT_W4_UB(in0, in1, in2, in3, src1);
+ ILVRL_B2_UB(src0, src1, srcl0, srcl1);
+ HSUB_UB2_SH(srcl0, srcl1, t0, t1);
+ VSHF_H2_SH(t0, t1, t0, t1, mask0, mask1, t2, t3);
+ ADDSUB2(t2, t3, t0, t1);
+ t0 = SRLI_H(t0, 3);
+ VSHF_H2_SH(t0, t0, t1, t1, mask2, mask3, t3, t2);
+ tmp0 = __msa_hadd_s_w(t3, t3);
+ tmp2 = __msa_hsub_s_w(t3, t3);
+ FILL_W2_SW(1812, 937, tmp1, tmp3);
+ DPADD_SH2_SW(t2, t2, cnst0, cnst1, tmp3, tmp1);
+ SRAI_W2_SW(tmp1, tmp3, 9);
+ PCKEV_H2_SH(tmp1, tmp0, tmp3, tmp2, t0, t1);
+ VSHF_H2_SH(t0, t1, t0, t1, mask0, mask1, t2, t3);
+ ADDSUB2(t2, t3, t0, t1);
+ VSHF_H2_SH(t0, t0, t1, t1, mask2, mask3, t3, t2);
+ tmp0 = __msa_hadd_s_w(t3, t3);
+ tmp2 = __msa_hsub_s_w(t3, t3);
+ ADDVI_W2_SW(tmp0, 7, tmp2, 7, tmp0, tmp2);
+ SRAI_W2_SW(tmp0, tmp2, 4);
+ FILL_W2_SW(12000, 51000, tmp1, tmp3);
+ DPADD_SH2_SW(t2, t2, cnst0, cnst1, tmp3, tmp1);
+ SRAI_W2_SW(tmp1, tmp3, 16);
+ UNPCK_R_SH_SW(t1, tmp4);
+ tmp5 = __msa_ceqi_w(tmp4, 0);
+ tmp4 = (v4i32)__msa_nor_v((v16u8)tmp5, (v16u8)tmp5);
+ tmp5 = __msa_fill_w(1);
+ tmp5 = (v4i32)__msa_and_v((v16u8)tmp5, (v16u8)tmp4);
+ tmp1 += tmp5;
+ PCKEV_H2_SH(tmp1, tmp0, tmp3, tmp2, t0, t1);
+ out0 = __msa_copy_s_d((v2i64)t0, 0);
+ out1 = __msa_copy_s_d((v2i64)t0, 1);
+ out2 = __msa_copy_s_d((v2i64)t1, 0);
+ out3 = __msa_copy_s_d((v2i64)t1, 1);
+ SD4(out0, out1, out2, out3, out, 8);
+}
+
+static void FTransformWHT_MSA(const int16_t* in, int16_t* out) {
+ v8i16 in0 = { 0 };
+ v8i16 in1 = { 0 };
+ v8i16 tmp0, tmp1, tmp2, tmp3;
+ v8i16 out0, out1;
+ const v8i16 mask0 = { 0, 1, 2, 3, 8, 9, 10, 11 };
+ const v8i16 mask1 = { 4, 5, 6, 7, 12, 13, 14, 15 };
+ const v8i16 mask2 = { 0, 4, 8, 12, 1, 5, 9, 13 };
+ const v8i16 mask3 = { 3, 7, 11, 15, 2, 6, 10, 14 };
+
+ in0 = __msa_insert_h(in0, 0, in[ 0]);
+ in0 = __msa_insert_h(in0, 1, in[ 64]);
+ in0 = __msa_insert_h(in0, 2, in[128]);
+ in0 = __msa_insert_h(in0, 3, in[192]);
+ in0 = __msa_insert_h(in0, 4, in[ 16]);
+ in0 = __msa_insert_h(in0, 5, in[ 80]);
+ in0 = __msa_insert_h(in0, 6, in[144]);
+ in0 = __msa_insert_h(in0, 7, in[208]);
+ in1 = __msa_insert_h(in1, 0, in[ 48]);
+ in1 = __msa_insert_h(in1, 1, in[112]);
+ in1 = __msa_insert_h(in1, 2, in[176]);
+ in1 = __msa_insert_h(in1, 3, in[240]);
+ in1 = __msa_insert_h(in1, 4, in[ 32]);
+ in1 = __msa_insert_h(in1, 5, in[ 96]);
+ in1 = __msa_insert_h(in1, 6, in[160]);
+ in1 = __msa_insert_h(in1, 7, in[224]);
+ ADDSUB2(in0, in1, tmp0, tmp1);
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3);
+ ADDSUB2(tmp2, tmp3, tmp0, tmp1);
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask2, mask3, in0, in1);
+ ADDSUB2(in0, in1, tmp0, tmp1);
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3);
+ ADDSUB2(tmp2, tmp3, out0, out1);
+ SRAI_H2_SH(out0, out1, 1);
+ ST_SH2(out0, out1, out, 8);
+}
+
+static int TTransform_MSA(const uint8_t* in, const uint16_t* w) {
+ int sum;
+ uint32_t in0_m, in1_m, in2_m, in3_m;
+ v16i8 src0 = { 0 };
+ v8i16 in0, in1, tmp0, tmp1, tmp2, tmp3;
+ v4i32 dst0, dst1;
+ const v16i8 zero = { 0 };
+ const v8i16 mask0 = { 0, 1, 2, 3, 8, 9, 10, 11 };
+ const v8i16 mask1 = { 4, 5, 6, 7, 12, 13, 14, 15 };
+ const v8i16 mask2 = { 0, 4, 8, 12, 1, 5, 9, 13 };
+ const v8i16 mask3 = { 3, 7, 11, 15, 2, 6, 10, 14 };
+
+ LW4(in, BPS, in0_m, in1_m, in2_m, in3_m);
+ INSERT_W4_SB(in0_m, in1_m, in2_m, in3_m, src0);
+ ILVRL_B2_SH(zero, src0, tmp0, tmp1);
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask2, mask3, in0, in1);
+ ADDSUB2(in0, in1, tmp0, tmp1);
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3);
+ ADDSUB2(tmp2, tmp3, tmp0, tmp1);
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask2, mask3, in0, in1);
+ ADDSUB2(in0, in1, tmp0, tmp1);
+ VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3);
+ ADDSUB2(tmp2, tmp3, tmp0, tmp1);
+ tmp0 = __msa_add_a_h(tmp0, (v8i16)zero);
+ tmp1 = __msa_add_a_h(tmp1, (v8i16)zero);
+ LD_SH2(w, 8, tmp2, tmp3);
+ DOTP_SH2_SW(tmp0, tmp1, tmp2, tmp3, dst0, dst1);
+ dst0 = dst0 + dst1;
+ sum = HADD_SW_S32(dst0);
+ return sum;
+}
+
+static int Disto4x4_MSA(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ const int sum1 = TTransform_MSA(a, w);
+ const int sum2 = TTransform_MSA(b, w);
+ return abs(sum2 - sum1) >> 5;
+}
+
+static int Disto16x16_MSA(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4_MSA(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+
+//------------------------------------------------------------------------------
+// Histogram
+
+static void CollectHistogram_MSA(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block,
+ VP8Histogram* const histo) {
+ int j;
+ int distribution[MAX_COEFF_THRESH + 1] = { 0 };
+ for (j = start_block; j < end_block; ++j) {
+ int16_t out[16];
+ VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
+ {
+ int k;
+ v8i16 coeff0, coeff1;
+ const v8i16 zero = { 0 };
+ const v8i16 max_coeff_thr = __msa_ldi_h(MAX_COEFF_THRESH);
+ LD_SH2(&out[0], 8, coeff0, coeff1);
+ coeff0 = __msa_add_a_h(coeff0, zero);
+ coeff1 = __msa_add_a_h(coeff1, zero);
+ SRAI_H2_SH(coeff0, coeff1, 3);
+ coeff0 = __msa_min_s_h(coeff0, max_coeff_thr);
+ coeff1 = __msa_min_s_h(coeff1, max_coeff_thr);
+ ST_SH2(coeff0, coeff1, &out[0], 8);
+ for (k = 0; k < 16; ++k) {
+ ++distribution[out[k]];
+ }
+ }
+ }
+ VP8SetHistogramData(distribution, histo);
+}
+
+//------------------------------------------------------------------------------
+// Intra predictions
+
+// luma 4x4 prediction
+
+#define DST(x, y) dst[(x) + (y) * BPS]
+#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
+#define AVG2(a, b) (((a) + (b) + 1) >> 1)
+
+static WEBP_INLINE void VE4(uint8_t* dst, const uint8_t* top) { // vertical
+ const v16u8 A1 = { 0 };
+ const uint64_t val_m = LD(top - 1);
+ const v16u8 A = (v16u8)__msa_insert_d((v2i64)A1, 0, val_m);
+ const v16u8 B = SLDI_UB(A, A, 1);
+ const v16u8 C = SLDI_UB(A, A, 2);
+ const v16u8 AC = __msa_ave_u_b(A, C);
+ const v16u8 B2 = __msa_ave_u_b(B, B);
+ const v16u8 R = __msa_aver_u_b(AC, B2);
+ const uint32_t out = __msa_copy_s_w((v4i32)R, 0);
+ SW4(out, out, out, out, dst, BPS);
+}
+
+static WEBP_INLINE void HE4(uint8_t* dst, const uint8_t* top) { // horizontal
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(X, I, J));
+ WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(I, J, K));
+ WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(J, K, L));
+ WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L));
+}
+
+static WEBP_INLINE void DC4(uint8_t* dst, const uint8_t* top) {
+ uint32_t dc = 4;
+ int i;
+ for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i];
+ dc >>= 3;
+ dc = dc | (dc << 8) | (dc << 16) | (dc << 24);
+ SW4(dc, dc, dc, dc, dst, BPS);
+}
+
+static WEBP_INLINE void RD4(uint8_t* dst, const uint8_t* top) {
+ const v16u8 A2 = { 0 };
+ const uint64_t val_m = LD(top - 5);
+ const v16u8 A1 = (v16u8)__msa_insert_d((v2i64)A2, 0, val_m);
+ const v16u8 A = (v16u8)__msa_insert_b((v16i8)A1, 8, top[3]);
+ const v16u8 B = SLDI_UB(A, A, 1);
+ const v16u8 C = SLDI_UB(A, A, 2);
+ const v16u8 AC = __msa_ave_u_b(A, C);
+ const v16u8 B2 = __msa_ave_u_b(B, B);
+ const v16u8 R0 = __msa_aver_u_b(AC, B2);
+ const v16u8 R1 = SLDI_UB(R0, R0, 1);
+ const v16u8 R2 = SLDI_UB(R1, R1, 1);
+ const v16u8 R3 = SLDI_UB(R2, R2, 1);
+ const uint32_t val0 = __msa_copy_s_w((v4i32)R0, 0);
+ const uint32_t val1 = __msa_copy_s_w((v4i32)R1, 0);
+ const uint32_t val2 = __msa_copy_s_w((v4i32)R2, 0);
+ const uint32_t val3 = __msa_copy_s_w((v4i32)R3, 0);
+ SW4(val3, val2, val1, val0, dst, BPS);
+}
+
+static WEBP_INLINE void LD4(uint8_t* dst, const uint8_t* top) {
+ const v16u8 A1 = { 0 };
+ const uint64_t val_m = LD(top);
+ const v16u8 A = (v16u8)__msa_insert_d((v2i64)A1, 0, val_m);
+ const v16u8 B = SLDI_UB(A, A, 1);
+ const v16u8 C1 = SLDI_UB(A, A, 2);
+ const v16u8 C = (v16u8)__msa_insert_b((v16i8)C1, 6, top[7]);
+ const v16u8 AC = __msa_ave_u_b(A, C);
+ const v16u8 B2 = __msa_ave_u_b(B, B);
+ const v16u8 R0 = __msa_aver_u_b(AC, B2);
+ const v16u8 R1 = SLDI_UB(R0, R0, 1);
+ const v16u8 R2 = SLDI_UB(R1, R1, 1);
+ const v16u8 R3 = SLDI_UB(R2, R2, 1);
+ const uint32_t val0 = __msa_copy_s_w((v4i32)R0, 0);
+ const uint32_t val1 = __msa_copy_s_w((v4i32)R1, 0);
+ const uint32_t val2 = __msa_copy_s_w((v4i32)R2, 0);
+ const uint32_t val3 = __msa_copy_s_w((v4i32)R3, 0);
+ SW4(val0, val1, val2, val3, dst, BPS);
+}
+
+static WEBP_INLINE void VR4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ DST(0, 0) = DST(1, 2) = AVG2(X, A);
+ DST(1, 0) = DST(2, 2) = AVG2(A, B);
+ DST(2, 0) = DST(3, 2) = AVG2(B, C);
+ DST(3, 0) = AVG2(C, D);
+ DST(0, 3) = AVG3(K, J, I);
+ DST(0, 2) = AVG3(J, I, X);
+ DST(0, 1) = DST(1, 3) = AVG3(I, X, A);
+ DST(1, 1) = DST(2, 3) = AVG3(X, A, B);
+ DST(2, 1) = DST(3, 3) = AVG3(A, B, C);
+ DST(3, 1) = AVG3(B, C, D);
+}
+
+static WEBP_INLINE void VL4(uint8_t* dst, const uint8_t* top) {
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ const int D = top[3];
+ const int E = top[4];
+ const int F = top[5];
+ const int G = top[6];
+ const int H = top[7];
+ DST(0, 0) = AVG2(A, B);
+ DST(1, 0) = DST(0, 2) = AVG2(B, C);
+ DST(2, 0) = DST(1, 2) = AVG2(C, D);
+ DST(3, 0) = DST(2, 2) = AVG2(D, E);
+ DST(0, 1) = AVG3(A, B, C);
+ DST(1, 1) = DST(0, 3) = AVG3(B, C, D);
+ DST(2, 1) = DST(1, 3) = AVG3(C, D, E);
+ DST(3, 1) = DST(2, 3) = AVG3(D, E, F);
+ DST(3, 2) = AVG3(E, F, G);
+ DST(3, 3) = AVG3(F, G, H);
+}
+
+static WEBP_INLINE void HU4(uint8_t* dst, const uint8_t* top) {
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ DST(0, 0) = AVG2(I, J);
+ DST(2, 0) = DST(0, 1) = AVG2(J, K);
+ DST(2, 1) = DST(0, 2) = AVG2(K, L);
+ DST(1, 0) = AVG3(I, J, K);
+ DST(3, 0) = DST(1, 1) = AVG3(J, K, L);
+ DST(3, 1) = DST(1, 2) = AVG3(K, L, L);
+ DST(3, 2) = DST(2, 2) =
+ DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
+}
+
+static WEBP_INLINE void HD4(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+ DST(0, 0) = DST(2, 1) = AVG2(I, X);
+ DST(0, 1) = DST(2, 2) = AVG2(J, I);
+ DST(0, 2) = DST(2, 3) = AVG2(K, J);
+ DST(0, 3) = AVG2(L, K);
+ DST(3, 0) = AVG3(A, B, C);
+ DST(2, 0) = AVG3(X, A, B);
+ DST(1, 0) = DST(3, 1) = AVG3(I, X, A);
+ DST(1, 1) = DST(3, 2) = AVG3(J, I, X);
+ DST(1, 2) = DST(3, 3) = AVG3(K, J, I);
+ DST(1, 3) = AVG3(L, K, J);
+}
+
+static WEBP_INLINE void TM4(uint8_t* dst, const uint8_t* top) {
+ const v16i8 zero = { 0 };
+ const v8i16 TL = (v8i16)__msa_fill_h(top[-1]);
+ const v8i16 L0 = (v8i16)__msa_fill_h(top[-2]);
+ const v8i16 L1 = (v8i16)__msa_fill_h(top[-3]);
+ const v8i16 L2 = (v8i16)__msa_fill_h(top[-4]);
+ const v8i16 L3 = (v8i16)__msa_fill_h(top[-5]);
+ const v16u8 T1 = LD_UB(top);
+ const v8i16 T = (v8i16)__msa_ilvr_b(zero, (v16i8)T1);
+ const v8i16 d = T - TL;
+ v8i16 r0, r1, r2, r3;
+ ADD4(d, L0, d, L1, d, L2, d, L3, r0, r1, r2, r3);
+ CLIP_SH4_0_255(r0, r1, r2, r3);
+ PCKEV_ST4x4_UB(r0, r1, r2, r3, dst, BPS);
+}
+
+#undef DST
+#undef AVG3
+#undef AVG2
+
+static void Intra4Preds_MSA(uint8_t* dst, const uint8_t* top) {
+ DC4(I4DC4 + dst, top);
+ TM4(I4TM4 + dst, top);
+ VE4(I4VE4 + dst, top);
+ HE4(I4HE4 + dst, top);
+ RD4(I4RD4 + dst, top);
+ VR4(I4VR4 + dst, top);
+ LD4(I4LD4 + dst, top);
+ VL4(I4VL4 + dst, top);
+ HD4(I4HD4 + dst, top);
+ HU4(I4HU4 + dst, top);
+}
+
+// luma 16x16 prediction
+
+#define STORE16x16(out, dst) do { \
+ ST_UB8(out, out, out, out, out, out, out, out, dst + 0 * BPS, BPS); \
+ ST_UB8(out, out, out, out, out, out, out, out, dst + 8 * BPS, BPS); \
+} while (0)
+
+static WEBP_INLINE void VerticalPred16x16(uint8_t* dst, const uint8_t* top) {
+ if (top != NULL) {
+ const v16u8 out = LD_UB(top);
+ STORE16x16(out, dst);
+ } else {
+ const v16u8 out = (v16u8)__msa_fill_b(0x7f);
+ STORE16x16(out, dst);
+ }
+}
+
+static WEBP_INLINE void HorizontalPred16x16(uint8_t* dst,
+ const uint8_t* left) {
+ if (left != NULL) {
+ int j;
+ for (j = 0; j < 16; j += 4) {
+ const v16u8 L0 = (v16u8)__msa_fill_b(left[0]);
+ const v16u8 L1 = (v16u8)__msa_fill_b(left[1]);
+ const v16u8 L2 = (v16u8)__msa_fill_b(left[2]);
+ const v16u8 L3 = (v16u8)__msa_fill_b(left[3]);
+ ST_UB4(L0, L1, L2, L3, dst, BPS);
+ dst += 4 * BPS;
+ left += 4;
+ }
+ } else {
+ const v16u8 out = (v16u8)__msa_fill_b(0x81);
+ STORE16x16(out, dst);
+ }
+}
+
+static WEBP_INLINE void TrueMotion16x16(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ if (left != NULL) {
+ if (top != NULL) {
+ int j;
+ v8i16 d1, d2;
+ const v16i8 zero = { 0 };
+ const v8i16 TL = (v8i16)__msa_fill_h(left[-1]);
+ const v16u8 T = LD_UB(top);
+ ILVRL_B2_SH(zero, T, d1, d2);
+ SUB2(d1, TL, d2, TL, d1, d2);
+ for (j = 0; j < 16; j += 4) {
+ v16i8 t0, t1, t2, t3;
+ v8i16 r0, r1, r2, r3, r4, r5, r6, r7;
+ const v8i16 L0 = (v8i16)__msa_fill_h(left[j + 0]);
+ const v8i16 L1 = (v8i16)__msa_fill_h(left[j + 1]);
+ const v8i16 L2 = (v8i16)__msa_fill_h(left[j + 2]);
+ const v8i16 L3 = (v8i16)__msa_fill_h(left[j + 3]);
+ ADD4(d1, L0, d1, L1, d1, L2, d1, L3, r0, r1, r2, r3);
+ ADD4(d2, L0, d2, L1, d2, L2, d2, L3, r4, r5, r6, r7);
+ CLIP_SH4_0_255(r0, r1, r2, r3);
+ CLIP_SH4_0_255(r4, r5, r6, r7);
+ PCKEV_B4_SB(r4, r0, r5, r1, r6, r2, r7, r3, t0, t1, t2, t3);
+ ST_SB4(t0, t1, t2, t3, dst, BPS);
+ dst += 4 * BPS;
+ }
+ } else {
+ HorizontalPred16x16(dst, left);
+ }
+ } else {
+ if (top != NULL) {
+ VerticalPred16x16(dst, top);
+ } else {
+ const v16u8 out = (v16u8)__msa_fill_b(0x81);
+ STORE16x16(out, dst);
+ }
+ }
+}
+
+static WEBP_INLINE void DCMode16x16(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ int DC;
+ v16u8 out;
+ if (top != NULL && left != NULL) {
+ const v16u8 rtop = LD_UB(top);
+ const v8u16 dctop = __msa_hadd_u_h(rtop, rtop);
+ const v16u8 rleft = LD_UB(left);
+ const v8u16 dcleft = __msa_hadd_u_h(rleft, rleft);
+ const v8u16 dctemp = dctop + dcleft;
+ DC = HADD_UH_U32(dctemp);
+ DC = (DC + 16) >> 5;
+ } else if (left != NULL) { // left but no top
+ const v16u8 rleft = LD_UB(left);
+ const v8u16 dcleft = __msa_hadd_u_h(rleft, rleft);
+ DC = HADD_UH_U32(dcleft);
+ DC = (DC + DC + 16) >> 5;
+ } else if (top != NULL) { // top but no left
+ const v16u8 rtop = LD_UB(top);
+ const v8u16 dctop = __msa_hadd_u_h(rtop, rtop);
+ DC = HADD_UH_U32(dctop);
+ DC = (DC + DC + 16) >> 5;
+ } else { // no top, no left, nothing.
+ DC = 0x80;
+ }
+ out = (v16u8)__msa_fill_b(DC);
+ STORE16x16(out, dst);
+}
+
+static void Intra16Preds_MSA(uint8_t* dst,
+ const uint8_t* left, const uint8_t* top) {
+ DCMode16x16(I16DC16 + dst, left, top);
+ VerticalPred16x16(I16VE16 + dst, top);
+ HorizontalPred16x16(I16HE16 + dst, left);
+ TrueMotion16x16(I16TM16 + dst, left, top);
+}
+
+// Chroma 8x8 prediction
+
+#define CALC_DC8(in, out) do { \
+ const v8u16 temp0 = __msa_hadd_u_h(in, in); \
+ const v4u32 temp1 = __msa_hadd_u_w(temp0, temp0); \
+ const v2i64 temp2 = (v2i64)__msa_hadd_u_d(temp1, temp1); \
+ const v2i64 temp3 = __msa_splati_d(temp2, 1); \
+ const v2i64 temp4 = temp3 + temp2; \
+ const v16i8 temp5 = (v16i8)__msa_srari_d(temp4, 4); \
+ const v2i64 temp6 = (v2i64)__msa_splati_b(temp5, 0); \
+ out = __msa_copy_s_d(temp6, 0); \
+} while (0)
+
+#define STORE8x8(out, dst) do { \
+ SD4(out, out, out, out, dst + 0 * BPS, BPS); \
+ SD4(out, out, out, out, dst + 4 * BPS, BPS); \
+} while (0)
+
+static WEBP_INLINE void VerticalPred8x8(uint8_t* dst, const uint8_t* top) {
+ if (top != NULL) {
+ const uint64_t out = LD(top);
+ STORE8x8(out, dst);
+ } else {
+ const uint64_t out = 0x7f7f7f7f7f7f7f7fULL;
+ STORE8x8(out, dst);
+ }
+}
+
+static WEBP_INLINE void HorizontalPred8x8(uint8_t* dst, const uint8_t* left) {
+ if (left != NULL) {
+ int j;
+ for (j = 0; j < 8; j += 4) {
+ const v16u8 L0 = (v16u8)__msa_fill_b(left[0]);
+ const v16u8 L1 = (v16u8)__msa_fill_b(left[1]);
+ const v16u8 L2 = (v16u8)__msa_fill_b(left[2]);
+ const v16u8 L3 = (v16u8)__msa_fill_b(left[3]);
+ const uint64_t out0 = __msa_copy_s_d((v2i64)L0, 0);
+ const uint64_t out1 = __msa_copy_s_d((v2i64)L1, 0);
+ const uint64_t out2 = __msa_copy_s_d((v2i64)L2, 0);
+ const uint64_t out3 = __msa_copy_s_d((v2i64)L3, 0);
+ SD4(out0, out1, out2, out3, dst, BPS);
+ dst += 4 * BPS;
+ left += 4;
+ }
+ } else {
+ const uint64_t out = 0x8181818181818181ULL;
+ STORE8x8(out, dst);
+ }
+}
+
+static WEBP_INLINE void TrueMotion8x8(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ if (left != NULL) {
+ if (top != NULL) {
+ int j;
+ const v8i16 TL = (v8i16)__msa_fill_h(left[-1]);
+ const v16u8 T1 = LD_UB(top);
+ const v16i8 zero = { 0 };
+ const v8i16 T = (v8i16)__msa_ilvr_b(zero, (v16i8)T1);
+ const v8i16 d = T - TL;
+ for (j = 0; j < 8; j += 4) {
+ uint64_t out0, out1, out2, out3;
+ v16i8 t0, t1;
+ v8i16 r0 = (v8i16)__msa_fill_h(left[j + 0]);
+ v8i16 r1 = (v8i16)__msa_fill_h(left[j + 1]);
+ v8i16 r2 = (v8i16)__msa_fill_h(left[j + 2]);
+ v8i16 r3 = (v8i16)__msa_fill_h(left[j + 3]);
+ ADD4(d, r0, d, r1, d, r2, d, r3, r0, r1, r2, r3);
+ CLIP_SH4_0_255(r0, r1, r2, r3);
+ PCKEV_B2_SB(r1, r0, r3, r2, t0, t1);
+ out0 = __msa_copy_s_d((v2i64)t0, 0);
+ out1 = __msa_copy_s_d((v2i64)t0, 1);
+ out2 = __msa_copy_s_d((v2i64)t1, 0);
+ out3 = __msa_copy_s_d((v2i64)t1, 1);
+ SD4(out0, out1, out2, out3, dst, BPS);
+ dst += 4 * BPS;
+ }
+ } else {
+ HorizontalPred8x8(dst, left);
+ }
+ } else {
+ if (top != NULL) {
+ VerticalPred8x8(dst, top);
+ } else {
+ const uint64_t out = 0x8181818181818181ULL;
+ STORE8x8(out, dst);
+ }
+ }
+}
+
+static WEBP_INLINE void DCMode8x8(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ uint64_t out;
+ v16u8 src = { 0 };
+ if (top != NULL && left != NULL) {
+ const uint64_t left_m = LD(left);
+ const uint64_t top_m = LD(top);
+ INSERT_D2_UB(left_m, top_m, src);
+ CALC_DC8(src, out);
+ } else if (left != NULL) { // left but no top
+ const uint64_t left_m = LD(left);
+ INSERT_D2_UB(left_m, left_m, src);
+ CALC_DC8(src, out);
+ } else if (top != NULL) { // top but no left
+ const uint64_t top_m = LD(top);
+ INSERT_D2_UB(top_m, top_m, src);
+ CALC_DC8(src, out);
+ } else { // no top, no left, nothing.
+ src = (v16u8)__msa_fill_b(0x80);
+ out = __msa_copy_s_d((v2i64)src, 0);
+ }
+ STORE8x8(out, dst);
+}
+
+static void IntraChromaPreds_MSA(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ // U block
+ DCMode8x8(C8DC8 + dst, left, top);
+ VerticalPred8x8(C8VE8 + dst, top);
+ HorizontalPred8x8(C8HE8 + dst, left);
+ TrueMotion8x8(C8TM8 + dst, left, top);
+ // V block
+ dst += 8;
+ if (top != NULL) top += 8;
+ if (left != NULL) left += 16;
+ DCMode8x8(C8DC8 + dst, left, top);
+ VerticalPred8x8(C8VE8 + dst, top);
+ HorizontalPred8x8(C8HE8 + dst, left);
+ TrueMotion8x8(C8TM8 + dst, left, top);
+}
+
+//------------------------------------------------------------------------------
+// Metric
+
+#define PACK_DOTP_UB4_SW(in0, in1, in2, in3, out0, out1, out2, out3) do { \
+ v16u8 tmp0, tmp1; \
+ v8i16 tmp2, tmp3; \
+ ILVRL_B2_UB(in0, in1, tmp0, tmp1); \
+ HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \
+ DOTP_SH2_SW(tmp2, tmp3, tmp2, tmp3, out0, out1); \
+ ILVRL_B2_UB(in2, in3, tmp0, tmp1); \
+ HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \
+ DOTP_SH2_SW(tmp2, tmp3, tmp2, tmp3, out2, out3); \
+} while (0)
+
+#define PACK_DPADD_UB4_SW(in0, in1, in2, in3, out0, out1, out2, out3) do { \
+ v16u8 tmp0, tmp1; \
+ v8i16 tmp2, tmp3; \
+ ILVRL_B2_UB(in0, in1, tmp0, tmp1); \
+ HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \
+ DPADD_SH2_SW(tmp2, tmp3, tmp2, tmp3, out0, out1); \
+ ILVRL_B2_UB(in2, in3, tmp0, tmp1); \
+ HSUB_UB2_SH(tmp0, tmp1, tmp2, tmp3); \
+ DPADD_SH2_SW(tmp2, tmp3, tmp2, tmp3, out2, out3); \
+} while (0)
+
+static int SSE16x16_MSA(const uint8_t* a, const uint8_t* b) {
+ uint32_t sum;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7;
+ v4i32 out0, out1, out2, out3;
+
+ LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7);
+ PACK_DOTP_UB4_SW(src0, ref0, src1, ref1, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src2, ref2, src3, ref3, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src4, ref4, src5, ref5, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src6, ref6, src7, ref7, out0, out1, out2, out3);
+ a += 8 * BPS;
+ b += 8 * BPS;
+ LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7);
+ PACK_DPADD_UB4_SW(src0, ref0, src1, ref1, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src2, ref2, src3, ref3, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src4, ref4, src5, ref5, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src6, ref6, src7, ref7, out0, out1, out2, out3);
+ out0 += out1;
+ out2 += out3;
+ out0 += out2;
+ sum = HADD_SW_S32(out0);
+ return sum;
+}
+
+static int SSE16x8_MSA(const uint8_t* a, const uint8_t* b) {
+ uint32_t sum;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7;
+ v4i32 out0, out1, out2, out3;
+
+ LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7);
+ PACK_DOTP_UB4_SW(src0, ref0, src1, ref1, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src2, ref2, src3, ref3, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src4, ref4, src5, ref5, out0, out1, out2, out3);
+ PACK_DPADD_UB4_SW(src6, ref6, src7, ref7, out0, out1, out2, out3);
+ out0 += out1;
+ out2 += out3;
+ out0 += out2;
+ sum = HADD_SW_S32(out0);
+ return sum;
+}
+
+static int SSE8x8_MSA(const uint8_t* a, const uint8_t* b) {
+ uint32_t sum;
+ v16u8 src0, src1, src2, src3, src4, src5, src6, src7;
+ v16u8 ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7;
+ v16u8 t0, t1, t2, t3;
+ v4i32 out0, out1, out2, out3;
+
+ LD_UB8(a, BPS, src0, src1, src2, src3, src4, src5, src6, src7);
+ LD_UB8(b, BPS, ref0, ref1, ref2, ref3, ref4, ref5, ref6, ref7);
+ ILVR_B4_UB(src0, src1, src2, src3, ref0, ref1, ref2, ref3, t0, t1, t2, t3);
+ PACK_DOTP_UB4_SW(t0, t2, t1, t3, out0, out1, out2, out3);
+ ILVR_B4_UB(src4, src5, src6, src7, ref4, ref5, ref6, ref7, t0, t1, t2, t3);
+ PACK_DPADD_UB4_SW(t0, t2, t1, t3, out0, out1, out2, out3);
+ out0 += out1;
+ out2 += out3;
+ out0 += out2;
+ sum = HADD_SW_S32(out0);
+ return sum;
+}
+
+static int SSE4x4_MSA(const uint8_t* a, const uint8_t* b) {
+ uint32_t sum = 0;
+ uint32_t src0, src1, src2, src3, ref0, ref1, ref2, ref3;
+ v16u8 src = { 0 }, ref = { 0 }, tmp0, tmp1;
+ v8i16 diff0, diff1;
+ v4i32 out0, out1;
+
+ LW4(a, BPS, src0, src1, src2, src3);
+ LW4(b, BPS, ref0, ref1, ref2, ref3);
+ INSERT_W4_UB(src0, src1, src2, src3, src);
+ INSERT_W4_UB(ref0, ref1, ref2, ref3, ref);
+ ILVRL_B2_UB(src, ref, tmp0, tmp1);
+ HSUB_UB2_SH(tmp0, tmp1, diff0, diff1);
+ DOTP_SH2_SW(diff0, diff1, diff0, diff1, out0, out1);
+ out0 += out1;
+ sum = HADD_SW_S32(out0);
+ return sum;
+}
+
+//------------------------------------------------------------------------------
+// Quantization
+
+static int QuantizeBlock_MSA(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ int sum;
+ v8i16 in0, in1, sh0, sh1, out0, out1;
+ v8i16 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, sign0, sign1;
+ v4i32 s0, s1, s2, s3, b0, b1, b2, b3, t0, t1, t2, t3;
+ const v8i16 zero = { 0 };
+ const v8i16 zigzag0 = { 0, 1, 4, 8, 5, 2, 3, 6 };
+ const v8i16 zigzag1 = { 9, 12, 13, 10, 7, 11, 14, 15 };
+ const v8i16 maxlevel = __msa_fill_h(MAX_LEVEL);
+
+ LD_SH2(&in[0], 8, in0, in1);
+ LD_SH2(&mtx->sharpen_[0], 8, sh0, sh1);
+ tmp4 = __msa_add_a_h(in0, zero);
+ tmp5 = __msa_add_a_h(in1, zero);
+ ILVRL_H2_SH(sh0, tmp4, tmp0, tmp1);
+ ILVRL_H2_SH(sh1, tmp5, tmp2, tmp3);
+ HADD_SH4_SW(tmp0, tmp1, tmp2, tmp3, s0, s1, s2, s3);
+ sign0 = (in0 < zero);
+ sign1 = (in1 < zero); // sign
+ LD_SH2(&mtx->iq_[0], 8, tmp0, tmp1); // iq
+ ILVRL_H2_SW(zero, tmp0, t0, t1);
+ ILVRL_H2_SW(zero, tmp1, t2, t3);
+ LD_SW4(&mtx->bias_[0], 4, b0, b1, b2, b3); // bias
+ MUL4(t0, s0, t1, s1, t2, s2, t3, s3, t0, t1, t2, t3);
+ ADD4(b0, t0, b1, t1, b2, t2, b3, t3, b0, b1, b2, b3);
+ SRAI_W4_SW(b0, b1, b2, b3, 17);
+ PCKEV_H2_SH(b1, b0, b3, b2, tmp2, tmp3);
+ tmp0 = (tmp2 > maxlevel);
+ tmp1 = (tmp3 > maxlevel);
+ tmp2 = (v8i16)__msa_bmnz_v((v16u8)tmp2, (v16u8)maxlevel, (v16u8)tmp0);
+ tmp3 = (v8i16)__msa_bmnz_v((v16u8)tmp3, (v16u8)maxlevel, (v16u8)tmp1);
+ SUB2(zero, tmp2, zero, tmp3, tmp0, tmp1);
+ tmp2 = (v8i16)__msa_bmnz_v((v16u8)tmp2, (v16u8)tmp0, (v16u8)sign0);
+ tmp3 = (v8i16)__msa_bmnz_v((v16u8)tmp3, (v16u8)tmp1, (v16u8)sign1);
+ LD_SW4(&mtx->zthresh_[0], 4, t0, t1, t2, t3); // zthresh
+ t0 = (s0 > t0);
+ t1 = (s1 > t1);
+ t2 = (s2 > t2);
+ t3 = (s3 > t3);
+ PCKEV_H2_SH(t1, t0, t3, t2, tmp0, tmp1);
+ tmp4 = (v8i16)__msa_bmnz_v((v16u8)zero, (v16u8)tmp2, (v16u8)tmp0);
+ tmp5 = (v8i16)__msa_bmnz_v((v16u8)zero, (v16u8)tmp3, (v16u8)tmp1);
+ LD_SH2(&mtx->q_[0], 8, tmp0, tmp1);
+ MUL2(tmp4, tmp0, tmp5, tmp1, in0, in1);
+ VSHF_H2_SH(tmp4, tmp5, tmp4, tmp5, zigzag0, zigzag1, out0, out1);
+ ST_SH2(in0, in1, &in[0], 8);
+ ST_SH2(out0, out1, &out[0], 8);
+ out0 = __msa_add_a_h(out0, out1);
+ sum = HADD_SH_S32(out0);
+ return (sum > 0);
+}
+
+static int Quantize2Blocks_MSA(int16_t in[32], int16_t out[32],
+ const VP8Matrix* const mtx) {
+ int nz;
+ nz = VP8EncQuantizeBlock(in + 0 * 16, out + 0 * 16, mtx) << 0;
+ nz |= VP8EncQuantizeBlock(in + 1 * 16, out + 1 * 16, mtx) << 1;
+ return nz;
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspInitMSA(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitMSA(void) {
+ VP8ITransform = ITransform_MSA;
+ VP8FTransform = FTransform_MSA;
+ VP8FTransformWHT = FTransformWHT_MSA;
+
+ VP8TDisto4x4 = Disto4x4_MSA;
+ VP8TDisto16x16 = Disto16x16_MSA;
+ VP8CollectHistogram = CollectHistogram_MSA;
+
+ VP8EncPredLuma4 = Intra4Preds_MSA;
+ VP8EncPredLuma16 = Intra16Preds_MSA;
+ VP8EncPredChroma8 = IntraChromaPreds_MSA;
+
+ VP8SSE16x16 = SSE16x16_MSA;
+ VP8SSE16x8 = SSE16x8_MSA;
+ VP8SSE8x8 = SSE8x8_MSA;
+ VP8SSE4x4 = SSE4x4_MSA;
+
+ VP8EncQuantizeBlock = QuantizeBlock_MSA;
+ VP8EncQuantize2Blocks = Quantize2Blocks_MSA;
+ VP8EncQuantizeBlockWHT = QuantizeBlock_MSA;
+}
+
+#else // !WEBP_USE_MSA
+
+WEBP_DSP_INIT_STUB(VP8EncDspInitMSA)
+
+#endif // WEBP_USE_MSA
diff --git a/src/third_party/libwebp/src/dsp/enc_neon.c b/src/third_party/libwebp/src/dsp/enc_neon.c
new file mode 100644
index 0000000..43bf124
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc_neon.c
@@ -0,0 +1,938 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// ARM NEON version of speed-critical encoding functions.
+//
+// adapted from libvpx (http://www.webmproject.org/code/)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON)
+
+#include <assert.h>
+
+#include "src/dsp/neon.h"
+#include "src/enc/vp8i_enc.h"
+
+//------------------------------------------------------------------------------
+// Transforms (Paragraph 14.4)
+
+// Inverse transform.
+// This code is pretty much the same as TransformOne in the dec_neon.c, except
+// for subtraction to *ref. See the comments there for algorithmic explanations.
+
+static const int16_t kC1 = 20091;
+static const int16_t kC2 = 17734; // half of kC2, actually. See comment above.
+
+// This code works but is *slower* than the inlined-asm version below
+// (with gcc-4.6). So we disable it for now. Later, it'll be conditional to
+// WEBP_USE_INTRINSICS define.
+// With gcc-4.8, it's a little faster speed than inlined-assembly.
+#if defined(WEBP_USE_INTRINSICS)
+
+// Treats 'v' as an uint8x8_t and zero extends to an int16x8_t.
+static WEBP_INLINE int16x8_t ConvertU8ToS16_NEON(uint32x2_t v) {
+ return vreinterpretq_s16_u16(vmovl_u8(vreinterpret_u8_u32(v)));
+}
+
+// Performs unsigned 8b saturation on 'dst01' and 'dst23' storing the result
+// to the corresponding rows of 'dst'.
+static WEBP_INLINE void SaturateAndStore4x4_NEON(uint8_t* const dst,
+ const int16x8_t dst01,
+ const int16x8_t dst23) {
+ // Unsigned saturate to 8b.
+ const uint8x8_t dst01_u8 = vqmovun_s16(dst01);
+ const uint8x8_t dst23_u8 = vqmovun_s16(dst23);
+
+ // Store the results.
+ vst1_lane_u32((uint32_t*)(dst + 0 * BPS), vreinterpret_u32_u8(dst01_u8), 0);
+ vst1_lane_u32((uint32_t*)(dst + 1 * BPS), vreinterpret_u32_u8(dst01_u8), 1);
+ vst1_lane_u32((uint32_t*)(dst + 2 * BPS), vreinterpret_u32_u8(dst23_u8), 0);
+ vst1_lane_u32((uint32_t*)(dst + 3 * BPS), vreinterpret_u32_u8(dst23_u8), 1);
+}
+
+static WEBP_INLINE void Add4x4_NEON(const int16x8_t row01,
+ const int16x8_t row23,
+ const uint8_t* const ref,
+ uint8_t* const dst) {
+ uint32x2_t dst01 = vdup_n_u32(0);
+ uint32x2_t dst23 = vdup_n_u32(0);
+
+ // Load the source pixels.
+ dst01 = vld1_lane_u32((uint32_t*)(ref + 0 * BPS), dst01, 0);
+ dst23 = vld1_lane_u32((uint32_t*)(ref + 2 * BPS), dst23, 0);
+ dst01 = vld1_lane_u32((uint32_t*)(ref + 1 * BPS), dst01, 1);
+ dst23 = vld1_lane_u32((uint32_t*)(ref + 3 * BPS), dst23, 1);
+
+ {
+ // Convert to 16b.
+ const int16x8_t dst01_s16 = ConvertU8ToS16_NEON(dst01);
+ const int16x8_t dst23_s16 = ConvertU8ToS16_NEON(dst23);
+
+ // Descale with rounding.
+ const int16x8_t out01 = vrsraq_n_s16(dst01_s16, row01, 3);
+ const int16x8_t out23 = vrsraq_n_s16(dst23_s16, row23, 3);
+ // Add the inverse transform.
+ SaturateAndStore4x4_NEON(dst, out01, out23);
+ }
+}
+
+static WEBP_INLINE void Transpose8x2_NEON(const int16x8_t in0,
+ const int16x8_t in1,
+ int16x8x2_t* const out) {
+ // a0 a1 a2 a3 | b0 b1 b2 b3 => a0 b0 c0 d0 | a1 b1 c1 d1
+ // c0 c1 c2 c3 | d0 d1 d2 d3 a2 b2 c2 d2 | a3 b3 c3 d3
+ const int16x8x2_t tmp0 = vzipq_s16(in0, in1); // a0 c0 a1 c1 a2 c2 ...
+ // b0 d0 b1 d1 b2 d2 ...
+ *out = vzipq_s16(tmp0.val[0], tmp0.val[1]);
+}
+
+static WEBP_INLINE void TransformPass_NEON(int16x8x2_t* const rows) {
+ // {rows} = in0 | in4
+ // in8 | in12
+ // B1 = in4 | in12
+ const int16x8_t B1 =
+ vcombine_s16(vget_high_s16(rows->val[0]), vget_high_s16(rows->val[1]));
+ // C0 = kC1 * in4 | kC1 * in12
+ // C1 = kC2 * in4 | kC2 * in12
+ const int16x8_t C0 = vsraq_n_s16(B1, vqdmulhq_n_s16(B1, kC1), 1);
+ const int16x8_t C1 = vqdmulhq_n_s16(B1, kC2);
+ const int16x4_t a = vqadd_s16(vget_low_s16(rows->val[0]),
+ vget_low_s16(rows->val[1])); // in0 + in8
+ const int16x4_t b = vqsub_s16(vget_low_s16(rows->val[0]),
+ vget_low_s16(rows->val[1])); // in0 - in8
+ // c = kC2 * in4 - kC1 * in12
+ // d = kC1 * in4 + kC2 * in12
+ const int16x4_t c = vqsub_s16(vget_low_s16(C1), vget_high_s16(C0));
+ const int16x4_t d = vqadd_s16(vget_low_s16(C0), vget_high_s16(C1));
+ const int16x8_t D0 = vcombine_s16(a, b); // D0 = a | b
+ const int16x8_t D1 = vcombine_s16(d, c); // D1 = d | c
+ const int16x8_t E0 = vqaddq_s16(D0, D1); // a+d | b+c
+ const int16x8_t E_tmp = vqsubq_s16(D0, D1); // a-d | b-c
+ const int16x8_t E1 = vcombine_s16(vget_high_s16(E_tmp), vget_low_s16(E_tmp));
+ Transpose8x2_NEON(E0, E1, rows);
+}
+
+static void ITransformOne_NEON(const uint8_t* ref,
+ const int16_t* in, uint8_t* dst) {
+ int16x8x2_t rows;
+ INIT_VECTOR2(rows, vld1q_s16(in + 0), vld1q_s16(in + 8));
+ TransformPass_NEON(&rows);
+ TransformPass_NEON(&rows);
+ Add4x4_NEON(rows.val[0], rows.val[1], ref, dst);
+}
+
+#else
+
+static void ITransformOne_NEON(const uint8_t* ref,
+ const int16_t* in, uint8_t* dst) {
+ const int kBPS = BPS;
+ const int16_t kC1C2[] = { kC1, kC2, 0, 0 };
+
+ __asm__ volatile (
+ "vld1.16 {q1, q2}, [%[in]] \n"
+ "vld1.16 {d0}, [%[kC1C2]] \n"
+
+ // d2: in[0]
+ // d3: in[8]
+ // d4: in[4]
+ // d5: in[12]
+ "vswp d3, d4 \n"
+
+ // q8 = {in[4], in[12]} * kC1 * 2 >> 16
+ // q9 = {in[4], in[12]} * kC2 >> 16
+ "vqdmulh.s16 q8, q2, d0[0] \n"
+ "vqdmulh.s16 q9, q2, d0[1] \n"
+
+ // d22 = a = in[0] + in[8]
+ // d23 = b = in[0] - in[8]
+ "vqadd.s16 d22, d2, d3 \n"
+ "vqsub.s16 d23, d2, d3 \n"
+
+ // q8 = in[4]/[12] * kC1 >> 16
+ "vshr.s16 q8, q8, #1 \n"
+
+ // Add {in[4], in[12]} back after the multiplication.
+ "vqadd.s16 q8, q2, q8 \n"
+
+ // d20 = c = in[4]*kC2 - in[12]*kC1
+ // d21 = d = in[4]*kC1 + in[12]*kC2
+ "vqsub.s16 d20, d18, d17 \n"
+ "vqadd.s16 d21, d19, d16 \n"
+
+ // d2 = tmp[0] = a + d
+ // d3 = tmp[1] = b + c
+ // d4 = tmp[2] = b - c
+ // d5 = tmp[3] = a - d
+ "vqadd.s16 d2, d22, d21 \n"
+ "vqadd.s16 d3, d23, d20 \n"
+ "vqsub.s16 d4, d23, d20 \n"
+ "vqsub.s16 d5, d22, d21 \n"
+
+ "vzip.16 q1, q2 \n"
+ "vzip.16 q1, q2 \n"
+
+ "vswp d3, d4 \n"
+
+ // q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16
+ // q9 = {tmp[4], tmp[12]} * kC2 >> 16
+ "vqdmulh.s16 q8, q2, d0[0] \n"
+ "vqdmulh.s16 q9, q2, d0[1] \n"
+
+ // d22 = a = tmp[0] + tmp[8]
+ // d23 = b = tmp[0] - tmp[8]
+ "vqadd.s16 d22, d2, d3 \n"
+ "vqsub.s16 d23, d2, d3 \n"
+
+ "vshr.s16 q8, q8, #1 \n"
+ "vqadd.s16 q8, q2, q8 \n"
+
+ // d20 = c = in[4]*kC2 - in[12]*kC1
+ // d21 = d = in[4]*kC1 + in[12]*kC2
+ "vqsub.s16 d20, d18, d17 \n"
+ "vqadd.s16 d21, d19, d16 \n"
+
+ // d2 = tmp[0] = a + d
+ // d3 = tmp[1] = b + c
+ // d4 = tmp[2] = b - c
+ // d5 = tmp[3] = a - d
+ "vqadd.s16 d2, d22, d21 \n"
+ "vqadd.s16 d3, d23, d20 \n"
+ "vqsub.s16 d4, d23, d20 \n"
+ "vqsub.s16 d5, d22, d21 \n"
+
+ "vld1.32 d6[0], [%[ref]], %[kBPS] \n"
+ "vld1.32 d6[1], [%[ref]], %[kBPS] \n"
+ "vld1.32 d7[0], [%[ref]], %[kBPS] \n"
+ "vld1.32 d7[1], [%[ref]], %[kBPS] \n"
+
+ "sub %[ref], %[ref], %[kBPS], lsl #2 \n"
+
+ // (val) + 4 >> 3
+ "vrshr.s16 d2, d2, #3 \n"
+ "vrshr.s16 d3, d3, #3 \n"
+ "vrshr.s16 d4, d4, #3 \n"
+ "vrshr.s16 d5, d5, #3 \n"
+
+ "vzip.16 q1, q2 \n"
+ "vzip.16 q1, q2 \n"
+
+ // Must accumulate before saturating
+ "vmovl.u8 q8, d6 \n"
+ "vmovl.u8 q9, d7 \n"
+
+ "vqadd.s16 q1, q1, q8 \n"
+ "vqadd.s16 q2, q2, q9 \n"
+
+ "vqmovun.s16 d0, q1 \n"
+ "vqmovun.s16 d1, q2 \n"
+
+ "vst1.32 d0[0], [%[dst]], %[kBPS] \n"
+ "vst1.32 d0[1], [%[dst]], %[kBPS] \n"
+ "vst1.32 d1[0], [%[dst]], %[kBPS] \n"
+ "vst1.32 d1[1], [%[dst]] \n"
+
+ : [in] "+r"(in), [dst] "+r"(dst) // modified registers
+ : [kBPS] "r"(kBPS), [kC1C2] "r"(kC1C2), [ref] "r"(ref) // constants
+ : "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" // clobbered
+ );
+}
+
+#endif // WEBP_USE_INTRINSICS
+
+static void ITransform_NEON(const uint8_t* ref,
+ const int16_t* in, uint8_t* dst, int do_two) {
+ ITransformOne_NEON(ref, in, dst);
+ if (do_two) {
+ ITransformOne_NEON(ref + 4, in + 16, dst + 4);
+ }
+}
+
+// Load all 4x4 pixels into a single uint8x16_t variable.
+static uint8x16_t Load4x4_NEON(const uint8_t* src) {
+ uint32x4_t out = vdupq_n_u32(0);
+ out = vld1q_lane_u32((const uint32_t*)(src + 0 * BPS), out, 0);
+ out = vld1q_lane_u32((const uint32_t*)(src + 1 * BPS), out, 1);
+ out = vld1q_lane_u32((const uint32_t*)(src + 2 * BPS), out, 2);
+ out = vld1q_lane_u32((const uint32_t*)(src + 3 * BPS), out, 3);
+ return vreinterpretq_u8_u32(out);
+}
+
+// Forward transform.
+
+#if defined(WEBP_USE_INTRINSICS)
+
+static WEBP_INLINE void Transpose4x4_S16_NEON(const int16x4_t A,
+ const int16x4_t B,
+ const int16x4_t C,
+ const int16x4_t D,
+ int16x8_t* const out01,
+ int16x8_t* const out32) {
+ const int16x4x2_t AB = vtrn_s16(A, B);
+ const int16x4x2_t CD = vtrn_s16(C, D);
+ const int32x2x2_t tmp02 = vtrn_s32(vreinterpret_s32_s16(AB.val[0]),
+ vreinterpret_s32_s16(CD.val[0]));
+ const int32x2x2_t tmp13 = vtrn_s32(vreinterpret_s32_s16(AB.val[1]),
+ vreinterpret_s32_s16(CD.val[1]));
+ *out01 = vreinterpretq_s16_s64(
+ vcombine_s64(vreinterpret_s64_s32(tmp02.val[0]),
+ vreinterpret_s64_s32(tmp13.val[0])));
+ *out32 = vreinterpretq_s16_s64(
+ vcombine_s64(vreinterpret_s64_s32(tmp13.val[1]),
+ vreinterpret_s64_s32(tmp02.val[1])));
+}
+
+static WEBP_INLINE int16x8_t DiffU8ToS16_NEON(const uint8x8_t a,
+ const uint8x8_t b) {
+ return vreinterpretq_s16_u16(vsubl_u8(a, b));
+}
+
+static void FTransform_NEON(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ int16x8_t d0d1, d3d2; // working 4x4 int16 variables
+ {
+ const uint8x16_t S0 = Load4x4_NEON(src);
+ const uint8x16_t R0 = Load4x4_NEON(ref);
+ const int16x8_t D0D1 = DiffU8ToS16_NEON(vget_low_u8(S0), vget_low_u8(R0));
+ const int16x8_t D2D3 = DiffU8ToS16_NEON(vget_high_u8(S0), vget_high_u8(R0));
+ const int16x4_t D0 = vget_low_s16(D0D1);
+ const int16x4_t D1 = vget_high_s16(D0D1);
+ const int16x4_t D2 = vget_low_s16(D2D3);
+ const int16x4_t D3 = vget_high_s16(D2D3);
+ Transpose4x4_S16_NEON(D0, D1, D2, D3, &d0d1, &d3d2);
+ }
+ { // 1rst pass
+ const int32x4_t kCst937 = vdupq_n_s32(937);
+ const int32x4_t kCst1812 = vdupq_n_s32(1812);
+ const int16x8_t a0a1 = vaddq_s16(d0d1, d3d2); // d0+d3 | d1+d2 (=a0|a1)
+ const int16x8_t a3a2 = vsubq_s16(d0d1, d3d2); // d0-d3 | d1-d2 (=a3|a2)
+ const int16x8_t a0a1_2 = vshlq_n_s16(a0a1, 3);
+ const int16x4_t tmp0 = vadd_s16(vget_low_s16(a0a1_2),
+ vget_high_s16(a0a1_2));
+ const int16x4_t tmp2 = vsub_s16(vget_low_s16(a0a1_2),
+ vget_high_s16(a0a1_2));
+ const int32x4_t a3_2217 = vmull_n_s16(vget_low_s16(a3a2), 2217);
+ const int32x4_t a2_2217 = vmull_n_s16(vget_high_s16(a3a2), 2217);
+ const int32x4_t a2_p_a3 = vmlal_n_s16(a2_2217, vget_low_s16(a3a2), 5352);
+ const int32x4_t a3_m_a2 = vmlsl_n_s16(a3_2217, vget_high_s16(a3a2), 5352);
+ const int16x4_t tmp1 = vshrn_n_s32(vaddq_s32(a2_p_a3, kCst1812), 9);
+ const int16x4_t tmp3 = vshrn_n_s32(vaddq_s32(a3_m_a2, kCst937), 9);
+ Transpose4x4_S16_NEON(tmp0, tmp1, tmp2, tmp3, &d0d1, &d3d2);
+ }
+ { // 2nd pass
+ // the (1<<16) addition is for the replacement: a3!=0 <-> 1-(a3==0)
+ const int32x4_t kCst12000 = vdupq_n_s32(12000 + (1 << 16));
+ const int32x4_t kCst51000 = vdupq_n_s32(51000);
+ const int16x8_t a0a1 = vaddq_s16(d0d1, d3d2); // d0+d3 | d1+d2 (=a0|a1)
+ const int16x8_t a3a2 = vsubq_s16(d0d1, d3d2); // d0-d3 | d1-d2 (=a3|a2)
+ const int16x4_t a0_k7 = vadd_s16(vget_low_s16(a0a1), vdup_n_s16(7));
+ const int16x4_t out0 = vshr_n_s16(vadd_s16(a0_k7, vget_high_s16(a0a1)), 4);
+ const int16x4_t out2 = vshr_n_s16(vsub_s16(a0_k7, vget_high_s16(a0a1)), 4);
+ const int32x4_t a3_2217 = vmull_n_s16(vget_low_s16(a3a2), 2217);
+ const int32x4_t a2_2217 = vmull_n_s16(vget_high_s16(a3a2), 2217);
+ const int32x4_t a2_p_a3 = vmlal_n_s16(a2_2217, vget_low_s16(a3a2), 5352);
+ const int32x4_t a3_m_a2 = vmlsl_n_s16(a3_2217, vget_high_s16(a3a2), 5352);
+ const int16x4_t tmp1 = vaddhn_s32(a2_p_a3, kCst12000);
+ const int16x4_t out3 = vaddhn_s32(a3_m_a2, kCst51000);
+ const int16x4_t a3_eq_0 =
+ vreinterpret_s16_u16(vceq_s16(vget_low_s16(a3a2), vdup_n_s16(0)));
+ const int16x4_t out1 = vadd_s16(tmp1, a3_eq_0);
+ vst1_s16(out + 0, out0);
+ vst1_s16(out + 4, out1);
+ vst1_s16(out + 8, out2);
+ vst1_s16(out + 12, out3);
+ }
+}
+
+#else
+
+// adapted from vp8/encoder/arm/neon/shortfdct_neon.asm
+static const int16_t kCoeff16[] = {
+ 5352, 5352, 5352, 5352, 2217, 2217, 2217, 2217
+};
+static const int32_t kCoeff32[] = {
+ 1812, 1812, 1812, 1812,
+ 937, 937, 937, 937,
+ 12000, 12000, 12000, 12000,
+ 51000, 51000, 51000, 51000
+};
+
+static void FTransform_NEON(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ const int kBPS = BPS;
+ const uint8_t* src_ptr = src;
+ const uint8_t* ref_ptr = ref;
+ const int16_t* coeff16 = kCoeff16;
+ const int32_t* coeff32 = kCoeff32;
+
+ __asm__ volatile (
+ // load src into q4, q5 in high half
+ "vld1.8 {d8}, [%[src_ptr]], %[kBPS] \n"
+ "vld1.8 {d10}, [%[src_ptr]], %[kBPS] \n"
+ "vld1.8 {d9}, [%[src_ptr]], %[kBPS] \n"
+ "vld1.8 {d11}, [%[src_ptr]] \n"
+
+ // load ref into q6, q7 in high half
+ "vld1.8 {d12}, [%[ref_ptr]], %[kBPS] \n"
+ "vld1.8 {d14}, [%[ref_ptr]], %[kBPS] \n"
+ "vld1.8 {d13}, [%[ref_ptr]], %[kBPS] \n"
+ "vld1.8 {d15}, [%[ref_ptr]] \n"
+
+ // Pack the high values in to q4 and q6
+ "vtrn.32 q4, q5 \n"
+ "vtrn.32 q6, q7 \n"
+
+ // d[0-3] = src - ref
+ "vsubl.u8 q0, d8, d12 \n"
+ "vsubl.u8 q1, d9, d13 \n"
+
+ // load coeff16 into q8(d16=5352, d17=2217)
+ "vld1.16 {q8}, [%[coeff16]] \n"
+
+ // load coeff32 high half into q9 = 1812, q10 = 937
+ "vld1.32 {q9, q10}, [%[coeff32]]! \n"
+
+ // load coeff32 low half into q11=12000, q12=51000
+ "vld1.32 {q11,q12}, [%[coeff32]] \n"
+
+ // part 1
+ // Transpose. Register dN is the same as dN in C
+ "vtrn.32 d0, d2 \n"
+ "vtrn.32 d1, d3 \n"
+ "vtrn.16 d0, d1 \n"
+ "vtrn.16 d2, d3 \n"
+
+ "vadd.s16 d4, d0, d3 \n" // a0 = d0 + d3
+ "vadd.s16 d5, d1, d2 \n" // a1 = d1 + d2
+ "vsub.s16 d6, d1, d2 \n" // a2 = d1 - d2
+ "vsub.s16 d7, d0, d3 \n" // a3 = d0 - d3
+
+ "vadd.s16 d0, d4, d5 \n" // a0 + a1
+ "vshl.s16 d0, d0, #3 \n" // temp[0+i*4] = (a0+a1) << 3
+ "vsub.s16 d2, d4, d5 \n" // a0 - a1
+ "vshl.s16 d2, d2, #3 \n" // (temp[2+i*4] = (a0-a1) << 3
+
+ "vmlal.s16 q9, d7, d16 \n" // a3*5352 + 1812
+ "vmlal.s16 q10, d7, d17 \n" // a3*2217 + 937
+ "vmlal.s16 q9, d6, d17 \n" // a2*2217 + a3*5352 + 1812
+ "vmlsl.s16 q10, d6, d16 \n" // a3*2217 + 937 - a2*5352
+
+ // temp[1+i*4] = (d2*2217 + d3*5352 + 1812) >> 9
+ // temp[3+i*4] = (d3*2217 + 937 - d2*5352) >> 9
+ "vshrn.s32 d1, q9, #9 \n"
+ "vshrn.s32 d3, q10, #9 \n"
+
+ // part 2
+ // transpose d0=ip[0], d1=ip[4], d2=ip[8], d3=ip[12]
+ "vtrn.32 d0, d2 \n"
+ "vtrn.32 d1, d3 \n"
+ "vtrn.16 d0, d1 \n"
+ "vtrn.16 d2, d3 \n"
+
+ "vmov.s16 d26, #7 \n"
+
+ "vadd.s16 d4, d0, d3 \n" // a1 = ip[0] + ip[12]
+ "vadd.s16 d5, d1, d2 \n" // b1 = ip[4] + ip[8]
+ "vsub.s16 d6, d1, d2 \n" // c1 = ip[4] - ip[8]
+ "vadd.s16 d4, d4, d26 \n" // a1 + 7
+ "vsub.s16 d7, d0, d3 \n" // d1 = ip[0] - ip[12]
+
+ "vadd.s16 d0, d4, d5 \n" // op[0] = a1 + b1 + 7
+ "vsub.s16 d2, d4, d5 \n" // op[8] = a1 - b1 + 7
+
+ "vmlal.s16 q11, d7, d16 \n" // d1*5352 + 12000
+ "vmlal.s16 q12, d7, d17 \n" // d1*2217 + 51000
+
+ "vceq.s16 d4, d7, #0 \n"
+
+ "vshr.s16 d0, d0, #4 \n"
+ "vshr.s16 d2, d2, #4 \n"
+
+ "vmlal.s16 q11, d6, d17 \n" // c1*2217 + d1*5352 + 12000
+ "vmlsl.s16 q12, d6, d16 \n" // d1*2217 - c1*5352 + 51000
+
+ "vmvn d4, d4 \n" // !(d1 == 0)
+ // op[4] = (c1*2217 + d1*5352 + 12000)>>16
+ "vshrn.s32 d1, q11, #16 \n"
+ // op[4] += (d1!=0)
+ "vsub.s16 d1, d1, d4 \n"
+ // op[12]= (d1*2217 - c1*5352 + 51000)>>16
+ "vshrn.s32 d3, q12, #16 \n"
+
+ // set result to out array
+ "vst1.16 {q0, q1}, [%[out]] \n"
+ : [src_ptr] "+r"(src_ptr), [ref_ptr] "+r"(ref_ptr),
+ [coeff32] "+r"(coeff32) // modified registers
+ : [kBPS] "r"(kBPS), [coeff16] "r"(coeff16),
+ [out] "r"(out) // constants
+ : "memory", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9",
+ "q10", "q11", "q12", "q13" // clobbered
+ );
+}
+
+#endif
+
+#define LOAD_LANE_16b(VALUE, LANE) do { \
+ (VALUE) = vld1_lane_s16(src, (VALUE), (LANE)); \
+ src += stride; \
+} while (0)
+
+static void FTransformWHT_NEON(const int16_t* src, int16_t* out) {
+ const int stride = 16;
+ const int16x4_t zero = vdup_n_s16(0);
+ int32x4x4_t tmp0;
+ int16x4x4_t in;
+ INIT_VECTOR4(in, zero, zero, zero, zero);
+ LOAD_LANE_16b(in.val[0], 0);
+ LOAD_LANE_16b(in.val[1], 0);
+ LOAD_LANE_16b(in.val[2], 0);
+ LOAD_LANE_16b(in.val[3], 0);
+ LOAD_LANE_16b(in.val[0], 1);
+ LOAD_LANE_16b(in.val[1], 1);
+ LOAD_LANE_16b(in.val[2], 1);
+ LOAD_LANE_16b(in.val[3], 1);
+ LOAD_LANE_16b(in.val[0], 2);
+ LOAD_LANE_16b(in.val[1], 2);
+ LOAD_LANE_16b(in.val[2], 2);
+ LOAD_LANE_16b(in.val[3], 2);
+ LOAD_LANE_16b(in.val[0], 3);
+ LOAD_LANE_16b(in.val[1], 3);
+ LOAD_LANE_16b(in.val[2], 3);
+ LOAD_LANE_16b(in.val[3], 3);
+
+ {
+ // a0 = in[0 * 16] + in[2 * 16]
+ // a1 = in[1 * 16] + in[3 * 16]
+ // a2 = in[1 * 16] - in[3 * 16]
+ // a3 = in[0 * 16] - in[2 * 16]
+ const int32x4_t a0 = vaddl_s16(in.val[0], in.val[2]);
+ const int32x4_t a1 = vaddl_s16(in.val[1], in.val[3]);
+ const int32x4_t a2 = vsubl_s16(in.val[1], in.val[3]);
+ const int32x4_t a3 = vsubl_s16(in.val[0], in.val[2]);
+ tmp0.val[0] = vaddq_s32(a0, a1);
+ tmp0.val[1] = vaddq_s32(a3, a2);
+ tmp0.val[2] = vsubq_s32(a3, a2);
+ tmp0.val[3] = vsubq_s32(a0, a1);
+ }
+ {
+ const int32x4x4_t tmp1 = Transpose4x4_NEON(tmp0);
+ // a0 = tmp[0 + i] + tmp[ 8 + i]
+ // a1 = tmp[4 + i] + tmp[12 + i]
+ // a2 = tmp[4 + i] - tmp[12 + i]
+ // a3 = tmp[0 + i] - tmp[ 8 + i]
+ const int32x4_t a0 = vaddq_s32(tmp1.val[0], tmp1.val[2]);
+ const int32x4_t a1 = vaddq_s32(tmp1.val[1], tmp1.val[3]);
+ const int32x4_t a2 = vsubq_s32(tmp1.val[1], tmp1.val[3]);
+ const int32x4_t a3 = vsubq_s32(tmp1.val[0], tmp1.val[2]);
+ const int32x4_t b0 = vhaddq_s32(a0, a1); // (a0 + a1) >> 1
+ const int32x4_t b1 = vhaddq_s32(a3, a2); // (a3 + a2) >> 1
+ const int32x4_t b2 = vhsubq_s32(a3, a2); // (a3 - a2) >> 1
+ const int32x4_t b3 = vhsubq_s32(a0, a1); // (a0 - a1) >> 1
+ const int16x4_t out0 = vmovn_s32(b0);
+ const int16x4_t out1 = vmovn_s32(b1);
+ const int16x4_t out2 = vmovn_s32(b2);
+ const int16x4_t out3 = vmovn_s32(b3);
+
+ vst1_s16(out + 0, out0);
+ vst1_s16(out + 4, out1);
+ vst1_s16(out + 8, out2);
+ vst1_s16(out + 12, out3);
+ }
+}
+#undef LOAD_LANE_16b
+
+//------------------------------------------------------------------------------
+// Texture distortion
+//
+// We try to match the spectral content (weighted) between source and
+// reconstructed samples.
+
+// a 0123, b 0123
+// a 4567, b 4567
+// a 89ab, b 89ab
+// a cdef, b cdef
+//
+// transpose
+//
+// a 048c, b 048c
+// a 159d, b 159d
+// a 26ae, b 26ae
+// a 37bf, b 37bf
+//
+static WEBP_INLINE int16x8x4_t DistoTranspose4x4S16_NEON(int16x8x4_t q4_in) {
+ const int16x8x2_t q2_tmp0 = vtrnq_s16(q4_in.val[0], q4_in.val[1]);
+ const int16x8x2_t q2_tmp1 = vtrnq_s16(q4_in.val[2], q4_in.val[3]);
+ const int32x4x2_t q2_tmp2 = vtrnq_s32(vreinterpretq_s32_s16(q2_tmp0.val[0]),
+ vreinterpretq_s32_s16(q2_tmp1.val[0]));
+ const int32x4x2_t q2_tmp3 = vtrnq_s32(vreinterpretq_s32_s16(q2_tmp0.val[1]),
+ vreinterpretq_s32_s16(q2_tmp1.val[1]));
+ q4_in.val[0] = vreinterpretq_s16_s32(q2_tmp2.val[0]);
+ q4_in.val[2] = vreinterpretq_s16_s32(q2_tmp2.val[1]);
+ q4_in.val[1] = vreinterpretq_s16_s32(q2_tmp3.val[0]);
+ q4_in.val[3] = vreinterpretq_s16_s32(q2_tmp3.val[1]);
+ return q4_in;
+}
+
+static WEBP_INLINE int16x8x4_t DistoHorizontalPass_NEON(
+ const int16x8x4_t q4_in) {
+ // {a0, a1} = {in[0] + in[2], in[1] + in[3]}
+ // {a3, a2} = {in[0] - in[2], in[1] - in[3]}
+ const int16x8_t q_a0 = vaddq_s16(q4_in.val[0], q4_in.val[2]);
+ const int16x8_t q_a1 = vaddq_s16(q4_in.val[1], q4_in.val[3]);
+ const int16x8_t q_a3 = vsubq_s16(q4_in.val[0], q4_in.val[2]);
+ const int16x8_t q_a2 = vsubq_s16(q4_in.val[1], q4_in.val[3]);
+ int16x8x4_t q4_out;
+ // tmp[0] = a0 + a1
+ // tmp[1] = a3 + a2
+ // tmp[2] = a3 - a2
+ // tmp[3] = a0 - a1
+ INIT_VECTOR4(q4_out,
+ vabsq_s16(vaddq_s16(q_a0, q_a1)),
+ vabsq_s16(vaddq_s16(q_a3, q_a2)),
+ vabdq_s16(q_a3, q_a2), vabdq_s16(q_a0, q_a1));
+ return q4_out;
+}
+
+static WEBP_INLINE int16x8x4_t DistoVerticalPass_NEON(const uint8x8x4_t q4_in) {
+ const int16x8_t q_a0 = vreinterpretq_s16_u16(vaddl_u8(q4_in.val[0],
+ q4_in.val[2]));
+ const int16x8_t q_a1 = vreinterpretq_s16_u16(vaddl_u8(q4_in.val[1],
+ q4_in.val[3]));
+ const int16x8_t q_a2 = vreinterpretq_s16_u16(vsubl_u8(q4_in.val[1],
+ q4_in.val[3]));
+ const int16x8_t q_a3 = vreinterpretq_s16_u16(vsubl_u8(q4_in.val[0],
+ q4_in.val[2]));
+ int16x8x4_t q4_out;
+
+ INIT_VECTOR4(q4_out,
+ vaddq_s16(q_a0, q_a1), vaddq_s16(q_a3, q_a2),
+ vsubq_s16(q_a3, q_a2), vsubq_s16(q_a0, q_a1));
+ return q4_out;
+}
+
+static WEBP_INLINE int16x4x4_t DistoLoadW_NEON(const uint16_t* w) {
+ const uint16x8_t q_w07 = vld1q_u16(&w[0]);
+ const uint16x8_t q_w8f = vld1q_u16(&w[8]);
+ int16x4x4_t d4_w;
+ INIT_VECTOR4(d4_w,
+ vget_low_s16(vreinterpretq_s16_u16(q_w07)),
+ vget_high_s16(vreinterpretq_s16_u16(q_w07)),
+ vget_low_s16(vreinterpretq_s16_u16(q_w8f)),
+ vget_high_s16(vreinterpretq_s16_u16(q_w8f)));
+ return d4_w;
+}
+
+static WEBP_INLINE int32x2_t DistoSum_NEON(const int16x8x4_t q4_in,
+ const int16x4x4_t d4_w) {
+ int32x2_t d_sum;
+ // sum += w[ 0] * abs(b0);
+ // sum += w[ 4] * abs(b1);
+ // sum += w[ 8] * abs(b2);
+ // sum += w[12] * abs(b3);
+ int32x4_t q_sum0 = vmull_s16(d4_w.val[0], vget_low_s16(q4_in.val[0]));
+ int32x4_t q_sum1 = vmull_s16(d4_w.val[1], vget_low_s16(q4_in.val[1]));
+ int32x4_t q_sum2 = vmull_s16(d4_w.val[2], vget_low_s16(q4_in.val[2]));
+ int32x4_t q_sum3 = vmull_s16(d4_w.val[3], vget_low_s16(q4_in.val[3]));
+ q_sum0 = vmlsl_s16(q_sum0, d4_w.val[0], vget_high_s16(q4_in.val[0]));
+ q_sum1 = vmlsl_s16(q_sum1, d4_w.val[1], vget_high_s16(q4_in.val[1]));
+ q_sum2 = vmlsl_s16(q_sum2, d4_w.val[2], vget_high_s16(q4_in.val[2]));
+ q_sum3 = vmlsl_s16(q_sum3, d4_w.val[3], vget_high_s16(q4_in.val[3]));
+
+ q_sum0 = vaddq_s32(q_sum0, q_sum1);
+ q_sum2 = vaddq_s32(q_sum2, q_sum3);
+ q_sum2 = vaddq_s32(q_sum0, q_sum2);
+ d_sum = vpadd_s32(vget_low_s32(q_sum2), vget_high_s32(q_sum2));
+ d_sum = vpadd_s32(d_sum, d_sum);
+ return d_sum;
+}
+
+#define LOAD_LANE_32b(src, VALUE, LANE) \
+ (VALUE) = vld1_lane_u32((const uint32_t*)(src), (VALUE), (LANE))
+
+// Hadamard transform
+// Returns the weighted sum of the absolute value of transformed coefficients.
+// w[] contains a row-major 4 by 4 symmetric matrix.
+static int Disto4x4_NEON(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ uint32x2_t d_in_ab_0123 = vdup_n_u32(0);
+ uint32x2_t d_in_ab_4567 = vdup_n_u32(0);
+ uint32x2_t d_in_ab_89ab = vdup_n_u32(0);
+ uint32x2_t d_in_ab_cdef = vdup_n_u32(0);
+ uint8x8x4_t d4_in;
+
+ // load data a, b
+ LOAD_LANE_32b(a + 0 * BPS, d_in_ab_0123, 0);
+ LOAD_LANE_32b(a + 1 * BPS, d_in_ab_4567, 0);
+ LOAD_LANE_32b(a + 2 * BPS, d_in_ab_89ab, 0);
+ LOAD_LANE_32b(a + 3 * BPS, d_in_ab_cdef, 0);
+ LOAD_LANE_32b(b + 0 * BPS, d_in_ab_0123, 1);
+ LOAD_LANE_32b(b + 1 * BPS, d_in_ab_4567, 1);
+ LOAD_LANE_32b(b + 2 * BPS, d_in_ab_89ab, 1);
+ LOAD_LANE_32b(b + 3 * BPS, d_in_ab_cdef, 1);
+ INIT_VECTOR4(d4_in,
+ vreinterpret_u8_u32(d_in_ab_0123),
+ vreinterpret_u8_u32(d_in_ab_4567),
+ vreinterpret_u8_u32(d_in_ab_89ab),
+ vreinterpret_u8_u32(d_in_ab_cdef));
+
+ {
+ // Vertical pass first to avoid a transpose (vertical and horizontal passes
+ // are commutative because w/kWeightY is symmetric) and subsequent
+ // transpose.
+ const int16x8x4_t q4_v = DistoVerticalPass_NEON(d4_in);
+ const int16x4x4_t d4_w = DistoLoadW_NEON(w);
+ // horizontal pass
+ const int16x8x4_t q4_t = DistoTranspose4x4S16_NEON(q4_v);
+ const int16x8x4_t q4_h = DistoHorizontalPass_NEON(q4_t);
+ int32x2_t d_sum = DistoSum_NEON(q4_h, d4_w);
+
+ // abs(sum2 - sum1) >> 5
+ d_sum = vabs_s32(d_sum);
+ d_sum = vshr_n_s32(d_sum, 5);
+ return vget_lane_s32(d_sum, 0);
+ }
+}
+#undef LOAD_LANE_32b
+
+static int Disto16x16_NEON(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4_NEON(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+
+//------------------------------------------------------------------------------
+
+static void CollectHistogram_NEON(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block,
+ VP8Histogram* const histo) {
+ const uint16x8_t max_coeff_thresh = vdupq_n_u16(MAX_COEFF_THRESH);
+ int j;
+ int distribution[MAX_COEFF_THRESH + 1] = { 0 };
+ for (j = start_block; j < end_block; ++j) {
+ int16_t out[16];
+ FTransform_NEON(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
+ {
+ int k;
+ const int16x8_t a0 = vld1q_s16(out + 0);
+ const int16x8_t b0 = vld1q_s16(out + 8);
+ const uint16x8_t a1 = vreinterpretq_u16_s16(vabsq_s16(a0));
+ const uint16x8_t b1 = vreinterpretq_u16_s16(vabsq_s16(b0));
+ const uint16x8_t a2 = vshrq_n_u16(a1, 3);
+ const uint16x8_t b2 = vshrq_n_u16(b1, 3);
+ const uint16x8_t a3 = vminq_u16(a2, max_coeff_thresh);
+ const uint16x8_t b3 = vminq_u16(b2, max_coeff_thresh);
+ vst1q_s16(out + 0, vreinterpretq_s16_u16(a3));
+ vst1q_s16(out + 8, vreinterpretq_s16_u16(b3));
+ // Convert coefficients to bin.
+ for (k = 0; k < 16; ++k) {
+ ++distribution[out[k]];
+ }
+ }
+ }
+ VP8SetHistogramData(distribution, histo);
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE void AccumulateSSE16_NEON(const uint8_t* const a,
+ const uint8_t* const b,
+ uint32x4_t* const sum) {
+ const uint8x16_t a0 = vld1q_u8(a);
+ const uint8x16_t b0 = vld1q_u8(b);
+ const uint8x16_t abs_diff = vabdq_u8(a0, b0);
+ const uint16x8_t prod1 = vmull_u8(vget_low_u8(abs_diff),
+ vget_low_u8(abs_diff));
+ const uint16x8_t prod2 = vmull_u8(vget_high_u8(abs_diff),
+ vget_high_u8(abs_diff));
+ /* pair-wise adds and widen */
+ const uint32x4_t sum1 = vpaddlq_u16(prod1);
+ const uint32x4_t sum2 = vpaddlq_u16(prod2);
+ *sum = vaddq_u32(*sum, vaddq_u32(sum1, sum2));
+}
+
+// Horizontal sum of all four uint32_t values in 'sum'.
+static int SumToInt_NEON(uint32x4_t sum) {
+ const uint64x2_t sum2 = vpaddlq_u32(sum);
+ const uint64_t sum3 = vgetq_lane_u64(sum2, 0) + vgetq_lane_u64(sum2, 1);
+ return (int)sum3;
+}
+
+static int SSE16x16_NEON(const uint8_t* a, const uint8_t* b) {
+ uint32x4_t sum = vdupq_n_u32(0);
+ int y;
+ for (y = 0; y < 16; ++y) {
+ AccumulateSSE16_NEON(a + y * BPS, b + y * BPS, &sum);
+ }
+ return SumToInt_NEON(sum);
+}
+
+static int SSE16x8_NEON(const uint8_t* a, const uint8_t* b) {
+ uint32x4_t sum = vdupq_n_u32(0);
+ int y;
+ for (y = 0; y < 8; ++y) {
+ AccumulateSSE16_NEON(a + y * BPS, b + y * BPS, &sum);
+ }
+ return SumToInt_NEON(sum);
+}
+
+static int SSE8x8_NEON(const uint8_t* a, const uint8_t* b) {
+ uint32x4_t sum = vdupq_n_u32(0);
+ int y;
+ for (y = 0; y < 8; ++y) {
+ const uint8x8_t a0 = vld1_u8(a + y * BPS);
+ const uint8x8_t b0 = vld1_u8(b + y * BPS);
+ const uint8x8_t abs_diff = vabd_u8(a0, b0);
+ const uint16x8_t prod = vmull_u8(abs_diff, abs_diff);
+ sum = vpadalq_u16(sum, prod);
+ }
+ return SumToInt_NEON(sum);
+}
+
+static int SSE4x4_NEON(const uint8_t* a, const uint8_t* b) {
+ const uint8x16_t a0 = Load4x4_NEON(a);
+ const uint8x16_t b0 = Load4x4_NEON(b);
+ const uint8x16_t abs_diff = vabdq_u8(a0, b0);
+ const uint16x8_t prod1 = vmull_u8(vget_low_u8(abs_diff),
+ vget_low_u8(abs_diff));
+ const uint16x8_t prod2 = vmull_u8(vget_high_u8(abs_diff),
+ vget_high_u8(abs_diff));
+ /* pair-wise adds and widen */
+ const uint32x4_t sum1 = vpaddlq_u16(prod1);
+ const uint32x4_t sum2 = vpaddlq_u16(prod2);
+ return SumToInt_NEON(vaddq_u32(sum1, sum2));
+}
+
+//------------------------------------------------------------------------------
+
+// Compilation with gcc-4.6.x is problematic for now.
+#if !defined(WORK_AROUND_GCC)
+
+static int16x8_t Quantize_NEON(int16_t* const in,
+ const VP8Matrix* const mtx, int offset) {
+ const uint16x8_t sharp = vld1q_u16(&mtx->sharpen_[offset]);
+ const uint16x8_t q = vld1q_u16(&mtx->q_[offset]);
+ const uint16x8_t iq = vld1q_u16(&mtx->iq_[offset]);
+ const uint32x4_t bias0 = vld1q_u32(&mtx->bias_[offset + 0]);
+ const uint32x4_t bias1 = vld1q_u32(&mtx->bias_[offset + 4]);
+
+ const int16x8_t a = vld1q_s16(in + offset); // in
+ const uint16x8_t b = vreinterpretq_u16_s16(vabsq_s16(a)); // coeff = abs(in)
+ const int16x8_t sign = vshrq_n_s16(a, 15); // sign
+ const uint16x8_t c = vaddq_u16(b, sharp); // + sharpen
+ const uint32x4_t m0 = vmull_u16(vget_low_u16(c), vget_low_u16(iq));
+ const uint32x4_t m1 = vmull_u16(vget_high_u16(c), vget_high_u16(iq));
+ const uint32x4_t m2 = vhaddq_u32(m0, bias0);
+ const uint32x4_t m3 = vhaddq_u32(m1, bias1); // (coeff * iQ + bias) >> 1
+ const uint16x8_t c0 = vcombine_u16(vshrn_n_u32(m2, 16),
+ vshrn_n_u32(m3, 16)); // QFIX=17 = 16+1
+ const uint16x8_t c1 = vminq_u16(c0, vdupq_n_u16(MAX_LEVEL));
+ const int16x8_t c2 = veorq_s16(vreinterpretq_s16_u16(c1), sign);
+ const int16x8_t c3 = vsubq_s16(c2, sign); // restore sign
+ const int16x8_t c4 = vmulq_s16(c3, vreinterpretq_s16_u16(q));
+ vst1q_s16(in + offset, c4);
+ assert(QFIX == 17); // this function can't work as is if QFIX != 16+1
+ return c3;
+}
+
+static const uint8_t kShuffles[4][8] = {
+ { 0, 1, 2, 3, 8, 9, 16, 17 },
+ { 10, 11, 4, 5, 6, 7, 12, 13 },
+ { 18, 19, 24, 25, 26, 27, 20, 21 },
+ { 14, 15, 22, 23, 28, 29, 30, 31 }
+};
+
+static int QuantizeBlock_NEON(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ const int16x8_t out0 = Quantize_NEON(in, mtx, 0);
+ const int16x8_t out1 = Quantize_NEON(in, mtx, 8);
+ uint8x8x4_t shuffles;
+ // vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
+ // non-standard versions there.
+#if defined(__APPLE__) && defined(__aarch64__) && \
+ defined(__apple_build_version__) && (__apple_build_version__< 6020037)
+ uint8x16x2_t all_out;
+ INIT_VECTOR2(all_out, vreinterpretq_u8_s16(out0), vreinterpretq_u8_s16(out1));
+ INIT_VECTOR4(shuffles,
+ vtbl2q_u8(all_out, vld1_u8(kShuffles[0])),
+ vtbl2q_u8(all_out, vld1_u8(kShuffles[1])),
+ vtbl2q_u8(all_out, vld1_u8(kShuffles[2])),
+ vtbl2q_u8(all_out, vld1_u8(kShuffles[3])));
+#else
+ uint8x8x4_t all_out;
+ INIT_VECTOR4(all_out,
+ vreinterpret_u8_s16(vget_low_s16(out0)),
+ vreinterpret_u8_s16(vget_high_s16(out0)),
+ vreinterpret_u8_s16(vget_low_s16(out1)),
+ vreinterpret_u8_s16(vget_high_s16(out1)));
+ INIT_VECTOR4(shuffles,
+ vtbl4_u8(all_out, vld1_u8(kShuffles[0])),
+ vtbl4_u8(all_out, vld1_u8(kShuffles[1])),
+ vtbl4_u8(all_out, vld1_u8(kShuffles[2])),
+ vtbl4_u8(all_out, vld1_u8(kShuffles[3])));
+#endif
+ // Zigzag reordering
+ vst1_u8((uint8_t*)(out + 0), shuffles.val[0]);
+ vst1_u8((uint8_t*)(out + 4), shuffles.val[1]);
+ vst1_u8((uint8_t*)(out + 8), shuffles.val[2]);
+ vst1_u8((uint8_t*)(out + 12), shuffles.val[3]);
+ // test zeros
+ if (*(uint64_t*)(out + 0) != 0) return 1;
+ if (*(uint64_t*)(out + 4) != 0) return 1;
+ if (*(uint64_t*)(out + 8) != 0) return 1;
+ if (*(uint64_t*)(out + 12) != 0) return 1;
+ return 0;
+}
+
+static int Quantize2Blocks_NEON(int16_t in[32], int16_t out[32],
+ const VP8Matrix* const mtx) {
+ int nz;
+ nz = QuantizeBlock_NEON(in + 0 * 16, out + 0 * 16, mtx) << 0;
+ nz |= QuantizeBlock_NEON(in + 1 * 16, out + 1 * 16, mtx) << 1;
+ return nz;
+}
+
+#endif // !WORK_AROUND_GCC
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspInitNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitNEON(void) {
+ VP8ITransform = ITransform_NEON;
+ VP8FTransform = FTransform_NEON;
+
+ VP8FTransformWHT = FTransformWHT_NEON;
+
+ VP8TDisto4x4 = Disto4x4_NEON;
+ VP8TDisto16x16 = Disto16x16_NEON;
+ VP8CollectHistogram = CollectHistogram_NEON;
+
+ VP8SSE16x16 = SSE16x16_NEON;
+ VP8SSE16x8 = SSE16x8_NEON;
+ VP8SSE8x8 = SSE8x8_NEON;
+ VP8SSE4x4 = SSE4x4_NEON;
+
+#if !defined(WORK_AROUND_GCC)
+ VP8EncQuantizeBlock = QuantizeBlock_NEON;
+ VP8EncQuantize2Blocks = Quantize2Blocks_NEON;
+#endif
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(VP8EncDspInitNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/enc_sse2.c b/src/third_party/libwebp/src/dsp/enc_sse2.c
new file mode 100644
index 0000000..1d14f74
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc_sse2.c
@@ -0,0 +1,1387 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 version of speed-critical encoding functions.
+//
+// Author: Christian Duvivier (cduvivier@google.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+#include <emmintrin.h>
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h> // for abs()
+#endif
+
+#include "src/dsp/common_sse2.h"
+#include "src/enc/cost_enc.h"
+#include "src/enc/vp8i_enc.h"
+
+//------------------------------------------------------------------------------
+// Transforms (Paragraph 14.4)
+
+// Does one or two inverse transforms.
+static void ITransform_SSE2(const uint8_t* ref, const int16_t* in, uint8_t* dst,
+ int do_two) {
+ // This implementation makes use of 16-bit fixed point versions of two
+ // multiply constants:
+ // K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
+ // K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
+ //
+ // To be able to use signed 16-bit integers, we use the following trick to
+ // have constants within range:
+ // - Associated constants are obtained by subtracting the 16-bit fixed point
+ // version of one:
+ // k = K - (1 << 16) => K = k + (1 << 16)
+ // K1 = 85267 => k1 = 20091
+ // K2 = 35468 => k2 = -30068
+ // - The multiplication of a variable by a constant become the sum of the
+ // variable and the multiplication of that variable by the associated
+ // constant:
+ // (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
+ const __m128i k1 = _mm_set1_epi16(20091);
+ const __m128i k2 = _mm_set1_epi16(-30068);
+ __m128i T0, T1, T2, T3;
+
+ // Load and concatenate the transform coefficients (we'll do two inverse
+ // transforms in parallel). In the case of only one inverse transform, the
+ // second half of the vectors will just contain random value we'll never
+ // use nor store.
+ __m128i in0, in1, in2, in3;
+ {
+ in0 = _mm_loadl_epi64((const __m128i*)&in[0]);
+ in1 = _mm_loadl_epi64((const __m128i*)&in[4]);
+ in2 = _mm_loadl_epi64((const __m128i*)&in[8]);
+ in3 = _mm_loadl_epi64((const __m128i*)&in[12]);
+ // a00 a10 a20 a30 x x x x
+ // a01 a11 a21 a31 x x x x
+ // a02 a12 a22 a32 x x x x
+ // a03 a13 a23 a33 x x x x
+ if (do_two) {
+ const __m128i inB0 = _mm_loadl_epi64((const __m128i*)&in[16]);
+ const __m128i inB1 = _mm_loadl_epi64((const __m128i*)&in[20]);
+ const __m128i inB2 = _mm_loadl_epi64((const __m128i*)&in[24]);
+ const __m128i inB3 = _mm_loadl_epi64((const __m128i*)&in[28]);
+ in0 = _mm_unpacklo_epi64(in0, inB0);
+ in1 = _mm_unpacklo_epi64(in1, inB1);
+ in2 = _mm_unpacklo_epi64(in2, inB2);
+ in3 = _mm_unpacklo_epi64(in3, inB3);
+ // a00 a10 a20 a30 b00 b10 b20 b30
+ // a01 a11 a21 a31 b01 b11 b21 b31
+ // a02 a12 a22 a32 b02 b12 b22 b32
+ // a03 a13 a23 a33 b03 b13 b23 b33
+ }
+ }
+
+ // Vertical pass and subsequent transpose.
+ {
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ const __m128i a = _mm_add_epi16(in0, in2);
+ const __m128i b = _mm_sub_epi16(in0, in2);
+ // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
+ const __m128i c1 = _mm_mulhi_epi16(in1, k2);
+ const __m128i c2 = _mm_mulhi_epi16(in3, k1);
+ const __m128i c3 = _mm_sub_epi16(in1, in3);
+ const __m128i c4 = _mm_sub_epi16(c1, c2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
+ const __m128i d1 = _mm_mulhi_epi16(in1, k1);
+ const __m128i d2 = _mm_mulhi_epi16(in3, k2);
+ const __m128i d3 = _mm_add_epi16(in1, in3);
+ const __m128i d4 = _mm_add_epi16(d1, d2);
+ const __m128i d = _mm_add_epi16(d3, d4);
+
+ // Second pass.
+ const __m128i tmp0 = _mm_add_epi16(a, d);
+ const __m128i tmp1 = _mm_add_epi16(b, c);
+ const __m128i tmp2 = _mm_sub_epi16(b, c);
+ const __m128i tmp3 = _mm_sub_epi16(a, d);
+
+ // Transpose the two 4x4.
+ VP8Transpose_2_4x4_16b(&tmp0, &tmp1, &tmp2, &tmp3, &T0, &T1, &T2, &T3);
+ }
+
+ // Horizontal pass and subsequent transpose.
+ {
+ // First pass, c and d calculations are longer because of the "trick"
+ // multiplications.
+ const __m128i four = _mm_set1_epi16(4);
+ const __m128i dc = _mm_add_epi16(T0, four);
+ const __m128i a = _mm_add_epi16(dc, T2);
+ const __m128i b = _mm_sub_epi16(dc, T2);
+ // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
+ const __m128i c1 = _mm_mulhi_epi16(T1, k2);
+ const __m128i c2 = _mm_mulhi_epi16(T3, k1);
+ const __m128i c3 = _mm_sub_epi16(T1, T3);
+ const __m128i c4 = _mm_sub_epi16(c1, c2);
+ const __m128i c = _mm_add_epi16(c3, c4);
+ // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
+ const __m128i d1 = _mm_mulhi_epi16(T1, k1);
+ const __m128i d2 = _mm_mulhi_epi16(T3, k2);
+ const __m128i d3 = _mm_add_epi16(T1, T3);
+ const __m128i d4 = _mm_add_epi16(d1, d2);
+ const __m128i d = _mm_add_epi16(d3, d4);
+
+ // Second pass.
+ const __m128i tmp0 = _mm_add_epi16(a, d);
+ const __m128i tmp1 = _mm_add_epi16(b, c);
+ const __m128i tmp2 = _mm_sub_epi16(b, c);
+ const __m128i tmp3 = _mm_sub_epi16(a, d);
+ const __m128i shifted0 = _mm_srai_epi16(tmp0, 3);
+ const __m128i shifted1 = _mm_srai_epi16(tmp1, 3);
+ const __m128i shifted2 = _mm_srai_epi16(tmp2, 3);
+ const __m128i shifted3 = _mm_srai_epi16(tmp3, 3);
+
+ // Transpose the two 4x4.
+ VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1,
+ &T2, &T3);
+ }
+
+ // Add inverse transform to 'ref' and store.
+ {
+ const __m128i zero = _mm_setzero_si128();
+ // Load the reference(s).
+ __m128i ref0, ref1, ref2, ref3;
+ if (do_two) {
+ // Load eight bytes/pixels per line.
+ ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
+ ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
+ ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
+ ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
+ } else {
+ // Load four bytes/pixels per line.
+ ref0 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[0 * BPS]));
+ ref1 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[1 * BPS]));
+ ref2 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[2 * BPS]));
+ ref3 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[3 * BPS]));
+ }
+ // Convert to 16b.
+ ref0 = _mm_unpacklo_epi8(ref0, zero);
+ ref1 = _mm_unpacklo_epi8(ref1, zero);
+ ref2 = _mm_unpacklo_epi8(ref2, zero);
+ ref3 = _mm_unpacklo_epi8(ref3, zero);
+ // Add the inverse transform(s).
+ ref0 = _mm_add_epi16(ref0, T0);
+ ref1 = _mm_add_epi16(ref1, T1);
+ ref2 = _mm_add_epi16(ref2, T2);
+ ref3 = _mm_add_epi16(ref3, T3);
+ // Unsigned saturate to 8b.
+ ref0 = _mm_packus_epi16(ref0, ref0);
+ ref1 = _mm_packus_epi16(ref1, ref1);
+ ref2 = _mm_packus_epi16(ref2, ref2);
+ ref3 = _mm_packus_epi16(ref3, ref3);
+ // Store the results.
+ if (do_two) {
+ // Store eight bytes/pixels per line.
+ _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0);
+ _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1);
+ _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2);
+ _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
+ } else {
+ // Store four bytes/pixels per line.
+ WebPUint32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0));
+ WebPUint32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1));
+ WebPUint32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2));
+ WebPUint32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3));
+ }
+ }
+}
+
+static void FTransformPass1_SSE2(const __m128i* const in01,
+ const __m128i* const in23,
+ __m128i* const out01,
+ __m128i* const out32) {
+ const __m128i k937 = _mm_set1_epi32(937);
+ const __m128i k1812 = _mm_set1_epi32(1812);
+
+ const __m128i k88p = _mm_set_epi16(8, 8, 8, 8, 8, 8, 8, 8);
+ const __m128i k88m = _mm_set_epi16(-8, 8, -8, 8, -8, 8, -8, 8);
+ const __m128i k5352_2217p = _mm_set_epi16(2217, 5352, 2217, 5352,
+ 2217, 5352, 2217, 5352);
+ const __m128i k5352_2217m = _mm_set_epi16(-5352, 2217, -5352, 2217,
+ -5352, 2217, -5352, 2217);
+
+ // *in01 = 00 01 10 11 02 03 12 13
+ // *in23 = 20 21 30 31 22 23 32 33
+ const __m128i shuf01_p = _mm_shufflehi_epi16(*in01, _MM_SHUFFLE(2, 3, 0, 1));
+ const __m128i shuf23_p = _mm_shufflehi_epi16(*in23, _MM_SHUFFLE(2, 3, 0, 1));
+ // 00 01 10 11 03 02 13 12
+ // 20 21 30 31 23 22 33 32
+ const __m128i s01 = _mm_unpacklo_epi64(shuf01_p, shuf23_p);
+ const __m128i s32 = _mm_unpackhi_epi64(shuf01_p, shuf23_p);
+ // 00 01 10 11 20 21 30 31
+ // 03 02 13 12 23 22 33 32
+ const __m128i a01 = _mm_add_epi16(s01, s32);
+ const __m128i a32 = _mm_sub_epi16(s01, s32);
+ // [d0 + d3 | d1 + d2 | ...] = [a0 a1 | a0' a1' | ... ]
+ // [d0 - d3 | d1 - d2 | ...] = [a3 a2 | a3' a2' | ... ]
+
+ const __m128i tmp0 = _mm_madd_epi16(a01, k88p); // [ (a0 + a1) << 3, ... ]
+ const __m128i tmp2 = _mm_madd_epi16(a01, k88m); // [ (a0 - a1) << 3, ... ]
+ const __m128i tmp1_1 = _mm_madd_epi16(a32, k5352_2217p);
+ const __m128i tmp3_1 = _mm_madd_epi16(a32, k5352_2217m);
+ const __m128i tmp1_2 = _mm_add_epi32(tmp1_1, k1812);
+ const __m128i tmp3_2 = _mm_add_epi32(tmp3_1, k937);
+ const __m128i tmp1 = _mm_srai_epi32(tmp1_2, 9);
+ const __m128i tmp3 = _mm_srai_epi32(tmp3_2, 9);
+ const __m128i s03 = _mm_packs_epi32(tmp0, tmp2);
+ const __m128i s12 = _mm_packs_epi32(tmp1, tmp3);
+ const __m128i s_lo = _mm_unpacklo_epi16(s03, s12); // 0 1 0 1 0 1...
+ const __m128i s_hi = _mm_unpackhi_epi16(s03, s12); // 2 3 2 3 2 3
+ const __m128i v23 = _mm_unpackhi_epi32(s_lo, s_hi);
+ *out01 = _mm_unpacklo_epi32(s_lo, s_hi);
+ *out32 = _mm_shuffle_epi32(v23, _MM_SHUFFLE(1, 0, 3, 2)); // 3 2 3 2 3 2..
+}
+
+static void FTransformPass2_SSE2(const __m128i* const v01,
+ const __m128i* const v32,
+ int16_t* out) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i seven = _mm_set1_epi16(7);
+ const __m128i k5352_2217 = _mm_set_epi16(5352, 2217, 5352, 2217,
+ 5352, 2217, 5352, 2217);
+ const __m128i k2217_5352 = _mm_set_epi16(2217, -5352, 2217, -5352,
+ 2217, -5352, 2217, -5352);
+ const __m128i k12000_plus_one = _mm_set1_epi32(12000 + (1 << 16));
+ const __m128i k51000 = _mm_set1_epi32(51000);
+
+ // Same operations are done on the (0,3) and (1,2) pairs.
+ // a3 = v0 - v3
+ // a2 = v1 - v2
+ const __m128i a32 = _mm_sub_epi16(*v01, *v32);
+ const __m128i a22 = _mm_unpackhi_epi64(a32, a32);
+
+ const __m128i b23 = _mm_unpacklo_epi16(a22, a32);
+ const __m128i c1 = _mm_madd_epi16(b23, k5352_2217);
+ const __m128i c3 = _mm_madd_epi16(b23, k2217_5352);
+ const __m128i d1 = _mm_add_epi32(c1, k12000_plus_one);
+ const __m128i d3 = _mm_add_epi32(c3, k51000);
+ const __m128i e1 = _mm_srai_epi32(d1, 16);
+ const __m128i e3 = _mm_srai_epi32(d3, 16);
+ // f1 = ((b3 * 5352 + b2 * 2217 + 12000) >> 16)
+ // f3 = ((b3 * 2217 - b2 * 5352 + 51000) >> 16)
+ const __m128i f1 = _mm_packs_epi32(e1, e1);
+ const __m128i f3 = _mm_packs_epi32(e3, e3);
+ // g1 = f1 + (a3 != 0);
+ // The compare will return (0xffff, 0) for (==0, !=0). To turn that into the
+ // desired (0, 1), we add one earlier through k12000_plus_one.
+ // -> g1 = f1 + 1 - (a3 == 0)
+ const __m128i g1 = _mm_add_epi16(f1, _mm_cmpeq_epi16(a32, zero));
+
+ // a0 = v0 + v3
+ // a1 = v1 + v2
+ const __m128i a01 = _mm_add_epi16(*v01, *v32);
+ const __m128i a01_plus_7 = _mm_add_epi16(a01, seven);
+ const __m128i a11 = _mm_unpackhi_epi64(a01, a01);
+ const __m128i c0 = _mm_add_epi16(a01_plus_7, a11);
+ const __m128i c2 = _mm_sub_epi16(a01_plus_7, a11);
+ // d0 = (a0 + a1 + 7) >> 4;
+ // d2 = (a0 - a1 + 7) >> 4;
+ const __m128i d0 = _mm_srai_epi16(c0, 4);
+ const __m128i d2 = _mm_srai_epi16(c2, 4);
+
+ const __m128i d0_g1 = _mm_unpacklo_epi64(d0, g1);
+ const __m128i d2_f3 = _mm_unpacklo_epi64(d2, f3);
+ _mm_storeu_si128((__m128i*)&out[0], d0_g1);
+ _mm_storeu_si128((__m128i*)&out[8], d2_f3);
+}
+
+static void FTransform_SSE2(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ const __m128i zero = _mm_setzero_si128();
+ // Load src.
+ const __m128i src0 = _mm_loadl_epi64((const __m128i*)&src[0 * BPS]);
+ const __m128i src1 = _mm_loadl_epi64((const __m128i*)&src[1 * BPS]);
+ const __m128i src2 = _mm_loadl_epi64((const __m128i*)&src[2 * BPS]);
+ const __m128i src3 = _mm_loadl_epi64((const __m128i*)&src[3 * BPS]);
+ // 00 01 02 03 *
+ // 10 11 12 13 *
+ // 20 21 22 23 *
+ // 30 31 32 33 *
+ // Shuffle.
+ const __m128i src_0 = _mm_unpacklo_epi16(src0, src1);
+ const __m128i src_1 = _mm_unpacklo_epi16(src2, src3);
+ // 00 01 10 11 02 03 12 13 * * ...
+ // 20 21 30 31 22 22 32 33 * * ...
+
+ // Load ref.
+ const __m128i ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
+ const __m128i ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
+ const __m128i ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
+ const __m128i ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
+ const __m128i ref_0 = _mm_unpacklo_epi16(ref0, ref1);
+ const __m128i ref_1 = _mm_unpacklo_epi16(ref2, ref3);
+
+ // Convert both to 16 bit.
+ const __m128i src_0_16b = _mm_unpacklo_epi8(src_0, zero);
+ const __m128i src_1_16b = _mm_unpacklo_epi8(src_1, zero);
+ const __m128i ref_0_16b = _mm_unpacklo_epi8(ref_0, zero);
+ const __m128i ref_1_16b = _mm_unpacklo_epi8(ref_1, zero);
+
+ // Compute the difference.
+ const __m128i row01 = _mm_sub_epi16(src_0_16b, ref_0_16b);
+ const __m128i row23 = _mm_sub_epi16(src_1_16b, ref_1_16b);
+ __m128i v01, v32;
+
+ // First pass
+ FTransformPass1_SSE2(&row01, &row23, &v01, &v32);
+
+ // Second pass
+ FTransformPass2_SSE2(&v01, &v32, out);
+}
+
+static void FTransform2_SSE2(const uint8_t* src, const uint8_t* ref,
+ int16_t* out) {
+ const __m128i zero = _mm_setzero_si128();
+
+ // Load src and convert to 16b.
+ const __m128i src0 = _mm_loadl_epi64((const __m128i*)&src[0 * BPS]);
+ const __m128i src1 = _mm_loadl_epi64((const __m128i*)&src[1 * BPS]);
+ const __m128i src2 = _mm_loadl_epi64((const __m128i*)&src[2 * BPS]);
+ const __m128i src3 = _mm_loadl_epi64((const __m128i*)&src[3 * BPS]);
+ const __m128i src_0 = _mm_unpacklo_epi8(src0, zero);
+ const __m128i src_1 = _mm_unpacklo_epi8(src1, zero);
+ const __m128i src_2 = _mm_unpacklo_epi8(src2, zero);
+ const __m128i src_3 = _mm_unpacklo_epi8(src3, zero);
+ // Load ref and convert to 16b.
+ const __m128i ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]);
+ const __m128i ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]);
+ const __m128i ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]);
+ const __m128i ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
+ const __m128i ref_0 = _mm_unpacklo_epi8(ref0, zero);
+ const __m128i ref_1 = _mm_unpacklo_epi8(ref1, zero);
+ const __m128i ref_2 = _mm_unpacklo_epi8(ref2, zero);
+ const __m128i ref_3 = _mm_unpacklo_epi8(ref3, zero);
+ // Compute difference. -> 00 01 02 03 00' 01' 02' 03'
+ const __m128i diff0 = _mm_sub_epi16(src_0, ref_0);
+ const __m128i diff1 = _mm_sub_epi16(src_1, ref_1);
+ const __m128i diff2 = _mm_sub_epi16(src_2, ref_2);
+ const __m128i diff3 = _mm_sub_epi16(src_3, ref_3);
+
+ // Unpack and shuffle
+ // 00 01 02 03 0 0 0 0
+ // 10 11 12 13 0 0 0 0
+ // 20 21 22 23 0 0 0 0
+ // 30 31 32 33 0 0 0 0
+ const __m128i shuf01l = _mm_unpacklo_epi32(diff0, diff1);
+ const __m128i shuf23l = _mm_unpacklo_epi32(diff2, diff3);
+ const __m128i shuf01h = _mm_unpackhi_epi32(diff0, diff1);
+ const __m128i shuf23h = _mm_unpackhi_epi32(diff2, diff3);
+ __m128i v01l, v32l;
+ __m128i v01h, v32h;
+
+ // First pass
+ FTransformPass1_SSE2(&shuf01l, &shuf23l, &v01l, &v32l);
+ FTransformPass1_SSE2(&shuf01h, &shuf23h, &v01h, &v32h);
+
+ // Second pass
+ FTransformPass2_SSE2(&v01l, &v32l, out + 0);
+ FTransformPass2_SSE2(&v01h, &v32h, out + 16);
+}
+
+static void FTransformWHTRow_SSE2(const int16_t* const in, __m128i* const out) {
+ const __m128i kMult = _mm_set_epi16(-1, 1, -1, 1, 1, 1, 1, 1);
+ const __m128i src0 = _mm_loadl_epi64((__m128i*)&in[0 * 16]);
+ const __m128i src1 = _mm_loadl_epi64((__m128i*)&in[1 * 16]);
+ const __m128i src2 = _mm_loadl_epi64((__m128i*)&in[2 * 16]);
+ const __m128i src3 = _mm_loadl_epi64((__m128i*)&in[3 * 16]);
+ const __m128i A01 = _mm_unpacklo_epi16(src0, src1); // A0 A1 | ...
+ const __m128i A23 = _mm_unpacklo_epi16(src2, src3); // A2 A3 | ...
+ const __m128i B0 = _mm_adds_epi16(A01, A23); // a0 | a1 | ...
+ const __m128i B1 = _mm_subs_epi16(A01, A23); // a3 | a2 | ...
+ const __m128i C0 = _mm_unpacklo_epi32(B0, B1); // a0 | a1 | a3 | a2 | ...
+ const __m128i C1 = _mm_unpacklo_epi32(B1, B0); // a3 | a2 | a0 | a1 | ...
+ const __m128i D = _mm_unpacklo_epi64(C0, C1); // a0 a1 a3 a2 a3 a2 a0 a1
+ *out = _mm_madd_epi16(D, kMult);
+}
+
+static void FTransformWHT_SSE2(const int16_t* in, int16_t* out) {
+ // Input is 12b signed.
+ __m128i row0, row1, row2, row3;
+ // Rows are 14b signed.
+ FTransformWHTRow_SSE2(in + 0 * 64, &row0);
+ FTransformWHTRow_SSE2(in + 1 * 64, &row1);
+ FTransformWHTRow_SSE2(in + 2 * 64, &row2);
+ FTransformWHTRow_SSE2(in + 3 * 64, &row3);
+
+ {
+ // The a* are 15b signed.
+ const __m128i a0 = _mm_add_epi32(row0, row2);
+ const __m128i a1 = _mm_add_epi32(row1, row3);
+ const __m128i a2 = _mm_sub_epi32(row1, row3);
+ const __m128i a3 = _mm_sub_epi32(row0, row2);
+ const __m128i a0a3 = _mm_packs_epi32(a0, a3);
+ const __m128i a1a2 = _mm_packs_epi32(a1, a2);
+
+ // The b* are 16b signed.
+ const __m128i b0b1 = _mm_add_epi16(a0a3, a1a2);
+ const __m128i b3b2 = _mm_sub_epi16(a0a3, a1a2);
+ const __m128i tmp_b2b3 = _mm_unpackhi_epi64(b3b2, b3b2);
+ const __m128i b2b3 = _mm_unpacklo_epi64(tmp_b2b3, b3b2);
+
+ _mm_storeu_si128((__m128i*)&out[0], _mm_srai_epi16(b0b1, 1));
+ _mm_storeu_si128((__m128i*)&out[8], _mm_srai_epi16(b2b3, 1));
+ }
+}
+
+//------------------------------------------------------------------------------
+// Compute susceptibility based on DCT-coeff histograms:
+// the higher, the "easier" the macroblock is to compress.
+
+static void CollectHistogram_SSE2(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block,
+ VP8Histogram* const histo) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH);
+ int j;
+ int distribution[MAX_COEFF_THRESH + 1] = { 0 };
+ for (j = start_block; j < end_block; ++j) {
+ int16_t out[16];
+ int k;
+
+ FTransform_SSE2(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
+
+ // Convert coefficients to bin (within out[]).
+ {
+ // Load.
+ const __m128i out0 = _mm_loadu_si128((__m128i*)&out[0]);
+ const __m128i out1 = _mm_loadu_si128((__m128i*)&out[8]);
+ const __m128i d0 = _mm_sub_epi16(zero, out0);
+ const __m128i d1 = _mm_sub_epi16(zero, out1);
+ const __m128i abs0 = _mm_max_epi16(out0, d0); // abs(v), 16b
+ const __m128i abs1 = _mm_max_epi16(out1, d1);
+ // v = abs(out) >> 3
+ const __m128i v0 = _mm_srai_epi16(abs0, 3);
+ const __m128i v1 = _mm_srai_epi16(abs1, 3);
+ // bin = min(v, MAX_COEFF_THRESH)
+ const __m128i bin0 = _mm_min_epi16(v0, max_coeff_thresh);
+ const __m128i bin1 = _mm_min_epi16(v1, max_coeff_thresh);
+ // Store.
+ _mm_storeu_si128((__m128i*)&out[0], bin0);
+ _mm_storeu_si128((__m128i*)&out[8], bin1);
+ }
+
+ // Convert coefficients to bin.
+ for (k = 0; k < 16; ++k) {
+ ++distribution[out[k]];
+ }
+ }
+ VP8SetHistogramData(distribution, histo);
+}
+
+//------------------------------------------------------------------------------
+// Intra predictions
+
+// helper for chroma-DC predictions
+static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) {
+ int j;
+ const __m128i values = _mm_set1_epi8(v);
+ for (j = 0; j < 8; ++j) {
+ _mm_storel_epi64((__m128i*)(dst + j * BPS), values);
+ }
+}
+
+static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) {
+ int j;
+ const __m128i values = _mm_set1_epi8(v);
+ for (j = 0; j < 16; ++j) {
+ _mm_store_si128((__m128i*)(dst + j * BPS), values);
+ }
+}
+
+static WEBP_INLINE void Fill_SSE2(uint8_t* dst, int value, int size) {
+ if (size == 4) {
+ int j;
+ for (j = 0; j < 4; ++j) {
+ memset(dst + j * BPS, value, 4);
+ }
+ } else if (size == 8) {
+ Put8x8uv_SSE2(value, dst);
+ } else {
+ Put16_SSE2(value, dst);
+ }
+}
+
+static WEBP_INLINE void VE8uv_SSE2(uint8_t* dst, const uint8_t* top) {
+ int j;
+ const __m128i top_values = _mm_loadl_epi64((const __m128i*)top);
+ for (j = 0; j < 8; ++j) {
+ _mm_storel_epi64((__m128i*)(dst + j * BPS), top_values);
+ }
+}
+
+static WEBP_INLINE void VE16_SSE2(uint8_t* dst, const uint8_t* top) {
+ const __m128i top_values = _mm_load_si128((const __m128i*)top);
+ int j;
+ for (j = 0; j < 16; ++j) {
+ _mm_store_si128((__m128i*)(dst + j * BPS), top_values);
+ }
+}
+
+static WEBP_INLINE void VerticalPred_SSE2(uint8_t* dst,
+ const uint8_t* top, int size) {
+ if (top != NULL) {
+ if (size == 8) {
+ VE8uv_SSE2(dst, top);
+ } else {
+ VE16_SSE2(dst, top);
+ }
+ } else {
+ Fill_SSE2(dst, 127, size);
+ }
+}
+
+static WEBP_INLINE void HE8uv_SSE2(uint8_t* dst, const uint8_t* left) {
+ int j;
+ for (j = 0; j < 8; ++j) {
+ const __m128i values = _mm_set1_epi8(left[j]);
+ _mm_storel_epi64((__m128i*)dst, values);
+ dst += BPS;
+ }
+}
+
+static WEBP_INLINE void HE16_SSE2(uint8_t* dst, const uint8_t* left) {
+ int j;
+ for (j = 0; j < 16; ++j) {
+ const __m128i values = _mm_set1_epi8(left[j]);
+ _mm_store_si128((__m128i*)dst, values);
+ dst += BPS;
+ }
+}
+
+static WEBP_INLINE void HorizontalPred_SSE2(uint8_t* dst,
+ const uint8_t* left, int size) {
+ if (left != NULL) {
+ if (size == 8) {
+ HE8uv_SSE2(dst, left);
+ } else {
+ HE16_SSE2(dst, left);
+ }
+ } else {
+ Fill_SSE2(dst, 129, size);
+ }
+}
+
+static WEBP_INLINE void TM_SSE2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top, int size) {
+ const __m128i zero = _mm_setzero_si128();
+ int y;
+ if (size == 8) {
+ const __m128i top_values = _mm_loadl_epi64((const __m128i*)top);
+ const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
+ for (y = 0; y < 8; ++y, dst += BPS) {
+ const int val = left[y] - left[-1];
+ const __m128i base = _mm_set1_epi16(val);
+ const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
+ _mm_storel_epi64((__m128i*)dst, out);
+ }
+ } else {
+ const __m128i top_values = _mm_load_si128((const __m128i*)top);
+ const __m128i top_base_0 = _mm_unpacklo_epi8(top_values, zero);
+ const __m128i top_base_1 = _mm_unpackhi_epi8(top_values, zero);
+ for (y = 0; y < 16; ++y, dst += BPS) {
+ const int val = left[y] - left[-1];
+ const __m128i base = _mm_set1_epi16(val);
+ const __m128i out_0 = _mm_add_epi16(base, top_base_0);
+ const __m128i out_1 = _mm_add_epi16(base, top_base_1);
+ const __m128i out = _mm_packus_epi16(out_0, out_1);
+ _mm_store_si128((__m128i*)dst, out);
+ }
+ }
+}
+
+static WEBP_INLINE void TrueMotion_SSE2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top, int size) {
+ if (left != NULL) {
+ if (top != NULL) {
+ TM_SSE2(dst, left, top, size);
+ } else {
+ HorizontalPred_SSE2(dst, left, size);
+ }
+ } else {
+ // true motion without left samples (hence: with default 129 value)
+ // is equivalent to VE prediction where you just copy the top samples.
+ // Note that if top samples are not available, the default value is
+ // then 129, and not 127 as in the VerticalPred case.
+ if (top != NULL) {
+ VerticalPred_SSE2(dst, top, size);
+ } else {
+ Fill_SSE2(dst, 129, size);
+ }
+ }
+}
+
+static WEBP_INLINE void DC8uv_SSE2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ const __m128i top_values = _mm_loadl_epi64((const __m128i*)top);
+ const __m128i left_values = _mm_loadl_epi64((const __m128i*)left);
+ const __m128i combined = _mm_unpacklo_epi64(top_values, left_values);
+ const int DC = VP8HorizontalAdd8b(&combined) + 8;
+ Put8x8uv_SSE2(DC >> 4, dst);
+}
+
+static WEBP_INLINE void DC8uvNoLeft_SSE2(uint8_t* dst, const uint8_t* top) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i top_values = _mm_loadl_epi64((const __m128i*)top);
+ const __m128i sum = _mm_sad_epu8(top_values, zero);
+ const int DC = _mm_cvtsi128_si32(sum) + 4;
+ Put8x8uv_SSE2(DC >> 3, dst);
+}
+
+static WEBP_INLINE void DC8uvNoTop_SSE2(uint8_t* dst, const uint8_t* left) {
+ // 'left' is contiguous so we can reuse the top summation.
+ DC8uvNoLeft_SSE2(dst, left);
+}
+
+static WEBP_INLINE void DC8uvNoTopLeft_SSE2(uint8_t* dst) {
+ Put8x8uv_SSE2(0x80, dst);
+}
+
+static WEBP_INLINE void DC8uvMode_SSE2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ if (top != NULL) {
+ if (left != NULL) { // top and left present
+ DC8uv_SSE2(dst, left, top);
+ } else { // top, but no left
+ DC8uvNoLeft_SSE2(dst, top);
+ }
+ } else if (left != NULL) { // left but no top
+ DC8uvNoTop_SSE2(dst, left);
+ } else { // no top, no left, nothing.
+ DC8uvNoTopLeft_SSE2(dst);
+ }
+}
+
+static WEBP_INLINE void DC16_SSE2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ const __m128i top_row = _mm_load_si128((const __m128i*)top);
+ const __m128i left_row = _mm_load_si128((const __m128i*)left);
+ const int DC =
+ VP8HorizontalAdd8b(&top_row) + VP8HorizontalAdd8b(&left_row) + 16;
+ Put16_SSE2(DC >> 5, dst);
+}
+
+static WEBP_INLINE void DC16NoLeft_SSE2(uint8_t* dst, const uint8_t* top) {
+ const __m128i top_row = _mm_load_si128((const __m128i*)top);
+ const int DC = VP8HorizontalAdd8b(&top_row) + 8;
+ Put16_SSE2(DC >> 4, dst);
+}
+
+static WEBP_INLINE void DC16NoTop_SSE2(uint8_t* dst, const uint8_t* left) {
+ // 'left' is contiguous so we can reuse the top summation.
+ DC16NoLeft_SSE2(dst, left);
+}
+
+static WEBP_INLINE void DC16NoTopLeft_SSE2(uint8_t* dst) {
+ Put16_SSE2(0x80, dst);
+}
+
+static WEBP_INLINE void DC16Mode_SSE2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ if (top != NULL) {
+ if (left != NULL) { // top and left present
+ DC16_SSE2(dst, left, top);
+ } else { // top, but no left
+ DC16NoLeft_SSE2(dst, top);
+ }
+ } else if (left != NULL) { // left but no top
+ DC16NoTop_SSE2(dst, left);
+ } else { // no top, no left, nothing.
+ DC16NoTopLeft_SSE2(dst);
+ }
+}
+
+//------------------------------------------------------------------------------
+// 4x4 predictions
+
+#define DST(x, y) dst[(x) + (y) * BPS]
+#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
+#define AVG2(a, b) (((a) + (b) + 1) >> 1)
+
+// We use the following 8b-arithmetic tricks:
+// (a + 2 * b + c + 2) >> 2 = (AC + b + 1) >> 1
+// where: AC = (a + c) >> 1 = [(a + c + 1) >> 1] - [(a^c) & 1]
+// and:
+// (a + 2 * b + c + 2) >> 2 = (AB + BC + 1) >> 1 - (ab|bc)&lsb
+// where: AC = (a + b + 1) >> 1, BC = (b + c + 1) >> 1
+// and ab = a ^ b, bc = b ^ c, lsb = (AC^BC)&1
+
+static WEBP_INLINE void VE4_SSE2(uint8_t* dst,
+ const uint8_t* top) { // vertical
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i ABCDEFGH = _mm_loadl_epi64((__m128i*)(top - 1));
+ const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1);
+ const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2);
+ const __m128i a = _mm_avg_epu8(ABCDEFGH, CDEFGH00);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one);
+ const __m128i b = _mm_subs_epu8(a, lsb);
+ const __m128i avg = _mm_avg_epu8(b, BCDEFGH0);
+ const uint32_t vals = _mm_cvtsi128_si32(avg);
+ int i;
+ for (i = 0; i < 4; ++i) {
+ WebPUint32ToMem(dst + i * BPS, vals);
+ }
+}
+
+static WEBP_INLINE void HE4_SSE2(uint8_t* dst,
+ const uint8_t* top) { // horizontal
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(X, I, J));
+ WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(I, J, K));
+ WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(J, K, L));
+ WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L));
+}
+
+static WEBP_INLINE void DC4_SSE2(uint8_t* dst, const uint8_t* top) {
+ uint32_t dc = 4;
+ int i;
+ for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i];
+ Fill_SSE2(dst, dc >> 3, 4);
+}
+
+static WEBP_INLINE void LD4_SSE2(uint8_t* dst,
+ const uint8_t* top) { // Down-Left
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i ABCDEFGH = _mm_loadl_epi64((const __m128i*)top);
+ const __m128i BCDEFGH0 = _mm_srli_si128(ABCDEFGH, 1);
+ const __m128i CDEFGH00 = _mm_srli_si128(ABCDEFGH, 2);
+ const __m128i CDEFGHH0 = _mm_insert_epi16(CDEFGH00, top[7], 3);
+ const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, CDEFGHH0);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one);
+ const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
+ const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0);
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+}
+
+static WEBP_INLINE void VR4_SSE2(uint8_t* dst,
+ const uint8_t* top) { // Vertical-Right
+ const __m128i one = _mm_set1_epi8(1);
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int X = top[-1];
+ const __m128i XABCD = _mm_loadl_epi64((const __m128i*)(top - 1));
+ const __m128i ABCD0 = _mm_srli_si128(XABCD, 1);
+ const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0);
+ const __m128i _XABCD = _mm_slli_si128(XABCD, 1);
+ const __m128i IXABCD = _mm_insert_epi16(_XABCD, I | (X << 8), 0);
+ const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
+ const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
+ const __m128i efgh = _mm_avg_epu8(avg2, XABCD);
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd ));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh ));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
+
+ // these two are hard to implement in SSE2, so we keep the C-version:
+ DST(0, 2) = AVG3(J, I, X);
+ DST(0, 3) = AVG3(K, J, I);
+}
+
+static WEBP_INLINE void VL4_SSE2(uint8_t* dst,
+ const uint8_t* top) { // Vertical-Left
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i ABCDEFGH = _mm_loadl_epi64((const __m128i*)top);
+ const __m128i BCDEFGH_ = _mm_srli_si128(ABCDEFGH, 1);
+ const __m128i CDEFGH__ = _mm_srli_si128(ABCDEFGH, 2);
+ const __m128i avg1 = _mm_avg_epu8(ABCDEFGH, BCDEFGH_);
+ const __m128i avg2 = _mm_avg_epu8(CDEFGH__, BCDEFGH_);
+ const __m128i avg3 = _mm_avg_epu8(avg1, avg2);
+ const __m128i lsb1 = _mm_and_si128(_mm_xor_si128(avg1, avg2), one);
+ const __m128i ab = _mm_xor_si128(ABCDEFGH, BCDEFGH_);
+ const __m128i bc = _mm_xor_si128(CDEFGH__, BCDEFGH_);
+ const __m128i abbc = _mm_or_si128(ab, bc);
+ const __m128i lsb2 = _mm_and_si128(abbc, lsb1);
+ const __m128i avg4 = _mm_subs_epu8(avg3, lsb2);
+ const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 ));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 ));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
+
+ // these two are hard to get and irregular
+ DST(3, 2) = (extra_out >> 0) & 0xff;
+ DST(3, 3) = (extra_out >> 8) & 0xff;
+}
+
+static WEBP_INLINE void RD4_SSE2(uint8_t* dst,
+ const uint8_t* top) { // Down-right
+ const __m128i one = _mm_set1_epi8(1);
+ const __m128i LKJIXABC = _mm_loadl_epi64((const __m128i*)(top - 5));
+ const __m128i LKJIXABCD = _mm_insert_epi16(LKJIXABC, top[3], 4);
+ const __m128i KJIXABCD_ = _mm_srli_si128(LKJIXABCD, 1);
+ const __m128i JIXABCD__ = _mm_srli_si128(LKJIXABCD, 2);
+ const __m128i avg1 = _mm_avg_epu8(JIXABCD__, LKJIXABCD);
+ const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one);
+ const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
+ const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_);
+ WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg ));
+ WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+ WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+ WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+}
+
+static WEBP_INLINE void HU4_SSE2(uint8_t* dst, const uint8_t* top) {
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ DST(0, 0) = AVG2(I, J);
+ DST(2, 0) = DST(0, 1) = AVG2(J, K);
+ DST(2, 1) = DST(0, 2) = AVG2(K, L);
+ DST(1, 0) = AVG3(I, J, K);
+ DST(3, 0) = DST(1, 1) = AVG3(J, K, L);
+ DST(3, 1) = DST(1, 2) = AVG3(K, L, L);
+ DST(3, 2) = DST(2, 2) =
+ DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
+}
+
+static WEBP_INLINE void HD4_SSE2(uint8_t* dst, const uint8_t* top) {
+ const int X = top[-1];
+ const int I = top[-2];
+ const int J = top[-3];
+ const int K = top[-4];
+ const int L = top[-5];
+ const int A = top[0];
+ const int B = top[1];
+ const int C = top[2];
+
+ DST(0, 0) = DST(2, 1) = AVG2(I, X);
+ DST(0, 1) = DST(2, 2) = AVG2(J, I);
+ DST(0, 2) = DST(2, 3) = AVG2(K, J);
+ DST(0, 3) = AVG2(L, K);
+
+ DST(3, 0) = AVG3(A, B, C);
+ DST(2, 0) = AVG3(X, A, B);
+ DST(1, 0) = DST(3, 1) = AVG3(I, X, A);
+ DST(1, 1) = DST(3, 2) = AVG3(J, I, X);
+ DST(1, 2) = DST(3, 3) = AVG3(K, J, I);
+ DST(1, 3) = AVG3(L, K, J);
+}
+
+static WEBP_INLINE void TM4_SSE2(uint8_t* dst, const uint8_t* top) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top));
+ const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
+ int y;
+ for (y = 0; y < 4; ++y, dst += BPS) {
+ const int val = top[-2 - y] - top[-1];
+ const __m128i base = _mm_set1_epi16(val);
+ const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
+ WebPUint32ToMem(dst, _mm_cvtsi128_si32(out));
+ }
+}
+
+#undef DST
+#undef AVG3
+#undef AVG2
+
+//------------------------------------------------------------------------------
+// luma 4x4 prediction
+
+// Left samples are top[-5 .. -2], top_left is top[-1], top are
+// located at top[0..3], and top right is top[4..7]
+static void Intra4Preds_SSE2(uint8_t* dst, const uint8_t* top) {
+ DC4_SSE2(I4DC4 + dst, top);
+ TM4_SSE2(I4TM4 + dst, top);
+ VE4_SSE2(I4VE4 + dst, top);
+ HE4_SSE2(I4HE4 + dst, top);
+ RD4_SSE2(I4RD4 + dst, top);
+ VR4_SSE2(I4VR4 + dst, top);
+ LD4_SSE2(I4LD4 + dst, top);
+ VL4_SSE2(I4VL4 + dst, top);
+ HD4_SSE2(I4HD4 + dst, top);
+ HU4_SSE2(I4HU4 + dst, top);
+}
+
+//------------------------------------------------------------------------------
+// Chroma 8x8 prediction (paragraph 12.2)
+
+static void IntraChromaPreds_SSE2(uint8_t* dst, const uint8_t* left,
+ const uint8_t* top) {
+ // U block
+ DC8uvMode_SSE2(C8DC8 + dst, left, top);
+ VerticalPred_SSE2(C8VE8 + dst, top, 8);
+ HorizontalPred_SSE2(C8HE8 + dst, left, 8);
+ TrueMotion_SSE2(C8TM8 + dst, left, top, 8);
+ // V block
+ dst += 8;
+ if (top != NULL) top += 8;
+ if (left != NULL) left += 16;
+ DC8uvMode_SSE2(C8DC8 + dst, left, top);
+ VerticalPred_SSE2(C8VE8 + dst, top, 8);
+ HorizontalPred_SSE2(C8HE8 + dst, left, 8);
+ TrueMotion_SSE2(C8TM8 + dst, left, top, 8);
+}
+
+//------------------------------------------------------------------------------
+// luma 16x16 prediction (paragraph 12.3)
+
+static void Intra16Preds_SSE2(uint8_t* dst,
+ const uint8_t* left, const uint8_t* top) {
+ DC16Mode_SSE2(I16DC16 + dst, left, top);
+ VerticalPred_SSE2(I16VE16 + dst, top, 16);
+ HorizontalPred_SSE2(I16HE16 + dst, left, 16);
+ TrueMotion_SSE2(I16TM16 + dst, left, top, 16);
+}
+
+//------------------------------------------------------------------------------
+// Metric
+
+static WEBP_INLINE void SubtractAndAccumulate_SSE2(const __m128i a,
+ const __m128i b,
+ __m128i* const sum) {
+ // take abs(a-b) in 8b
+ const __m128i a_b = _mm_subs_epu8(a, b);
+ const __m128i b_a = _mm_subs_epu8(b, a);
+ const __m128i abs_a_b = _mm_or_si128(a_b, b_a);
+ // zero-extend to 16b
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i C0 = _mm_unpacklo_epi8(abs_a_b, zero);
+ const __m128i C1 = _mm_unpackhi_epi8(abs_a_b, zero);
+ // multiply with self
+ const __m128i sum1 = _mm_madd_epi16(C0, C0);
+ const __m128i sum2 = _mm_madd_epi16(C1, C1);
+ *sum = _mm_add_epi32(sum1, sum2);
+}
+
+static WEBP_INLINE int SSE_16xN_SSE2(const uint8_t* a, const uint8_t* b,
+ int num_pairs) {
+ __m128i sum = _mm_setzero_si128();
+ int32_t tmp[4];
+ int i;
+
+ for (i = 0; i < num_pairs; ++i) {
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[BPS * 0]);
+ const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[BPS * 0]);
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[BPS * 1]);
+ const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[BPS * 1]);
+ __m128i sum1, sum2;
+ SubtractAndAccumulate_SSE2(a0, b0, &sum1);
+ SubtractAndAccumulate_SSE2(a1, b1, &sum2);
+ sum = _mm_add_epi32(sum, _mm_add_epi32(sum1, sum2));
+ a += 2 * BPS;
+ b += 2 * BPS;
+ }
+ _mm_storeu_si128((__m128i*)tmp, sum);
+ return (tmp[3] + tmp[2] + tmp[1] + tmp[0]);
+}
+
+static int SSE16x16_SSE2(const uint8_t* a, const uint8_t* b) {
+ return SSE_16xN_SSE2(a, b, 8);
+}
+
+static int SSE16x8_SSE2(const uint8_t* a, const uint8_t* b) {
+ return SSE_16xN_SSE2(a, b, 4);
+}
+
+#define LOAD_8x16b(ptr) \
+ _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(ptr)), zero)
+
+static int SSE8x8_SSE2(const uint8_t* a, const uint8_t* b) {
+ const __m128i zero = _mm_setzero_si128();
+ int num_pairs = 4;
+ __m128i sum = zero;
+ int32_t tmp[4];
+ while (num_pairs-- > 0) {
+ const __m128i a0 = LOAD_8x16b(&a[BPS * 0]);
+ const __m128i a1 = LOAD_8x16b(&a[BPS * 1]);
+ const __m128i b0 = LOAD_8x16b(&b[BPS * 0]);
+ const __m128i b1 = LOAD_8x16b(&b[BPS * 1]);
+ // subtract
+ const __m128i c0 = _mm_subs_epi16(a0, b0);
+ const __m128i c1 = _mm_subs_epi16(a1, b1);
+ // multiply/accumulate with self
+ const __m128i d0 = _mm_madd_epi16(c0, c0);
+ const __m128i d1 = _mm_madd_epi16(c1, c1);
+ // collect
+ const __m128i sum01 = _mm_add_epi32(d0, d1);
+ sum = _mm_add_epi32(sum, sum01);
+ a += 2 * BPS;
+ b += 2 * BPS;
+ }
+ _mm_storeu_si128((__m128i*)tmp, sum);
+ return (tmp[3] + tmp[2] + tmp[1] + tmp[0]);
+}
+#undef LOAD_8x16b
+
+static int SSE4x4_SSE2(const uint8_t* a, const uint8_t* b) {
+ const __m128i zero = _mm_setzero_si128();
+
+ // Load values. Note that we read 8 pixels instead of 4,
+ // but the a/b buffers are over-allocated to that effect.
+ const __m128i a0 = _mm_loadl_epi64((const __m128i*)&a[BPS * 0]);
+ const __m128i a1 = _mm_loadl_epi64((const __m128i*)&a[BPS * 1]);
+ const __m128i a2 = _mm_loadl_epi64((const __m128i*)&a[BPS * 2]);
+ const __m128i a3 = _mm_loadl_epi64((const __m128i*)&a[BPS * 3]);
+ const __m128i b0 = _mm_loadl_epi64((const __m128i*)&b[BPS * 0]);
+ const __m128i b1 = _mm_loadl_epi64((const __m128i*)&b[BPS * 1]);
+ const __m128i b2 = _mm_loadl_epi64((const __m128i*)&b[BPS * 2]);
+ const __m128i b3 = _mm_loadl_epi64((const __m128i*)&b[BPS * 3]);
+ // Combine pair of lines.
+ const __m128i a01 = _mm_unpacklo_epi32(a0, a1);
+ const __m128i a23 = _mm_unpacklo_epi32(a2, a3);
+ const __m128i b01 = _mm_unpacklo_epi32(b0, b1);
+ const __m128i b23 = _mm_unpacklo_epi32(b2, b3);
+ // Convert to 16b.
+ const __m128i a01s = _mm_unpacklo_epi8(a01, zero);
+ const __m128i a23s = _mm_unpacklo_epi8(a23, zero);
+ const __m128i b01s = _mm_unpacklo_epi8(b01, zero);
+ const __m128i b23s = _mm_unpacklo_epi8(b23, zero);
+ // subtract, square and accumulate
+ const __m128i d0 = _mm_subs_epi16(a01s, b01s);
+ const __m128i d1 = _mm_subs_epi16(a23s, b23s);
+ const __m128i e0 = _mm_madd_epi16(d0, d0);
+ const __m128i e1 = _mm_madd_epi16(d1, d1);
+ const __m128i sum = _mm_add_epi32(e0, e1);
+
+ int32_t tmp[4];
+ _mm_storeu_si128((__m128i*)tmp, sum);
+ return (tmp[3] + tmp[2] + tmp[1] + tmp[0]);
+}
+
+//------------------------------------------------------------------------------
+
+static void Mean16x4_SSE2(const uint8_t* ref, uint32_t dc[4]) {
+ const __m128i mask = _mm_set1_epi16(0x00ff);
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)&ref[BPS * 0]);
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)&ref[BPS * 1]);
+ const __m128i a2 = _mm_loadu_si128((const __m128i*)&ref[BPS * 2]);
+ const __m128i a3 = _mm_loadu_si128((const __m128i*)&ref[BPS * 3]);
+ const __m128i b0 = _mm_srli_epi16(a0, 8); // hi byte
+ const __m128i b1 = _mm_srli_epi16(a1, 8);
+ const __m128i b2 = _mm_srli_epi16(a2, 8);
+ const __m128i b3 = _mm_srli_epi16(a3, 8);
+ const __m128i c0 = _mm_and_si128(a0, mask); // lo byte
+ const __m128i c1 = _mm_and_si128(a1, mask);
+ const __m128i c2 = _mm_and_si128(a2, mask);
+ const __m128i c3 = _mm_and_si128(a3, mask);
+ const __m128i d0 = _mm_add_epi32(b0, c0);
+ const __m128i d1 = _mm_add_epi32(b1, c1);
+ const __m128i d2 = _mm_add_epi32(b2, c2);
+ const __m128i d3 = _mm_add_epi32(b3, c3);
+ const __m128i e0 = _mm_add_epi32(d0, d1);
+ const __m128i e1 = _mm_add_epi32(d2, d3);
+ const __m128i f0 = _mm_add_epi32(e0, e1);
+ uint16_t tmp[8];
+ _mm_storeu_si128((__m128i*)tmp, f0);
+ dc[0] = tmp[0] + tmp[1];
+ dc[1] = tmp[2] + tmp[3];
+ dc[2] = tmp[4] + tmp[5];
+ dc[3] = tmp[6] + tmp[7];
+}
+
+//------------------------------------------------------------------------------
+// Texture distortion
+//
+// We try to match the spectral content (weighted) between source and
+// reconstructed samples.
+
+// Hadamard transform
+// Returns the weighted sum of the absolute value of transformed coefficients.
+// w[] contains a row-major 4 by 4 symmetric matrix.
+static int TTransform_SSE2(const uint8_t* inA, const uint8_t* inB,
+ const uint16_t* const w) {
+ int32_t sum[4];
+ __m128i tmp_0, tmp_1, tmp_2, tmp_3;
+ const __m128i zero = _mm_setzero_si128();
+
+ // Load and combine inputs.
+ {
+ const __m128i inA_0 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 0]);
+ const __m128i inA_1 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 1]);
+ const __m128i inA_2 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 2]);
+ const __m128i inA_3 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 3]);
+ const __m128i inB_0 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 0]);
+ const __m128i inB_1 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 1]);
+ const __m128i inB_2 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 2]);
+ const __m128i inB_3 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 3]);
+
+ // Combine inA and inB (we'll do two transforms in parallel).
+ const __m128i inAB_0 = _mm_unpacklo_epi32(inA_0, inB_0);
+ const __m128i inAB_1 = _mm_unpacklo_epi32(inA_1, inB_1);
+ const __m128i inAB_2 = _mm_unpacklo_epi32(inA_2, inB_2);
+ const __m128i inAB_3 = _mm_unpacklo_epi32(inA_3, inB_3);
+ tmp_0 = _mm_unpacklo_epi8(inAB_0, zero);
+ tmp_1 = _mm_unpacklo_epi8(inAB_1, zero);
+ tmp_2 = _mm_unpacklo_epi8(inAB_2, zero);
+ tmp_3 = _mm_unpacklo_epi8(inAB_3, zero);
+ // a00 a01 a02 a03 b00 b01 b02 b03
+ // a10 a11 a12 a13 b10 b11 b12 b13
+ // a20 a21 a22 a23 b20 b21 b22 b23
+ // a30 a31 a32 a33 b30 b31 b32 b33
+ }
+
+ // Vertical pass first to avoid a transpose (vertical and horizontal passes
+ // are commutative because w/kWeightY is symmetric) and subsequent transpose.
+ {
+ // Calculate a and b (two 4x4 at once).
+ const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2);
+ const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3);
+ const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3);
+ const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2);
+ const __m128i b0 = _mm_add_epi16(a0, a1);
+ const __m128i b1 = _mm_add_epi16(a3, a2);
+ const __m128i b2 = _mm_sub_epi16(a3, a2);
+ const __m128i b3 = _mm_sub_epi16(a0, a1);
+ // a00 a01 a02 a03 b00 b01 b02 b03
+ // a10 a11 a12 a13 b10 b11 b12 b13
+ // a20 a21 a22 a23 b20 b21 b22 b23
+ // a30 a31 a32 a33 b30 b31 b32 b33
+
+ // Transpose the two 4x4.
+ VP8Transpose_2_4x4_16b(&b0, &b1, &b2, &b3, &tmp_0, &tmp_1, &tmp_2, &tmp_3);
+ }
+
+ // Horizontal pass and difference of weighted sums.
+ {
+ // Load all inputs.
+ const __m128i w_0 = _mm_loadu_si128((const __m128i*)&w[0]);
+ const __m128i w_8 = _mm_loadu_si128((const __m128i*)&w[8]);
+
+ // Calculate a and b (two 4x4 at once).
+ const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2);
+ const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3);
+ const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3);
+ const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2);
+ const __m128i b0 = _mm_add_epi16(a0, a1);
+ const __m128i b1 = _mm_add_epi16(a3, a2);
+ const __m128i b2 = _mm_sub_epi16(a3, a2);
+ const __m128i b3 = _mm_sub_epi16(a0, a1);
+
+ // Separate the transforms of inA and inB.
+ __m128i A_b0 = _mm_unpacklo_epi64(b0, b1);
+ __m128i A_b2 = _mm_unpacklo_epi64(b2, b3);
+ __m128i B_b0 = _mm_unpackhi_epi64(b0, b1);
+ __m128i B_b2 = _mm_unpackhi_epi64(b2, b3);
+
+ {
+ const __m128i d0 = _mm_sub_epi16(zero, A_b0);
+ const __m128i d1 = _mm_sub_epi16(zero, A_b2);
+ const __m128i d2 = _mm_sub_epi16(zero, B_b0);
+ const __m128i d3 = _mm_sub_epi16(zero, B_b2);
+ A_b0 = _mm_max_epi16(A_b0, d0); // abs(v), 16b
+ A_b2 = _mm_max_epi16(A_b2, d1);
+ B_b0 = _mm_max_epi16(B_b0, d2);
+ B_b2 = _mm_max_epi16(B_b2, d3);
+ }
+
+ // weighted sums
+ A_b0 = _mm_madd_epi16(A_b0, w_0);
+ A_b2 = _mm_madd_epi16(A_b2, w_8);
+ B_b0 = _mm_madd_epi16(B_b0, w_0);
+ B_b2 = _mm_madd_epi16(B_b2, w_8);
+ A_b0 = _mm_add_epi32(A_b0, A_b2);
+ B_b0 = _mm_add_epi32(B_b0, B_b2);
+
+ // difference of weighted sums
+ A_b0 = _mm_sub_epi32(A_b0, B_b0);
+ _mm_storeu_si128((__m128i*)&sum[0], A_b0);
+ }
+ return sum[0] + sum[1] + sum[2] + sum[3];
+}
+
+static int Disto4x4_SSE2(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ const int diff_sum = TTransform_SSE2(a, b, w);
+ return abs(diff_sum) >> 5;
+}
+
+static int Disto16x16_SSE2(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4_SSE2(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+
+//------------------------------------------------------------------------------
+// Quantization
+//
+
+static WEBP_INLINE int DoQuantizeBlock_SSE2(int16_t in[16], int16_t out[16],
+ const uint16_t* const sharpen,
+ const VP8Matrix* const mtx) {
+ const __m128i max_coeff_2047 = _mm_set1_epi16(MAX_LEVEL);
+ const __m128i zero = _mm_setzero_si128();
+ __m128i coeff0, coeff8;
+ __m128i out0, out8;
+ __m128i packed_out;
+
+ // Load all inputs.
+ __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]);
+ __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]);
+ const __m128i iq0 = _mm_loadu_si128((const __m128i*)&mtx->iq_[0]);
+ const __m128i iq8 = _mm_loadu_si128((const __m128i*)&mtx->iq_[8]);
+ const __m128i q0 = _mm_loadu_si128((const __m128i*)&mtx->q_[0]);
+ const __m128i q8 = _mm_loadu_si128((const __m128i*)&mtx->q_[8]);
+
+ // extract sign(in) (0x0000 if positive, 0xffff if negative)
+ const __m128i sign0 = _mm_cmpgt_epi16(zero, in0);
+ const __m128i sign8 = _mm_cmpgt_epi16(zero, in8);
+
+ // coeff = abs(in) = (in ^ sign) - sign
+ coeff0 = _mm_xor_si128(in0, sign0);
+ coeff8 = _mm_xor_si128(in8, sign8);
+ coeff0 = _mm_sub_epi16(coeff0, sign0);
+ coeff8 = _mm_sub_epi16(coeff8, sign8);
+
+ // coeff = abs(in) + sharpen
+ if (sharpen != NULL) {
+ const __m128i sharpen0 = _mm_loadu_si128((const __m128i*)&sharpen[0]);
+ const __m128i sharpen8 = _mm_loadu_si128((const __m128i*)&sharpen[8]);
+ coeff0 = _mm_add_epi16(coeff0, sharpen0);
+ coeff8 = _mm_add_epi16(coeff8, sharpen8);
+ }
+
+ // out = (coeff * iQ + B) >> QFIX
+ {
+ // doing calculations with 32b precision (QFIX=17)
+ // out = (coeff * iQ)
+ const __m128i coeff_iQ0H = _mm_mulhi_epu16(coeff0, iq0);
+ const __m128i coeff_iQ0L = _mm_mullo_epi16(coeff0, iq0);
+ const __m128i coeff_iQ8H = _mm_mulhi_epu16(coeff8, iq8);
+ const __m128i coeff_iQ8L = _mm_mullo_epi16(coeff8, iq8);
+ __m128i out_00 = _mm_unpacklo_epi16(coeff_iQ0L, coeff_iQ0H);
+ __m128i out_04 = _mm_unpackhi_epi16(coeff_iQ0L, coeff_iQ0H);
+ __m128i out_08 = _mm_unpacklo_epi16(coeff_iQ8L, coeff_iQ8H);
+ __m128i out_12 = _mm_unpackhi_epi16(coeff_iQ8L, coeff_iQ8H);
+ // out = (coeff * iQ + B)
+ const __m128i bias_00 = _mm_loadu_si128((const __m128i*)&mtx->bias_[0]);
+ const __m128i bias_04 = _mm_loadu_si128((const __m128i*)&mtx->bias_[4]);
+ const __m128i bias_08 = _mm_loadu_si128((const __m128i*)&mtx->bias_[8]);
+ const __m128i bias_12 = _mm_loadu_si128((const __m128i*)&mtx->bias_[12]);
+ out_00 = _mm_add_epi32(out_00, bias_00);
+ out_04 = _mm_add_epi32(out_04, bias_04);
+ out_08 = _mm_add_epi32(out_08, bias_08);
+ out_12 = _mm_add_epi32(out_12, bias_12);
+ // out = QUANTDIV(coeff, iQ, B, QFIX)
+ out_00 = _mm_srai_epi32(out_00, QFIX);
+ out_04 = _mm_srai_epi32(out_04, QFIX);
+ out_08 = _mm_srai_epi32(out_08, QFIX);
+ out_12 = _mm_srai_epi32(out_12, QFIX);
+
+ // pack result as 16b
+ out0 = _mm_packs_epi32(out_00, out_04);
+ out8 = _mm_packs_epi32(out_08, out_12);
+
+ // if (coeff > 2047) coeff = 2047
+ out0 = _mm_min_epi16(out0, max_coeff_2047);
+ out8 = _mm_min_epi16(out8, max_coeff_2047);
+ }
+
+ // get sign back (if (sign[j]) out_n = -out_n)
+ out0 = _mm_xor_si128(out0, sign0);
+ out8 = _mm_xor_si128(out8, sign8);
+ out0 = _mm_sub_epi16(out0, sign0);
+ out8 = _mm_sub_epi16(out8, sign8);
+
+ // in = out * Q
+ in0 = _mm_mullo_epi16(out0, q0);
+ in8 = _mm_mullo_epi16(out8, q8);
+
+ _mm_storeu_si128((__m128i*)&in[0], in0);
+ _mm_storeu_si128((__m128i*)&in[8], in8);
+
+ // zigzag the output before storing it.
+ //
+ // The zigzag pattern can almost be reproduced with a small sequence of
+ // shuffles. After it, we only need to swap the 7th (ending up in third
+ // position instead of twelfth) and 8th values.
+ {
+ __m128i outZ0, outZ8;
+ outZ0 = _mm_shufflehi_epi16(out0, _MM_SHUFFLE(2, 1, 3, 0));
+ outZ0 = _mm_shuffle_epi32 (outZ0, _MM_SHUFFLE(3, 1, 2, 0));
+ outZ0 = _mm_shufflehi_epi16(outZ0, _MM_SHUFFLE(3, 1, 0, 2));
+ outZ8 = _mm_shufflelo_epi16(out8, _MM_SHUFFLE(3, 0, 2, 1));
+ outZ8 = _mm_shuffle_epi32 (outZ8, _MM_SHUFFLE(3, 1, 2, 0));
+ outZ8 = _mm_shufflelo_epi16(outZ8, _MM_SHUFFLE(1, 3, 2, 0));
+ _mm_storeu_si128((__m128i*)&out[0], outZ0);
+ _mm_storeu_si128((__m128i*)&out[8], outZ8);
+ packed_out = _mm_packs_epi16(outZ0, outZ8);
+ }
+ {
+ const int16_t outZ_12 = out[12];
+ const int16_t outZ_3 = out[3];
+ out[3] = outZ_12;
+ out[12] = outZ_3;
+ }
+
+ // detect if all 'out' values are zeroes or not
+ return (_mm_movemask_epi8(_mm_cmpeq_epi8(packed_out, zero)) != 0xffff);
+}
+
+static int QuantizeBlock_SSE2(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ return DoQuantizeBlock_SSE2(in, out, &mtx->sharpen_[0], mtx);
+}
+
+static int QuantizeBlockWHT_SSE2(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ return DoQuantizeBlock_SSE2(in, out, NULL, mtx);
+}
+
+static int Quantize2Blocks_SSE2(int16_t in[32], int16_t out[32],
+ const VP8Matrix* const mtx) {
+ int nz;
+ const uint16_t* const sharpen = &mtx->sharpen_[0];
+ nz = DoQuantizeBlock_SSE2(in + 0 * 16, out + 0 * 16, sharpen, mtx) << 0;
+ nz |= DoQuantizeBlock_SSE2(in + 1 * 16, out + 1 * 16, sharpen, mtx) << 1;
+ return nz;
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitSSE2(void) {
+ VP8CollectHistogram = CollectHistogram_SSE2;
+ VP8EncPredLuma16 = Intra16Preds_SSE2;
+ VP8EncPredChroma8 = IntraChromaPreds_SSE2;
+ VP8EncPredLuma4 = Intra4Preds_SSE2;
+ VP8EncQuantizeBlock = QuantizeBlock_SSE2;
+ VP8EncQuantize2Blocks = Quantize2Blocks_SSE2;
+ VP8EncQuantizeBlockWHT = QuantizeBlockWHT_SSE2;
+ VP8ITransform = ITransform_SSE2;
+ VP8FTransform = FTransform_SSE2;
+ VP8FTransform2 = FTransform2_SSE2;
+ VP8FTransformWHT = FTransformWHT_SSE2;
+ VP8SSE16x16 = SSE16x16_SSE2;
+ VP8SSE16x8 = SSE16x8_SSE2;
+ VP8SSE8x8 = SSE8x8_SSE2;
+ VP8SSE4x4 = SSE4x4_SSE2;
+ VP8TDisto4x4 = Disto4x4_SSE2;
+ VP8TDisto16x16 = Disto16x16_SSE2;
+ VP8Mean16x4 = Mean16x4_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(VP8EncDspInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/enc_sse41.c b/src/third_party/libwebp/src/dsp/enc_sse41.c
new file mode 100644
index 0000000..eade29a
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/enc_sse41.c
@@ -0,0 +1,344 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE4 version of some encoding functions.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE41)
+#include <smmintrin.h>
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <stdlib.h> // for abs()
+#endif
+
+#include "src/dsp/common_sse2.h"
+#include "src/enc/vp8i_enc.h"
+
+//------------------------------------------------------------------------------
+// Compute susceptibility based on DCT-coeff histograms.
+
+static void CollectHistogram_SSE41(const uint8_t* ref, const uint8_t* pred,
+ int start_block, int end_block,
+ VP8Histogram* const histo) {
+ const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH);
+ int j;
+ int distribution[MAX_COEFF_THRESH + 1] = { 0 };
+ for (j = start_block; j < end_block; ++j) {
+ int16_t out[16];
+ int k;
+
+ VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
+
+ // Convert coefficients to bin (within out[]).
+ {
+ // Load.
+ const __m128i out0 = _mm_loadu_si128((__m128i*)&out[0]);
+ const __m128i out1 = _mm_loadu_si128((__m128i*)&out[8]);
+ // v = abs(out) >> 3
+ const __m128i abs0 = _mm_abs_epi16(out0);
+ const __m128i abs1 = _mm_abs_epi16(out1);
+ const __m128i v0 = _mm_srai_epi16(abs0, 3);
+ const __m128i v1 = _mm_srai_epi16(abs1, 3);
+ // bin = min(v, MAX_COEFF_THRESH)
+ const __m128i bin0 = _mm_min_epi16(v0, max_coeff_thresh);
+ const __m128i bin1 = _mm_min_epi16(v1, max_coeff_thresh);
+ // Store.
+ _mm_storeu_si128((__m128i*)&out[0], bin0);
+ _mm_storeu_si128((__m128i*)&out[8], bin1);
+ }
+
+ // Convert coefficients to bin.
+ for (k = 0; k < 16; ++k) {
+ ++distribution[out[k]];
+ }
+ }
+ VP8SetHistogramData(distribution, histo);
+}
+
+//------------------------------------------------------------------------------
+// Texture distortion
+//
+// We try to match the spectral content (weighted) between source and
+// reconstructed samples.
+
+// Hadamard transform
+// Returns the weighted sum of the absolute value of transformed coefficients.
+// w[] contains a row-major 4 by 4 symmetric matrix.
+static int TTransform_SSE41(const uint8_t* inA, const uint8_t* inB,
+ const uint16_t* const w) {
+ int32_t sum[4];
+ __m128i tmp_0, tmp_1, tmp_2, tmp_3;
+
+ // Load and combine inputs.
+ {
+ const __m128i inA_0 = _mm_loadu_si128((const __m128i*)&inA[BPS * 0]);
+ const __m128i inA_1 = _mm_loadu_si128((const __m128i*)&inA[BPS * 1]);
+ const __m128i inA_2 = _mm_loadu_si128((const __m128i*)&inA[BPS * 2]);
+ // In SSE4.1, with gcc 4.8 at least (maybe other versions),
+ // _mm_loadu_si128 is faster than _mm_loadl_epi64. But for the last lump
+ // of inA and inB, _mm_loadl_epi64 is still used not to have an out of
+ // bound read.
+ const __m128i inA_3 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 3]);
+ const __m128i inB_0 = _mm_loadu_si128((const __m128i*)&inB[BPS * 0]);
+ const __m128i inB_1 = _mm_loadu_si128((const __m128i*)&inB[BPS * 1]);
+ const __m128i inB_2 = _mm_loadu_si128((const __m128i*)&inB[BPS * 2]);
+ const __m128i inB_3 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 3]);
+
+ // Combine inA and inB (we'll do two transforms in parallel).
+ const __m128i inAB_0 = _mm_unpacklo_epi32(inA_0, inB_0);
+ const __m128i inAB_1 = _mm_unpacklo_epi32(inA_1, inB_1);
+ const __m128i inAB_2 = _mm_unpacklo_epi32(inA_2, inB_2);
+ const __m128i inAB_3 = _mm_unpacklo_epi32(inA_3, inB_3);
+ tmp_0 = _mm_cvtepu8_epi16(inAB_0);
+ tmp_1 = _mm_cvtepu8_epi16(inAB_1);
+ tmp_2 = _mm_cvtepu8_epi16(inAB_2);
+ tmp_3 = _mm_cvtepu8_epi16(inAB_3);
+ // a00 a01 a02 a03 b00 b01 b02 b03
+ // a10 a11 a12 a13 b10 b11 b12 b13
+ // a20 a21 a22 a23 b20 b21 b22 b23
+ // a30 a31 a32 a33 b30 b31 b32 b33
+ }
+
+ // Vertical pass first to avoid a transpose (vertical and horizontal passes
+ // are commutative because w/kWeightY is symmetric) and subsequent transpose.
+ {
+ // Calculate a and b (two 4x4 at once).
+ const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2);
+ const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3);
+ const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3);
+ const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2);
+ const __m128i b0 = _mm_add_epi16(a0, a1);
+ const __m128i b1 = _mm_add_epi16(a3, a2);
+ const __m128i b2 = _mm_sub_epi16(a3, a2);
+ const __m128i b3 = _mm_sub_epi16(a0, a1);
+ // a00 a01 a02 a03 b00 b01 b02 b03
+ // a10 a11 a12 a13 b10 b11 b12 b13
+ // a20 a21 a22 a23 b20 b21 b22 b23
+ // a30 a31 a32 a33 b30 b31 b32 b33
+
+ // Transpose the two 4x4.
+ VP8Transpose_2_4x4_16b(&b0, &b1, &b2, &b3, &tmp_0, &tmp_1, &tmp_2, &tmp_3);
+ }
+
+ // Horizontal pass and difference of weighted sums.
+ {
+ // Load all inputs.
+ const __m128i w_0 = _mm_loadu_si128((const __m128i*)&w[0]);
+ const __m128i w_8 = _mm_loadu_si128((const __m128i*)&w[8]);
+
+ // Calculate a and b (two 4x4 at once).
+ const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2);
+ const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3);
+ const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3);
+ const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2);
+ const __m128i b0 = _mm_add_epi16(a0, a1);
+ const __m128i b1 = _mm_add_epi16(a3, a2);
+ const __m128i b2 = _mm_sub_epi16(a3, a2);
+ const __m128i b3 = _mm_sub_epi16(a0, a1);
+
+ // Separate the transforms of inA and inB.
+ __m128i A_b0 = _mm_unpacklo_epi64(b0, b1);
+ __m128i A_b2 = _mm_unpacklo_epi64(b2, b3);
+ __m128i B_b0 = _mm_unpackhi_epi64(b0, b1);
+ __m128i B_b2 = _mm_unpackhi_epi64(b2, b3);
+
+ A_b0 = _mm_abs_epi16(A_b0);
+ A_b2 = _mm_abs_epi16(A_b2);
+ B_b0 = _mm_abs_epi16(B_b0);
+ B_b2 = _mm_abs_epi16(B_b2);
+
+ // weighted sums
+ A_b0 = _mm_madd_epi16(A_b0, w_0);
+ A_b2 = _mm_madd_epi16(A_b2, w_8);
+ B_b0 = _mm_madd_epi16(B_b0, w_0);
+ B_b2 = _mm_madd_epi16(B_b2, w_8);
+ A_b0 = _mm_add_epi32(A_b0, A_b2);
+ B_b0 = _mm_add_epi32(B_b0, B_b2);
+
+ // difference of weighted sums
+ A_b2 = _mm_sub_epi32(A_b0, B_b0);
+ _mm_storeu_si128((__m128i*)&sum[0], A_b2);
+ }
+ return sum[0] + sum[1] + sum[2] + sum[3];
+}
+
+static int Disto4x4_SSE41(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ const int diff_sum = TTransform_SSE41(a, b, w);
+ return abs(diff_sum) >> 5;
+}
+
+static int Disto16x16_SSE41(const uint8_t* const a, const uint8_t* const b,
+ const uint16_t* const w) {
+ int D = 0;
+ int x, y;
+ for (y = 0; y < 16 * BPS; y += 4 * BPS) {
+ for (x = 0; x < 16; x += 4) {
+ D += Disto4x4_SSE41(a + x + y, b + x + y, w);
+ }
+ }
+ return D;
+}
+
+//------------------------------------------------------------------------------
+// Quantization
+//
+
+// Generates a pshufb constant for shuffling 16b words.
+#define PSHUFB_CST(A,B,C,D,E,F,G,H) \
+ _mm_set_epi8(2 * (H) + 1, 2 * (H) + 0, 2 * (G) + 1, 2 * (G) + 0, \
+ 2 * (F) + 1, 2 * (F) + 0, 2 * (E) + 1, 2 * (E) + 0, \
+ 2 * (D) + 1, 2 * (D) + 0, 2 * (C) + 1, 2 * (C) + 0, \
+ 2 * (B) + 1, 2 * (B) + 0, 2 * (A) + 1, 2 * (A) + 0)
+
+static WEBP_INLINE int DoQuantizeBlock_SSE41(int16_t in[16], int16_t out[16],
+ const uint16_t* const sharpen,
+ const VP8Matrix* const mtx) {
+ const __m128i max_coeff_2047 = _mm_set1_epi16(MAX_LEVEL);
+ const __m128i zero = _mm_setzero_si128();
+ __m128i out0, out8;
+ __m128i packed_out;
+
+ // Load all inputs.
+ __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]);
+ __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]);
+ const __m128i iq0 = _mm_loadu_si128((const __m128i*)&mtx->iq_[0]);
+ const __m128i iq8 = _mm_loadu_si128((const __m128i*)&mtx->iq_[8]);
+ const __m128i q0 = _mm_loadu_si128((const __m128i*)&mtx->q_[0]);
+ const __m128i q8 = _mm_loadu_si128((const __m128i*)&mtx->q_[8]);
+
+ // coeff = abs(in)
+ __m128i coeff0 = _mm_abs_epi16(in0);
+ __m128i coeff8 = _mm_abs_epi16(in8);
+
+ // coeff = abs(in) + sharpen
+ if (sharpen != NULL) {
+ const __m128i sharpen0 = _mm_loadu_si128((const __m128i*)&sharpen[0]);
+ const __m128i sharpen8 = _mm_loadu_si128((const __m128i*)&sharpen[8]);
+ coeff0 = _mm_add_epi16(coeff0, sharpen0);
+ coeff8 = _mm_add_epi16(coeff8, sharpen8);
+ }
+
+ // out = (coeff * iQ + B) >> QFIX
+ {
+ // doing calculations with 32b precision (QFIX=17)
+ // out = (coeff * iQ)
+ const __m128i coeff_iQ0H = _mm_mulhi_epu16(coeff0, iq0);
+ const __m128i coeff_iQ0L = _mm_mullo_epi16(coeff0, iq0);
+ const __m128i coeff_iQ8H = _mm_mulhi_epu16(coeff8, iq8);
+ const __m128i coeff_iQ8L = _mm_mullo_epi16(coeff8, iq8);
+ __m128i out_00 = _mm_unpacklo_epi16(coeff_iQ0L, coeff_iQ0H);
+ __m128i out_04 = _mm_unpackhi_epi16(coeff_iQ0L, coeff_iQ0H);
+ __m128i out_08 = _mm_unpacklo_epi16(coeff_iQ8L, coeff_iQ8H);
+ __m128i out_12 = _mm_unpackhi_epi16(coeff_iQ8L, coeff_iQ8H);
+ // out = (coeff * iQ + B)
+ const __m128i bias_00 = _mm_loadu_si128((const __m128i*)&mtx->bias_[0]);
+ const __m128i bias_04 = _mm_loadu_si128((const __m128i*)&mtx->bias_[4]);
+ const __m128i bias_08 = _mm_loadu_si128((const __m128i*)&mtx->bias_[8]);
+ const __m128i bias_12 = _mm_loadu_si128((const __m128i*)&mtx->bias_[12]);
+ out_00 = _mm_add_epi32(out_00, bias_00);
+ out_04 = _mm_add_epi32(out_04, bias_04);
+ out_08 = _mm_add_epi32(out_08, bias_08);
+ out_12 = _mm_add_epi32(out_12, bias_12);
+ // out = QUANTDIV(coeff, iQ, B, QFIX)
+ out_00 = _mm_srai_epi32(out_00, QFIX);
+ out_04 = _mm_srai_epi32(out_04, QFIX);
+ out_08 = _mm_srai_epi32(out_08, QFIX);
+ out_12 = _mm_srai_epi32(out_12, QFIX);
+
+ // pack result as 16b
+ out0 = _mm_packs_epi32(out_00, out_04);
+ out8 = _mm_packs_epi32(out_08, out_12);
+
+ // if (coeff > 2047) coeff = 2047
+ out0 = _mm_min_epi16(out0, max_coeff_2047);
+ out8 = _mm_min_epi16(out8, max_coeff_2047);
+ }
+
+ // put sign back
+ out0 = _mm_sign_epi16(out0, in0);
+ out8 = _mm_sign_epi16(out8, in8);
+
+ // in = out * Q
+ in0 = _mm_mullo_epi16(out0, q0);
+ in8 = _mm_mullo_epi16(out8, q8);
+
+ _mm_storeu_si128((__m128i*)&in[0], in0);
+ _mm_storeu_si128((__m128i*)&in[8], in8);
+
+ // zigzag the output before storing it. The re-ordering is:
+ // 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15
+ // -> 0 1 4[8]5 2 3 6 | 9 12 13 10 [7]11 14 15
+ // There's only two misplaced entries ([8] and [7]) that are crossing the
+ // reg's boundaries.
+ // We use pshufb instead of pshuflo/pshufhi.
+ {
+ const __m128i kCst_lo = PSHUFB_CST(0, 1, 4, -1, 5, 2, 3, 6);
+ const __m128i kCst_7 = PSHUFB_CST(-1, -1, -1, -1, 7, -1, -1, -1);
+ const __m128i tmp_lo = _mm_shuffle_epi8(out0, kCst_lo);
+ const __m128i tmp_7 = _mm_shuffle_epi8(out0, kCst_7); // extract #7
+ const __m128i kCst_hi = PSHUFB_CST(1, 4, 5, 2, -1, 3, 6, 7);
+ const __m128i kCst_8 = PSHUFB_CST(-1, -1, -1, 0, -1, -1, -1, -1);
+ const __m128i tmp_hi = _mm_shuffle_epi8(out8, kCst_hi);
+ const __m128i tmp_8 = _mm_shuffle_epi8(out8, kCst_8); // extract #8
+ const __m128i out_z0 = _mm_or_si128(tmp_lo, tmp_8);
+ const __m128i out_z8 = _mm_or_si128(tmp_hi, tmp_7);
+ _mm_storeu_si128((__m128i*)&out[0], out_z0);
+ _mm_storeu_si128((__m128i*)&out[8], out_z8);
+ packed_out = _mm_packs_epi16(out_z0, out_z8);
+ }
+
+ // detect if all 'out' values are zeroes or not
+ return (_mm_movemask_epi8(_mm_cmpeq_epi8(packed_out, zero)) != 0xffff);
+}
+
+#undef PSHUFB_CST
+
+static int QuantizeBlock_SSE41(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ return DoQuantizeBlock_SSE41(in, out, &mtx->sharpen_[0], mtx);
+}
+
+static int QuantizeBlockWHT_SSE41(int16_t in[16], int16_t out[16],
+ const VP8Matrix* const mtx) {
+ return DoQuantizeBlock_SSE41(in, out, NULL, mtx);
+}
+
+static int Quantize2Blocks_SSE41(int16_t in[32], int16_t out[32],
+ const VP8Matrix* const mtx) {
+ int nz;
+ const uint16_t* const sharpen = &mtx->sharpen_[0];
+ nz = DoQuantizeBlock_SSE41(in + 0 * 16, out + 0 * 16, sharpen, mtx) << 0;
+ nz |= DoQuantizeBlock_SSE41(in + 1 * 16, out + 1 * 16, sharpen, mtx) << 1;
+ return nz;
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8EncDspInitSSE41(void);
+WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInitSSE41(void) {
+ VP8CollectHistogram = CollectHistogram_SSE41;
+ VP8EncQuantizeBlock = QuantizeBlock_SSE41;
+ VP8EncQuantize2Blocks = Quantize2Blocks_SSE41;
+ VP8EncQuantizeBlockWHT = QuantizeBlockWHT_SSE41;
+ VP8TDisto4x4 = Disto4x4_SSE41;
+ VP8TDisto16x16 = Disto16x16_SSE41;
+}
+
+#else // !WEBP_USE_SSE41
+
+WEBP_DSP_INIT_STUB(VP8EncDspInitSSE41)
+
+#endif // WEBP_USE_SSE41
diff --git a/src/third_party/libwebp/src/dsp/filters.c b/src/third_party/libwebp/src/dsp/filters.c
new file mode 100644
index 0000000..069a22e
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/filters.c
@@ -0,0 +1,287 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Spatial prediction using various filters
+//
+// Author: Urvang (urvang@google.com)
+
+#include "src/dsp/dsp.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+//------------------------------------------------------------------------------
+// Helpful macro.
+
+# define SANITY_CHECK(in, out) \
+ assert((in) != NULL); \
+ assert((out) != NULL); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
+ (void)height; // Silence unused warning.
+
+#if !WEBP_NEON_OMIT_C_CODE
+static WEBP_INLINE void PredictLine_C(const uint8_t* src, const uint8_t* pred,
+ uint8_t* dst, int length, int inverse) {
+ int i;
+ if (inverse) {
+ for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i];
+ } else {
+ for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i];
+ }
+}
+
+//------------------------------------------------------------------------------
+// Horizontal filter.
+
+static WEBP_INLINE void DoHorizontalFilter_C(const uint8_t* in,
+ int width, int height, int stride,
+ int row, int num_rows,
+ int inverse, uint8_t* out) {
+ const uint8_t* preds;
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+ preds = inverse ? out : in;
+
+ if (row == 0) {
+ // Leftmost pixel is the same as input for topmost scanline.
+ out[0] = in[0];
+ PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
+ row = 1;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ // Leftmost pixel is predicted from above.
+ PredictLine_C(in, preds - stride, out, 1, inverse);
+ PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
+ ++row;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Vertical filter.
+
+static WEBP_INLINE void DoVerticalFilter_C(const uint8_t* in,
+ int width, int height, int stride,
+ int row, int num_rows,
+ int inverse, uint8_t* out) {
+ const uint8_t* preds;
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+ preds = inverse ? out : in;
+
+ if (row == 0) {
+ // Very first top-left pixel is copied.
+ out[0] = in[0];
+ // Rest of top scan-line is left-predicted.
+ PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
+ row = 1;
+ in += stride;
+ out += stride;
+ } else {
+ // We are starting from in-between. Make sure 'preds' points to prev row.
+ preds -= stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ PredictLine_C(in, preds, out, width, inverse);
+ ++row;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//------------------------------------------------------------------------------
+// Gradient filter.
+
+static WEBP_INLINE int GradientPredictor_C(uint8_t a, uint8_t b, uint8_t c) {
+ const int g = a + b - c;
+ return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in,
+ int width, int height, int stride,
+ int row, int num_rows,
+ int inverse, uint8_t* out) {
+ const uint8_t* preds;
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+ preds = inverse ? out : in;
+
+ // left prediction for top scan-line
+ if (row == 0) {
+ out[0] = in[0];
+ PredictLine_C(in + 1, preds, out + 1, width - 1, inverse);
+ row = 1;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ int w;
+ // leftmost pixel: predict from above.
+ PredictLine_C(in, preds - stride, out, 1, inverse);
+ for (w = 1; w < width; ++w) {
+ const int pred = GradientPredictor_C(preds[w - 1],
+ preds[w - stride],
+ preds[w - stride - 1]);
+ out[w] = in[w] + (inverse ? pred : -pred);
+ }
+ ++row;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+#undef SANITY_CHECK
+
+//------------------------------------------------------------------------------
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void HorizontalFilter_C(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoHorizontalFilter_C(data, width, height, stride, 0, height, 0,
+ filtered_data);
+}
+
+static void VerticalFilter_C(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoVerticalFilter_C(data, width, height, stride, 0, height, 0, filtered_data);
+}
+
+static void GradientFilter_C(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoGradientFilter_C(data, width, height, stride, 0, height, 0, filtered_data);
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+//------------------------------------------------------------------------------
+
+static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ uint8_t pred = (prev == NULL) ? 0 : prev[0];
+ int i;
+ for (i = 0; i < width; ++i) {
+ out[i] = pred + in[i];
+ pred = out[i];
+ }
+}
+
+#if !WEBP_NEON_OMIT_C_CODE
+static void VerticalUnfilter_C(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_C(NULL, in, out, width);
+ } else {
+ int i;
+ for (i = 0; i < width; ++i) out[i] = prev[i] + in[i];
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_C(NULL, in, out, width);
+ } else {
+ uint8_t top = prev[0], top_left = top, left = top;
+ int i;
+ for (i = 0; i < width; ++i) {
+ top = prev[i]; // need to read this first, in case prev==out
+ left = in[i] + GradientPredictor_C(left, top, top_left);
+ top_left = top;
+ out[i] = left;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Init function
+
+WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
+WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
+
+extern void VP8FiltersInitMIPSdspR2(void);
+extern void VP8FiltersInitMSA(void);
+extern void VP8FiltersInitNEON(void);
+extern void VP8FiltersInitSSE2(void);
+
+WEBP_DSP_INIT_FUNC(VP8FiltersInit) {
+ WebPUnfilters[WEBP_FILTER_NONE] = NULL;
+#if !WEBP_NEON_OMIT_C_CODE
+ WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_C;
+ WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_C;
+#endif
+ WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_C;
+
+ WebPFilters[WEBP_FILTER_NONE] = NULL;
+#if !WEBP_NEON_OMIT_C_CODE
+ WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_C;
+ WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_C;
+ WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_C;
+#endif
+
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ VP8FiltersInitSSE2();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ VP8FiltersInitMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_MSA)
+ if (VP8GetCPUInfo(kMSA)) {
+ VP8FiltersInitMSA();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ VP8FiltersInitNEON();
+ }
+#endif
+
+ assert(WebPUnfilters[WEBP_FILTER_HORIZONTAL] != NULL);
+ assert(WebPUnfilters[WEBP_FILTER_VERTICAL] != NULL);
+ assert(WebPUnfilters[WEBP_FILTER_GRADIENT] != NULL);
+ assert(WebPFilters[WEBP_FILTER_HORIZONTAL] != NULL);
+ assert(WebPFilters[WEBP_FILTER_VERTICAL] != NULL);
+ assert(WebPFilters[WEBP_FILTER_GRADIENT] != NULL);
+}
diff --git a/src/third_party/libwebp/src/dsp/filters_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/filters_mips_dsp_r2.c
new file mode 100644
index 0000000..9382b12
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/filters_mips_dsp_r2.c
@@ -0,0 +1,402 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Spatial prediction using various filters
+//
+// Author(s): Branimir Vasic (branimir.vasic@imgtec.com)
+// Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#include "src/dsp/dsp.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+//------------------------------------------------------------------------------
+// Helpful macro.
+
+# define SANITY_CHECK(in, out) \
+ assert(in != NULL); \
+ assert(out != NULL); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
+ (void)height; // Silence unused warning.
+
+#define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \
+ const uint8_t* psrc = (uint8_t*)(SRC); \
+ uint8_t* pdst = (uint8_t*)(DST); \
+ const int ilength = (int)(LENGTH); \
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6; \
+ __asm__ volatile ( \
+ ".set push \n\t" \
+ ".set noreorder \n\t" \
+ "srl %[temp0], %[length], 2 \n\t" \
+ "beqz %[temp0], 4f \n\t" \
+ " andi %[temp6], %[length], 3 \n\t" \
+ ".if " #INVERSE " \n\t" \
+ "1: \n\t" \
+ "lbu %[temp1], -1(%[dst]) \n\t" \
+ "lbu %[temp2], 0(%[src]) \n\t" \
+ "lbu %[temp3], 1(%[src]) \n\t" \
+ "lbu %[temp4], 2(%[src]) \n\t" \
+ "lbu %[temp5], 3(%[src]) \n\t" \
+ "addu %[temp1], %[temp1], %[temp2] \n\t" \
+ "addu %[temp2], %[temp1], %[temp3] \n\t" \
+ "addu %[temp3], %[temp2], %[temp4] \n\t" \
+ "addu %[temp4], %[temp3], %[temp5] \n\t" \
+ "sb %[temp1], 0(%[dst]) \n\t" \
+ "sb %[temp2], 1(%[dst]) \n\t" \
+ "sb %[temp3], 2(%[dst]) \n\t" \
+ "sb %[temp4], 3(%[dst]) \n\t" \
+ "addiu %[src], %[src], 4 \n\t" \
+ "addiu %[temp0], %[temp0], -1 \n\t" \
+ "bnez %[temp0], 1b \n\t" \
+ " addiu %[dst], %[dst], 4 \n\t" \
+ ".else \n\t" \
+ "1: \n\t" \
+ "ulw %[temp1], -1(%[src]) \n\t" \
+ "ulw %[temp2], 0(%[src]) \n\t" \
+ "addiu %[src], %[src], 4 \n\t" \
+ "addiu %[temp0], %[temp0], -1 \n\t" \
+ "subu.qb %[temp3], %[temp2], %[temp1] \n\t" \
+ "usw %[temp3], 0(%[dst]) \n\t" \
+ "bnez %[temp0], 1b \n\t" \
+ " addiu %[dst], %[dst], 4 \n\t" \
+ ".endif \n\t" \
+ "4: \n\t" \
+ "beqz %[temp6], 3f \n\t" \
+ " nop \n\t" \
+ "2: \n\t" \
+ "lbu %[temp2], 0(%[src]) \n\t" \
+ ".if " #INVERSE " \n\t" \
+ "lbu %[temp1], -1(%[dst]) \n\t" \
+ "addu %[temp3], %[temp1], %[temp2] \n\t" \
+ ".else \n\t" \
+ "lbu %[temp1], -1(%[src]) \n\t" \
+ "subu %[temp3], %[temp1], %[temp2] \n\t" \
+ ".endif \n\t" \
+ "addiu %[src], %[src], 1 \n\t" \
+ "sb %[temp3], 0(%[dst]) \n\t" \
+ "addiu %[temp6], %[temp6], -1 \n\t" \
+ "bnez %[temp6], 2b \n\t" \
+ " addiu %[dst], %[dst], 1 \n\t" \
+ "3: \n\t" \
+ ".set pop \n\t" \
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \
+ [temp6]"=&r"(temp6), [dst]"+&r"(pdst), [src]"+&r"(psrc) \
+ : [length]"r"(ilength) \
+ : "memory" \
+ ); \
+ } while (0)
+
+static WEBP_INLINE void PredictLine_MIPSdspR2(const uint8_t* src, uint8_t* dst,
+ int length) {
+ DO_PREDICT_LINE(src, dst, length, 0);
+}
+
+#define DO_PREDICT_LINE_VERTICAL(SRC, PRED, DST, LENGTH, INVERSE) do { \
+ const uint8_t* psrc = (uint8_t*)(SRC); \
+ const uint8_t* ppred = (uint8_t*)(PRED); \
+ uint8_t* pdst = (uint8_t*)(DST); \
+ const int ilength = (int)(LENGTH); \
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \
+ __asm__ volatile ( \
+ ".set push \n\t" \
+ ".set noreorder \n\t" \
+ "srl %[temp0], %[length], 0x3 \n\t" \
+ "beqz %[temp0], 4f \n\t" \
+ " andi %[temp7], %[length], 0x7 \n\t" \
+ "1: \n\t" \
+ "ulw %[temp1], 0(%[src]) \n\t" \
+ "ulw %[temp2], 0(%[pred]) \n\t" \
+ "ulw %[temp3], 4(%[src]) \n\t" \
+ "ulw %[temp4], 4(%[pred]) \n\t" \
+ "addiu %[src], %[src], 8 \n\t" \
+ ".if " #INVERSE " \n\t" \
+ "addu.qb %[temp5], %[temp1], %[temp2] \n\t" \
+ "addu.qb %[temp6], %[temp3], %[temp4] \n\t" \
+ ".else \n\t" \
+ "subu.qb %[temp5], %[temp1], %[temp2] \n\t" \
+ "subu.qb %[temp6], %[temp3], %[temp4] \n\t" \
+ ".endif \n\t" \
+ "addiu %[pred], %[pred], 8 \n\t" \
+ "usw %[temp5], 0(%[dst]) \n\t" \
+ "usw %[temp6], 4(%[dst]) \n\t" \
+ "addiu %[temp0], %[temp0], -1 \n\t" \
+ "bnez %[temp0], 1b \n\t" \
+ " addiu %[dst], %[dst], 8 \n\t" \
+ "4: \n\t" \
+ "beqz %[temp7], 3f \n\t" \
+ " nop \n\t" \
+ "2: \n\t" \
+ "lbu %[temp1], 0(%[src]) \n\t" \
+ "lbu %[temp2], 0(%[pred]) \n\t" \
+ "addiu %[src], %[src], 1 \n\t" \
+ "addiu %[pred], %[pred], 1 \n\t" \
+ ".if " #INVERSE " \n\t" \
+ "addu %[temp3], %[temp1], %[temp2] \n\t" \
+ ".else \n\t" \
+ "subu %[temp3], %[temp1], %[temp2] \n\t" \
+ ".endif \n\t" \
+ "sb %[temp3], 0(%[dst]) \n\t" \
+ "addiu %[temp7], %[temp7], -1 \n\t" \
+ "bnez %[temp7], 2b \n\t" \
+ " addiu %[dst], %[dst], 1 \n\t" \
+ "3: \n\t" \
+ ".set pop \n\t" \
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), [pred]"+&r"(ppred), \
+ [dst]"+&r"(pdst), [src]"+&r"(psrc) \
+ : [length]"r"(ilength) \
+ : "memory" \
+ ); \
+ } while (0)
+
+#define PREDICT_LINE_ONE_PASS(SRC, PRED, DST) do { \
+ int temp1, temp2, temp3; \
+ __asm__ volatile ( \
+ "lbu %[temp1], 0(%[src]) \n\t" \
+ "lbu %[temp2], 0(%[pred]) \n\t" \
+ "subu %[temp3], %[temp1], %[temp2] \n\t" \
+ "sb %[temp3], 0(%[dst]) \n\t" \
+ : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \
+ : [pred]"r"((PRED)), [dst]"r"((DST)), [src]"r"((SRC)) \
+ : "memory" \
+ ); \
+ } while (0)
+
+//------------------------------------------------------------------------------
+// Horizontal filter.
+
+#define FILTER_LINE_BY_LINE do { \
+ while (row < last_row) { \
+ PREDICT_LINE_ONE_PASS(in, preds - stride, out); \
+ DO_PREDICT_LINE(in + 1, out + 1, width - 1, 0); \
+ ++row; \
+ preds += stride; \
+ in += stride; \
+ out += stride; \
+ } \
+ } while (0)
+
+static WEBP_INLINE void DoHorizontalFilter_MIPSdspR2(const uint8_t* in,
+ int width, int height,
+ int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const uint8_t* preds;
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+ preds = in;
+
+ if (row == 0) {
+ // Leftmost pixel is the same as input for topmost scanline.
+ out[0] = in[0];
+ PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
+ row = 1;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ FILTER_LINE_BY_LINE;
+}
+#undef FILTER_LINE_BY_LINE
+
+static void HorizontalFilter_MIPSdspR2(const uint8_t* data,
+ int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoHorizontalFilter_MIPSdspR2(data, width, height, stride, 0, height,
+ filtered_data);
+}
+
+//------------------------------------------------------------------------------
+// Vertical filter.
+
+#define FILTER_LINE_BY_LINE do { \
+ while (row < last_row) { \
+ DO_PREDICT_LINE_VERTICAL(in, preds, out, width, 0); \
+ ++row; \
+ preds += stride; \
+ in += stride; \
+ out += stride; \
+ } \
+ } while (0)
+
+static WEBP_INLINE void DoVerticalFilter_MIPSdspR2(const uint8_t* in,
+ int width, int height,
+ int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const uint8_t* preds;
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+ preds = in;
+
+ if (row == 0) {
+ // Very first top-left pixel is copied.
+ out[0] = in[0];
+ // Rest of top scan-line is left-predicted.
+ PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
+ row = 1;
+ in += stride;
+ out += stride;
+ } else {
+ // We are starting from in-between. Make sure 'preds' points to prev row.
+ preds -= stride;
+ }
+
+ // Filter line-by-line.
+ FILTER_LINE_BY_LINE;
+}
+#undef FILTER_LINE_BY_LINE
+
+static void VerticalFilter_MIPSdspR2(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoVerticalFilter_MIPSdspR2(data, width, height, stride, 0, height,
+ filtered_data);
+}
+
+//------------------------------------------------------------------------------
+// Gradient filter.
+
+static int GradientPredictor_MIPSdspR2(uint8_t a, uint8_t b, uint8_t c) {
+ int temp0;
+ __asm__ volatile (
+ "addu %[temp0], %[a], %[b] \n\t"
+ "subu %[temp0], %[temp0], %[c] \n\t"
+ "shll_s.w %[temp0], %[temp0], 23 \n\t"
+ "precrqu_s.qb.ph %[temp0], %[temp0], $zero \n\t"
+ "srl %[temp0], %[temp0], 24 \n\t"
+ : [temp0]"=&r"(temp0)
+ : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+ );
+ return temp0;
+}
+
+#define FILTER_LINE_BY_LINE(PREDS, OPERATION) do { \
+ while (row < last_row) { \
+ int w; \
+ PREDICT_LINE_ONE_PASS(in, PREDS - stride, out); \
+ for (w = 1; w < width; ++w) { \
+ const int pred = GradientPredictor_MIPSdspR2(PREDS[w - 1], \
+ PREDS[w - stride], \
+ PREDS[w - stride - 1]); \
+ out[w] = in[w] OPERATION pred; \
+ } \
+ ++row; \
+ in += stride; \
+ out += stride; \
+ } \
+ } while (0)
+
+static void DoGradientFilter_MIPSdspR2(const uint8_t* in,
+ int width, int height, int stride,
+ int row, int num_rows, uint8_t* out) {
+ const uint8_t* preds;
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+ preds = in;
+
+ // left prediction for top scan-line
+ if (row == 0) {
+ out[0] = in[0];
+ PredictLine_MIPSdspR2(in + 1, out + 1, width - 1);
+ row = 1;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ FILTER_LINE_BY_LINE(in, -);
+}
+#undef FILTER_LINE_BY_LINE
+
+static void GradientFilter_MIPSdspR2(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoGradientFilter_MIPSdspR2(data, width, height, stride, 0, height,
+ filtered_data);
+}
+
+//------------------------------------------------------------------------------
+
+static void HorizontalUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ out[0] = in[0] + (prev == NULL ? 0 : prev[0]);
+ DO_PREDICT_LINE(in + 1, out + 1, width - 1, 1);
+}
+
+static void VerticalUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_MIPSdspR2(NULL, in, out, width);
+ } else {
+ DO_PREDICT_LINE_VERTICAL(in, prev, out, width, 1);
+ }
+}
+
+static void GradientUnfilter_MIPSdspR2(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_MIPSdspR2(NULL, in, out, width);
+ } else {
+ uint8_t top = prev[0], top_left = top, left = top;
+ int i;
+ for (i = 0; i < width; ++i) {
+ top = prev[i]; // need to read this first, in case prev==dst
+ left = in[i] + GradientPredictor_MIPSdspR2(left, top, top_left);
+ top_left = top;
+ out[i] = left;
+ }
+ }
+}
+
+#undef DO_PREDICT_LINE_VERTICAL
+#undef PREDICT_LINE_ONE_PASS
+#undef DO_PREDICT_LINE
+#undef SANITY_CHECK
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8FiltersInitMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMIPSdspR2(void) {
+ WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_MIPSdspR2;
+ WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_MIPSdspR2;
+ WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_MIPSdspR2;
+
+ WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_MIPSdspR2;
+ WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_MIPSdspR2;
+ WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(VP8FiltersInitMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/filters_msa.c b/src/third_party/libwebp/src/dsp/filters_msa.c
new file mode 100644
index 0000000..14c437d
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/filters_msa.c
@@ -0,0 +1,202 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA variant of alpha filters
+//
+// Author: Prashant Patil (prashant.patil@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA)
+
+#include "src/dsp/msa_macro.h"
+
+#include <assert.h>
+
+static WEBP_INLINE void PredictLineInverse0(const uint8_t* src,
+ const uint8_t* pred,
+ uint8_t* dst, int length) {
+ v16u8 src0, pred0, dst0;
+ assert(length >= 0);
+ while (length >= 32) {
+ v16u8 src1, pred1, dst1;
+ LD_UB2(src, 16, src0, src1);
+ LD_UB2(pred, 16, pred0, pred1);
+ SUB2(src0, pred0, src1, pred1, dst0, dst1);
+ ST_UB2(dst0, dst1, dst, 16);
+ src += 32;
+ pred += 32;
+ dst += 32;
+ length -= 32;
+ }
+ if (length > 0) {
+ int i;
+ if (length >= 16) {
+ src0 = LD_UB(src);
+ pred0 = LD_UB(pred);
+ dst0 = src0 - pred0;
+ ST_UB(dst0, dst);
+ src += 16;
+ pred += 16;
+ dst += 16;
+ length -= 16;
+ }
+ for (i = 0; i < length; i++) {
+ dst[i] = src[i] - pred[i];
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Helpful macro.
+
+#define SANITY_CHECK(in, out) \
+ assert(in != NULL); \
+ assert(out != NULL); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width);
+
+//------------------------------------------------------------------------------
+// Horrizontal filter
+
+static void HorizontalFilter_MSA(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ const uint8_t* preds = data;
+ const uint8_t* in = data;
+ uint8_t* out = filtered_data;
+ int row = 1;
+ SANITY_CHECK(in, out);
+
+ // Leftmost pixel is the same as input for topmost scanline.
+ out[0] = in[0];
+ PredictLineInverse0(in + 1, preds, out + 1, width - 1);
+ preds += stride;
+ in += stride;
+ out += stride;
+ // Filter line-by-line.
+ while (row < height) {
+ // Leftmost pixel is predicted from above.
+ PredictLineInverse0(in, preds - stride, out, 1);
+ PredictLineInverse0(in + 1, preds, out + 1, width - 1);
+ ++row;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Gradient filter
+
+static WEBP_INLINE void PredictLineGradient(const uint8_t* pinput,
+ const uint8_t* ppred,
+ uint8_t* poutput, int stride,
+ int size) {
+ int w;
+ const v16i8 zero = { 0 };
+ while (size >= 16) {
+ v16u8 pred0, dst0;
+ v8i16 a0, a1, b0, b1, c0, c1;
+ const v16u8 tmp0 = LD_UB(ppred - 1);
+ const v16u8 tmp1 = LD_UB(ppred - stride);
+ const v16u8 tmp2 = LD_UB(ppred - stride - 1);
+ const v16u8 src0 = LD_UB(pinput);
+ ILVRL_B2_SH(zero, tmp0, a0, a1);
+ ILVRL_B2_SH(zero, tmp1, b0, b1);
+ ILVRL_B2_SH(zero, tmp2, c0, c1);
+ ADD2(a0, b0, a1, b1, a0, a1);
+ SUB2(a0, c0, a1, c1, a0, a1);
+ CLIP_SH2_0_255(a0, a1);
+ pred0 = (v16u8)__msa_pckev_b((v16i8)a1, (v16i8)a0);
+ dst0 = src0 - pred0;
+ ST_UB(dst0, poutput);
+ ppred += 16;
+ pinput += 16;
+ poutput += 16;
+ size -= 16;
+ }
+ for (w = 0; w < size; ++w) {
+ const int pred = ppred[w - 1] + ppred[w - stride] - ppred[w - stride - 1];
+ poutput[w] = pinput[w] - (pred < 0 ? 0 : pred > 255 ? 255 : pred);
+ }
+}
+
+
+static void GradientFilter_MSA(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ const uint8_t* in = data;
+ const uint8_t* preds = data;
+ uint8_t* out = filtered_data;
+ int row = 1;
+ SANITY_CHECK(in, out);
+
+ // left prediction for top scan-line
+ out[0] = in[0];
+ PredictLineInverse0(in + 1, preds, out + 1, width - 1);
+ preds += stride;
+ in += stride;
+ out += stride;
+ // Filter line-by-line.
+ while (row < height) {
+ out[0] = in[0] - preds[- stride];
+ PredictLineGradient(preds + 1, in + 1, out + 1, stride, width - 1);
+ ++row;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Vertical filter
+
+static void VerticalFilter_MSA(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ const uint8_t* in = data;
+ const uint8_t* preds = data;
+ uint8_t* out = filtered_data;
+ int row = 1;
+ SANITY_CHECK(in, out);
+
+ // Very first top-left pixel is copied.
+ out[0] = in[0];
+ // Rest of top scan-line is left-predicted.
+ PredictLineInverse0(in + 1, preds, out + 1, width - 1);
+ in += stride;
+ out += stride;
+
+ // Filter line-by-line.
+ while (row < height) {
+ PredictLineInverse0(in, preds, out, width);
+ ++row;
+ preds += stride;
+ in += stride;
+ out += stride;
+ }
+}
+
+#undef SANITY_CHECK
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8FiltersInitMSA(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMSA(void) {
+ WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_MSA;
+ WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_MSA;
+ WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_MSA;
+}
+
+#else // !WEBP_USE_MSA
+
+WEBP_DSP_INIT_STUB(VP8FiltersInitMSA)
+
+#endif // WEBP_USE_MSA
diff --git a/src/third_party/libwebp/src/dsp/filters_neon.c b/src/third_party/libwebp/src/dsp/filters_neon.c
new file mode 100644
index 0000000..3e6a578
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/filters_neon.c
@@ -0,0 +1,329 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// NEON variant of alpha filters
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON)
+
+#include <assert.h>
+#include "src/dsp/neon.h"
+
+//------------------------------------------------------------------------------
+// Helpful macros.
+
+# define SANITY_CHECK(in, out) \
+ assert(in != NULL); \
+ assert(out != NULL); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
+ (void)height; // Silence unused warning.
+
+// load eight u8 and widen to s16
+#define U8_TO_S16(A) vreinterpretq_s16_u16(vmovl_u8(A))
+#define LOAD_U8_TO_S16(A) U8_TO_S16(vld1_u8(A))
+
+// shift left or right by N byte, inserting zeros
+#define SHIFT_RIGHT_N_Q(A, N) vextq_u8((A), zero, (N))
+#define SHIFT_LEFT_N_Q(A, N) vextq_u8(zero, (A), (16 - (N)) % 16)
+
+// rotate left by N bytes
+#define ROTATE_LEFT_N(A, N) vext_u8((A), (A), (N))
+// rotate right by N bytes
+#define ROTATE_RIGHT_N(A, N) vext_u8((A), (A), (8 - (N)) % 8)
+
+static void PredictLine_NEON(const uint8_t* src, const uint8_t* pred,
+ uint8_t* dst, int length) {
+ int i;
+ assert(length >= 0);
+ for (i = 0; i + 16 <= length; i += 16) {
+ const uint8x16_t A = vld1q_u8(&src[i]);
+ const uint8x16_t B = vld1q_u8(&pred[i]);
+ const uint8x16_t C = vsubq_u8(A, B);
+ vst1q_u8(&dst[i], C);
+ }
+ for (; i < length; ++i) dst[i] = src[i] - pred[i];
+}
+
+// Special case for left-based prediction (when preds==dst-1 or preds==src-1).
+static void PredictLineLeft_NEON(const uint8_t* src, uint8_t* dst, int length) {
+ PredictLine_NEON(src, src - 1, dst, length);
+}
+
+//------------------------------------------------------------------------------
+// Horizontal filter.
+
+static WEBP_INLINE void DoHorizontalFilter_NEON(const uint8_t* in,
+ int width, int height,
+ int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+
+ if (row == 0) {
+ // Leftmost pixel is the same as input for topmost scanline.
+ out[0] = in[0];
+ PredictLineLeft_NEON(in + 1, out + 1, width - 1);
+ row = 1;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ // Leftmost pixel is predicted from above.
+ out[0] = in[0] - in[-stride];
+ PredictLineLeft_NEON(in + 1, out + 1, width - 1);
+ ++row;
+ in += stride;
+ out += stride;
+ }
+}
+
+static void HorizontalFilter_NEON(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoHorizontalFilter_NEON(data, width, height, stride, 0, height,
+ filtered_data);
+}
+
+//------------------------------------------------------------------------------
+// Vertical filter.
+
+static WEBP_INLINE void DoVerticalFilter_NEON(const uint8_t* in,
+ int width, int height, int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+
+ if (row == 0) {
+ // Very first top-left pixel is copied.
+ out[0] = in[0];
+ // Rest of top scan-line is left-predicted.
+ PredictLineLeft_NEON(in + 1, out + 1, width - 1);
+ row = 1;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ PredictLine_NEON(in, in - stride, out, width);
+ ++row;
+ in += stride;
+ out += stride;
+ }
+}
+
+static void VerticalFilter_NEON(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoVerticalFilter_NEON(data, width, height, stride, 0, height,
+ filtered_data);
+}
+
+//------------------------------------------------------------------------------
+// Gradient filter.
+
+static WEBP_INLINE int GradientPredictor_C(uint8_t a, uint8_t b, uint8_t c) {
+ const int g = a + b - c;
+ return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
+}
+
+static void GradientPredictDirect_NEON(const uint8_t* const row,
+ const uint8_t* const top,
+ uint8_t* const out, int length) {
+ int i;
+ for (i = 0; i + 8 <= length; i += 8) {
+ const uint8x8_t A = vld1_u8(&row[i - 1]);
+ const uint8x8_t B = vld1_u8(&top[i + 0]);
+ const int16x8_t C = vreinterpretq_s16_u16(vaddl_u8(A, B));
+ const int16x8_t D = LOAD_U8_TO_S16(&top[i - 1]);
+ const uint8x8_t E = vqmovun_s16(vsubq_s16(C, D));
+ const uint8x8_t F = vld1_u8(&row[i + 0]);
+ vst1_u8(&out[i], vsub_u8(F, E));
+ }
+ for (; i < length; ++i) {
+ out[i] = row[i] - GradientPredictor_C(row[i - 1], top[i], top[i - 1]);
+ }
+}
+
+static WEBP_INLINE void DoGradientFilter_NEON(const uint8_t* in,
+ int width, int height,
+ int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+
+ // left prediction for top scan-line
+ if (row == 0) {
+ out[0] = in[0];
+ PredictLineLeft_NEON(in + 1, out + 1, width - 1);
+ row = 1;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ out[0] = in[0] - in[-stride];
+ GradientPredictDirect_NEON(in + 1, in + 1 - stride, out + 1, width - 1);
+ ++row;
+ in += stride;
+ out += stride;
+ }
+}
+
+static void GradientFilter_NEON(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoGradientFilter_NEON(data, width, height, stride, 0, height,
+ filtered_data);
+}
+
+#undef SANITY_CHECK
+
+//------------------------------------------------------------------------------
+// Inverse transforms
+
+static void HorizontalUnfilter_NEON(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ int i;
+ const uint8x16_t zero = vdupq_n_u8(0);
+ uint8x16_t last;
+ out[0] = in[0] + (prev == NULL ? 0 : prev[0]);
+ if (width <= 1) return;
+ last = vsetq_lane_u8(out[0], zero, 0);
+ for (i = 1; i + 16 <= width; i += 16) {
+ const uint8x16_t A0 = vld1q_u8(&in[i]);
+ const uint8x16_t A1 = vaddq_u8(A0, last);
+ const uint8x16_t A2 = SHIFT_LEFT_N_Q(A1, 1);
+ const uint8x16_t A3 = vaddq_u8(A1, A2);
+ const uint8x16_t A4 = SHIFT_LEFT_N_Q(A3, 2);
+ const uint8x16_t A5 = vaddq_u8(A3, A4);
+ const uint8x16_t A6 = SHIFT_LEFT_N_Q(A5, 4);
+ const uint8x16_t A7 = vaddq_u8(A5, A6);
+ const uint8x16_t A8 = SHIFT_LEFT_N_Q(A7, 8);
+ const uint8x16_t A9 = vaddq_u8(A7, A8);
+ vst1q_u8(&out[i], A9);
+ last = SHIFT_RIGHT_N_Q(A9, 15);
+ }
+ for (; i < width; ++i) out[i] = in[i] + out[i - 1];
+}
+
+static void VerticalUnfilter_NEON(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_NEON(NULL, in, out, width);
+ } else {
+ int i;
+ assert(width >= 0);
+ for (i = 0; i + 16 <= width; i += 16) {
+ const uint8x16_t A = vld1q_u8(&in[i]);
+ const uint8x16_t B = vld1q_u8(&prev[i]);
+ const uint8x16_t C = vaddq_u8(A, B);
+ vst1q_u8(&out[i], C);
+ }
+ for (; i < width; ++i) out[i] = in[i] + prev[i];
+ }
+}
+
+// GradientUnfilter_NEON is correct but slower than the C-version,
+// at least on ARM64. For armv7, it's a wash.
+// So best is to disable it for now, but keep the idea around...
+#if !defined(USE_GRADIENT_UNFILTER)
+#define USE_GRADIENT_UNFILTER 0 // ALTERNATE_CODE
+#endif
+
+#if (USE_GRADIENT_UNFILTER == 1)
+#define GRAD_PROCESS_LANE(L) do { \
+ const uint8x8_t tmp1 = ROTATE_RIGHT_N(pred, 1); /* rotate predictor in */ \
+ const int16x8_t tmp2 = vaddq_s16(BC, U8_TO_S16(tmp1)); \
+ const uint8x8_t delta = vqmovun_s16(tmp2); \
+ pred = vadd_u8(D, delta); \
+ out = vext_u8(out, ROTATE_LEFT_N(pred, (L)), 1); \
+} while (0)
+
+static void GradientPredictInverse_NEON(const uint8_t* const in,
+ const uint8_t* const top,
+ uint8_t* const row, int length) {
+ if (length > 0) {
+ int i;
+ uint8x8_t pred = vdup_n_u8(row[-1]); // left sample
+ uint8x8_t out = vdup_n_u8(0);
+ for (i = 0; i + 8 <= length; i += 8) {
+ const int16x8_t B = LOAD_U8_TO_S16(&top[i + 0]);
+ const int16x8_t C = LOAD_U8_TO_S16(&top[i - 1]);
+ const int16x8_t BC = vsubq_s16(B, C); // unclipped gradient basis B - C
+ const uint8x8_t D = vld1_u8(&in[i]); // base input
+ GRAD_PROCESS_LANE(0);
+ GRAD_PROCESS_LANE(1);
+ GRAD_PROCESS_LANE(2);
+ GRAD_PROCESS_LANE(3);
+ GRAD_PROCESS_LANE(4);
+ GRAD_PROCESS_LANE(5);
+ GRAD_PROCESS_LANE(6);
+ GRAD_PROCESS_LANE(7);
+ vst1_u8(&row[i], out);
+ }
+ for (; i < length; ++i) {
+ row[i] = in[i] + GradientPredictor_C(row[i - 1], top[i], top[i - 1]);
+ }
+ }
+}
+#undef GRAD_PROCESS_LANE
+
+static void GradientUnfilter_NEON(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_NEON(NULL, in, out, width);
+ } else {
+ out[0] = in[0] + prev[0]; // predict from above
+ GradientPredictInverse_NEON(in + 1, prev + 1, out + 1, width - 1);
+ }
+}
+
+#endif // USE_GRADIENT_UNFILTER
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8FiltersInitNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitNEON(void) {
+ WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_NEON;
+ WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_NEON;
+#if (USE_GRADIENT_UNFILTER == 1)
+ WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_NEON;
+#endif
+
+ WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_NEON;
+ WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_NEON;
+ WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(VP8FiltersInitNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/filters_sse2.c b/src/third_party/libwebp/src/dsp/filters_sse2.c
new file mode 100644
index 0000000..5a18895
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/filters_sse2.c
@@ -0,0 +1,333 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 variant of alpha filters
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+
+#include <assert.h>
+#include <emmintrin.h>
+#include <stdlib.h>
+#include <string.h>
+
+//------------------------------------------------------------------------------
+// Helpful macro.
+
+# define SANITY_CHECK(in, out) \
+ assert((in) != NULL); \
+ assert((out) != NULL); \
+ assert(width > 0); \
+ assert(height > 0); \
+ assert(stride >= width); \
+ assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
+ (void)height; // Silence unused warning.
+
+static void PredictLineTop_SSE2(const uint8_t* src, const uint8_t* pred,
+ uint8_t* dst, int length) {
+ int i;
+ const int max_pos = length & ~31;
+ assert(length >= 0);
+ for (i = 0; i < max_pos; i += 32) {
+ const __m128i A0 = _mm_loadu_si128((const __m128i*)&src[i + 0]);
+ const __m128i A1 = _mm_loadu_si128((const __m128i*)&src[i + 16]);
+ const __m128i B0 = _mm_loadu_si128((const __m128i*)&pred[i + 0]);
+ const __m128i B1 = _mm_loadu_si128((const __m128i*)&pred[i + 16]);
+ const __m128i C0 = _mm_sub_epi8(A0, B0);
+ const __m128i C1 = _mm_sub_epi8(A1, B1);
+ _mm_storeu_si128((__m128i*)&dst[i + 0], C0);
+ _mm_storeu_si128((__m128i*)&dst[i + 16], C1);
+ }
+ for (; i < length; ++i) dst[i] = src[i] - pred[i];
+}
+
+// Special case for left-based prediction (when preds==dst-1 or preds==src-1).
+static void PredictLineLeft_SSE2(const uint8_t* src, uint8_t* dst, int length) {
+ int i;
+ const int max_pos = length & ~31;
+ assert(length >= 0);
+ for (i = 0; i < max_pos; i += 32) {
+ const __m128i A0 = _mm_loadu_si128((const __m128i*)(src + i + 0 ));
+ const __m128i B0 = _mm_loadu_si128((const __m128i*)(src + i + 0 - 1));
+ const __m128i A1 = _mm_loadu_si128((const __m128i*)(src + i + 16 ));
+ const __m128i B1 = _mm_loadu_si128((const __m128i*)(src + i + 16 - 1));
+ const __m128i C0 = _mm_sub_epi8(A0, B0);
+ const __m128i C1 = _mm_sub_epi8(A1, B1);
+ _mm_storeu_si128((__m128i*)(dst + i + 0), C0);
+ _mm_storeu_si128((__m128i*)(dst + i + 16), C1);
+ }
+ for (; i < length; ++i) dst[i] = src[i] - src[i - 1];
+}
+
+//------------------------------------------------------------------------------
+// Horizontal filter.
+
+static WEBP_INLINE void DoHorizontalFilter_SSE2(const uint8_t* in,
+ int width, int height,
+ int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+
+ if (row == 0) {
+ // Leftmost pixel is the same as input for topmost scanline.
+ out[0] = in[0];
+ PredictLineLeft_SSE2(in + 1, out + 1, width - 1);
+ row = 1;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ // Leftmost pixel is predicted from above.
+ out[0] = in[0] - in[-stride];
+ PredictLineLeft_SSE2(in + 1, out + 1, width - 1);
+ ++row;
+ in += stride;
+ out += stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Vertical filter.
+
+static WEBP_INLINE void DoVerticalFilter_SSE2(const uint8_t* in,
+ int width, int height, int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+
+ if (row == 0) {
+ // Very first top-left pixel is copied.
+ out[0] = in[0];
+ // Rest of top scan-line is left-predicted.
+ PredictLineLeft_SSE2(in + 1, out + 1, width - 1);
+ row = 1;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ PredictLineTop_SSE2(in, in - stride, out, width);
+ ++row;
+ in += stride;
+ out += stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Gradient filter.
+
+static WEBP_INLINE int GradientPredictor_SSE2(uint8_t a, uint8_t b, uint8_t c) {
+ const int g = a + b - c;
+ return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
+}
+
+static void GradientPredictDirect_SSE2(const uint8_t* const row,
+ const uint8_t* const top,
+ uint8_t* const out, int length) {
+ const int max_pos = length & ~7;
+ int i;
+ const __m128i zero = _mm_setzero_si128();
+ for (i = 0; i < max_pos; i += 8) {
+ const __m128i A0 = _mm_loadl_epi64((const __m128i*)&row[i - 1]);
+ const __m128i B0 = _mm_loadl_epi64((const __m128i*)&top[i]);
+ const __m128i C0 = _mm_loadl_epi64((const __m128i*)&top[i - 1]);
+ const __m128i D = _mm_loadl_epi64((const __m128i*)&row[i]);
+ const __m128i A1 = _mm_unpacklo_epi8(A0, zero);
+ const __m128i B1 = _mm_unpacklo_epi8(B0, zero);
+ const __m128i C1 = _mm_unpacklo_epi8(C0, zero);
+ const __m128i E = _mm_add_epi16(A1, B1);
+ const __m128i F = _mm_sub_epi16(E, C1);
+ const __m128i G = _mm_packus_epi16(F, zero);
+ const __m128i H = _mm_sub_epi8(D, G);
+ _mm_storel_epi64((__m128i*)(out + i), H);
+ }
+ for (; i < length; ++i) {
+ out[i] = row[i] - GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
+ }
+}
+
+static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in,
+ int width, int height, int stride,
+ int row, int num_rows,
+ uint8_t* out) {
+ const size_t start_offset = row * stride;
+ const int last_row = row + num_rows;
+ SANITY_CHECK(in, out);
+ in += start_offset;
+ out += start_offset;
+
+ // left prediction for top scan-line
+ if (row == 0) {
+ out[0] = in[0];
+ PredictLineLeft_SSE2(in + 1, out + 1, width - 1);
+ row = 1;
+ in += stride;
+ out += stride;
+ }
+
+ // Filter line-by-line.
+ while (row < last_row) {
+ out[0] = in[0] - in[-stride];
+ GradientPredictDirect_SSE2(in + 1, in + 1 - stride, out + 1, width - 1);
+ ++row;
+ in += stride;
+ out += stride;
+ }
+}
+
+#undef SANITY_CHECK
+
+//------------------------------------------------------------------------------
+
+static void HorizontalFilter_SSE2(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoHorizontalFilter_SSE2(data, width, height, stride, 0, height,
+ filtered_data);
+}
+
+static void VerticalFilter_SSE2(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoVerticalFilter_SSE2(data, width, height, stride, 0, height, filtered_data);
+}
+
+static void GradientFilter_SSE2(const uint8_t* data, int width, int height,
+ int stride, uint8_t* filtered_data) {
+ DoGradientFilter_SSE2(data, width, height, stride, 0, height, filtered_data);
+}
+
+//------------------------------------------------------------------------------
+// Inverse transforms
+
+static void HorizontalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ int i;
+ __m128i last;
+ out[0] = in[0] + (prev == NULL ? 0 : prev[0]);
+ if (width <= 1) return;
+ last = _mm_set_epi32(0, 0, 0, out[0]);
+ for (i = 1; i + 8 <= width; i += 8) {
+ const __m128i A0 = _mm_loadl_epi64((const __m128i*)(in + i));
+ const __m128i A1 = _mm_add_epi8(A0, last);
+ const __m128i A2 = _mm_slli_si128(A1, 1);
+ const __m128i A3 = _mm_add_epi8(A1, A2);
+ const __m128i A4 = _mm_slli_si128(A3, 2);
+ const __m128i A5 = _mm_add_epi8(A3, A4);
+ const __m128i A6 = _mm_slli_si128(A5, 4);
+ const __m128i A7 = _mm_add_epi8(A5, A6);
+ _mm_storel_epi64((__m128i*)(out + i), A7);
+ last = _mm_srli_epi64(A7, 56);
+ }
+ for (; i < width; ++i) out[i] = in[i] + out[i - 1];
+}
+
+static void VerticalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_SSE2(NULL, in, out, width);
+ } else {
+ int i;
+ const int max_pos = width & ~31;
+ assert(width >= 0);
+ for (i = 0; i < max_pos; i += 32) {
+ const __m128i A0 = _mm_loadu_si128((const __m128i*)&in[i + 0]);
+ const __m128i A1 = _mm_loadu_si128((const __m128i*)&in[i + 16]);
+ const __m128i B0 = _mm_loadu_si128((const __m128i*)&prev[i + 0]);
+ const __m128i B1 = _mm_loadu_si128((const __m128i*)&prev[i + 16]);
+ const __m128i C0 = _mm_add_epi8(A0, B0);
+ const __m128i C1 = _mm_add_epi8(A1, B1);
+ _mm_storeu_si128((__m128i*)&out[i + 0], C0);
+ _mm_storeu_si128((__m128i*)&out[i + 16], C1);
+ }
+ for (; i < width; ++i) out[i] = in[i] + prev[i];
+ }
+}
+
+static void GradientPredictInverse_SSE2(const uint8_t* const in,
+ const uint8_t* const top,
+ uint8_t* const row, int length) {
+ if (length > 0) {
+ int i;
+ const int max_pos = length & ~7;
+ const __m128i zero = _mm_setzero_si128();
+ __m128i A = _mm_set_epi32(0, 0, 0, row[-1]); // left sample
+ for (i = 0; i < max_pos; i += 8) {
+ const __m128i tmp0 = _mm_loadl_epi64((const __m128i*)&top[i]);
+ const __m128i tmp1 = _mm_loadl_epi64((const __m128i*)&top[i - 1]);
+ const __m128i B = _mm_unpacklo_epi8(tmp0, zero);
+ const __m128i C = _mm_unpacklo_epi8(tmp1, zero);
+ const __m128i D = _mm_loadl_epi64((const __m128i*)&in[i]); // base input
+ const __m128i E = _mm_sub_epi16(B, C); // unclipped gradient basis B - C
+ __m128i out = zero; // accumulator for output
+ __m128i mask_hi = _mm_set_epi32(0, 0, 0, 0xff);
+ int k = 8;
+ while (1) {
+ const __m128i tmp3 = _mm_add_epi16(A, E); // delta = A + B - C
+ const __m128i tmp4 = _mm_packus_epi16(tmp3, zero); // saturate delta
+ const __m128i tmp5 = _mm_add_epi8(tmp4, D); // add to in[]
+ A = _mm_and_si128(tmp5, mask_hi); // 1-complement clip
+ out = _mm_or_si128(out, A); // accumulate output
+ if (--k == 0) break;
+ A = _mm_slli_si128(A, 1); // rotate left sample
+ mask_hi = _mm_slli_si128(mask_hi, 1); // rotate mask
+ A = _mm_unpacklo_epi8(A, zero); // convert 8b->16b
+ }
+ A = _mm_srli_si128(A, 7); // prepare left sample for next iteration
+ _mm_storel_epi64((__m128i*)&row[i], out);
+ }
+ for (; i < length; ++i) {
+ row[i] = in[i] + GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
+ }
+ }
+}
+
+static void GradientUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
+ uint8_t* out, int width) {
+ if (prev == NULL) {
+ HorizontalUnfilter_SSE2(NULL, in, out, width);
+ } else {
+ out[0] = in[0] + prev[0]; // predict from above
+ GradientPredictInverse_SSE2(in + 1, prev + 1, out + 1, width - 1);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8FiltersInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitSSE2(void) {
+ WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter_SSE2;
+ WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter_SSE2;
+ WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter_SSE2;
+
+ WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter_SSE2;
+ WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter_SSE2;
+ WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(VP8FiltersInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/lossless.c b/src/third_party/libwebp/src/dsp/lossless.c
new file mode 100644
index 0000000..0053eff
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless.c
@@ -0,0 +1,666 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Image transforms and color space conversion methods for lossless decoder.
+//
+// Authors: Vikas Arora (vikaas.arora@gmail.com)
+// Jyrki Alakuijala (jyrki@google.com)
+// Urvang Joshi (urvang@google.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/math_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#else
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#endif
+
+#include "src/dec/vp8li_dec.h"
+#include "src/utils/endian_inl_utils.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+
+#define MAX_DIFF_COST (1e30f)
+
+//------------------------------------------------------------------------------
+// Image transforms.
+
+static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) {
+ return (((a0 ^ a1) & 0xfefefefeu) >> 1) + (a0 & a1);
+}
+
+static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) {
+ return Average2(Average2(a0, a2), a1);
+}
+
+static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1,
+ uint32_t a2, uint32_t a3) {
+ return Average2(Average2(a0, a1), Average2(a2, a3));
+}
+
+static WEBP_INLINE uint32_t Clip255(uint32_t a) {
+ if (a < 256) {
+ return a;
+ }
+ // return 0, when a is a negative integer.
+ // return 255, when a is positive.
+ return ~a >> 24;
+}
+
+static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) {
+ return Clip255(a + b - c);
+}
+
+static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
+ uint32_t c2) {
+ const int a = AddSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24);
+ const int r = AddSubtractComponentFull((c0 >> 16) & 0xff,
+ (c1 >> 16) & 0xff,
+ (c2 >> 16) & 0xff);
+ const int g = AddSubtractComponentFull((c0 >> 8) & 0xff,
+ (c1 >> 8) & 0xff,
+ (c2 >> 8) & 0xff);
+ const int b = AddSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff);
+ return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b;
+}
+
+static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) {
+ return Clip255(a + (a - b) / 2);
+}
+
+static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
+ uint32_t c2) {
+ const uint32_t ave = Average2(c0, c1);
+ const int a = AddSubtractComponentHalf(ave >> 24, c2 >> 24);
+ const int r = AddSubtractComponentHalf((ave >> 16) & 0xff, (c2 >> 16) & 0xff);
+ const int g = AddSubtractComponentHalf((ave >> 8) & 0xff, (c2 >> 8) & 0xff);
+ const int b = AddSubtractComponentHalf((ave >> 0) & 0xff, (c2 >> 0) & 0xff);
+ return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b;
+}
+
+// gcc <= 4.9 on ARM generates incorrect code in Select() when Sub3() is
+// inlined.
+#if defined(__arm__) && LOCAL_GCC_VERSION <= 0x409
+# define LOCAL_INLINE __attribute__ ((noinline))
+#else
+# define LOCAL_INLINE WEBP_INLINE
+#endif
+
+static LOCAL_INLINE int Sub3(int a, int b, int c) {
+ const int pb = b - c;
+ const int pa = a - c;
+ return abs(pb) - abs(pa);
+}
+
+#undef LOCAL_INLINE
+
+static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
+ const int pa_minus_pb =
+ Sub3((a >> 24) , (b >> 24) , (c >> 24) ) +
+ Sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) +
+ Sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) +
+ Sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff);
+ return (pa_minus_pb <= 0) ? a : b;
+}
+
+//------------------------------------------------------------------------------
+// Predictors
+
+static uint32_t Predictor0_C(uint32_t left, const uint32_t* const top) {
+ (void)top;
+ (void)left;
+ return ARGB_BLACK;
+}
+static uint32_t Predictor1_C(uint32_t left, const uint32_t* const top) {
+ (void)top;
+ return left;
+}
+static uint32_t Predictor2_C(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return top[0];
+}
+static uint32_t Predictor3_C(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return top[1];
+}
+static uint32_t Predictor4_C(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return top[-1];
+}
+static uint32_t Predictor5_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average3(left, top[0], top[1]);
+ return pred;
+}
+static uint32_t Predictor6_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(left, top[-1]);
+ return pred;
+}
+static uint32_t Predictor7_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(left, top[0]);
+ return pred;
+}
+static uint32_t Predictor8_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(top[-1], top[0]);
+ (void)left;
+ return pred;
+}
+static uint32_t Predictor9_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(top[0], top[1]);
+ (void)left;
+ return pred;
+}
+static uint32_t Predictor10_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average4(left, top[-1], top[0], top[1]);
+ return pred;
+}
+static uint32_t Predictor11_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Select(top[0], left, top[-1]);
+ return pred;
+}
+static uint32_t Predictor12_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]);
+ return pred;
+}
+static uint32_t Predictor13_C(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]);
+ return pred;
+}
+
+GENERATE_PREDICTOR_ADD(Predictor0_C, PredictorAdd0_C)
+static void PredictorAdd1_C(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint32_t left = out[-1];
+ for (i = 0; i < num_pixels; ++i) {
+ out[i] = left = VP8LAddPixels(in[i], left);
+ }
+ (void)upper;
+}
+GENERATE_PREDICTOR_ADD(Predictor2_C, PredictorAdd2_C)
+GENERATE_PREDICTOR_ADD(Predictor3_C, PredictorAdd3_C)
+GENERATE_PREDICTOR_ADD(Predictor4_C, PredictorAdd4_C)
+GENERATE_PREDICTOR_ADD(Predictor5_C, PredictorAdd5_C)
+GENERATE_PREDICTOR_ADD(Predictor6_C, PredictorAdd6_C)
+GENERATE_PREDICTOR_ADD(Predictor7_C, PredictorAdd7_C)
+GENERATE_PREDICTOR_ADD(Predictor8_C, PredictorAdd8_C)
+GENERATE_PREDICTOR_ADD(Predictor9_C, PredictorAdd9_C)
+GENERATE_PREDICTOR_ADD(Predictor10_C, PredictorAdd10_C)
+GENERATE_PREDICTOR_ADD(Predictor11_C, PredictorAdd11_C)
+GENERATE_PREDICTOR_ADD(Predictor12_C, PredictorAdd12_C)
+GENERATE_PREDICTOR_ADD(Predictor13_C, PredictorAdd13_C)
+
+//------------------------------------------------------------------------------
+
+// Inverse prediction.
+static void PredictorInverseTransform_C(const VP8LTransform* const transform,
+ int y_start, int y_end,
+ const uint32_t* in, uint32_t* out) {
+ const int width = transform->xsize_;
+ if (y_start == 0) { // First Row follows the L (mode=1) mode.
+ PredictorAdd0_C(in, NULL, 1, out);
+ PredictorAdd1_C(in + 1, NULL, width - 1, out + 1);
+ in += width;
+ out += width;
+ ++y_start;
+ }
+
+ {
+ int y = y_start;
+ const int tile_width = 1 << transform->bits_;
+ const int mask = tile_width - 1;
+ const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_);
+ const uint32_t* pred_mode_base =
+ transform->data_ + (y >> transform->bits_) * tiles_per_row;
+
+ while (y < y_end) {
+ const uint32_t* pred_mode_src = pred_mode_base;
+ int x = 1;
+ // First pixel follows the T (mode=2) mode.
+ PredictorAdd2_C(in, out - width, 1, out);
+ // .. the rest:
+ while (x < width) {
+ const VP8LPredictorAddSubFunc pred_func =
+ VP8LPredictorsAdd[((*pred_mode_src++) >> 8) & 0xf];
+ int x_end = (x & ~mask) + tile_width;
+ if (x_end > width) x_end = width;
+ pred_func(in + x, out + x - width, x_end - x, out + x);
+ x = x_end;
+ }
+ in += width;
+ out += width;
+ ++y;
+ if ((y & mask) == 0) { // Use the same mask, since tiles are squares.
+ pred_mode_base += tiles_per_row;
+ }
+ }
+ }
+}
+
+// Add green to blue and red channels (i.e. perform the inverse transform of
+// 'subtract green').
+void VP8LAddGreenToBlueAndRed_C(const uint32_t* src, int num_pixels,
+ uint32_t* dst) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) {
+ const uint32_t argb = src[i];
+ const uint32_t green = ((argb >> 8) & 0xff);
+ uint32_t red_blue = (argb & 0x00ff00ffu);
+ red_blue += (green << 16) | green;
+ red_blue &= 0x00ff00ffu;
+ dst[i] = (argb & 0xff00ff00u) | red_blue;
+ }
+}
+
+static WEBP_INLINE int ColorTransformDelta(int8_t color_pred,
+ int8_t color) {
+ return ((int)color_pred * color) >> 5;
+}
+
+static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code,
+ VP8LMultipliers* const m) {
+ m->green_to_red_ = (color_code >> 0) & 0xff;
+ m->green_to_blue_ = (color_code >> 8) & 0xff;
+ m->red_to_blue_ = (color_code >> 16) & 0xff;
+}
+
+void VP8LTransformColorInverse_C(const VP8LMultipliers* const m,
+ const uint32_t* src, int num_pixels,
+ uint32_t* dst) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) {
+ const uint32_t argb = src[i];
+ const uint32_t green = argb >> 8;
+ const uint32_t red = argb >> 16;
+ int new_red = red & 0xff;
+ int new_blue = argb & 0xff;
+ new_red += ColorTransformDelta(m->green_to_red_, green);
+ new_red &= 0xff;
+ new_blue += ColorTransformDelta(m->green_to_blue_, green);
+ new_blue += ColorTransformDelta(m->red_to_blue_, new_red);
+ new_blue &= 0xff;
+ dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue);
+ }
+}
+
+// Color space inverse transform.
+static void ColorSpaceInverseTransform_C(const VP8LTransform* const transform,
+ int y_start, int y_end,
+ const uint32_t* src, uint32_t* dst) {
+ const int width = transform->xsize_;
+ const int tile_width = 1 << transform->bits_;
+ const int mask = tile_width - 1;
+ const int safe_width = width & ~mask;
+ const int remaining_width = width - safe_width;
+ const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_);
+ int y = y_start;
+ const uint32_t* pred_row =
+ transform->data_ + (y >> transform->bits_) * tiles_per_row;
+
+ while (y < y_end) {
+ const uint32_t* pred = pred_row;
+ VP8LMultipliers m = { 0, 0, 0 };
+ const uint32_t* const src_safe_end = src + safe_width;
+ const uint32_t* const src_end = src + width;
+ while (src < src_safe_end) {
+ ColorCodeToMultipliers(*pred++, &m);
+ VP8LTransformColorInverse(&m, src, tile_width, dst);
+ src += tile_width;
+ dst += tile_width;
+ }
+ if (src < src_end) { // Left-overs using C-version.
+ ColorCodeToMultipliers(*pred++, &m);
+ VP8LTransformColorInverse(&m, src, remaining_width, dst);
+ src += remaining_width;
+ dst += remaining_width;
+ }
+ ++y;
+ if ((y & mask) == 0) pred_row += tiles_per_row;
+ }
+}
+
+// Separate out pixels packed together using pixel-bundling.
+// We define two methods for ARGB data (uint32_t) and alpha-only data (uint8_t).
+#define COLOR_INDEX_INVERSE(FUNC_NAME, F_NAME, STATIC_DECL, TYPE, BIT_SUFFIX, \
+ GET_INDEX, GET_VALUE) \
+static void F_NAME(const TYPE* src, const uint32_t* const color_map, \
+ TYPE* dst, int y_start, int y_end, int width) { \
+ int y; \
+ for (y = y_start; y < y_end; ++y) { \
+ int x; \
+ for (x = 0; x < width; ++x) { \
+ *dst++ = GET_VALUE(color_map[GET_INDEX(*src++)]); \
+ } \
+ } \
+} \
+STATIC_DECL void FUNC_NAME(const VP8LTransform* const transform, \
+ int y_start, int y_end, const TYPE* src, \
+ TYPE* dst) { \
+ int y; \
+ const int bits_per_pixel = 8 >> transform->bits_; \
+ const int width = transform->xsize_; \
+ const uint32_t* const color_map = transform->data_; \
+ if (bits_per_pixel < 8) { \
+ const int pixels_per_byte = 1 << transform->bits_; \
+ const int count_mask = pixels_per_byte - 1; \
+ const uint32_t bit_mask = (1 << bits_per_pixel) - 1; \
+ for (y = y_start; y < y_end; ++y) { \
+ uint32_t packed_pixels = 0; \
+ int x; \
+ for (x = 0; x < width; ++x) { \
+ /* We need to load fresh 'packed_pixels' once every */ \
+ /* 'pixels_per_byte' increments of x. Fortunately, pixels_per_byte */ \
+ /* is a power of 2, so can just use a mask for that, instead of */ \
+ /* decrementing a counter. */ \
+ if ((x & count_mask) == 0) packed_pixels = GET_INDEX(*src++); \
+ *dst++ = GET_VALUE(color_map[packed_pixels & bit_mask]); \
+ packed_pixels >>= bits_per_pixel; \
+ } \
+ } \
+ } else { \
+ VP8LMapColor##BIT_SUFFIX(src, color_map, dst, y_start, y_end, width); \
+ } \
+}
+
+COLOR_INDEX_INVERSE(ColorIndexInverseTransform_C, MapARGB_C, static,
+ uint32_t, 32b, VP8GetARGBIndex, VP8GetARGBValue)
+COLOR_INDEX_INVERSE(VP8LColorIndexInverseTransformAlpha, MapAlpha_C, ,
+ uint8_t, 8b, VP8GetAlphaIndex, VP8GetAlphaValue)
+
+#undef COLOR_INDEX_INVERSE
+
+void VP8LInverseTransform(const VP8LTransform* const transform,
+ int row_start, int row_end,
+ const uint32_t* const in, uint32_t* const out) {
+ const int width = transform->xsize_;
+ assert(row_start < row_end);
+ assert(row_end <= transform->ysize_);
+ switch (transform->type_) {
+ case SUBTRACT_GREEN:
+ VP8LAddGreenToBlueAndRed(in, (row_end - row_start) * width, out);
+ break;
+ case PREDICTOR_TRANSFORM:
+ PredictorInverseTransform_C(transform, row_start, row_end, in, out);
+ if (row_end != transform->ysize_) {
+ // The last predicted row in this iteration will be the top-pred row
+ // for the first row in next iteration.
+ memcpy(out - width, out + (row_end - row_start - 1) * width,
+ width * sizeof(*out));
+ }
+ break;
+ case CROSS_COLOR_TRANSFORM:
+ ColorSpaceInverseTransform_C(transform, row_start, row_end, in, out);
+ break;
+ case COLOR_INDEXING_TRANSFORM:
+ if (in == out && transform->bits_ > 0) {
+ // Move packed pixels to the end of unpacked region, so that unpacking
+ // can occur seamlessly.
+ // Also, note that this is the only transform that applies on
+ // the effective width of VP8LSubSampleSize(xsize_, bits_). All other
+ // transforms work on effective width of xsize_.
+ const int out_stride = (row_end - row_start) * width;
+ const int in_stride = (row_end - row_start) *
+ VP8LSubSampleSize(transform->xsize_, transform->bits_);
+ uint32_t* const src = out + out_stride - in_stride;
+ memmove(src, out, in_stride * sizeof(*src));
+ ColorIndexInverseTransform_C(transform, row_start, row_end, src, out);
+ } else {
+ ColorIndexInverseTransform_C(transform, row_start, row_end, in, out);
+ }
+ break;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Color space conversion.
+
+static int is_big_endian(void) {
+ static const union {
+ uint16_t w;
+ uint8_t b[2];
+ } tmp = { 1 };
+ return (tmp.b[0] != 1);
+}
+
+void VP8LConvertBGRAToRGB_C(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const src_end = src + num_pixels;
+ while (src < src_end) {
+ const uint32_t argb = *src++;
+ *dst++ = (argb >> 16) & 0xff;
+ *dst++ = (argb >> 8) & 0xff;
+ *dst++ = (argb >> 0) & 0xff;
+ }
+}
+
+void VP8LConvertBGRAToRGBA_C(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const src_end = src + num_pixels;
+ while (src < src_end) {
+ const uint32_t argb = *src++;
+ *dst++ = (argb >> 16) & 0xff;
+ *dst++ = (argb >> 8) & 0xff;
+ *dst++ = (argb >> 0) & 0xff;
+ *dst++ = (argb >> 24) & 0xff;
+ }
+}
+
+void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const src_end = src + num_pixels;
+ while (src < src_end) {
+ const uint32_t argb = *src++;
+ const uint8_t rg = ((argb >> 16) & 0xf0) | ((argb >> 12) & 0xf);
+ const uint8_t ba = ((argb >> 0) & 0xf0) | ((argb >> 28) & 0xf);
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ *dst++ = ba;
+ *dst++ = rg;
+#else
+ *dst++ = rg;
+ *dst++ = ba;
+#endif
+ }
+}
+
+void VP8LConvertBGRAToRGB565_C(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const src_end = src + num_pixels;
+ while (src < src_end) {
+ const uint32_t argb = *src++;
+ const uint8_t rg = ((argb >> 16) & 0xf8) | ((argb >> 13) & 0x7);
+ const uint8_t gb = ((argb >> 5) & 0xe0) | ((argb >> 3) & 0x1f);
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ *dst++ = gb;
+ *dst++ = rg;
+#else
+ *dst++ = rg;
+ *dst++ = gb;
+#endif
+ }
+}
+
+void VP8LConvertBGRAToBGR_C(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const src_end = src + num_pixels;
+ while (src < src_end) {
+ const uint32_t argb = *src++;
+ *dst++ = (argb >> 0) & 0xff;
+ *dst++ = (argb >> 8) & 0xff;
+ *dst++ = (argb >> 16) & 0xff;
+ }
+}
+
+static void CopyOrSwap(const uint32_t* src, int num_pixels, uint8_t* dst,
+ int swap_on_big_endian) {
+ if (is_big_endian() == swap_on_big_endian) {
+ const uint32_t* const src_end = src + num_pixels;
+ while (src < src_end) {
+ const uint32_t argb = *src++;
+ WebPUint32ToMem(dst, BSwap32(argb));
+ dst += sizeof(argb);
+ }
+ } else {
+ memcpy(dst, src, num_pixels * sizeof(*src));
+ }
+}
+
+void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels,
+ WEBP_CSP_MODE out_colorspace, uint8_t* const rgba) {
+ switch (out_colorspace) {
+ case MODE_RGB:
+ VP8LConvertBGRAToRGB(in_data, num_pixels, rgba);
+ break;
+ case MODE_RGBA:
+ VP8LConvertBGRAToRGBA(in_data, num_pixels, rgba);
+ break;
+ case MODE_rgbA:
+ VP8LConvertBGRAToRGBA(in_data, num_pixels, rgba);
+ WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0);
+ break;
+ case MODE_BGR:
+ VP8LConvertBGRAToBGR(in_data, num_pixels, rgba);
+ break;
+ case MODE_BGRA:
+ CopyOrSwap(in_data, num_pixels, rgba, 1);
+ break;
+ case MODE_bgrA:
+ CopyOrSwap(in_data, num_pixels, rgba, 1);
+ WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0);
+ break;
+ case MODE_ARGB:
+ CopyOrSwap(in_data, num_pixels, rgba, 0);
+ break;
+ case MODE_Argb:
+ CopyOrSwap(in_data, num_pixels, rgba, 0);
+ WebPApplyAlphaMultiply(rgba, 1, num_pixels, 1, 0);
+ break;
+ case MODE_RGBA_4444:
+ VP8LConvertBGRAToRGBA4444(in_data, num_pixels, rgba);
+ break;
+ case MODE_rgbA_4444:
+ VP8LConvertBGRAToRGBA4444(in_data, num_pixels, rgba);
+ WebPApplyAlphaMultiply4444(rgba, num_pixels, 1, 0);
+ break;
+ case MODE_RGB_565:
+ VP8LConvertBGRAToRGB565(in_data, num_pixels, rgba);
+ break;
+ default:
+ assert(0); // Code flow should not reach here.
+ }
+}
+
+//------------------------------------------------------------------------------
+
+VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed;
+VP8LPredictorAddSubFunc VP8LPredictorsAdd[16];
+VP8LPredictorFunc VP8LPredictors[16];
+
+// exposed plain-C implementations
+VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16];
+VP8LPredictorFunc VP8LPredictors_C[16];
+
+VP8LTransformColorInverseFunc VP8LTransformColorInverse;
+
+VP8LConvertFunc VP8LConvertBGRAToRGB;
+VP8LConvertFunc VP8LConvertBGRAToRGBA;
+VP8LConvertFunc VP8LConvertBGRAToRGBA4444;
+VP8LConvertFunc VP8LConvertBGRAToRGB565;
+VP8LConvertFunc VP8LConvertBGRAToBGR;
+
+VP8LMapARGBFunc VP8LMapColor32b;
+VP8LMapAlphaFunc VP8LMapColor8b;
+
+extern void VP8LDspInitSSE2(void);
+extern void VP8LDspInitNEON(void);
+extern void VP8LDspInitMIPSdspR2(void);
+extern void VP8LDspInitMSA(void);
+
+#define COPY_PREDICTOR_ARRAY(IN, OUT) do { \
+ (OUT)[0] = IN##0_C; \
+ (OUT)[1] = IN##1_C; \
+ (OUT)[2] = IN##2_C; \
+ (OUT)[3] = IN##3_C; \
+ (OUT)[4] = IN##4_C; \
+ (OUT)[5] = IN##5_C; \
+ (OUT)[6] = IN##6_C; \
+ (OUT)[7] = IN##7_C; \
+ (OUT)[8] = IN##8_C; \
+ (OUT)[9] = IN##9_C; \
+ (OUT)[10] = IN##10_C; \
+ (OUT)[11] = IN##11_C; \
+ (OUT)[12] = IN##12_C; \
+ (OUT)[13] = IN##13_C; \
+ (OUT)[14] = IN##0_C; /* <- padding security sentinels*/ \
+ (OUT)[15] = IN##0_C; \
+} while (0);
+
+WEBP_DSP_INIT_FUNC(VP8LDspInit) {
+ COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors)
+ COPY_PREDICTOR_ARRAY(Predictor, VP8LPredictors_C)
+ COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd)
+ COPY_PREDICTOR_ARRAY(PredictorAdd, VP8LPredictorsAdd_C)
+
+#if !WEBP_NEON_OMIT_C_CODE
+ VP8LAddGreenToBlueAndRed = VP8LAddGreenToBlueAndRed_C;
+
+ VP8LTransformColorInverse = VP8LTransformColorInverse_C;
+
+ VP8LConvertBGRAToRGBA = VP8LConvertBGRAToRGBA_C;
+ VP8LConvertBGRAToRGB = VP8LConvertBGRAToRGB_C;
+ VP8LConvertBGRAToBGR = VP8LConvertBGRAToBGR_C;
+#endif
+
+ VP8LConvertBGRAToRGBA4444 = VP8LConvertBGRAToRGBA4444_C;
+ VP8LConvertBGRAToRGB565 = VP8LConvertBGRAToRGB565_C;
+
+ VP8LMapColor32b = MapARGB_C;
+ VP8LMapColor8b = MapAlpha_C;
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ VP8LDspInitSSE2();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ VP8LDspInitMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_MSA)
+ if (VP8GetCPUInfo(kMSA)) {
+ VP8LDspInitMSA();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ VP8LDspInitNEON();
+ }
+#endif
+
+ assert(VP8LAddGreenToBlueAndRed != NULL);
+ assert(VP8LTransformColorInverse != NULL);
+ assert(VP8LConvertBGRAToRGBA != NULL);
+ assert(VP8LConvertBGRAToRGB != NULL);
+ assert(VP8LConvertBGRAToBGR != NULL);
+ assert(VP8LConvertBGRAToRGBA4444 != NULL);
+ assert(VP8LConvertBGRAToRGB565 != NULL);
+ assert(VP8LMapColor32b != NULL);
+ assert(VP8LMapColor8b != NULL);
+}
+#undef COPY_PREDICTOR_ARRAY
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dsp/lossless.h b/src/third_party/libwebp/src/dsp/lossless.h
new file mode 100644
index 0000000..b2bbdfc
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless.h
@@ -0,0 +1,225 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Image transforms and color space conversion methods for lossless decoder.
+//
+// Authors: Vikas Arora (vikaas.arora@gmail.com)
+// Jyrki Alakuijala (jyrki@google.com)
+
+#ifndef WEBP_DSP_LOSSLESS_H_
+#define WEBP_DSP_LOSSLESS_H_
+
+#include "src/webp/types.h"
+#include "src/webp/decode.h"
+
+#include "src/enc/histogram_enc.h"
+#include "src/utils/utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Decoding
+
+typedef uint32_t (*VP8LPredictorFunc)(uint32_t left, const uint32_t* const top);
+extern VP8LPredictorFunc VP8LPredictors[16];
+extern VP8LPredictorFunc VP8LPredictors_C[16];
+// These Add/Sub function expects upper[-1] and out[-1] to be readable.
+typedef void (*VP8LPredictorAddSubFunc)(const uint32_t* in,
+ const uint32_t* upper, int num_pixels,
+ uint32_t* out);
+extern VP8LPredictorAddSubFunc VP8LPredictorsAdd[16];
+extern VP8LPredictorAddSubFunc VP8LPredictorsAdd_C[16];
+
+typedef void (*VP8LProcessDecBlueAndRedFunc)(const uint32_t* src,
+ int num_pixels, uint32_t* dst);
+extern VP8LProcessDecBlueAndRedFunc VP8LAddGreenToBlueAndRed;
+
+typedef struct {
+ // Note: the members are uint8_t, so that any negative values are
+ // automatically converted to "mod 256" values.
+ uint8_t green_to_red_;
+ uint8_t green_to_blue_;
+ uint8_t red_to_blue_;
+} VP8LMultipliers;
+typedef void (*VP8LTransformColorInverseFunc)(const VP8LMultipliers* const m,
+ const uint32_t* src,
+ int num_pixels, uint32_t* dst);
+extern VP8LTransformColorInverseFunc VP8LTransformColorInverse;
+
+struct VP8LTransform; // Defined in dec/vp8li.h.
+
+// Performs inverse transform of data given transform information, start and end
+// rows. Transform will be applied to rows [row_start, row_end[.
+// The *in and *out pointers refer to source and destination data respectively
+// corresponding to the intermediate row (row_start).
+void VP8LInverseTransform(const struct VP8LTransform* const transform,
+ int row_start, int row_end,
+ const uint32_t* const in, uint32_t* const out);
+
+// Color space conversion.
+typedef void (*VP8LConvertFunc)(const uint32_t* src, int num_pixels,
+ uint8_t* dst);
+extern VP8LConvertFunc VP8LConvertBGRAToRGB;
+extern VP8LConvertFunc VP8LConvertBGRAToRGBA;
+extern VP8LConvertFunc VP8LConvertBGRAToRGBA4444;
+extern VP8LConvertFunc VP8LConvertBGRAToRGB565;
+extern VP8LConvertFunc VP8LConvertBGRAToBGR;
+
+// Converts from BGRA to other color spaces.
+void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels,
+ WEBP_CSP_MODE out_colorspace, uint8_t* const rgba);
+
+typedef void (*VP8LMapARGBFunc)(const uint32_t* src,
+ const uint32_t* const color_map,
+ uint32_t* dst, int y_start,
+ int y_end, int width);
+typedef void (*VP8LMapAlphaFunc)(const uint8_t* src,
+ const uint32_t* const color_map,
+ uint8_t* dst, int y_start,
+ int y_end, int width);
+
+extern VP8LMapARGBFunc VP8LMapColor32b;
+extern VP8LMapAlphaFunc VP8LMapColor8b;
+
+// Similar to the static method ColorIndexInverseTransform() that is part of
+// lossless.c, but used only for alpha decoding. It takes uint8_t (rather than
+// uint32_t) arguments for 'src' and 'dst'.
+void VP8LColorIndexInverseTransformAlpha(
+ const struct VP8LTransform* const transform, int y_start, int y_end,
+ const uint8_t* src, uint8_t* dst);
+
+// Expose some C-only fallback functions
+void VP8LTransformColorInverse_C(const VP8LMultipliers* const m,
+ const uint32_t* src, int num_pixels,
+ uint32_t* dst);
+
+void VP8LConvertBGRAToRGB_C(const uint32_t* src, int num_pixels, uint8_t* dst);
+void VP8LConvertBGRAToRGBA_C(const uint32_t* src, int num_pixels, uint8_t* dst);
+void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src,
+ int num_pixels, uint8_t* dst);
+void VP8LConvertBGRAToRGB565_C(const uint32_t* src,
+ int num_pixels, uint8_t* dst);
+void VP8LConvertBGRAToBGR_C(const uint32_t* src, int num_pixels, uint8_t* dst);
+void VP8LAddGreenToBlueAndRed_C(const uint32_t* src, int num_pixels,
+ uint32_t* dst);
+
+// Must be called before calling any of the above methods.
+void VP8LDspInit(void);
+
+//------------------------------------------------------------------------------
+// Encoding
+
+typedef void (*VP8LProcessEncBlueAndRedFunc)(uint32_t* dst, int num_pixels);
+extern VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed;
+typedef void (*VP8LTransformColorFunc)(const VP8LMultipliers* const m,
+ uint32_t* dst, int num_pixels);
+extern VP8LTransformColorFunc VP8LTransformColor;
+typedef void (*VP8LCollectColorBlueTransformsFunc)(
+ const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_blue, int red_to_blue, int histo[]);
+extern VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms;
+
+typedef void (*VP8LCollectColorRedTransformsFunc)(
+ const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_red, int histo[]);
+extern VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms;
+
+// Expose some C-only fallback functions
+void VP8LTransformColor_C(const VP8LMultipliers* const m,
+ uint32_t* data, int num_pixels);
+void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels);
+void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_red, int histo[]);
+void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_blue, int red_to_blue,
+ int histo[]);
+
+extern VP8LPredictorAddSubFunc VP8LPredictorsSub[16];
+extern VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16];
+
+// -----------------------------------------------------------------------------
+// Huffman-cost related functions.
+
+typedef double (*VP8LCostFunc)(const uint32_t* population, int length);
+typedef double (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y,
+ int length);
+typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256],
+ const int Y[256]);
+
+extern VP8LCostFunc VP8LExtraCost;
+extern VP8LCostCombinedFunc VP8LExtraCostCombined;
+extern VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy;
+
+typedef struct { // small struct to hold counters
+ int counts[2]; // index: 0=zero steak, 1=non-zero streak
+ int streaks[2][2]; // [zero/non-zero][streak<3 / streak>=3]
+} VP8LStreaks;
+
+typedef struct { // small struct to hold bit entropy results
+ double entropy; // entropy
+ uint32_t sum; // sum of the population
+ int nonzeros; // number of non-zero elements in the population
+ uint32_t max_val; // maximum value in the population
+ uint32_t nonzero_code; // index of the last non-zero in the population
+} VP8LBitEntropy;
+
+void VP8LBitEntropyInit(VP8LBitEntropy* const entropy);
+
+// Get the combined symbol bit entropy and Huffman cost stats for the
+// distributions 'X' and 'Y'. Those results can then be refined according to
+// codec specific heuristics.
+typedef void (*VP8LGetCombinedEntropyUnrefinedFunc)(
+ const uint32_t X[], const uint32_t Y[], int length,
+ VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats);
+extern VP8LGetCombinedEntropyUnrefinedFunc VP8LGetCombinedEntropyUnrefined;
+
+// Get the entropy for the distribution 'X'.
+typedef void (*VP8LGetEntropyUnrefinedFunc)(const uint32_t X[], int length,
+ VP8LBitEntropy* const bit_entropy,
+ VP8LStreaks* const stats);
+extern VP8LGetEntropyUnrefinedFunc VP8LGetEntropyUnrefined;
+
+void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n,
+ VP8LBitEntropy* const entropy);
+
+typedef void (*VP8LHistogramAddFunc)(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ VP8LHistogram* const out);
+extern VP8LHistogramAddFunc VP8LHistogramAdd;
+
+// -----------------------------------------------------------------------------
+// PrefixEncode()
+
+typedef int (*VP8LVectorMismatchFunc)(const uint32_t* const array1,
+ const uint32_t* const array2, int length);
+// Returns the first index where array1 and array2 are different.
+extern VP8LVectorMismatchFunc VP8LVectorMismatch;
+
+typedef void (*VP8LBundleColorMapFunc)(const uint8_t* const row, int width,
+ int xbits, uint32_t* dst);
+extern VP8LBundleColorMapFunc VP8LBundleColorMap;
+void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits,
+ uint32_t* dst);
+
+// Must be called before calling any of the above methods.
+void VP8LEncDspInit(void);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_DSP_LOSSLESS_H_
diff --git a/src/third_party/libwebp/src/dsp/lossless_common.h b/src/third_party/libwebp/src/dsp/lossless_common.h
new file mode 100644
index 0000000..a2648d1
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_common.h
@@ -0,0 +1,202 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Image transforms and color space conversion methods for lossless decoder.
+//
+// Authors: Vikas Arora (vikaas.arora@gmail.com)
+// Jyrki Alakuijala (jyrki@google.com)
+// Vincent Rabaud (vrabaud@google.com)
+
+#ifndef WEBP_DSP_LOSSLESS_COMMON_H_
+#define WEBP_DSP_LOSSLESS_COMMON_H_
+
+#include "src/webp/types.h"
+
+#include "src/utils/utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Decoding
+
+// color mapping related functions.
+static WEBP_INLINE uint32_t VP8GetARGBIndex(uint32_t idx) {
+ return (idx >> 8) & 0xff;
+}
+
+static WEBP_INLINE uint8_t VP8GetAlphaIndex(uint8_t idx) {
+ return idx;
+}
+
+static WEBP_INLINE uint32_t VP8GetARGBValue(uint32_t val) {
+ return val;
+}
+
+static WEBP_INLINE uint8_t VP8GetAlphaValue(uint32_t val) {
+ return (val >> 8) & 0xff;
+}
+
+//------------------------------------------------------------------------------
+// Misc methods.
+
+// Computes sampled size of 'size' when sampling using 'sampling bits'.
+static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size,
+ uint32_t sampling_bits) {
+ return (size + (1 << sampling_bits) - 1) >> sampling_bits;
+}
+
+// Converts near lossless quality into max number of bits shaved off.
+static WEBP_INLINE int VP8LNearLosslessBits(int near_lossless_quality) {
+ // 100 -> 0
+ // 80..99 -> 1
+ // 60..79 -> 2
+ // 40..59 -> 3
+ // 20..39 -> 4
+ // 0..19 -> 5
+ return 5 - near_lossless_quality / 20;
+}
+
+// -----------------------------------------------------------------------------
+// Faster logarithm for integers. Small values use a look-up table.
+
+// The threshold till approximate version of log_2 can be used.
+// Practically, we can get rid of the call to log() as the two values match to
+// very high degree (the ratio of these two is 0.99999x).
+// Keeping a high threshold for now.
+#define APPROX_LOG_WITH_CORRECTION_MAX 65536
+#define APPROX_LOG_MAX 4096
+#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086
+#define LOG_LOOKUP_IDX_MAX 256
+extern const float kLog2Table[LOG_LOOKUP_IDX_MAX];
+extern const float kSLog2Table[LOG_LOOKUP_IDX_MAX];
+typedef float (*VP8LFastLog2SlowFunc)(uint32_t v);
+
+extern VP8LFastLog2SlowFunc VP8LFastLog2Slow;
+extern VP8LFastLog2SlowFunc VP8LFastSLog2Slow;
+
+static WEBP_INLINE float VP8LFastLog2(uint32_t v) {
+ return (v < LOG_LOOKUP_IDX_MAX) ? kLog2Table[v] : VP8LFastLog2Slow(v);
+}
+// Fast calculation of v * log2(v) for integer input.
+static WEBP_INLINE float VP8LFastSLog2(uint32_t v) {
+ return (v < LOG_LOOKUP_IDX_MAX) ? kSLog2Table[v] : VP8LFastSLog2Slow(v);
+}
+
+// -----------------------------------------------------------------------------
+// PrefixEncode()
+
+// Splitting of distance and length codes into prefixes and
+// extra bits. The prefixes are encoded with an entropy code
+// while the extra bits are stored just as normal bits.
+static WEBP_INLINE void VP8LPrefixEncodeBitsNoLUT(int distance, int* const code,
+ int* const extra_bits) {
+ const int highest_bit = BitsLog2Floor(--distance);
+ const int second_highest_bit = (distance >> (highest_bit - 1)) & 1;
+ *extra_bits = highest_bit - 1;
+ *code = 2 * highest_bit + second_highest_bit;
+}
+
+static WEBP_INLINE void VP8LPrefixEncodeNoLUT(int distance, int* const code,
+ int* const extra_bits,
+ int* const extra_bits_value) {
+ const int highest_bit = BitsLog2Floor(--distance);
+ const int second_highest_bit = (distance >> (highest_bit - 1)) & 1;
+ *extra_bits = highest_bit - 1;
+ *extra_bits_value = distance & ((1 << *extra_bits) - 1);
+ *code = 2 * highest_bit + second_highest_bit;
+}
+
+#define PREFIX_LOOKUP_IDX_MAX 512
+typedef struct {
+ int8_t code_;
+ int8_t extra_bits_;
+} VP8LPrefixCode;
+
+// These tables are derived using VP8LPrefixEncodeNoLUT.
+extern const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX];
+extern const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX];
+static WEBP_INLINE void VP8LPrefixEncodeBits(int distance, int* const code,
+ int* const extra_bits) {
+ if (distance < PREFIX_LOOKUP_IDX_MAX) {
+ const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance];
+ *code = prefix_code.code_;
+ *extra_bits = prefix_code.extra_bits_;
+ } else {
+ VP8LPrefixEncodeBitsNoLUT(distance, code, extra_bits);
+ }
+}
+
+static WEBP_INLINE void VP8LPrefixEncode(int distance, int* const code,
+ int* const extra_bits,
+ int* const extra_bits_value) {
+ if (distance < PREFIX_LOOKUP_IDX_MAX) {
+ const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance];
+ *code = prefix_code.code_;
+ *extra_bits = prefix_code.extra_bits_;
+ *extra_bits_value = kPrefixEncodeExtraBitsValue[distance];
+ } else {
+ VP8LPrefixEncodeNoLUT(distance, code, extra_bits, extra_bits_value);
+ }
+}
+
+// Sum of each component, mod 256.
+static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
+uint32_t VP8LAddPixels(uint32_t a, uint32_t b) {
+ const uint32_t alpha_and_green = (a & 0xff00ff00u) + (b & 0xff00ff00u);
+ const uint32_t red_and_blue = (a & 0x00ff00ffu) + (b & 0x00ff00ffu);
+ return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu);
+}
+
+// Difference of each component, mod 256.
+static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
+uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
+ const uint32_t alpha_and_green =
+ 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u);
+ const uint32_t red_and_blue =
+ 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu);
+ return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu);
+}
+
+//------------------------------------------------------------------------------
+// Transform-related functions use din both encoding and decoding.
+
+// Macros used to create a batch predictor that iteratively uses a
+// one-pixel predictor.
+
+// The predictor is added to the output pixel (which
+// is therefore considered as a residual) to get the final prediction.
+#define GENERATE_PREDICTOR_ADD(PREDICTOR, PREDICTOR_ADD) \
+static void PREDICTOR_ADD(const uint32_t* in, const uint32_t* upper, \
+ int num_pixels, uint32_t* out) { \
+ int x; \
+ for (x = 0; x < num_pixels; ++x) { \
+ const uint32_t pred = (PREDICTOR)(out[x - 1], upper + x); \
+ out[x] = VP8LAddPixels(in[x], pred); \
+ } \
+}
+
+// It subtracts the prediction from the input pixel and stores the residual
+// in the output pixel.
+#define GENERATE_PREDICTOR_SUB(PREDICTOR, PREDICTOR_SUB) \
+static void PREDICTOR_SUB(const uint32_t* in, const uint32_t* upper, \
+ int num_pixels, uint32_t* out) { \
+ int x; \
+ for (x = 0; x < num_pixels; ++x) { \
+ const uint32_t pred = (PREDICTOR)(in[x - 1], upper + x); \
+ out[x] = VP8LSubPixels(in[x], pred); \
+ } \
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_DSP_LOSSLESS_COMMON_H_
diff --git a/src/third_party/libwebp/src/dsp/lossless_enc.c b/src/third_party/libwebp/src/dsp/lossless_enc.c
new file mode 100644
index 0000000..d608326
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_enc.c
@@ -0,0 +1,1011 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Image transform methods for lossless encoder.
+//
+// Authors: Vikas Arora (vikaas.arora@gmail.com)
+// Jyrki Alakuijala (jyrki@google.com)
+// Urvang Joshi (urvang@google.com)
+
+#include "src/dsp/dsp.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include "src/dec/vp8li_dec.h"
+#include "src/utils/endian_inl_utils.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+#include "src/dsp/yuv.h"
+
+// lookup table for small values of log2(int)
+const float kLog2Table[LOG_LOOKUP_IDX_MAX] = {
+ 0.0000000000000000f, 0.0000000000000000f,
+ 1.0000000000000000f, 1.5849625007211560f,
+ 2.0000000000000000f, 2.3219280948873621f,
+ 2.5849625007211560f, 2.8073549220576041f,
+ 3.0000000000000000f, 3.1699250014423121f,
+ 3.3219280948873621f, 3.4594316186372973f,
+ 3.5849625007211560f, 3.7004397181410921f,
+ 3.8073549220576041f, 3.9068905956085187f,
+ 4.0000000000000000f, 4.0874628412503390f,
+ 4.1699250014423121f, 4.2479275134435852f,
+ 4.3219280948873626f, 4.3923174227787606f,
+ 4.4594316186372973f, 4.5235619560570130f,
+ 4.5849625007211560f, 4.6438561897747243f,
+ 4.7004397181410917f, 4.7548875021634682f,
+ 4.8073549220576037f, 4.8579809951275718f,
+ 4.9068905956085187f, 4.9541963103868749f,
+ 5.0000000000000000f, 5.0443941193584533f,
+ 5.0874628412503390f, 5.1292830169449663f,
+ 5.1699250014423121f, 5.2094533656289501f,
+ 5.2479275134435852f, 5.2854022188622487f,
+ 5.3219280948873626f, 5.3575520046180837f,
+ 5.3923174227787606f, 5.4262647547020979f,
+ 5.4594316186372973f, 5.4918530963296747f,
+ 5.5235619560570130f, 5.5545888516776376f,
+ 5.5849625007211560f, 5.6147098441152083f,
+ 5.6438561897747243f, 5.6724253419714951f,
+ 5.7004397181410917f, 5.7279204545631987f,
+ 5.7548875021634682f, 5.7813597135246599f,
+ 5.8073549220576037f, 5.8328900141647412f,
+ 5.8579809951275718f, 5.8826430493618415f,
+ 5.9068905956085187f, 5.9307373375628866f,
+ 5.9541963103868749f, 5.9772799234999167f,
+ 6.0000000000000000f, 6.0223678130284543f,
+ 6.0443941193584533f, 6.0660891904577720f,
+ 6.0874628412503390f, 6.1085244567781691f,
+ 6.1292830169449663f, 6.1497471195046822f,
+ 6.1699250014423121f, 6.1898245588800175f,
+ 6.2094533656289501f, 6.2288186904958804f,
+ 6.2479275134435852f, 6.2667865406949010f,
+ 6.2854022188622487f, 6.3037807481771030f,
+ 6.3219280948873626f, 6.3398500028846243f,
+ 6.3575520046180837f, 6.3750394313469245f,
+ 6.3923174227787606f, 6.4093909361377017f,
+ 6.4262647547020979f, 6.4429434958487279f,
+ 6.4594316186372973f, 6.4757334309663976f,
+ 6.4918530963296747f, 6.5077946401986963f,
+ 6.5235619560570130f, 6.5391588111080309f,
+ 6.5545888516776376f, 6.5698556083309478f,
+ 6.5849625007211560f, 6.5999128421871278f,
+ 6.6147098441152083f, 6.6293566200796094f,
+ 6.6438561897747243f, 6.6582114827517946f,
+ 6.6724253419714951f, 6.6865005271832185f,
+ 6.7004397181410917f, 6.7142455176661224f,
+ 6.7279204545631987f, 6.7414669864011464f,
+ 6.7548875021634682f, 6.7681843247769259f,
+ 6.7813597135246599f, 6.7944158663501061f,
+ 6.8073549220576037f, 6.8201789624151878f,
+ 6.8328900141647412f, 6.8454900509443747f,
+ 6.8579809951275718f, 6.8703647195834047f,
+ 6.8826430493618415f, 6.8948177633079437f,
+ 6.9068905956085187f, 6.9188632372745946f,
+ 6.9307373375628866f, 6.9425145053392398f,
+ 6.9541963103868749f, 6.9657842846620869f,
+ 6.9772799234999167f, 6.9886846867721654f,
+ 7.0000000000000000f, 7.0112272554232539f,
+ 7.0223678130284543f, 7.0334230015374501f,
+ 7.0443941193584533f, 7.0552824355011898f,
+ 7.0660891904577720f, 7.0768155970508308f,
+ 7.0874628412503390f, 7.0980320829605263f,
+ 7.1085244567781691f, 7.1189410727235076f,
+ 7.1292830169449663f, 7.1395513523987936f,
+ 7.1497471195046822f, 7.1598713367783890f,
+ 7.1699250014423121f, 7.1799090900149344f,
+ 7.1898245588800175f, 7.1996723448363644f,
+ 7.2094533656289501f, 7.2191685204621611f,
+ 7.2288186904958804f, 7.2384047393250785f,
+ 7.2479275134435852f, 7.2573878426926521f,
+ 7.2667865406949010f, 7.2761244052742375f,
+ 7.2854022188622487f, 7.2946207488916270f,
+ 7.3037807481771030f, 7.3128829552843557f,
+ 7.3219280948873626f, 7.3309168781146167f,
+ 7.3398500028846243f, 7.3487281542310771f,
+ 7.3575520046180837f, 7.3663222142458160f,
+ 7.3750394313469245f, 7.3837042924740519f,
+ 7.3923174227787606f, 7.4008794362821843f,
+ 7.4093909361377017f, 7.4178525148858982f,
+ 7.4262647547020979f, 7.4346282276367245f,
+ 7.4429434958487279f, 7.4512111118323289f,
+ 7.4594316186372973f, 7.4676055500829976f,
+ 7.4757334309663976f, 7.4838157772642563f,
+ 7.4918530963296747f, 7.4998458870832056f,
+ 7.5077946401986963f, 7.5156998382840427f,
+ 7.5235619560570130f, 7.5313814605163118f,
+ 7.5391588111080309f, 7.5468944598876364f,
+ 7.5545888516776376f, 7.5622424242210728f,
+ 7.5698556083309478f, 7.5774288280357486f,
+ 7.5849625007211560f, 7.5924570372680806f,
+ 7.5999128421871278f, 7.6073303137496104f,
+ 7.6147098441152083f, 7.6220518194563764f,
+ 7.6293566200796094f, 7.6366246205436487f,
+ 7.6438561897747243f, 7.6510516911789281f,
+ 7.6582114827517946f, 7.6653359171851764f,
+ 7.6724253419714951f, 7.6794800995054464f,
+ 7.6865005271832185f, 7.6934869574993252f,
+ 7.7004397181410917f, 7.7073591320808825f,
+ 7.7142455176661224f, 7.7210991887071855f,
+ 7.7279204545631987f, 7.7347096202258383f,
+ 7.7414669864011464f, 7.7481928495894605f,
+ 7.7548875021634682f, 7.7615512324444795f,
+ 7.7681843247769259f, 7.7747870596011736f,
+ 7.7813597135246599f, 7.7879025593914317f,
+ 7.7944158663501061f, 7.8008998999203047f,
+ 7.8073549220576037f, 7.8137811912170374f,
+ 7.8201789624151878f, 7.8265484872909150f,
+ 7.8328900141647412f, 7.8392037880969436f,
+ 7.8454900509443747f, 7.8517490414160571f,
+ 7.8579809951275718f, 7.8641861446542797f,
+ 7.8703647195834047f, 7.8765169465649993f,
+ 7.8826430493618415f, 7.8887432488982591f,
+ 7.8948177633079437f, 7.9008668079807486f,
+ 7.9068905956085187f, 7.9128893362299619f,
+ 7.9188632372745946f, 7.9248125036057812f,
+ 7.9307373375628866f, 7.9366379390025709f,
+ 7.9425145053392398f, 7.9483672315846778f,
+ 7.9541963103868749f, 7.9600019320680805f,
+ 7.9657842846620869f, 7.9715435539507719f,
+ 7.9772799234999167f, 7.9829935746943103f,
+ 7.9886846867721654f, 7.9943534368588577f
+};
+
+const float kSLog2Table[LOG_LOOKUP_IDX_MAX] = {
+ 0.00000000f, 0.00000000f, 2.00000000f, 4.75488750f,
+ 8.00000000f, 11.60964047f, 15.50977500f, 19.65148445f,
+ 24.00000000f, 28.52932501f, 33.21928095f, 38.05374781f,
+ 43.01955001f, 48.10571634f, 53.30296891f, 58.60335893f,
+ 64.00000000f, 69.48686830f, 75.05865003f, 80.71062276f,
+ 86.43856190f, 92.23866588f, 98.10749561f, 104.04192499f,
+ 110.03910002f, 116.09640474f, 122.21143267f, 128.38196256f,
+ 134.60593782f, 140.88144886f, 147.20671787f, 153.58008562f,
+ 160.00000000f, 166.46500594f, 172.97373660f, 179.52490559f,
+ 186.11730005f, 192.74977453f, 199.42124551f, 206.13068654f,
+ 212.87712380f, 219.65963219f, 226.47733176f, 233.32938445f,
+ 240.21499122f, 247.13338933f, 254.08384998f, 261.06567603f,
+ 268.07820003f, 275.12078236f, 282.19280949f, 289.29369244f,
+ 296.42286534f, 303.57978409f, 310.76392512f, 317.97478424f,
+ 325.21187564f, 332.47473081f, 339.76289772f, 347.07593991f,
+ 354.41343574f, 361.77497759f, 369.16017124f, 376.56863518f,
+ 384.00000000f, 391.45390785f, 398.93001188f, 406.42797576f,
+ 413.94747321f, 421.48818752f, 429.04981119f, 436.63204548f,
+ 444.23460010f, 451.85719280f, 459.49954906f, 467.16140179f,
+ 474.84249102f, 482.54256363f, 490.26137307f, 497.99867911f,
+ 505.75424759f, 513.52785023f, 521.31926438f, 529.12827280f,
+ 536.95466351f, 544.79822957f, 552.65876890f, 560.53608414f,
+ 568.42998244f, 576.34027536f, 584.26677867f, 592.20931226f,
+ 600.16769996f, 608.14176943f, 616.13135206f, 624.13628279f,
+ 632.15640007f, 640.19154569f, 648.24156472f, 656.30630539f,
+ 664.38561898f, 672.47935976f, 680.58738488f, 688.70955430f,
+ 696.84573069f, 704.99577935f, 713.15956818f, 721.33696754f,
+ 729.52785023f, 737.73209140f, 745.94956849f, 754.18016116f,
+ 762.42375127f, 770.68022275f, 778.94946161f, 787.23135586f,
+ 795.52579543f, 803.83267219f, 812.15187982f, 820.48331383f,
+ 828.82687147f, 837.18245171f, 845.54995518f, 853.92928416f,
+ 862.32034249f, 870.72303558f, 879.13727036f, 887.56295522f,
+ 896.00000000f, 904.44831595f, 912.90781569f, 921.37841320f,
+ 929.86002376f, 938.35256392f, 946.85595152f, 955.37010560f,
+ 963.89494641f, 972.43039537f, 980.97637504f, 989.53280911f,
+ 998.09962237f, 1006.67674069f, 1015.26409097f, 1023.86160116f,
+ 1032.46920021f, 1041.08681805f, 1049.71438560f, 1058.35183469f,
+ 1066.99909811f, 1075.65610955f, 1084.32280357f, 1092.99911564f,
+ 1101.68498204f, 1110.38033993f, 1119.08512727f, 1127.79928282f,
+ 1136.52274614f, 1145.25545758f, 1153.99735821f, 1162.74838989f,
+ 1171.50849518f, 1180.27761738f, 1189.05570047f, 1197.84268914f,
+ 1206.63852876f, 1215.44316535f, 1224.25654560f, 1233.07861684f,
+ 1241.90932703f, 1250.74862473f, 1259.59645914f, 1268.45278005f,
+ 1277.31753781f, 1286.19068338f, 1295.07216828f, 1303.96194457f,
+ 1312.85996488f, 1321.76618236f, 1330.68055071f, 1339.60302413f,
+ 1348.53355734f, 1357.47210556f, 1366.41862452f, 1375.37307041f,
+ 1384.33539991f, 1393.30557020f, 1402.28353887f, 1411.26926400f,
+ 1420.26270412f, 1429.26381818f, 1438.27256558f, 1447.28890615f,
+ 1456.31280014f, 1465.34420819f, 1474.38309138f, 1483.42941118f,
+ 1492.48312945f, 1501.54420843f, 1510.61261078f, 1519.68829949f,
+ 1528.77123795f, 1537.86138993f, 1546.95871952f, 1556.06319119f,
+ 1565.17476976f, 1574.29342040f, 1583.41910860f, 1592.55180020f,
+ 1601.69146137f, 1610.83805860f, 1619.99155871f, 1629.15192882f,
+ 1638.31913637f, 1647.49314911f, 1656.67393509f, 1665.86146266f,
+ 1675.05570047f, 1684.25661744f, 1693.46418280f, 1702.67836605f,
+ 1711.89913698f, 1721.12646563f, 1730.36032233f, 1739.60067768f,
+ 1748.84750254f, 1758.10076802f, 1767.36044551f, 1776.62650662f,
+ 1785.89892323f, 1795.17766747f, 1804.46271172f, 1813.75402857f,
+ 1823.05159087f, 1832.35537170f, 1841.66534438f, 1850.98148244f,
+ 1860.30375965f, 1869.63214999f, 1878.96662767f, 1888.30716711f,
+ 1897.65374295f, 1907.00633003f, 1916.36490342f, 1925.72943838f,
+ 1935.09991037f, 1944.47629506f, 1953.85856831f, 1963.24670620f,
+ 1972.64068498f, 1982.04048108f, 1991.44607117f, 2000.85743204f,
+ 2010.27454072f, 2019.69737440f, 2029.12591044f, 2038.56012640f
+};
+
+const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX] = {
+ { 0, 0}, { 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 4, 1}, { 4, 1}, { 5, 1},
+ { 5, 1}, { 6, 2}, { 6, 2}, { 6, 2}, { 6, 2}, { 7, 2}, { 7, 2}, { 7, 2},
+ { 7, 2}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3}, { 8, 3},
+ { 8, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3}, { 9, 3},
+ { 9, 3}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4},
+ {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4}, {10, 4},
+ {10, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4},
+ {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4}, {11, 4},
+ {11, 4}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5},
+ {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5},
+ {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5},
+ {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5}, {12, 5},
+ {12, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5},
+ {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5},
+ {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5},
+ {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5}, {13, 5},
+ {13, 5}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {14, 6},
+ {14, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6},
+ {15, 6}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7}, {16, 7},
+ {16, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+ {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7}, {17, 7},
+};
+
+const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX] = {
+ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3,
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+ 127,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126
+};
+
+static float FastSLog2Slow_C(uint32_t v) {
+ assert(v >= LOG_LOOKUP_IDX_MAX);
+ if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
+ int log_cnt = 0;
+ uint32_t y = 1;
+ int correction = 0;
+ const float v_f = (float)v;
+ const uint32_t orig_v = v;
+ do {
+ ++log_cnt;
+ v = v >> 1;
+ y = y << 1;
+ } while (v >= LOG_LOOKUP_IDX_MAX);
+ // vf = (2^log_cnt) * Xf; where y = 2^log_cnt and Xf < 256
+ // Xf = floor(Xf) * (1 + (v % y) / v)
+ // log2(Xf) = log2(floor(Xf)) + log2(1 + (v % y) / v)
+ // The correction factor: log(1 + d) ~ d; for very small d values, so
+ // log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v
+ // LOG_2_RECIPROCAL ~ 23/16
+ correction = (23 * (orig_v & (y - 1))) >> 4;
+ return v_f * (kLog2Table[v] + log_cnt) + correction;
+ } else {
+ return (float)(LOG_2_RECIPROCAL * v * log((double)v));
+ }
+}
+
+static float FastLog2Slow_C(uint32_t v) {
+ assert(v >= LOG_LOOKUP_IDX_MAX);
+ if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
+ int log_cnt = 0;
+ uint32_t y = 1;
+ const uint32_t orig_v = v;
+ double log_2;
+ do {
+ ++log_cnt;
+ v = v >> 1;
+ y = y << 1;
+ } while (v >= LOG_LOOKUP_IDX_MAX);
+ log_2 = kLog2Table[v] + log_cnt;
+ if (orig_v >= APPROX_LOG_MAX) {
+ // Since the division is still expensive, add this correction factor only
+ // for large values of 'v'.
+ const int correction = (23 * (orig_v & (y - 1))) >> 4;
+ log_2 += (double)correction / orig_v;
+ }
+ return (float)log_2;
+ } else {
+ return (float)(LOG_2_RECIPROCAL * log((double)v));
+ }
+}
+
+//------------------------------------------------------------------------------
+// Methods to calculate Entropy (Shannon).
+
+// Compute the combined Shanon's entropy for distribution {X} and {X+Y}
+static float CombinedShannonEntropy_C(const int X[256], const int Y[256]) {
+ int i;
+ double retval = 0.;
+ int sumX = 0, sumXY = 0;
+ for (i = 0; i < 256; ++i) {
+ const int x = X[i];
+ if (x != 0) {
+ const int xy = x + Y[i];
+ sumX += x;
+ retval -= VP8LFastSLog2(x);
+ sumXY += xy;
+ retval -= VP8LFastSLog2(xy);
+ } else if (Y[i] != 0) {
+ sumXY += Y[i];
+ retval -= VP8LFastSLog2(Y[i]);
+ }
+ }
+ retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY);
+ return (float)retval;
+}
+
+void VP8LBitEntropyInit(VP8LBitEntropy* const entropy) {
+ entropy->entropy = 0.;
+ entropy->sum = 0;
+ entropy->nonzeros = 0;
+ entropy->max_val = 0;
+ entropy->nonzero_code = VP8L_NON_TRIVIAL_SYM;
+}
+
+void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n,
+ VP8LBitEntropy* const entropy) {
+ int i;
+
+ VP8LBitEntropyInit(entropy);
+
+ for (i = 0; i < n; ++i) {
+ if (array[i] != 0) {
+ entropy->sum += array[i];
+ entropy->nonzero_code = i;
+ ++entropy->nonzeros;
+ entropy->entropy -= VP8LFastSLog2(array[i]);
+ if (entropy->max_val < array[i]) {
+ entropy->max_val = array[i];
+ }
+ }
+ }
+ entropy->entropy += VP8LFastSLog2(entropy->sum);
+}
+
+static WEBP_INLINE void GetEntropyUnrefinedHelper(
+ uint32_t val, int i, uint32_t* const val_prev, int* const i_prev,
+ VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats) {
+ const int streak = i - *i_prev;
+
+ // Gather info for the bit entropy.
+ if (*val_prev != 0) {
+ bit_entropy->sum += (*val_prev) * streak;
+ bit_entropy->nonzeros += streak;
+ bit_entropy->nonzero_code = *i_prev;
+ bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak;
+ if (bit_entropy->max_val < *val_prev) {
+ bit_entropy->max_val = *val_prev;
+ }
+ }
+
+ // Gather info for the Huffman cost.
+ stats->counts[*val_prev != 0] += (streak > 3);
+ stats->streaks[*val_prev != 0][(streak > 3)] += streak;
+
+ *val_prev = val;
+ *i_prev = i;
+}
+
+static void GetEntropyUnrefined_C(const uint32_t X[], int length,
+ VP8LBitEntropy* const bit_entropy,
+ VP8LStreaks* const stats) {
+ int i;
+ int i_prev = 0;
+ uint32_t x_prev = X[0];
+
+ memset(stats, 0, sizeof(*stats));
+ VP8LBitEntropyInit(bit_entropy);
+
+ for (i = 1; i < length; ++i) {
+ const uint32_t x = X[i];
+ if (x != x_prev) {
+ GetEntropyUnrefinedHelper(x, i, &x_prev, &i_prev, bit_entropy, stats);
+ }
+ }
+ GetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats);
+
+ bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum);
+}
+
+static void GetCombinedEntropyUnrefined_C(const uint32_t X[],
+ const uint32_t Y[],
+ int length,
+ VP8LBitEntropy* const bit_entropy,
+ VP8LStreaks* const stats) {
+ int i = 1;
+ int i_prev = 0;
+ uint32_t xy_prev = X[0] + Y[0];
+
+ memset(stats, 0, sizeof(*stats));
+ VP8LBitEntropyInit(bit_entropy);
+
+ for (i = 1; i < length; ++i) {
+ const uint32_t xy = X[i] + Y[i];
+ if (xy != xy_prev) {
+ GetEntropyUnrefinedHelper(xy, i, &xy_prev, &i_prev, bit_entropy, stats);
+ }
+ }
+ GetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, bit_entropy, stats);
+
+ bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum);
+}
+
+//------------------------------------------------------------------------------
+
+void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) {
+ const int argb = argb_data[i];
+ const int green = (argb >> 8) & 0xff;
+ const uint32_t new_r = (((argb >> 16) & 0xff) - green) & 0xff;
+ const uint32_t new_b = (((argb >> 0) & 0xff) - green) & 0xff;
+ argb_data[i] = (argb & 0xff00ff00u) | (new_r << 16) | new_b;
+ }
+}
+
+static WEBP_INLINE int ColorTransformDelta(int8_t color_pred, int8_t color) {
+ return ((int)color_pred * color) >> 5;
+}
+
+void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data,
+ int num_pixels) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) {
+ const uint32_t argb = data[i];
+ const uint32_t green = argb >> 8;
+ const uint32_t red = argb >> 16;
+ int new_red = red & 0xff;
+ int new_blue = argb & 0xff;
+ new_red -= ColorTransformDelta(m->green_to_red_, green);
+ new_red &= 0xff;
+ new_blue -= ColorTransformDelta(m->green_to_blue_, green);
+ new_blue -= ColorTransformDelta(m->red_to_blue_, red);
+ new_blue &= 0xff;
+ data[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue);
+ }
+}
+
+static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
+ uint32_t argb) {
+ const uint32_t green = argb >> 8;
+ int new_red = argb >> 16;
+ new_red -= ColorTransformDelta(green_to_red, green);
+ return (new_red & 0xff);
+}
+
+static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue,
+ uint8_t red_to_blue,
+ uint32_t argb) {
+ const uint32_t green = argb >> 8;
+ const uint32_t red = argb >> 16;
+ uint8_t new_blue = argb;
+ new_blue -= ColorTransformDelta(green_to_blue, green);
+ new_blue -= ColorTransformDelta(red_to_blue, red);
+ return (new_blue & 0xff);
+}
+
+void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_red, int histo[]) {
+ while (tile_height-- > 0) {
+ int x;
+ for (x = 0; x < tile_width; ++x) {
+ ++histo[TransformColorRed(green_to_red, argb[x])];
+ }
+ argb += stride;
+ }
+}
+
+void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_blue, int red_to_blue,
+ int histo[]) {
+ while (tile_height-- > 0) {
+ int x;
+ for (x = 0; x < tile_width; ++x) {
+ ++histo[TransformColorBlue(green_to_blue, red_to_blue, argb[x])];
+ }
+ argb += stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+static int VectorMismatch_C(const uint32_t* const array1,
+ const uint32_t* const array2, int length) {
+ int match_len = 0;
+
+ while (match_len < length && array1[match_len] == array2[match_len]) {
+ ++match_len;
+ }
+ return match_len;
+}
+
+// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel.
+void VP8LBundleColorMap_C(const uint8_t* const row, int width, int xbits,
+ uint32_t* dst) {
+ int x;
+ if (xbits > 0) {
+ const int bit_depth = 1 << (3 - xbits);
+ const int mask = (1 << xbits) - 1;
+ uint32_t code = 0xff000000;
+ for (x = 0; x < width; ++x) {
+ const int xsub = x & mask;
+ if (xsub == 0) {
+ code = 0xff000000;
+ }
+ code |= row[x] << (8 + bit_depth * xsub);
+ dst[x >> xbits] = code;
+ }
+ } else {
+ for (x = 0; x < width; ++x) dst[x] = 0xff000000 | (row[x] << 8);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+static double ExtraCost_C(const uint32_t* population, int length) {
+ int i;
+ double cost = 0.;
+ for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2];
+ return cost;
+}
+
+static double ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y,
+ int length) {
+ int i;
+ double cost = 0.;
+ for (i = 2; i < length - 2; ++i) {
+ const int xy = X[i + 2] + Y[i + 2];
+ cost += (i >> 1) * xy;
+ }
+ return cost;
+}
+
+//------------------------------------------------------------------------------
+
+static void HistogramAdd_C(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ VP8LHistogram* const out) {
+ int i;
+ const int literal_size = VP8LHistogramNumCodes(a->palette_code_bits_);
+ assert(a->palette_code_bits_ == b->palette_code_bits_);
+ if (b != out) {
+ for (i = 0; i < literal_size; ++i) {
+ out->literal_[i] = a->literal_[i] + b->literal_[i];
+ }
+ for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
+ out->distance_[i] = a->distance_[i] + b->distance_[i];
+ }
+ for (i = 0; i < NUM_LITERAL_CODES; ++i) {
+ out->red_[i] = a->red_[i] + b->red_[i];
+ out->blue_[i] = a->blue_[i] + b->blue_[i];
+ out->alpha_[i] = a->alpha_[i] + b->alpha_[i];
+ }
+ } else {
+ for (i = 0; i < literal_size; ++i) {
+ out->literal_[i] += a->literal_[i];
+ }
+ for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
+ out->distance_[i] += a->distance_[i];
+ }
+ for (i = 0; i < NUM_LITERAL_CODES; ++i) {
+ out->red_[i] += a->red_[i];
+ out->blue_[i] += a->blue_[i];
+ out->alpha_[i] += a->alpha_[i];
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Image transforms.
+
+static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) {
+ return (((a0 ^ a1) & 0xfefefefeu) >> 1) + (a0 & a1);
+}
+
+static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) {
+ return Average2(Average2(a0, a2), a1);
+}
+
+static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1,
+ uint32_t a2, uint32_t a3) {
+ return Average2(Average2(a0, a1), Average2(a2, a3));
+}
+
+static WEBP_INLINE uint32_t Clip255(uint32_t a) {
+ if (a < 256) {
+ return a;
+ }
+ // return 0, when a is a negative integer.
+ // return 255, when a is positive.
+ return ~a >> 24;
+}
+
+static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) {
+ return Clip255(a + b - c);
+}
+
+static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
+ uint32_t c2) {
+ const int a = AddSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24);
+ const int r = AddSubtractComponentFull((c0 >> 16) & 0xff,
+ (c1 >> 16) & 0xff,
+ (c2 >> 16) & 0xff);
+ const int g = AddSubtractComponentFull((c0 >> 8) & 0xff,
+ (c1 >> 8) & 0xff,
+ (c2 >> 8) & 0xff);
+ const int b = AddSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff);
+ return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b;
+}
+
+static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) {
+ return Clip255(a + (a - b) / 2);
+}
+
+static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
+ uint32_t c2) {
+ const uint32_t ave = Average2(c0, c1);
+ const int a = AddSubtractComponentHalf(ave >> 24, c2 >> 24);
+ const int r = AddSubtractComponentHalf((ave >> 16) & 0xff, (c2 >> 16) & 0xff);
+ const int g = AddSubtractComponentHalf((ave >> 8) & 0xff, (c2 >> 8) & 0xff);
+ const int b = AddSubtractComponentHalf((ave >> 0) & 0xff, (c2 >> 0) & 0xff);
+ return ((uint32_t)a << 24) | (r << 16) | (g << 8) | b;
+}
+
+// gcc-4.9 on ARM generates incorrect code in Select() when Sub3() is inlined.
+#if defined(__arm__) && \
+ (LOCAL_GCC_VERSION == 0x409 || LOCAL_GCC_VERSION == 0x408)
+# define LOCAL_INLINE __attribute__ ((noinline))
+#else
+# define LOCAL_INLINE WEBP_INLINE
+#endif
+
+static LOCAL_INLINE int Sub3(int a, int b, int c) {
+ const int pb = b - c;
+ const int pa = a - c;
+ return abs(pb) - abs(pa);
+}
+
+#undef LOCAL_INLINE
+
+static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
+ const int pa_minus_pb =
+ Sub3((a >> 24) , (b >> 24) , (c >> 24) ) +
+ Sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) +
+ Sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) +
+ Sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff);
+ return (pa_minus_pb <= 0) ? a : b;
+}
+
+//------------------------------------------------------------------------------
+// Predictors
+
+static uint32_t Predictor2(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return top[0];
+}
+static uint32_t Predictor3(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return top[1];
+}
+static uint32_t Predictor4(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return top[-1];
+}
+static uint32_t Predictor5(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average3(left, top[0], top[1]);
+ return pred;
+}
+static uint32_t Predictor6(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(left, top[-1]);
+ return pred;
+}
+static uint32_t Predictor7(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(left, top[0]);
+ return pred;
+}
+static uint32_t Predictor8(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(top[-1], top[0]);
+ (void)left;
+ return pred;
+}
+static uint32_t Predictor9(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2(top[0], top[1]);
+ (void)left;
+ return pred;
+}
+static uint32_t Predictor10(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average4(left, top[-1], top[0], top[1]);
+ return pred;
+}
+static uint32_t Predictor11(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Select(top[0], left, top[-1]);
+ return pred;
+}
+static uint32_t Predictor12(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]);
+ return pred;
+}
+static uint32_t Predictor13(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]);
+ return pred;
+}
+
+//------------------------------------------------------------------------------
+
+static void PredictorSub0_C(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) out[i] = VP8LSubPixels(in[i], ARGB_BLACK);
+ (void)upper;
+}
+
+static void PredictorSub1_C(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ for (i = 0; i < num_pixels; ++i) out[i] = VP8LSubPixels(in[i], in[i - 1]);
+ (void)upper;
+}
+
+GENERATE_PREDICTOR_SUB(Predictor2, PredictorSub2_C)
+GENERATE_PREDICTOR_SUB(Predictor3, PredictorSub3_C)
+GENERATE_PREDICTOR_SUB(Predictor4, PredictorSub4_C)
+GENERATE_PREDICTOR_SUB(Predictor5, PredictorSub5_C)
+GENERATE_PREDICTOR_SUB(Predictor6, PredictorSub6_C)
+GENERATE_PREDICTOR_SUB(Predictor7, PredictorSub7_C)
+GENERATE_PREDICTOR_SUB(Predictor8, PredictorSub8_C)
+GENERATE_PREDICTOR_SUB(Predictor9, PredictorSub9_C)
+GENERATE_PREDICTOR_SUB(Predictor10, PredictorSub10_C)
+GENERATE_PREDICTOR_SUB(Predictor11, PredictorSub11_C)
+GENERATE_PREDICTOR_SUB(Predictor12, PredictorSub12_C)
+GENERATE_PREDICTOR_SUB(Predictor13, PredictorSub13_C)
+
+//------------------------------------------------------------------------------
+
+VP8LProcessEncBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed;
+
+VP8LTransformColorFunc VP8LTransformColor;
+
+VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms;
+VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms;
+
+VP8LFastLog2SlowFunc VP8LFastLog2Slow;
+VP8LFastLog2SlowFunc VP8LFastSLog2Slow;
+
+VP8LCostFunc VP8LExtraCost;
+VP8LCostCombinedFunc VP8LExtraCostCombined;
+VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy;
+
+VP8LGetEntropyUnrefinedFunc VP8LGetEntropyUnrefined;
+VP8LGetCombinedEntropyUnrefinedFunc VP8LGetCombinedEntropyUnrefined;
+
+VP8LHistogramAddFunc VP8LHistogramAdd;
+
+VP8LVectorMismatchFunc VP8LVectorMismatch;
+VP8LBundleColorMapFunc VP8LBundleColorMap;
+
+VP8LPredictorAddSubFunc VP8LPredictorsSub[16];
+VP8LPredictorAddSubFunc VP8LPredictorsSub_C[16];
+
+extern void VP8LEncDspInitSSE2(void);
+extern void VP8LEncDspInitSSE41(void);
+extern void VP8LEncDspInitNEON(void);
+extern void VP8LEncDspInitMIPS32(void);
+extern void VP8LEncDspInitMIPSdspR2(void);
+extern void VP8LEncDspInitMSA(void);
+
+WEBP_DSP_INIT_FUNC(VP8LEncDspInit) {
+ VP8LDspInit();
+
+#if !WEBP_NEON_OMIT_C_CODE
+ VP8LSubtractGreenFromBlueAndRed = VP8LSubtractGreenFromBlueAndRed_C;
+
+ VP8LTransformColor = VP8LTransformColor_C;
+#endif
+
+ VP8LCollectColorBlueTransforms = VP8LCollectColorBlueTransforms_C;
+ VP8LCollectColorRedTransforms = VP8LCollectColorRedTransforms_C;
+
+ VP8LFastLog2Slow = FastLog2Slow_C;
+ VP8LFastSLog2Slow = FastSLog2Slow_C;
+
+ VP8LExtraCost = ExtraCost_C;
+ VP8LExtraCostCombined = ExtraCostCombined_C;
+ VP8LCombinedShannonEntropy = CombinedShannonEntropy_C;
+
+ VP8LGetEntropyUnrefined = GetEntropyUnrefined_C;
+ VP8LGetCombinedEntropyUnrefined = GetCombinedEntropyUnrefined_C;
+
+ VP8LHistogramAdd = HistogramAdd_C;
+
+ VP8LVectorMismatch = VectorMismatch_C;
+ VP8LBundleColorMap = VP8LBundleColorMap_C;
+
+ VP8LPredictorsSub[0] = PredictorSub0_C;
+ VP8LPredictorsSub[1] = PredictorSub1_C;
+ VP8LPredictorsSub[2] = PredictorSub2_C;
+ VP8LPredictorsSub[3] = PredictorSub3_C;
+ VP8LPredictorsSub[4] = PredictorSub4_C;
+ VP8LPredictorsSub[5] = PredictorSub5_C;
+ VP8LPredictorsSub[6] = PredictorSub6_C;
+ VP8LPredictorsSub[7] = PredictorSub7_C;
+ VP8LPredictorsSub[8] = PredictorSub8_C;
+ VP8LPredictorsSub[9] = PredictorSub9_C;
+ VP8LPredictorsSub[10] = PredictorSub10_C;
+ VP8LPredictorsSub[11] = PredictorSub11_C;
+ VP8LPredictorsSub[12] = PredictorSub12_C;
+ VP8LPredictorsSub[13] = PredictorSub13_C;
+ VP8LPredictorsSub[14] = PredictorSub0_C; // <- padding security sentinels
+ VP8LPredictorsSub[15] = PredictorSub0_C;
+
+ VP8LPredictorsSub_C[0] = PredictorSub0_C;
+ VP8LPredictorsSub_C[1] = PredictorSub1_C;
+ VP8LPredictorsSub_C[2] = PredictorSub2_C;
+ VP8LPredictorsSub_C[3] = PredictorSub3_C;
+ VP8LPredictorsSub_C[4] = PredictorSub4_C;
+ VP8LPredictorsSub_C[5] = PredictorSub5_C;
+ VP8LPredictorsSub_C[6] = PredictorSub6_C;
+ VP8LPredictorsSub_C[7] = PredictorSub7_C;
+ VP8LPredictorsSub_C[8] = PredictorSub8_C;
+ VP8LPredictorsSub_C[9] = PredictorSub9_C;
+ VP8LPredictorsSub_C[10] = PredictorSub10_C;
+ VP8LPredictorsSub_C[11] = PredictorSub11_C;
+ VP8LPredictorsSub_C[12] = PredictorSub12_C;
+ VP8LPredictorsSub_C[13] = PredictorSub13_C;
+ VP8LPredictorsSub_C[14] = PredictorSub0_C; // <- padding security sentinels
+ VP8LPredictorsSub_C[15] = PredictorSub0_C;
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ VP8LEncDspInitSSE2();
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ VP8LEncDspInitSSE41();
+ }
+#endif
+ }
+#endif
+#if defined(WEBP_USE_MIPS32)
+ if (VP8GetCPUInfo(kMIPS32)) {
+ VP8LEncDspInitMIPS32();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ VP8LEncDspInitMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_MSA)
+ if (VP8GetCPUInfo(kMSA)) {
+ VP8LEncDspInitMSA();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ VP8LEncDspInitNEON();
+ }
+#endif
+
+ assert(VP8LSubtractGreenFromBlueAndRed != NULL);
+ assert(VP8LTransformColor != NULL);
+ assert(VP8LCollectColorBlueTransforms != NULL);
+ assert(VP8LCollectColorRedTransforms != NULL);
+ assert(VP8LFastLog2Slow != NULL);
+ assert(VP8LFastSLog2Slow != NULL);
+ assert(VP8LExtraCost != NULL);
+ assert(VP8LExtraCostCombined != NULL);
+ assert(VP8LCombinedShannonEntropy != NULL);
+ assert(VP8LGetEntropyUnrefined != NULL);
+ assert(VP8LGetCombinedEntropyUnrefined != NULL);
+ assert(VP8LHistogramAdd != NULL);
+ assert(VP8LVectorMismatch != NULL);
+ assert(VP8LBundleColorMap != NULL);
+ assert(VP8LPredictorsSub[0] != NULL);
+ assert(VP8LPredictorsSub[1] != NULL);
+ assert(VP8LPredictorsSub[2] != NULL);
+ assert(VP8LPredictorsSub[3] != NULL);
+ assert(VP8LPredictorsSub[4] != NULL);
+ assert(VP8LPredictorsSub[5] != NULL);
+ assert(VP8LPredictorsSub[6] != NULL);
+ assert(VP8LPredictorsSub[7] != NULL);
+ assert(VP8LPredictorsSub[8] != NULL);
+ assert(VP8LPredictorsSub[9] != NULL);
+ assert(VP8LPredictorsSub[10] != NULL);
+ assert(VP8LPredictorsSub[11] != NULL);
+ assert(VP8LPredictorsSub[12] != NULL);
+ assert(VP8LPredictorsSub[13] != NULL);
+ assert(VP8LPredictorsSub[14] != NULL);
+ assert(VP8LPredictorsSub[15] != NULL);
+ assert(VP8LPredictorsSub_C[0] != NULL);
+ assert(VP8LPredictorsSub_C[1] != NULL);
+ assert(VP8LPredictorsSub_C[2] != NULL);
+ assert(VP8LPredictorsSub_C[3] != NULL);
+ assert(VP8LPredictorsSub_C[4] != NULL);
+ assert(VP8LPredictorsSub_C[5] != NULL);
+ assert(VP8LPredictorsSub_C[6] != NULL);
+ assert(VP8LPredictorsSub_C[7] != NULL);
+ assert(VP8LPredictorsSub_C[8] != NULL);
+ assert(VP8LPredictorsSub_C[9] != NULL);
+ assert(VP8LPredictorsSub_C[10] != NULL);
+ assert(VP8LPredictorsSub_C[11] != NULL);
+ assert(VP8LPredictorsSub_C[12] != NULL);
+ assert(VP8LPredictorsSub_C[13] != NULL);
+ assert(VP8LPredictorsSub_C[14] != NULL);
+ assert(VP8LPredictorsSub_C[15] != NULL);
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dsp/lossless_enc_mips32.c b/src/third_party/libwebp/src/dsp/lossless_enc_mips32.c
new file mode 100644
index 0000000..7583abd
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_enc_mips32.c
@@ -0,0 +1,439 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of lossless functions
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+// Jovan Zelincevic (jovan.zelincevic@imgtec.com)
+
+#include "src/dsp/dsp.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+
+#if defined(WEBP_USE_MIPS32)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/math_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+static float FastSLog2Slow_MIPS32(uint32_t v) {
+ assert(v >= LOG_LOOKUP_IDX_MAX);
+ if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
+ uint32_t log_cnt, y, correction;
+ const int c24 = 24;
+ const float v_f = (float)v;
+ uint32_t temp;
+
+ // Xf = 256 = 2^8
+ // log_cnt is index of leading one in upper 24 bits
+ __asm__ volatile(
+ "clz %[log_cnt], %[v] \n\t"
+ "addiu %[y], $zero, 1 \n\t"
+ "subu %[log_cnt], %[c24], %[log_cnt] \n\t"
+ "sllv %[y], %[y], %[log_cnt] \n\t"
+ "srlv %[temp], %[v], %[log_cnt] \n\t"
+ : [log_cnt]"=&r"(log_cnt), [y]"=&r"(y),
+ [temp]"=r"(temp)
+ : [c24]"r"(c24), [v]"r"(v)
+ );
+
+ // vf = (2^log_cnt) * Xf; where y = 2^log_cnt and Xf < 256
+ // Xf = floor(Xf) * (1 + (v % y) / v)
+ // log2(Xf) = log2(floor(Xf)) + log2(1 + (v % y) / v)
+ // The correction factor: log(1 + d) ~ d; for very small d values, so
+ // log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v
+ // LOG_2_RECIPROCAL ~ 23/16
+
+ // (v % y) = (v % 2^log_cnt) = v & (2^log_cnt - 1)
+ correction = (23 * (v & (y - 1))) >> 4;
+ return v_f * (kLog2Table[temp] + log_cnt) + correction;
+ } else {
+ return (float)(LOG_2_RECIPROCAL * v * log((double)v));
+ }
+}
+
+static float FastLog2Slow_MIPS32(uint32_t v) {
+ assert(v >= LOG_LOOKUP_IDX_MAX);
+ if (v < APPROX_LOG_WITH_CORRECTION_MAX) {
+ uint32_t log_cnt, y;
+ const int c24 = 24;
+ double log_2;
+ uint32_t temp;
+
+ __asm__ volatile(
+ "clz %[log_cnt], %[v] \n\t"
+ "addiu %[y], $zero, 1 \n\t"
+ "subu %[log_cnt], %[c24], %[log_cnt] \n\t"
+ "sllv %[y], %[y], %[log_cnt] \n\t"
+ "srlv %[temp], %[v], %[log_cnt] \n\t"
+ : [log_cnt]"=&r"(log_cnt), [y]"=&r"(y),
+ [temp]"=r"(temp)
+ : [c24]"r"(c24), [v]"r"(v)
+ );
+
+ log_2 = kLog2Table[temp] + log_cnt;
+ if (v >= APPROX_LOG_MAX) {
+ // Since the division is still expensive, add this correction factor only
+ // for large values of 'v'.
+
+ const uint32_t correction = (23 * (v & (y - 1))) >> 4;
+ log_2 += (double)correction / v;
+ }
+ return (float)log_2;
+ } else {
+ return (float)(LOG_2_RECIPROCAL * log((double)v));
+ }
+}
+
+// C version of this function:
+// int i = 0;
+// int64_t cost = 0;
+// const uint32_t* pop = &population[4];
+// const uint32_t* LoopEnd = &population[length];
+// while (pop != LoopEnd) {
+// ++i;
+// cost += i * *pop;
+// cost += i * *(pop + 1);
+// pop += 2;
+// }
+// return (double)cost;
+static double ExtraCost_MIPS32(const uint32_t* const population, int length) {
+ int i, temp0, temp1;
+ const uint32_t* pop = &population[4];
+ const uint32_t* const LoopEnd = &population[length];
+
+ __asm__ volatile(
+ "mult $zero, $zero \n\t"
+ "xor %[i], %[i], %[i] \n\t"
+ "beq %[pop], %[LoopEnd], 2f \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[pop]) \n\t"
+ "lw %[temp1], 4(%[pop]) \n\t"
+ "addiu %[i], %[i], 1 \n\t"
+ "addiu %[pop], %[pop], 8 \n\t"
+ "madd %[i], %[temp0] \n\t"
+ "madd %[i], %[temp1] \n\t"
+ "bne %[pop], %[LoopEnd], 1b \n\t"
+ "2: \n\t"
+ "mfhi %[temp0] \n\t"
+ "mflo %[temp1] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
+ [i]"=&r"(i), [pop]"+r"(pop)
+ : [LoopEnd]"r"(LoopEnd)
+ : "memory", "hi", "lo"
+ );
+
+ return (double)((int64_t)temp0 << 32 | temp1);
+}
+
+// C version of this function:
+// int i = 0;
+// int64_t cost = 0;
+// const uint32_t* pX = &X[4];
+// const uint32_t* pY = &Y[4];
+// const uint32_t* LoopEnd = &X[length];
+// while (pX != LoopEnd) {
+// const uint32_t xy0 = *pX + *pY;
+// const uint32_t xy1 = *(pX + 1) + *(pY + 1);
+// ++i;
+// cost += i * xy0;
+// cost += i * xy1;
+// pX += 2;
+// pY += 2;
+// }
+// return (double)cost;
+static double ExtraCostCombined_MIPS32(const uint32_t* const X,
+ const uint32_t* const Y, int length) {
+ int i, temp0, temp1, temp2, temp3;
+ const uint32_t* pX = &X[4];
+ const uint32_t* pY = &Y[4];
+ const uint32_t* const LoopEnd = &X[length];
+
+ __asm__ volatile(
+ "mult $zero, $zero \n\t"
+ "xor %[i], %[i], %[i] \n\t"
+ "beq %[pX], %[LoopEnd], 2f \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[pX]) \n\t"
+ "lw %[temp1], 0(%[pY]) \n\t"
+ "lw %[temp2], 4(%[pX]) \n\t"
+ "lw %[temp3], 4(%[pY]) \n\t"
+ "addiu %[i], %[i], 1 \n\t"
+ "addu %[temp0], %[temp0], %[temp1] \n\t"
+ "addu %[temp2], %[temp2], %[temp3] \n\t"
+ "addiu %[pX], %[pX], 8 \n\t"
+ "addiu %[pY], %[pY], 8 \n\t"
+ "madd %[i], %[temp0] \n\t"
+ "madd %[i], %[temp2] \n\t"
+ "bne %[pX], %[LoopEnd], 1b \n\t"
+ "2: \n\t"
+ "mfhi %[temp0] \n\t"
+ "mflo %[temp1] \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
+ [i]"=&r"(i), [pX]"+r"(pX), [pY]"+r"(pY)
+ : [LoopEnd]"r"(LoopEnd)
+ : "memory", "hi", "lo"
+ );
+
+ return (double)((int64_t)temp0 << 32 | temp1);
+}
+
+#define HUFFMAN_COST_PASS \
+ __asm__ volatile( \
+ "sll %[temp1], %[temp0], 3 \n\t" \
+ "addiu %[temp3], %[streak], -3 \n\t" \
+ "addu %[temp2], %[pstreaks], %[temp1] \n\t" \
+ "blez %[temp3], 1f \n\t" \
+ "srl %[temp1], %[temp1], 1 \n\t" \
+ "addu %[temp3], %[pcnts], %[temp1] \n\t" \
+ "lw %[temp0], 4(%[temp2]) \n\t" \
+ "lw %[temp1], 0(%[temp3]) \n\t" \
+ "addu %[temp0], %[temp0], %[streak] \n\t" \
+ "addiu %[temp1], %[temp1], 1 \n\t" \
+ "sw %[temp0], 4(%[temp2]) \n\t" \
+ "sw %[temp1], 0(%[temp3]) \n\t" \
+ "b 2f \n\t" \
+ "1: \n\t" \
+ "lw %[temp0], 0(%[temp2]) \n\t" \
+ "addu %[temp0], %[temp0], %[streak] \n\t" \
+ "sw %[temp0], 0(%[temp2]) \n\t" \
+ "2: \n\t" \
+ : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \
+ [temp3]"=&r"(temp3), [temp0]"+r"(temp0) \
+ : [pstreaks]"r"(pstreaks), [pcnts]"r"(pcnts), \
+ [streak]"r"(streak) \
+ : "memory" \
+ );
+
+// Returns the various RLE counts
+static WEBP_INLINE void GetEntropyUnrefinedHelper(
+ uint32_t val, int i, uint32_t* const val_prev, int* const i_prev,
+ VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats) {
+ int* const pstreaks = &stats->streaks[0][0];
+ int* const pcnts = &stats->counts[0];
+ int temp0, temp1, temp2, temp3;
+ const int streak = i - *i_prev;
+
+ // Gather info for the bit entropy.
+ if (*val_prev != 0) {
+ bit_entropy->sum += (*val_prev) * streak;
+ bit_entropy->nonzeros += streak;
+ bit_entropy->nonzero_code = *i_prev;
+ bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak;
+ if (bit_entropy->max_val < *val_prev) {
+ bit_entropy->max_val = *val_prev;
+ }
+ }
+
+ // Gather info for the Huffman cost.
+ temp0 = (*val_prev != 0);
+ HUFFMAN_COST_PASS
+
+ *val_prev = val;
+ *i_prev = i;
+}
+
+static void GetEntropyUnrefined_MIPS32(const uint32_t X[], int length,
+ VP8LBitEntropy* const bit_entropy,
+ VP8LStreaks* const stats) {
+ int i;
+ int i_prev = 0;
+ uint32_t x_prev = X[0];
+
+ memset(stats, 0, sizeof(*stats));
+ VP8LBitEntropyInit(bit_entropy);
+
+ for (i = 1; i < length; ++i) {
+ const uint32_t x = X[i];
+ if (x != x_prev) {
+ GetEntropyUnrefinedHelper(x, i, &x_prev, &i_prev, bit_entropy, stats);
+ }
+ }
+ GetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats);
+
+ bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum);
+}
+
+static void GetCombinedEntropyUnrefined_MIPS32(const uint32_t X[],
+ const uint32_t Y[],
+ int length,
+ VP8LBitEntropy* const entropy,
+ VP8LStreaks* const stats) {
+ int i = 1;
+ int i_prev = 0;
+ uint32_t xy_prev = X[0] + Y[0];
+
+ memset(stats, 0, sizeof(*stats));
+ VP8LBitEntropyInit(entropy);
+
+ for (i = 1; i < length; ++i) {
+ const uint32_t xy = X[i] + Y[i];
+ if (xy != xy_prev) {
+ GetEntropyUnrefinedHelper(xy, i, &xy_prev, &i_prev, entropy, stats);
+ }
+ }
+ GetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, entropy, stats);
+
+ entropy->entropy += VP8LFastSLog2(entropy->sum);
+}
+
+#define ASM_START \
+ __asm__ volatile( \
+ ".set push \n\t" \
+ ".set at \n\t" \
+ ".set macro \n\t" \
+ "1: \n\t"
+
+// P2 = P0 + P1
+// A..D - offsets
+// E - temp variable to tell macro
+// if pointer should be incremented
+// literal_ and successive histograms could be unaligned
+// so we must use ulw and usw
+#define ADD_TO_OUT(A, B, C, D, E, P0, P1, P2) \
+ "ulw %[temp0], " #A "(%[" #P0 "]) \n\t" \
+ "ulw %[temp1], " #B "(%[" #P0 "]) \n\t" \
+ "ulw %[temp2], " #C "(%[" #P0 "]) \n\t" \
+ "ulw %[temp3], " #D "(%[" #P0 "]) \n\t" \
+ "ulw %[temp4], " #A "(%[" #P1 "]) \n\t" \
+ "ulw %[temp5], " #B "(%[" #P1 "]) \n\t" \
+ "ulw %[temp6], " #C "(%[" #P1 "]) \n\t" \
+ "ulw %[temp7], " #D "(%[" #P1 "]) \n\t" \
+ "addu %[temp4], %[temp4], %[temp0] \n\t" \
+ "addu %[temp5], %[temp5], %[temp1] \n\t" \
+ "addu %[temp6], %[temp6], %[temp2] \n\t" \
+ "addu %[temp7], %[temp7], %[temp3] \n\t" \
+ "addiu %[" #P0 "], %[" #P0 "], 16 \n\t" \
+ ".if " #E " == 1 \n\t" \
+ "addiu %[" #P1 "], %[" #P1 "], 16 \n\t" \
+ ".endif \n\t" \
+ "usw %[temp4], " #A "(%[" #P2 "]) \n\t" \
+ "usw %[temp5], " #B "(%[" #P2 "]) \n\t" \
+ "usw %[temp6], " #C "(%[" #P2 "]) \n\t" \
+ "usw %[temp7], " #D "(%[" #P2 "]) \n\t" \
+ "addiu %[" #P2 "], %[" #P2 "], 16 \n\t" \
+ "bne %[" #P0 "], %[LoopEnd], 1b \n\t" \
+ ".set pop \n\t" \
+
+#define ASM_END_COMMON_0 \
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), \
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), \
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7), \
+ [pa]"+r"(pa), [pout]"+r"(pout)
+
+#define ASM_END_COMMON_1 \
+ : [LoopEnd]"r"(LoopEnd) \
+ : "memory", "at" \
+ );
+
+#define ASM_END_0 \
+ ASM_END_COMMON_0 \
+ , [pb]"+r"(pb) \
+ ASM_END_COMMON_1
+
+#define ASM_END_1 \
+ ASM_END_COMMON_0 \
+ ASM_END_COMMON_1
+
+#define ADD_VECTOR(A, B, OUT, SIZE, EXTRA_SIZE) do { \
+ const uint32_t* pa = (const uint32_t*)(A); \
+ const uint32_t* pb = (const uint32_t*)(B); \
+ uint32_t* pout = (uint32_t*)(OUT); \
+ const uint32_t* const LoopEnd = pa + (SIZE); \
+ assert((SIZE) % 4 == 0); \
+ ASM_START \
+ ADD_TO_OUT(0, 4, 8, 12, 1, pa, pb, pout) \
+ ASM_END_0 \
+ if ((EXTRA_SIZE) > 0) { \
+ const int last = (EXTRA_SIZE); \
+ int i; \
+ for (i = 0; i < last; ++i) pout[i] = pa[i] + pb[i]; \
+ } \
+} while (0)
+
+#define ADD_VECTOR_EQ(A, OUT, SIZE, EXTRA_SIZE) do { \
+ const uint32_t* pa = (const uint32_t*)(A); \
+ uint32_t* pout = (uint32_t*)(OUT); \
+ const uint32_t* const LoopEnd = pa + (SIZE); \
+ assert((SIZE) % 4 == 0); \
+ ASM_START \
+ ADD_TO_OUT(0, 4, 8, 12, 0, pa, pout, pout) \
+ ASM_END_1 \
+ if ((EXTRA_SIZE) > 0) { \
+ const int last = (EXTRA_SIZE); \
+ int i; \
+ for (i = 0; i < last; ++i) pout[i] += pa[i]; \
+ } \
+} while (0)
+
+static void HistogramAdd_MIPS32(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ VP8LHistogram* const out) {
+ uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+ const int extra_cache_size = VP8LHistogramNumCodes(a->palette_code_bits_)
+ - (NUM_LITERAL_CODES + NUM_LENGTH_CODES);
+ assert(a->palette_code_bits_ == b->palette_code_bits_);
+
+ if (b != out) {
+ ADD_VECTOR(a->literal_, b->literal_, out->literal_,
+ NUM_LITERAL_CODES + NUM_LENGTH_CODES, extra_cache_size);
+ ADD_VECTOR(a->distance_, b->distance_, out->distance_,
+ NUM_DISTANCE_CODES, 0);
+ ADD_VECTOR(a->red_, b->red_, out->red_, NUM_LITERAL_CODES, 0);
+ ADD_VECTOR(a->blue_, b->blue_, out->blue_, NUM_LITERAL_CODES, 0);
+ ADD_VECTOR(a->alpha_, b->alpha_, out->alpha_, NUM_LITERAL_CODES, 0);
+ } else {
+ ADD_VECTOR_EQ(a->literal_, out->literal_,
+ NUM_LITERAL_CODES + NUM_LENGTH_CODES, extra_cache_size);
+ ADD_VECTOR_EQ(a->distance_, out->distance_, NUM_DISTANCE_CODES, 0);
+ ADD_VECTOR_EQ(a->red_, out->red_, NUM_LITERAL_CODES, 0);
+ ADD_VECTOR_EQ(a->blue_, out->blue_, NUM_LITERAL_CODES, 0);
+ ADD_VECTOR_EQ(a->alpha_, out->alpha_, NUM_LITERAL_CODES, 0);
+ }
+}
+
+#undef ADD_VECTOR_EQ
+#undef ADD_VECTOR
+#undef ASM_END_1
+#undef ASM_END_0
+#undef ASM_END_COMMON_1
+#undef ASM_END_COMMON_0
+#undef ADD_TO_OUT
+#undef ASM_START
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LEncDspInitMIPS32(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMIPS32(void) {
+ VP8LFastSLog2Slow = FastSLog2Slow_MIPS32;
+ VP8LFastLog2Slow = FastLog2Slow_MIPS32;
+ VP8LExtraCost = ExtraCost_MIPS32;
+ VP8LExtraCostCombined = ExtraCostCombined_MIPS32;
+ VP8LGetEntropyUnrefined = GetEntropyUnrefined_MIPS32;
+ VP8LGetCombinedEntropyUnrefined = GetCombinedEntropyUnrefined_MIPS32;
+ VP8LHistogramAdd = HistogramAdd_MIPS32;
+}
+
+#else // !WEBP_USE_MIPS32
+
+WEBP_DSP_INIT_STUB(VP8LEncDspInitMIPS32)
+
+#endif // WEBP_USE_MIPS32
diff --git a/src/third_party/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c
new file mode 100644
index 0000000..5855e6a
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_enc_mips_dsp_r2.c
@@ -0,0 +1,281 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Image transform methods for lossless encoder.
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+// Jovan Zelincevic (jovan.zelincevic@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#include "src/dsp/lossless.h"
+
+static void SubtractGreenFromBlueAndRed_MIPSdspR2(uint32_t* argb_data,
+ int num_pixels) {
+ uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+ uint32_t* const p_loop1_end = argb_data + (num_pixels & ~3);
+ uint32_t* const p_loop2_end = p_loop1_end + (num_pixels & 3);
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[argb_data], %[p_loop1_end], 3f \n\t"
+ " nop \n\t"
+ "0: \n\t"
+ "lw %[temp0], 0(%[argb_data]) \n\t"
+ "lw %[temp1], 4(%[argb_data]) \n\t"
+ "lw %[temp2], 8(%[argb_data]) \n\t"
+ "lw %[temp3], 12(%[argb_data]) \n\t"
+ "ext %[temp4], %[temp0], 8, 8 \n\t"
+ "ext %[temp5], %[temp1], 8, 8 \n\t"
+ "ext %[temp6], %[temp2], 8, 8 \n\t"
+ "ext %[temp7], %[temp3], 8, 8 \n\t"
+ "addiu %[argb_data], %[argb_data], 16 \n\t"
+ "replv.ph %[temp4], %[temp4] \n\t"
+ "replv.ph %[temp5], %[temp5] \n\t"
+ "replv.ph %[temp6], %[temp6] \n\t"
+ "replv.ph %[temp7], %[temp7] \n\t"
+ "subu.qb %[temp0], %[temp0], %[temp4] \n\t"
+ "subu.qb %[temp1], %[temp1], %[temp5] \n\t"
+ "subu.qb %[temp2], %[temp2], %[temp6] \n\t"
+ "subu.qb %[temp3], %[temp3], %[temp7] \n\t"
+ "sw %[temp0], -16(%[argb_data]) \n\t"
+ "sw %[temp1], -12(%[argb_data]) \n\t"
+ "sw %[temp2], -8(%[argb_data]) \n\t"
+ "bne %[argb_data], %[p_loop1_end], 0b \n\t"
+ " sw %[temp3], -4(%[argb_data]) \n\t"
+ "3: \n\t"
+ "beq %[argb_data], %[p_loop2_end], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[argb_data]) \n\t"
+ "addiu %[argb_data], %[argb_data], 4 \n\t"
+ "ext %[temp4], %[temp0], 8, 8 \n\t"
+ "replv.ph %[temp4], %[temp4] \n\t"
+ "subu.qb %[temp0], %[temp0], %[temp4] \n\t"
+ "bne %[argb_data], %[p_loop2_end], 1b \n\t"
+ " sw %[temp0], -4(%[argb_data]) \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [argb_data]"+&r"(argb_data), [temp0]"=&r"(temp0),
+ [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp6]"=&r"(temp6),
+ [temp7]"=&r"(temp7)
+ : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end)
+ : "memory"
+ );
+}
+
+static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred,
+ int8_t color) {
+ return (uint32_t)((int)(color_pred) * color) >> 5;
+}
+
+static void TransformColor_MIPSdspR2(const VP8LMultipliers* const m,
+ uint32_t* data, int num_pixels) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ uint32_t argb, argb1, new_red, new_red1;
+ const uint32_t G_to_R = m->green_to_red_;
+ const uint32_t G_to_B = m->green_to_blue_;
+ const uint32_t R_to_B = m->red_to_blue_;
+ uint32_t* const p_loop_end = data + (num_pixels & ~1);
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[data], %[p_loop_end], 1f \n\t"
+ " nop \n\t"
+ "replv.ph %[temp0], %[G_to_R] \n\t"
+ "replv.ph %[temp1], %[G_to_B] \n\t"
+ "replv.ph %[temp2], %[R_to_B] \n\t"
+ "shll.ph %[temp0], %[temp0], 8 \n\t"
+ "shll.ph %[temp1], %[temp1], 8 \n\t"
+ "shll.ph %[temp2], %[temp2], 8 \n\t"
+ "shra.ph %[temp0], %[temp0], 8 \n\t"
+ "shra.ph %[temp1], %[temp1], 8 \n\t"
+ "shra.ph %[temp2], %[temp2], 8 \n\t"
+ "0: \n\t"
+ "lw %[argb], 0(%[data]) \n\t"
+ "lw %[argb1], 4(%[data]) \n\t"
+ "lhu %[new_red], 2(%[data]) \n\t"
+ "lhu %[new_red1], 6(%[data]) \n\t"
+ "precrq.qb.ph %[temp3], %[argb], %[argb1] \n\t"
+ "precr.qb.ph %[temp4], %[argb], %[argb1] \n\t"
+ "preceu.ph.qbra %[temp3], %[temp3] \n\t"
+ "preceu.ph.qbla %[temp4], %[temp4] \n\t"
+ "shll.ph %[temp3], %[temp3], 8 \n\t"
+ "shll.ph %[temp4], %[temp4], 8 \n\t"
+ "shra.ph %[temp3], %[temp3], 8 \n\t"
+ "shra.ph %[temp4], %[temp4], 8 \n\t"
+ "mul.ph %[temp5], %[temp3], %[temp0] \n\t"
+ "mul.ph %[temp3], %[temp3], %[temp1] \n\t"
+ "mul.ph %[temp4], %[temp4], %[temp2] \n\t"
+ "addiu %[data], %[data], 8 \n\t"
+ "ins %[new_red1], %[new_red], 16, 16 \n\t"
+ "ins %[argb1], %[argb], 16, 16 \n\t"
+ "shra.ph %[temp5], %[temp5], 5 \n\t"
+ "shra.ph %[temp3], %[temp3], 5 \n\t"
+ "shra.ph %[temp4], %[temp4], 5 \n\t"
+ "subu.ph %[new_red1], %[new_red1], %[temp5] \n\t"
+ "subu.ph %[argb1], %[argb1], %[temp3] \n\t"
+ "preceu.ph.qbra %[temp5], %[new_red1] \n\t"
+ "subu.ph %[argb1], %[argb1], %[temp4] \n\t"
+ "preceu.ph.qbra %[temp3], %[argb1] \n\t"
+ "sb %[temp5], -2(%[data]) \n\t"
+ "sb %[temp3], -4(%[data]) \n\t"
+ "sra %[temp5], %[temp5], 16 \n\t"
+ "sra %[temp3], %[temp3], 16 \n\t"
+ "sb %[temp5], -6(%[data]) \n\t"
+ "bne %[data], %[p_loop_end], 0b \n\t"
+ " sb %[temp3], -8(%[data]) \n\t"
+ "1: \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [new_red1]"=&r"(new_red1), [new_red]"=&r"(new_red),
+ [argb]"=&r"(argb), [argb1]"=&r"(argb1), [data]"+&r"(data)
+ : [G_to_R]"r"(G_to_R), [R_to_B]"r"(R_to_B),
+ [G_to_B]"r"(G_to_B), [p_loop_end]"r"(p_loop_end)
+ : "memory", "hi", "lo"
+ );
+
+ if (num_pixels & 1) {
+ const uint32_t argb_ = data[0];
+ const uint32_t green = argb_ >> 8;
+ const uint32_t red = argb_ >> 16;
+ uint32_t new_blue = argb_;
+ new_red = red;
+ new_red -= ColorTransformDelta(m->green_to_red_, green);
+ new_red &= 0xff;
+ new_blue -= ColorTransformDelta(m->green_to_blue_, green);
+ new_blue -= ColorTransformDelta(m->red_to_blue_, red);
+ new_blue &= 0xff;
+ data[0] = (argb_ & 0xff00ff00u) | (new_red << 16) | (new_blue);
+ }
+}
+
+static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue,
+ uint8_t red_to_blue,
+ uint32_t argb) {
+ const uint32_t green = argb >> 8;
+ const uint32_t red = argb >> 16;
+ uint8_t new_blue = argb;
+ new_blue -= ColorTransformDelta(green_to_blue, green);
+ new_blue -= ColorTransformDelta(red_to_blue, red);
+ return (new_blue & 0xff);
+}
+
+static void CollectColorBlueTransforms_MIPSdspR2(const uint32_t* argb,
+ int stride,
+ int tile_width,
+ int tile_height,
+ int green_to_blue,
+ int red_to_blue,
+ int histo[]) {
+ const int rtb = (red_to_blue << 16) | (red_to_blue & 0xffff);
+ const int gtb = (green_to_blue << 16) | (green_to_blue & 0xffff);
+ const uint32_t mask = 0xff00ffu;
+ while (tile_height-- > 0) {
+ int x;
+ const uint32_t* p_argb = argb;
+ argb += stride;
+ for (x = 0; x < (tile_width >> 1); ++x) {
+ int temp0, temp1, temp2, temp3, temp4, temp5, temp6;
+ __asm__ volatile (
+ "lw %[temp0], 0(%[p_argb]) \n\t"
+ "lw %[temp1], 4(%[p_argb]) \n\t"
+ "precr.qb.ph %[temp2], %[temp0], %[temp1] \n\t"
+ "ins %[temp1], %[temp0], 16, 16 \n\t"
+ "shra.ph %[temp2], %[temp2], 8 \n\t"
+ "shra.ph %[temp3], %[temp1], 8 \n\t"
+ "mul.ph %[temp5], %[temp2], %[rtb] \n\t"
+ "mul.ph %[temp6], %[temp3], %[gtb] \n\t"
+ "and %[temp4], %[temp1], %[mask] \n\t"
+ "addiu %[p_argb], %[p_argb], 8 \n\t"
+ "shra.ph %[temp5], %[temp5], 5 \n\t"
+ "shra.ph %[temp6], %[temp6], 5 \n\t"
+ "subu.qb %[temp2], %[temp4], %[temp5] \n\t"
+ "subu.qb %[temp2], %[temp2], %[temp6] \n\t"
+ : [p_argb]"+&r"(p_argb), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4),
+ [temp5]"=&r"(temp5), [temp6]"=&r"(temp6)
+ : [rtb]"r"(rtb), [gtb]"r"(gtb), [mask]"r"(mask)
+ : "memory", "hi", "lo"
+ );
+ ++histo[(uint8_t)(temp2 >> 16)];
+ ++histo[(uint8_t)temp2];
+ }
+ if (tile_width & 1) {
+ ++histo[TransformColorBlue(green_to_blue, red_to_blue, *p_argb)];
+ }
+ }
+}
+
+static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
+ uint32_t argb) {
+ const uint32_t green = argb >> 8;
+ uint32_t new_red = argb >> 16;
+ new_red -= ColorTransformDelta(green_to_red, green);
+ return (new_red & 0xff);
+}
+
+static void CollectColorRedTransforms_MIPSdspR2(const uint32_t* argb,
+ int stride,
+ int tile_width,
+ int tile_height,
+ int green_to_red,
+ int histo[]) {
+ const int gtr = (green_to_red << 16) | (green_to_red & 0xffff);
+ while (tile_height-- > 0) {
+ int x;
+ const uint32_t* p_argb = argb;
+ argb += stride;
+ for (x = 0; x < (tile_width >> 1); ++x) {
+ int temp0, temp1, temp2, temp3, temp4;
+ __asm__ volatile (
+ "lw %[temp0], 0(%[p_argb]) \n\t"
+ "lw %[temp1], 4(%[p_argb]) \n\t"
+ "precrq.ph.w %[temp4], %[temp0], %[temp1] \n\t"
+ "ins %[temp1], %[temp0], 16, 16 \n\t"
+ "shra.ph %[temp3], %[temp1], 8 \n\t"
+ "mul.ph %[temp2], %[temp3], %[gtr] \n\t"
+ "addiu %[p_argb], %[p_argb], 8 \n\t"
+ "shra.ph %[temp2], %[temp2], 5 \n\t"
+ "subu.qb %[temp2], %[temp4], %[temp2] \n\t"
+ : [p_argb]"+&r"(p_argb), [temp0]"=&r"(temp0), [temp1]"=&r"(temp1),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4)
+ : [gtr]"r"(gtr)
+ : "memory", "hi", "lo"
+ );
+ ++histo[(uint8_t)(temp2 >> 16)];
+ ++histo[(uint8_t)temp2];
+ }
+ if (tile_width & 1) {
+ ++histo[TransformColorRed(green_to_red, *p_argb)];
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LEncDspInitMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMIPSdspR2(void) {
+ VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_MIPSdspR2;
+ VP8LTransformColor = TransformColor_MIPSdspR2;
+ VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_MIPSdspR2;
+ VP8LCollectColorRedTransforms = CollectColorRedTransforms_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(VP8LEncDspInitMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/lossless_enc_msa.c b/src/third_party/libwebp/src/dsp/lossless_enc_msa.c
new file mode 100644
index 0000000..600dddf
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_enc_msa.c
@@ -0,0 +1,148 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA variant of Image transform methods for lossless encoder.
+//
+// Authors: Prashant Patil (Prashant.Patil@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA)
+
+#include "src/dsp/lossless.h"
+#include "src/dsp/msa_macro.h"
+
+#define TRANSFORM_COLOR_8(src0, src1, dst0, dst1, c0, c1, mask0, mask1) do { \
+ v8i16 g0, g1, t0, t1, t2, t3; \
+ v4i32 t4, t5; \
+ VSHF_B2_SH(src0, src0, src1, src1, mask0, mask0, g0, g1); \
+ DOTP_SB2_SH(g0, g1, c0, c0, t0, t1); \
+ SRAI_H2_SH(t0, t1, 5); \
+ t0 = __msa_subv_h((v8i16)src0, t0); \
+ t1 = __msa_subv_h((v8i16)src1, t1); \
+ t4 = __msa_srli_w((v4i32)src0, 16); \
+ t5 = __msa_srli_w((v4i32)src1, 16); \
+ DOTP_SB2_SH(t4, t5, c1, c1, t2, t3); \
+ SRAI_H2_SH(t2, t3, 5); \
+ SUB2(t0, t2, t1, t3, t0, t1); \
+ VSHF_B2_UB(src0, t0, src1, t1, mask1, mask1, dst0, dst1); \
+} while (0)
+
+#define TRANSFORM_COLOR_4(src, dst, c0, c1, mask0, mask1) do { \
+ const v16i8 g0 = VSHF_SB(src, src, mask0); \
+ v8i16 t0 = __msa_dotp_s_h(c0, g0); \
+ v8i16 t1; \
+ v4i32 t2; \
+ t0 = SRAI_H(t0, 5); \
+ t0 = __msa_subv_h((v8i16)src, t0); \
+ t2 = __msa_srli_w((v4i32)src, 16); \
+ t1 = __msa_dotp_s_h(c1, (v16i8)t2); \
+ t1 = SRAI_H(t1, 5); \
+ t0 = t0 - t1; \
+ dst = VSHF_UB(src, t0, mask1); \
+} while (0)
+
+static void TransformColor_MSA(const VP8LMultipliers* const m, uint32_t* data,
+ int num_pixels) {
+ v16u8 src0, dst0;
+ const v16i8 g2br = (v16i8)__msa_fill_w(m->green_to_blue_ |
+ (m->green_to_red_ << 16));
+ const v16i8 r2b = (v16i8)__msa_fill_w(m->red_to_blue_);
+ const v16u8 mask0 = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255,
+ 13, 255, 13, 255 };
+ const v16u8 mask1 = { 16, 1, 18, 3, 20, 5, 22, 7, 24, 9, 26, 11,
+ 28, 13, 30, 15 };
+
+ while (num_pixels >= 8) {
+ v16u8 src1, dst1;
+ LD_UB2(data, 4, src0, src1);
+ TRANSFORM_COLOR_8(src0, src1, dst0, dst1, g2br, r2b, mask0, mask1);
+ ST_UB2(dst0, dst1, data, 4);
+ data += 8;
+ num_pixels -= 8;
+ }
+ if (num_pixels > 0) {
+ if (num_pixels >= 4) {
+ src0 = LD_UB(data);
+ TRANSFORM_COLOR_4(src0, dst0, g2br, r2b, mask0, mask1);
+ ST_UB(dst0, data);
+ data += 4;
+ num_pixels -= 4;
+ }
+ if (num_pixels > 0) {
+ src0 = LD_UB(data);
+ TRANSFORM_COLOR_4(src0, dst0, g2br, r2b, mask0, mask1);
+ if (num_pixels == 3) {
+ const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0);
+ const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 2);
+ SD(pix_d, data + 0);
+ SW(pix_w, data + 2);
+ } else if (num_pixels == 2) {
+ const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0);
+ SD(pix_d, data);
+ } else {
+ const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 0);
+ SW(pix_w, data);
+ }
+ }
+ }
+}
+
+static void SubtractGreenFromBlueAndRed_MSA(uint32_t* argb_data,
+ int num_pixels) {
+ int i;
+ uint8_t* ptemp_data = (uint8_t*)argb_data;
+ v16u8 src0, dst0, tmp0;
+ const v16u8 mask = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255,
+ 13, 255, 13, 255 };
+
+ while (num_pixels >= 8) {
+ v16u8 src1, dst1, tmp1;
+ LD_UB2(ptemp_data, 16, src0, src1);
+ VSHF_B2_UB(src0, src1, src1, src0, mask, mask, tmp0, tmp1);
+ SUB2(src0, tmp0, src1, tmp1, dst0, dst1);
+ ST_UB2(dst0, dst1, ptemp_data, 16);
+ ptemp_data += 8 * 4;
+ num_pixels -= 8;
+ }
+ if (num_pixels > 0) {
+ if (num_pixels >= 4) {
+ src0 = LD_UB(ptemp_data);
+ tmp0 = VSHF_UB(src0, src0, mask);
+ dst0 = src0 - tmp0;
+ ST_UB(dst0, ptemp_data);
+ ptemp_data += 4 * 4;
+ num_pixels -= 4;
+ }
+ for (i = 0; i < num_pixels; i++) {
+ const uint8_t b = ptemp_data[0];
+ const uint8_t g = ptemp_data[1];
+ const uint8_t r = ptemp_data[2];
+ ptemp_data[0] = (b - g) & 0xff;
+ ptemp_data[2] = (r - g) & 0xff;
+ ptemp_data += 4;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LEncDspInitMSA(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMSA(void) {
+ VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_MSA;
+ VP8LTransformColor = TransformColor_MSA;
+}
+
+#else // !WEBP_USE_MSA
+
+WEBP_DSP_INIT_STUB(VP8LEncDspInitMSA)
+
+#endif // WEBP_USE_MSA
diff --git a/src/third_party/libwebp/src/dsp/lossless_enc_neon.c b/src/third_party/libwebp/src/dsp/lossless_enc_neon.c
new file mode 100644
index 0000000..7c7b73f
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_enc_neon.c
@@ -0,0 +1,144 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// NEON variant of methods for lossless encoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON)
+
+#include <arm_neon.h>
+
+#include "src/dsp/lossless.h"
+#include "src/dsp/neon.h"
+
+//------------------------------------------------------------------------------
+// Subtract-Green Transform
+
+// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
+// non-standard versions there.
+#if defined(__APPLE__) && defined(__aarch64__) && \
+ defined(__apple_build_version__) && (__apple_build_version__< 6020037)
+#define USE_VTBLQ
+#endif
+
+#ifdef USE_VTBLQ
+// 255 = byte will be zeroed
+static const uint8_t kGreenShuffle[16] = {
+ 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255
+};
+
+static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb,
+ const uint8x16_t shuffle) {
+ return vcombine_u8(vtbl1q_u8(argb, vget_low_u8(shuffle)),
+ vtbl1q_u8(argb, vget_high_u8(shuffle)));
+}
+#else // !USE_VTBLQ
+// 255 = byte will be zeroed
+static const uint8_t kGreenShuffle[8] = { 1, 255, 1, 255, 5, 255, 5, 255 };
+
+static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb,
+ const uint8x8_t shuffle) {
+ return vcombine_u8(vtbl1_u8(vget_low_u8(argb), shuffle),
+ vtbl1_u8(vget_high_u8(argb), shuffle));
+}
+#endif // USE_VTBLQ
+
+static void SubtractGreenFromBlueAndRed_NEON(uint32_t* argb_data,
+ int num_pixels) {
+ const uint32_t* const end = argb_data + (num_pixels & ~3);
+#ifdef USE_VTBLQ
+ const uint8x16_t shuffle = vld1q_u8(kGreenShuffle);
+#else
+ const uint8x8_t shuffle = vld1_u8(kGreenShuffle);
+#endif
+ for (; argb_data < end; argb_data += 4) {
+ const uint8x16_t argb = vld1q_u8((uint8_t*)argb_data);
+ const uint8x16_t greens = DoGreenShuffle_NEON(argb, shuffle);
+ vst1q_u8((uint8_t*)argb_data, vsubq_u8(argb, greens));
+ }
+ // fallthrough and finish off with plain-C
+ VP8LSubtractGreenFromBlueAndRed_C(argb_data, num_pixels & 3);
+}
+
+//------------------------------------------------------------------------------
+// Color Transform
+
+static void TransformColor_NEON(const VP8LMultipliers* const m,
+ uint32_t* argb_data, int num_pixels) {
+ // sign-extended multiplying constants, pre-shifted by 6.
+#define CST(X) (((int16_t)(m->X << 8)) >> 6)
+ const int16_t rb[8] = {
+ CST(green_to_blue_), CST(green_to_red_),
+ CST(green_to_blue_), CST(green_to_red_),
+ CST(green_to_blue_), CST(green_to_red_),
+ CST(green_to_blue_), CST(green_to_red_)
+ };
+ const int16x8_t mults_rb = vld1q_s16(rb);
+ const int16_t b2[8] = {
+ 0, CST(red_to_blue_), 0, CST(red_to_blue_),
+ 0, CST(red_to_blue_), 0, CST(red_to_blue_),
+ };
+ const int16x8_t mults_b2 = vld1q_s16(b2);
+#undef CST
+#ifdef USE_VTBLQ
+ static const uint8_t kg0g0[16] = {
+ 255, 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13
+ };
+ const uint8x16_t shuffle = vld1q_u8(kg0g0);
+#else
+ static const uint8_t k0g0g[8] = { 255, 1, 255, 1, 255, 5, 255, 5 };
+ const uint8x8_t shuffle = vld1_u8(k0g0g);
+#endif
+ const uint32x4_t mask_rb = vdupq_n_u32(0x00ff00ffu); // red-blue masks
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t in = vld1q_u8((uint8_t*)(argb_data + i));
+ // 0 g 0 g
+ const uint8x16_t greens = DoGreenShuffle_NEON(in, shuffle);
+ // x dr x db1
+ const int16x8_t A = vqdmulhq_s16(vreinterpretq_s16_u8(greens), mults_rb);
+ // r 0 b 0
+ const int16x8_t B = vshlq_n_s16(vreinterpretq_s16_u8(in), 8);
+ // x db2 0 0
+ const int16x8_t C = vqdmulhq_s16(B, mults_b2);
+ // 0 0 x db2
+ const uint32x4_t D = vshrq_n_u32(vreinterpretq_u32_s16(C), 16);
+ // x dr x db
+ const int8x16_t E = vaddq_s8(vreinterpretq_s8_u32(D),
+ vreinterpretq_s8_s16(A));
+ // 0 dr 0 db
+ const uint32x4_t F = vandq_u32(vreinterpretq_u32_s8(E), mask_rb);
+ const int8x16_t out = vsubq_s8(vreinterpretq_s8_u8(in),
+ vreinterpretq_s8_u32(F));
+ vst1q_s8((int8_t*)(argb_data + i), out);
+ }
+ // fallthrough and finish off with plain-C
+ VP8LTransformColor_C(m, argb_data + i, num_pixels - i);
+}
+
+#undef USE_VTBLQ
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LEncDspInitNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitNEON(void) {
+ VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_NEON;
+ VP8LTransformColor = TransformColor_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(VP8LEncDspInitNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/lossless_enc_sse2.c b/src/third_party/libwebp/src/dsp/lossless_enc_sse2.c
new file mode 100644
index 0000000..f84a990
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_enc_sse2.c
@@ -0,0 +1,705 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 variant of methods for lossless encoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+#include <assert.h>
+#include <emmintrin.h>
+#include "src/dsp/lossless.h"
+#include "src/dsp/common_sse2.h"
+#include "src/dsp/lossless_common.h"
+
+// For sign-extended multiplying constants, pre-shifted by 5:
+#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5)
+
+//------------------------------------------------------------------------------
+// Subtract-Green Transform
+
+static void SubtractGreenFromBlueAndRed_SSE2(uint32_t* argb_data,
+ int num_pixels) {
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]); // argb
+ const __m128i A = _mm_srli_epi16(in, 8); // 0 a 0 g
+ const __m128i B = _mm_shufflelo_epi16(A, _MM_SHUFFLE(2, 2, 0, 0));
+ const __m128i C = _mm_shufflehi_epi16(B, _MM_SHUFFLE(2, 2, 0, 0)); // 0g0g
+ const __m128i out = _mm_sub_epi8(in, C);
+ _mm_storeu_si128((__m128i*)&argb_data[i], out);
+ }
+ // fallthrough and finish off with plain-C
+ if (i != num_pixels) {
+ VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Color Transform
+
+#define MK_CST_16(HI, LO) \
+ _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff)))
+
+static void TransformColor_SSE2(const VP8LMultipliers* const m,
+ uint32_t* argb_data, int num_pixels) {
+ const __m128i mults_rb = MK_CST_16(CST_5b(m->green_to_red_),
+ CST_5b(m->green_to_blue_));
+ const __m128i mults_b2 = MK_CST_16(CST_5b(m->red_to_blue_), 0);
+ const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); // alpha-green masks
+ const __m128i mask_rb = _mm_set1_epi32(0x00ff00ff); // red-blue masks
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]); // argb
+ const __m128i A = _mm_and_si128(in, mask_ag); // a 0 g 0
+ const __m128i B = _mm_shufflelo_epi16(A, _MM_SHUFFLE(2, 2, 0, 0));
+ const __m128i C = _mm_shufflehi_epi16(B, _MM_SHUFFLE(2, 2, 0, 0)); // g0g0
+ const __m128i D = _mm_mulhi_epi16(C, mults_rb); // x dr x db1
+ const __m128i E = _mm_slli_epi16(in, 8); // r 0 b 0
+ const __m128i F = _mm_mulhi_epi16(E, mults_b2); // x db2 0 0
+ const __m128i G = _mm_srli_epi32(F, 16); // 0 0 x db2
+ const __m128i H = _mm_add_epi8(G, D); // x dr x db
+ const __m128i I = _mm_and_si128(H, mask_rb); // 0 dr 0 db
+ const __m128i out = _mm_sub_epi8(in, I);
+ _mm_storeu_si128((__m128i*)&argb_data[i], out);
+ }
+ // fallthrough and finish off with plain-C
+ if (i != num_pixels) {
+ VP8LTransformColor_C(m, argb_data + i, num_pixels - i);
+ }
+}
+
+//------------------------------------------------------------------------------
+#define SPAN 8
+static void CollectColorBlueTransforms_SSE2(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_blue, int red_to_blue,
+ int histo[]) {
+ const __m128i mults_r = MK_CST_16(CST_5b(red_to_blue), 0);
+ const __m128i mults_g = MK_CST_16(0, CST_5b(green_to_blue));
+ const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask
+ const __m128i mask_b = _mm_set1_epi32(0x0000ff); // blue mask
+ int y;
+ for (y = 0; y < tile_height; ++y) {
+ const uint32_t* const src = argb + y * stride;
+ int i, x;
+ for (x = 0; x + SPAN <= tile_width; x += SPAN) {
+ uint16_t values[SPAN];
+ const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
+ const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
+ const __m128i A0 = _mm_slli_epi16(in0, 8); // r 0 | b 0
+ const __m128i A1 = _mm_slli_epi16(in1, 8);
+ const __m128i B0 = _mm_and_si128(in0, mask_g); // 0 0 | g 0
+ const __m128i B1 = _mm_and_si128(in1, mask_g);
+ const __m128i C0 = _mm_mulhi_epi16(A0, mults_r); // x db | 0 0
+ const __m128i C1 = _mm_mulhi_epi16(A1, mults_r);
+ const __m128i D0 = _mm_mulhi_epi16(B0, mults_g); // 0 0 | x db
+ const __m128i D1 = _mm_mulhi_epi16(B1, mults_g);
+ const __m128i E0 = _mm_sub_epi8(in0, D0); // x x | x b'
+ const __m128i E1 = _mm_sub_epi8(in1, D1);
+ const __m128i F0 = _mm_srli_epi32(C0, 16); // 0 0 | x db
+ const __m128i F1 = _mm_srli_epi32(C1, 16);
+ const __m128i G0 = _mm_sub_epi8(E0, F0); // 0 0 | x b'
+ const __m128i G1 = _mm_sub_epi8(E1, F1);
+ const __m128i H0 = _mm_and_si128(G0, mask_b); // 0 0 | 0 b
+ const __m128i H1 = _mm_and_si128(G1, mask_b);
+ const __m128i I = _mm_packs_epi32(H0, H1); // 0 b' | 0 b'
+ _mm_storeu_si128((__m128i*)values, I);
+ for (i = 0; i < SPAN; ++i) ++histo[values[i]];
+ }
+ }
+ {
+ const int left_over = tile_width & (SPAN - 1);
+ if (left_over > 0) {
+ VP8LCollectColorBlueTransforms_C(argb + tile_width - left_over, stride,
+ left_over, tile_height,
+ green_to_blue, red_to_blue, histo);
+ }
+ }
+}
+
+static void CollectColorRedTransforms_SSE2(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_red, int histo[]) {
+ const __m128i mults_g = MK_CST_16(0, CST_5b(green_to_red));
+ const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask
+ const __m128i mask = _mm_set1_epi32(0xff);
+
+ int y;
+ for (y = 0; y < tile_height; ++y) {
+ const uint32_t* const src = argb + y * stride;
+ int i, x;
+ for (x = 0; x + SPAN <= tile_width; x += SPAN) {
+ uint16_t values[SPAN];
+ const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
+ const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
+ const __m128i A0 = _mm_and_si128(in0, mask_g); // 0 0 | g 0
+ const __m128i A1 = _mm_and_si128(in1, mask_g);
+ const __m128i B0 = _mm_srli_epi32(in0, 16); // 0 0 | x r
+ const __m128i B1 = _mm_srli_epi32(in1, 16);
+ const __m128i C0 = _mm_mulhi_epi16(A0, mults_g); // 0 0 | x dr
+ const __m128i C1 = _mm_mulhi_epi16(A1, mults_g);
+ const __m128i E0 = _mm_sub_epi8(B0, C0); // x x | x r'
+ const __m128i E1 = _mm_sub_epi8(B1, C1);
+ const __m128i F0 = _mm_and_si128(E0, mask); // 0 0 | 0 r'
+ const __m128i F1 = _mm_and_si128(E1, mask);
+ const __m128i I = _mm_packs_epi32(F0, F1);
+ _mm_storeu_si128((__m128i*)values, I);
+ for (i = 0; i < SPAN; ++i) ++histo[values[i]];
+ }
+ }
+ {
+ const int left_over = tile_width & (SPAN - 1);
+ if (left_over > 0) {
+ VP8LCollectColorRedTransforms_C(argb + tile_width - left_over, stride,
+ left_over, tile_height,
+ green_to_red, histo);
+ }
+ }
+}
+#undef SPAN
+#undef MK_CST_16
+
+//------------------------------------------------------------------------------
+
+#define LINE_SIZE 16 // 8 or 16
+static void AddVector_SSE2(const uint32_t* a, const uint32_t* b, uint32_t* out,
+ int size) {
+ int i;
+ assert(size % LINE_SIZE == 0);
+ for (i = 0; i < size; i += LINE_SIZE) {
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i + 0]);
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]);
+#if (LINE_SIZE == 16)
+ const __m128i a2 = _mm_loadu_si128((const __m128i*)&a[i + 8]);
+ const __m128i a3 = _mm_loadu_si128((const __m128i*)&a[i + 12]);
+#endif
+ const __m128i b0 = _mm_loadu_si128((const __m128i*)&b[i + 0]);
+ const __m128i b1 = _mm_loadu_si128((const __m128i*)&b[i + 4]);
+#if (LINE_SIZE == 16)
+ const __m128i b2 = _mm_loadu_si128((const __m128i*)&b[i + 8]);
+ const __m128i b3 = _mm_loadu_si128((const __m128i*)&b[i + 12]);
+#endif
+ _mm_storeu_si128((__m128i*)&out[i + 0], _mm_add_epi32(a0, b0));
+ _mm_storeu_si128((__m128i*)&out[i + 4], _mm_add_epi32(a1, b1));
+#if (LINE_SIZE == 16)
+ _mm_storeu_si128((__m128i*)&out[i + 8], _mm_add_epi32(a2, b2));
+ _mm_storeu_si128((__m128i*)&out[i + 12], _mm_add_epi32(a3, b3));
+#endif
+ }
+}
+
+static void AddVectorEq_SSE2(const uint32_t* a, uint32_t* out, int size) {
+ int i;
+ assert(size % LINE_SIZE == 0);
+ for (i = 0; i < size; i += LINE_SIZE) {
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)&a[i + 0]);
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)&a[i + 4]);
+#if (LINE_SIZE == 16)
+ const __m128i a2 = _mm_loadu_si128((const __m128i*)&a[i + 8]);
+ const __m128i a3 = _mm_loadu_si128((const __m128i*)&a[i + 12]);
+#endif
+ const __m128i b0 = _mm_loadu_si128((const __m128i*)&out[i + 0]);
+ const __m128i b1 = _mm_loadu_si128((const __m128i*)&out[i + 4]);
+#if (LINE_SIZE == 16)
+ const __m128i b2 = _mm_loadu_si128((const __m128i*)&out[i + 8]);
+ const __m128i b3 = _mm_loadu_si128((const __m128i*)&out[i + 12]);
+#endif
+ _mm_storeu_si128((__m128i*)&out[i + 0], _mm_add_epi32(a0, b0));
+ _mm_storeu_si128((__m128i*)&out[i + 4], _mm_add_epi32(a1, b1));
+#if (LINE_SIZE == 16)
+ _mm_storeu_si128((__m128i*)&out[i + 8], _mm_add_epi32(a2, b2));
+ _mm_storeu_si128((__m128i*)&out[i + 12], _mm_add_epi32(a3, b3));
+#endif
+ }
+}
+#undef LINE_SIZE
+
+// Note we are adding uint32_t's as *signed* int32's (using _mm_add_epi32). But
+// that's ok since the histogram values are less than 1<<28 (max picture size).
+static void HistogramAdd_SSE2(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ VP8LHistogram* const out) {
+ int i;
+ const int literal_size = VP8LHistogramNumCodes(a->palette_code_bits_);
+ assert(a->palette_code_bits_ == b->palette_code_bits_);
+ if (b != out) {
+ AddVector_SSE2(a->literal_, b->literal_, out->literal_, NUM_LITERAL_CODES);
+ AddVector_SSE2(a->red_, b->red_, out->red_, NUM_LITERAL_CODES);
+ AddVector_SSE2(a->blue_, b->blue_, out->blue_, NUM_LITERAL_CODES);
+ AddVector_SSE2(a->alpha_, b->alpha_, out->alpha_, NUM_LITERAL_CODES);
+ } else {
+ AddVectorEq_SSE2(a->literal_, out->literal_, NUM_LITERAL_CODES);
+ AddVectorEq_SSE2(a->red_, out->red_, NUM_LITERAL_CODES);
+ AddVectorEq_SSE2(a->blue_, out->blue_, NUM_LITERAL_CODES);
+ AddVectorEq_SSE2(a->alpha_, out->alpha_, NUM_LITERAL_CODES);
+ }
+ for (i = NUM_LITERAL_CODES; i < literal_size; ++i) {
+ out->literal_[i] = a->literal_[i] + b->literal_[i];
+ }
+ for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
+ out->distance_[i] = a->distance_[i] + b->distance_[i];
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entropy
+
+// Checks whether the X or Y contribution is worth computing and adding.
+// Used in loop unrolling.
+#define ANALYZE_X_OR_Y(x_or_y, j) \
+ do { \
+ if ((x_or_y)[i + (j)] != 0) retval -= VP8LFastSLog2((x_or_y)[i + (j)]); \
+ } while (0)
+
+// Checks whether the X + Y contribution is worth computing and adding.
+// Used in loop unrolling.
+#define ANALYZE_XY(j) \
+ do { \
+ if (tmp[j] != 0) { \
+ retval -= VP8LFastSLog2(tmp[j]); \
+ ANALYZE_X_OR_Y(X, j); \
+ } \
+ } while (0)
+
+static float CombinedShannonEntropy_SSE2(const int X[256], const int Y[256]) {
+ int i;
+ double retval = 0.;
+ int sumX, sumXY;
+ int32_t tmp[4];
+ __m128i zero = _mm_setzero_si128();
+ // Sums up X + Y, 4 ints at a time (and will merge it at the end for sumXY).
+ __m128i sumXY_128 = zero;
+ __m128i sumX_128 = zero;
+
+ for (i = 0; i < 256; i += 4) {
+ const __m128i x = _mm_loadu_si128((const __m128i*)(X + i));
+ const __m128i y = _mm_loadu_si128((const __m128i*)(Y + i));
+
+ // Check if any X is non-zero: this actually provides a speedup as X is
+ // usually sparse.
+ if (_mm_movemask_epi8(_mm_cmpeq_epi32(x, zero)) != 0xFFFF) {
+ const __m128i xy_128 = _mm_add_epi32(x, y);
+ sumXY_128 = _mm_add_epi32(sumXY_128, xy_128);
+
+ sumX_128 = _mm_add_epi32(sumX_128, x);
+
+ // Analyze the different X + Y.
+ _mm_storeu_si128((__m128i*)tmp, xy_128);
+
+ ANALYZE_XY(0);
+ ANALYZE_XY(1);
+ ANALYZE_XY(2);
+ ANALYZE_XY(3);
+ } else {
+ // X is fully 0, so only deal with Y.
+ sumXY_128 = _mm_add_epi32(sumXY_128, y);
+
+ ANALYZE_X_OR_Y(Y, 0);
+ ANALYZE_X_OR_Y(Y, 1);
+ ANALYZE_X_OR_Y(Y, 2);
+ ANALYZE_X_OR_Y(Y, 3);
+ }
+ }
+
+ // Sum up sumX_128 to get sumX.
+ _mm_storeu_si128((__m128i*)tmp, sumX_128);
+ sumX = tmp[3] + tmp[2] + tmp[1] + tmp[0];
+
+ // Sum up sumXY_128 to get sumXY.
+ _mm_storeu_si128((__m128i*)tmp, sumXY_128);
+ sumXY = tmp[3] + tmp[2] + tmp[1] + tmp[0];
+
+ retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY);
+ return (float)retval;
+}
+#undef ANALYZE_X_OR_Y
+#undef ANALYZE_XY
+
+//------------------------------------------------------------------------------
+
+static int VectorMismatch_SSE2(const uint32_t* const array1,
+ const uint32_t* const array2, int length) {
+ int match_len;
+
+ if (length >= 12) {
+ __m128i A0 = _mm_loadu_si128((const __m128i*)&array1[0]);
+ __m128i A1 = _mm_loadu_si128((const __m128i*)&array2[0]);
+ match_len = 0;
+ do {
+ // Loop unrolling and early load both provide a speedup of 10% for the
+ // current function. Also, max_limit can be MAX_LENGTH=4096 at most.
+ const __m128i cmpA = _mm_cmpeq_epi32(A0, A1);
+ const __m128i B0 =
+ _mm_loadu_si128((const __m128i*)&array1[match_len + 4]);
+ const __m128i B1 =
+ _mm_loadu_si128((const __m128i*)&array2[match_len + 4]);
+ if (_mm_movemask_epi8(cmpA) != 0xffff) break;
+ match_len += 4;
+
+ {
+ const __m128i cmpB = _mm_cmpeq_epi32(B0, B1);
+ A0 = _mm_loadu_si128((const __m128i*)&array1[match_len + 4]);
+ A1 = _mm_loadu_si128((const __m128i*)&array2[match_len + 4]);
+ if (_mm_movemask_epi8(cmpB) != 0xffff) break;
+ match_len += 4;
+ }
+ } while (match_len + 12 < length);
+ } else {
+ match_len = 0;
+ // Unroll the potential first two loops.
+ if (length >= 4 &&
+ _mm_movemask_epi8(_mm_cmpeq_epi32(
+ _mm_loadu_si128((const __m128i*)&array1[0]),
+ _mm_loadu_si128((const __m128i*)&array2[0]))) == 0xffff) {
+ match_len = 4;
+ if (length >= 8 &&
+ _mm_movemask_epi8(_mm_cmpeq_epi32(
+ _mm_loadu_si128((const __m128i*)&array1[4]),
+ _mm_loadu_si128((const __m128i*)&array2[4]))) == 0xffff) {
+ match_len = 8;
+ }
+ }
+ }
+
+ while (match_len < length && array1[match_len] == array2[match_len]) {
+ ++match_len;
+ }
+ return match_len;
+}
+
+// Bundles multiple (1, 2, 4 or 8) pixels into a single pixel.
+static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits,
+ uint32_t* dst) {
+ int x;
+ assert(xbits >= 0);
+ assert(xbits <= 3);
+ switch (xbits) {
+ case 0: {
+ const __m128i ff = _mm_set1_epi16(0xff00);
+ const __m128i zero = _mm_setzero_si128();
+ // Store 0xff000000 | (row[x] << 8).
+ for (x = 0; x + 16 <= width; x += 16, dst += 16) {
+ const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]);
+ const __m128i in_lo = _mm_unpacklo_epi8(zero, in);
+ const __m128i dst0 = _mm_unpacklo_epi16(in_lo, ff);
+ const __m128i dst1 = _mm_unpackhi_epi16(in_lo, ff);
+ const __m128i in_hi = _mm_unpackhi_epi8(zero, in);
+ const __m128i dst2 = _mm_unpacklo_epi16(in_hi, ff);
+ const __m128i dst3 = _mm_unpackhi_epi16(in_hi, ff);
+ _mm_storeu_si128((__m128i*)&dst[0], dst0);
+ _mm_storeu_si128((__m128i*)&dst[4], dst1);
+ _mm_storeu_si128((__m128i*)&dst[8], dst2);
+ _mm_storeu_si128((__m128i*)&dst[12], dst3);
+ }
+ break;
+ }
+ case 1: {
+ const __m128i ff = _mm_set1_epi16(0xff00);
+ const __m128i mul = _mm_set1_epi16(0x110);
+ for (x = 0; x + 16 <= width; x += 16, dst += 8) {
+ // 0a0b | (where a/b are 4 bits).
+ const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]);
+ const __m128i tmp = _mm_mullo_epi16(in, mul); // aba0
+ const __m128i pack = _mm_and_si128(tmp, ff); // ab00
+ const __m128i dst0 = _mm_unpacklo_epi16(pack, ff);
+ const __m128i dst1 = _mm_unpackhi_epi16(pack, ff);
+ _mm_storeu_si128((__m128i*)&dst[0], dst0);
+ _mm_storeu_si128((__m128i*)&dst[4], dst1);
+ }
+ break;
+ }
+ case 2: {
+ const __m128i mask_or = _mm_set1_epi32(0xff000000);
+ const __m128i mul_cst = _mm_set1_epi16(0x0104);
+ const __m128i mask_mul = _mm_set1_epi16(0x0f00);
+ for (x = 0; x + 16 <= width; x += 16, dst += 4) {
+ // 000a000b000c000d | (where a/b/c/d are 2 bits).
+ const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]);
+ const __m128i mul = _mm_mullo_epi16(in, mul_cst); // 00ab00b000cd00d0
+ const __m128i tmp = _mm_and_si128(mul, mask_mul); // 00ab000000cd0000
+ const __m128i shift = _mm_srli_epi32(tmp, 12); // 00000000ab000000
+ const __m128i pack = _mm_or_si128(shift, tmp); // 00000000abcd0000
+ // Convert to 0xff00**00.
+ const __m128i res = _mm_or_si128(pack, mask_or);
+ _mm_storeu_si128((__m128i*)dst, res);
+ }
+ break;
+ }
+ default: {
+ assert(xbits == 3);
+ for (x = 0; x + 16 <= width; x += 16, dst += 2) {
+ // 0000000a00000000b... | (where a/b are 1 bit).
+ const __m128i in = _mm_loadu_si128((const __m128i*)&row[x]);
+ const __m128i shift = _mm_slli_epi64(in, 7);
+ const uint32_t move = _mm_movemask_epi8(shift);
+ dst[0] = 0xff000000 | ((move & 0xff) << 8);
+ dst[1] = 0xff000000 | (move & 0xff00);
+ }
+ break;
+ }
+ }
+ if (x != width) {
+ VP8LBundleColorMap_C(row + x, width - x, xbits, dst);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Batch version of Predictor Transform subtraction
+
+static WEBP_INLINE void Average2_m128i(const __m128i* const a0,
+ const __m128i* const a1,
+ __m128i* const avg) {
+ // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1)
+ const __m128i ones = _mm_set1_epi8(1);
+ const __m128i avg1 = _mm_avg_epu8(*a0, *a1);
+ const __m128i one = _mm_and_si128(_mm_xor_si128(*a0, *a1), ones);
+ *avg = _mm_sub_epi8(avg1, one);
+}
+
+// Predictor0: ARGB_BLACK.
+static void PredictorSub0_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ const __m128i black = _mm_set1_epi32(ARGB_BLACK);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ const __m128i res = _mm_sub_epi8(src, black);
+ _mm_storeu_si128((__m128i*)&out[i], res);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsSub_C[0](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+#define GENERATE_PREDICTOR_1(X, IN) \
+static void PredictorSub##X##_SSE2(const uint32_t* in, const uint32_t* upper, \
+ int num_pixels, uint32_t* out) { \
+ int i; \
+ for (i = 0; i + 4 <= num_pixels; i += 4) { \
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \
+ const __m128i pred = _mm_loadu_si128((const __m128i*)&(IN)); \
+ const __m128i res = _mm_sub_epi8(src, pred); \
+ _mm_storeu_si128((__m128i*)&out[i], res); \
+ } \
+ if (i != num_pixels) { \
+ VP8LPredictorsSub_C[(X)](in + i, upper + i, num_pixels - i, out + i); \
+ } \
+}
+
+GENERATE_PREDICTOR_1(1, in[i - 1]) // Predictor1: L
+GENERATE_PREDICTOR_1(2, upper[i]) // Predictor2: T
+GENERATE_PREDICTOR_1(3, upper[i + 1]) // Predictor3: TR
+GENERATE_PREDICTOR_1(4, upper[i - 1]) // Predictor4: TL
+#undef GENERATE_PREDICTOR_1
+
+// Predictor5: avg2(avg2(L, TR), T)
+static void PredictorSub5_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]);
+ const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
+ const __m128i TR = _mm_loadu_si128((const __m128i*)&upper[i + 1]);
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ __m128i avg, pred, res;
+ Average2_m128i(&L, &TR, &avg);
+ Average2_m128i(&avg, &T, &pred);
+ res = _mm_sub_epi8(src, pred);
+ _mm_storeu_si128((__m128i*)&out[i], res);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsSub_C[5](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+#define GENERATE_PREDICTOR_2(X, A, B) \
+static void PredictorSub##X##_SSE2(const uint32_t* in, const uint32_t* upper, \
+ int num_pixels, uint32_t* out) { \
+ int i; \
+ for (i = 0; i + 4 <= num_pixels; i += 4) { \
+ const __m128i tA = _mm_loadu_si128((const __m128i*)&(A)); \
+ const __m128i tB = _mm_loadu_si128((const __m128i*)&(B)); \
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \
+ __m128i pred, res; \
+ Average2_m128i(&tA, &tB, &pred); \
+ res = _mm_sub_epi8(src, pred); \
+ _mm_storeu_si128((__m128i*)&out[i], res); \
+ } \
+ if (i != num_pixels) { \
+ VP8LPredictorsSub_C[(X)](in + i, upper + i, num_pixels - i, out + i); \
+ } \
+}
+
+GENERATE_PREDICTOR_2(6, in[i - 1], upper[i - 1]) // Predictor6: avg(L, TL)
+GENERATE_PREDICTOR_2(7, in[i - 1], upper[i]) // Predictor7: avg(L, T)
+GENERATE_PREDICTOR_2(8, upper[i - 1], upper[i]) // Predictor8: avg(TL, T)
+GENERATE_PREDICTOR_2(9, upper[i], upper[i + 1]) // Predictor9: average(T, TR)
+#undef GENERATE_PREDICTOR_2
+
+// Predictor10: avg(avg(L,TL), avg(T, TR)).
+static void PredictorSub10_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]);
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
+ const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
+ const __m128i TR = _mm_loadu_si128((const __m128i*)&upper[i + 1]);
+ __m128i avgTTR, avgLTL, avg, res;
+ Average2_m128i(&T, &TR, &avgTTR);
+ Average2_m128i(&L, &TL, &avgLTL);
+ Average2_m128i(&avgTTR, &avgLTL, &avg);
+ res = _mm_sub_epi8(src, avg);
+ _mm_storeu_si128((__m128i*)&out[i], res);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsSub_C[10](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+// Predictor11: select.
+static void GetSumAbsDiff32_SSE2(const __m128i* const A, const __m128i* const B,
+ __m128i* const out) {
+ // We can unpack with any value on the upper 32 bits, provided it's the same
+ // on both operands (to that their sum of abs diff is zero). Here we use *A.
+ const __m128i A_lo = _mm_unpacklo_epi32(*A, *A);
+ const __m128i B_lo = _mm_unpacklo_epi32(*B, *A);
+ const __m128i A_hi = _mm_unpackhi_epi32(*A, *A);
+ const __m128i B_hi = _mm_unpackhi_epi32(*B, *A);
+ const __m128i s_lo = _mm_sad_epu8(A_lo, B_lo);
+ const __m128i s_hi = _mm_sad_epu8(A_hi, B_hi);
+ *out = _mm_packs_epi32(s_lo, s_hi);
+}
+
+static void PredictorSub11_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]);
+ const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
+ const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ __m128i pa, pb;
+ GetSumAbsDiff32_SSE2(&T, &TL, &pa); // pa = sum |T-TL|
+ GetSumAbsDiff32_SSE2(&L, &TL, &pb); // pb = sum |L-TL|
+ {
+ const __m128i mask = _mm_cmpgt_epi32(pb, pa);
+ const __m128i A = _mm_and_si128(mask, L);
+ const __m128i B = _mm_andnot_si128(mask, T);
+ const __m128i pred = _mm_or_si128(A, B); // pred = (L > T)? L : T
+ const __m128i res = _mm_sub_epi8(src, pred);
+ _mm_storeu_si128((__m128i*)&out[i], res);
+ }
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsSub_C[11](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+// Predictor12: ClampedSubSubtractFull.
+static void PredictorSub12_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ const __m128i zero = _mm_setzero_si128();
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ const __m128i L = _mm_loadu_si128((const __m128i*)&in[i - 1]);
+ const __m128i L_lo = _mm_unpacklo_epi8(L, zero);
+ const __m128i L_hi = _mm_unpackhi_epi8(L, zero);
+ const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
+ const __m128i T_lo = _mm_unpacklo_epi8(T, zero);
+ const __m128i T_hi = _mm_unpackhi_epi8(T, zero);
+ const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
+ const __m128i TL_lo = _mm_unpacklo_epi8(TL, zero);
+ const __m128i TL_hi = _mm_unpackhi_epi8(TL, zero);
+ const __m128i diff_lo = _mm_sub_epi16(T_lo, TL_lo);
+ const __m128i diff_hi = _mm_sub_epi16(T_hi, TL_hi);
+ const __m128i pred_lo = _mm_add_epi16(L_lo, diff_lo);
+ const __m128i pred_hi = _mm_add_epi16(L_hi, diff_hi);
+ const __m128i pred = _mm_packus_epi16(pred_lo, pred_hi);
+ const __m128i res = _mm_sub_epi8(src, pred);
+ _mm_storeu_si128((__m128i*)&out[i], res);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsSub_C[12](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+// Predictors13: ClampedAddSubtractHalf
+static void PredictorSub13_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ const __m128i zero = _mm_setzero_si128();
+ for (i = 0; i + 2 <= num_pixels; i += 2) {
+ // we can only process two pixels at a time
+ const __m128i L = _mm_loadl_epi64((const __m128i*)&in[i - 1]);
+ const __m128i src = _mm_loadl_epi64((const __m128i*)&in[i]);
+ const __m128i T = _mm_loadl_epi64((const __m128i*)&upper[i]);
+ const __m128i TL = _mm_loadl_epi64((const __m128i*)&upper[i - 1]);
+ const __m128i L_lo = _mm_unpacklo_epi8(L, zero);
+ const __m128i T_lo = _mm_unpacklo_epi8(T, zero);
+ const __m128i TL_lo = _mm_unpacklo_epi8(TL, zero);
+ const __m128i sum = _mm_add_epi16(T_lo, L_lo);
+ const __m128i avg = _mm_srli_epi16(sum, 1);
+ const __m128i A1 = _mm_sub_epi16(avg, TL_lo);
+ const __m128i bit_fix = _mm_cmpgt_epi16(TL_lo, avg);
+ const __m128i A2 = _mm_sub_epi16(A1, bit_fix);
+ const __m128i A3 = _mm_srai_epi16(A2, 1);
+ const __m128i A4 = _mm_add_epi16(avg, A3);
+ const __m128i pred = _mm_packus_epi16(A4, A4);
+ const __m128i res = _mm_sub_epi8(src, pred);
+ _mm_storel_epi64((__m128i*)&out[i], res);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsSub_C[13](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LEncDspInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE2(void) {
+ VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE2;
+ VP8LTransformColor = TransformColor_SSE2;
+ VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE2;
+ VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE2;
+ VP8LHistogramAdd = HistogramAdd_SSE2;
+ VP8LCombinedShannonEntropy = CombinedShannonEntropy_SSE2;
+ VP8LVectorMismatch = VectorMismatch_SSE2;
+ VP8LBundleColorMap = BundleColorMap_SSE2;
+
+ VP8LPredictorsSub[0] = PredictorSub0_SSE2;
+ VP8LPredictorsSub[1] = PredictorSub1_SSE2;
+ VP8LPredictorsSub[2] = PredictorSub2_SSE2;
+ VP8LPredictorsSub[3] = PredictorSub3_SSE2;
+ VP8LPredictorsSub[4] = PredictorSub4_SSE2;
+ VP8LPredictorsSub[5] = PredictorSub5_SSE2;
+ VP8LPredictorsSub[6] = PredictorSub6_SSE2;
+ VP8LPredictorsSub[7] = PredictorSub7_SSE2;
+ VP8LPredictorsSub[8] = PredictorSub8_SSE2;
+ VP8LPredictorsSub[9] = PredictorSub9_SSE2;
+ VP8LPredictorsSub[10] = PredictorSub10_SSE2;
+ VP8LPredictorsSub[11] = PredictorSub11_SSE2;
+ VP8LPredictorsSub[12] = PredictorSub12_SSE2;
+ VP8LPredictorsSub[13] = PredictorSub13_SSE2;
+ VP8LPredictorsSub[14] = PredictorSub0_SSE2; // <- padding security sentinels
+ VP8LPredictorsSub[15] = PredictorSub0_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(VP8LEncDspInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/lossless_enc_sse41.c b/src/third_party/libwebp/src/dsp/lossless_enc_sse41.c
new file mode 100644
index 0000000..2e12a71
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_enc_sse41.c
@@ -0,0 +1,148 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE4.1 variant of methods for lossless encoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE41)
+#include <assert.h>
+#include <smmintrin.h>
+#include "src/dsp/lossless.h"
+
+// For sign-extended multiplying constants, pre-shifted by 5:
+#define CST_5b(X) (((int16_t)((uint16_t)(X) << 8)) >> 5)
+
+//------------------------------------------------------------------------------
+// Subtract-Green Transform
+
+static void SubtractGreenFromBlueAndRed_SSE41(uint32_t* argb_data,
+ int num_pixels) {
+ int i;
+ const __m128i kCstShuffle = _mm_set_epi8(-1, 13, -1, 13, -1, 9, -1, 9,
+ -1, 5, -1, 5, -1, 1, -1, 1);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]);
+ const __m128i in_0g0g = _mm_shuffle_epi8(in, kCstShuffle);
+ const __m128i out = _mm_sub_epi8(in, in_0g0g);
+ _mm_storeu_si128((__m128i*)&argb_data[i], out);
+ }
+ // fallthrough and finish off with plain-C
+ if (i != num_pixels) {
+ VP8LSubtractGreenFromBlueAndRed_C(argb_data + i, num_pixels - i);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Color Transform
+
+#define SPAN 8
+static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_blue, int red_to_blue,
+ int histo[]) {
+ const __m128i mults_r = _mm_set1_epi16(CST_5b(red_to_blue));
+ const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_blue));
+ const __m128i mask_g = _mm_set1_epi16(0xff00); // green mask
+ const __m128i mask_gb = _mm_set1_epi32(0xffff); // green/blue mask
+ const __m128i mask_b = _mm_set1_epi16(0x00ff); // blue mask
+ const __m128i shuffler_lo = _mm_setr_epi8(-1, 2, -1, 6, -1, 10, -1, 14, -1,
+ -1, -1, -1, -1, -1, -1, -1);
+ const __m128i shuffler_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, -1, 6, -1, 10, -1, 14);
+ int y;
+ for (y = 0; y < tile_height; ++y) {
+ const uint32_t* const src = argb + y * stride;
+ int i, x;
+ for (x = 0; x + SPAN <= tile_width; x += SPAN) {
+ uint16_t values[SPAN];
+ const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
+ const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
+ const __m128i r0 = _mm_shuffle_epi8(in0, shuffler_lo);
+ const __m128i r1 = _mm_shuffle_epi8(in1, shuffler_hi);
+ const __m128i r = _mm_or_si128(r0, r1); // r 0
+ const __m128i gb0 = _mm_and_si128(in0, mask_gb);
+ const __m128i gb1 = _mm_and_si128(in1, mask_gb);
+ const __m128i gb = _mm_packus_epi32(gb0, gb1); // g b
+ const __m128i g = _mm_and_si128(gb, mask_g); // g 0
+ const __m128i A = _mm_mulhi_epi16(r, mults_r); // x dbr
+ const __m128i B = _mm_mulhi_epi16(g, mults_g); // x dbg
+ const __m128i C = _mm_sub_epi8(gb, B); // x b'
+ const __m128i D = _mm_sub_epi8(C, A); // x b''
+ const __m128i E = _mm_and_si128(D, mask_b); // 0 b''
+ _mm_storeu_si128((__m128i*)values, E);
+ for (i = 0; i < SPAN; ++i) ++histo[values[i]];
+ }
+ }
+ {
+ const int left_over = tile_width & (SPAN - 1);
+ if (left_over > 0) {
+ VP8LCollectColorBlueTransforms_C(argb + tile_width - left_over, stride,
+ left_over, tile_height,
+ green_to_blue, red_to_blue, histo);
+ }
+ }
+}
+
+static void CollectColorRedTransforms_SSE41(const uint32_t* argb, int stride,
+ int tile_width, int tile_height,
+ int green_to_red, int histo[]) {
+ const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_red));
+ const __m128i mask_g = _mm_set1_epi32(0x00ff00); // green mask
+ const __m128i mask = _mm_set1_epi16(0xff);
+
+ int y;
+ for (y = 0; y < tile_height; ++y) {
+ const uint32_t* const src = argb + y * stride;
+ int i, x;
+ for (x = 0; x + SPAN <= tile_width; x += SPAN) {
+ uint16_t values[SPAN];
+ const __m128i in0 = _mm_loadu_si128((__m128i*)&src[x + 0]);
+ const __m128i in1 = _mm_loadu_si128((__m128i*)&src[x + SPAN / 2]);
+ const __m128i g0 = _mm_and_si128(in0, mask_g); // 0 0 | g 0
+ const __m128i g1 = _mm_and_si128(in1, mask_g);
+ const __m128i g = _mm_packus_epi32(g0, g1); // g 0
+ const __m128i A0 = _mm_srli_epi32(in0, 16); // 0 0 | x r
+ const __m128i A1 = _mm_srli_epi32(in1, 16);
+ const __m128i A = _mm_packus_epi32(A0, A1); // x r
+ const __m128i B = _mm_mulhi_epi16(g, mults_g); // x dr
+ const __m128i C = _mm_sub_epi8(A, B); // x r'
+ const __m128i D = _mm_and_si128(C, mask); // 0 r'
+ _mm_storeu_si128((__m128i*)values, D);
+ for (i = 0; i < SPAN; ++i) ++histo[values[i]];
+ }
+ }
+ {
+ const int left_over = tile_width & (SPAN - 1);
+ if (left_over > 0) {
+ VP8LCollectColorRedTransforms_C(argb + tile_width - left_over, stride,
+ left_over, tile_height, green_to_red,
+ histo);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LEncDspInitSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE41(void) {
+ VP8LSubtractGreenFromBlueAndRed = SubtractGreenFromBlueAndRed_SSE41;
+ VP8LCollectColorBlueTransforms = CollectColorBlueTransforms_SSE41;
+ VP8LCollectColorRedTransforms = CollectColorRedTransforms_SSE41;
+}
+
+#else // !WEBP_USE_SSE41
+
+WEBP_DSP_INIT_STUB(VP8LEncDspInitSSE41)
+
+#endif // WEBP_USE_SSE41
diff --git a/src/third_party/libwebp/src/dsp/lossless_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/lossless_mips_dsp_r2.c
new file mode 100644
index 0000000..9888854
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_mips_dsp_r2.c
@@ -0,0 +1,696 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Image transforms and color space conversion methods for lossless decoder.
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+// Jovan Zelincevic (jovan.zelincevic@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+
+#define MAP_COLOR_FUNCS(FUNC_NAME, TYPE, GET_INDEX, GET_VALUE) \
+static void FUNC_NAME(const TYPE* src, \
+ const uint32_t* const color_map, \
+ TYPE* dst, int y_start, int y_end, \
+ int width) { \
+ int y; \
+ for (y = y_start; y < y_end; ++y) { \
+ int x; \
+ for (x = 0; x < (width >> 2); ++x) { \
+ int tmp1, tmp2, tmp3, tmp4; \
+ __asm__ volatile ( \
+ ".ifc " #TYPE ", uint8_t \n\t" \
+ "lbu %[tmp1], 0(%[src]) \n\t" \
+ "lbu %[tmp2], 1(%[src]) \n\t" \
+ "lbu %[tmp3], 2(%[src]) \n\t" \
+ "lbu %[tmp4], 3(%[src]) \n\t" \
+ "addiu %[src], %[src], 4 \n\t" \
+ ".endif \n\t" \
+ ".ifc " #TYPE ", uint32_t \n\t" \
+ "lw %[tmp1], 0(%[src]) \n\t" \
+ "lw %[tmp2], 4(%[src]) \n\t" \
+ "lw %[tmp3], 8(%[src]) \n\t" \
+ "lw %[tmp4], 12(%[src]) \n\t" \
+ "ext %[tmp1], %[tmp1], 8, 8 \n\t" \
+ "ext %[tmp2], %[tmp2], 8, 8 \n\t" \
+ "ext %[tmp3], %[tmp3], 8, 8 \n\t" \
+ "ext %[tmp4], %[tmp4], 8, 8 \n\t" \
+ "addiu %[src], %[src], 16 \n\t" \
+ ".endif \n\t" \
+ "sll %[tmp1], %[tmp1], 2 \n\t" \
+ "sll %[tmp2], %[tmp2], 2 \n\t" \
+ "sll %[tmp3], %[tmp3], 2 \n\t" \
+ "sll %[tmp4], %[tmp4], 2 \n\t" \
+ "lwx %[tmp1], %[tmp1](%[color_map]) \n\t" \
+ "lwx %[tmp2], %[tmp2](%[color_map]) \n\t" \
+ "lwx %[tmp3], %[tmp3](%[color_map]) \n\t" \
+ "lwx %[tmp4], %[tmp4](%[color_map]) \n\t" \
+ ".ifc " #TYPE ", uint8_t \n\t" \
+ "ext %[tmp1], %[tmp1], 8, 8 \n\t" \
+ "ext %[tmp2], %[tmp2], 8, 8 \n\t" \
+ "ext %[tmp3], %[tmp3], 8, 8 \n\t" \
+ "ext %[tmp4], %[tmp4], 8, 8 \n\t" \
+ "sb %[tmp1], 0(%[dst]) \n\t" \
+ "sb %[tmp2], 1(%[dst]) \n\t" \
+ "sb %[tmp3], 2(%[dst]) \n\t" \
+ "sb %[tmp4], 3(%[dst]) \n\t" \
+ "addiu %[dst], %[dst], 4 \n\t" \
+ ".endif \n\t" \
+ ".ifc " #TYPE ", uint32_t \n\t" \
+ "sw %[tmp1], 0(%[dst]) \n\t" \
+ "sw %[tmp2], 4(%[dst]) \n\t" \
+ "sw %[tmp3], 8(%[dst]) \n\t" \
+ "sw %[tmp4], 12(%[dst]) \n\t" \
+ "addiu %[dst], %[dst], 16 \n\t" \
+ ".endif \n\t" \
+ : [tmp1]"=&r"(tmp1), [tmp2]"=&r"(tmp2), [tmp3]"=&r"(tmp3), \
+ [tmp4]"=&r"(tmp4), [src]"+&r"(src), [dst]"+r"(dst) \
+ : [color_map]"r"(color_map) \
+ : "memory" \
+ ); \
+ } \
+ for (x = 0; x < (width & 3); ++x) { \
+ *dst++ = GET_VALUE(color_map[GET_INDEX(*src++)]); \
+ } \
+ } \
+}
+
+MAP_COLOR_FUNCS(MapARGB_MIPSdspR2, uint32_t, VP8GetARGBIndex, VP8GetARGBValue)
+MAP_COLOR_FUNCS(MapAlpha_MIPSdspR2, uint8_t, VP8GetAlphaIndex, VP8GetAlphaValue)
+
+#undef MAP_COLOR_FUNCS
+
+static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
+ uint32_t c2) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ __asm__ volatile (
+ "preceu.ph.qbr %[temp1], %[c0] \n\t"
+ "preceu.ph.qbl %[temp2], %[c0] \n\t"
+ "preceu.ph.qbr %[temp3], %[c1] \n\t"
+ "preceu.ph.qbl %[temp4], %[c1] \n\t"
+ "preceu.ph.qbr %[temp5], %[c2] \n\t"
+ "preceu.ph.qbl %[temp0], %[c2] \n\t"
+ "subq.ph %[temp3], %[temp3], %[temp5] \n\t"
+ "subq.ph %[temp4], %[temp4], %[temp0] \n\t"
+ "addq.ph %[temp1], %[temp1], %[temp3] \n\t"
+ "addq.ph %[temp2], %[temp2], %[temp4] \n\t"
+ "shll_s.ph %[temp1], %[temp1], 7 \n\t"
+ "shll_s.ph %[temp2], %[temp2], 7 \n\t"
+ "precrqu_s.qb.ph %[temp2], %[temp2], %[temp1] \n\t"
+ : [temp0]"=r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5)
+ : [c0]"r"(c0), [c1]"r"(c1), [c2]"r"(c2)
+ : "memory"
+ );
+ return temp2;
+}
+
+static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
+ uint32_t c2) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ __asm__ volatile (
+ "adduh.qb %[temp5], %[c0], %[c1] \n\t"
+ "preceu.ph.qbr %[temp3], %[c2] \n\t"
+ "preceu.ph.qbr %[temp1], %[temp5] \n\t"
+ "preceu.ph.qbl %[temp2], %[temp5] \n\t"
+ "preceu.ph.qbl %[temp4], %[c2] \n\t"
+ "subq.ph %[temp3], %[temp1], %[temp3] \n\t"
+ "subq.ph %[temp4], %[temp2], %[temp4] \n\t"
+ "shrl.ph %[temp5], %[temp3], 15 \n\t"
+ "shrl.ph %[temp0], %[temp4], 15 \n\t"
+ "addq.ph %[temp3], %[temp3], %[temp5] \n\t"
+ "addq.ph %[temp4], %[temp0], %[temp4] \n\t"
+ "shra.ph %[temp3], %[temp3], 1 \n\t"
+ "shra.ph %[temp4], %[temp4], 1 \n\t"
+ "addq.ph %[temp1], %[temp1], %[temp3] \n\t"
+ "addq.ph %[temp2], %[temp2], %[temp4] \n\t"
+ "shll_s.ph %[temp1], %[temp1], 7 \n\t"
+ "shll_s.ph %[temp2], %[temp2], 7 \n\t"
+ "precrqu_s.qb.ph %[temp1], %[temp2], %[temp1] \n\t"
+ : [temp0]"=r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=r"(temp4), [temp5]"=&r"(temp5)
+ : [c0]"r"(c0), [c1]"r"(c1), [c2]"r"(c2)
+ : "memory"
+ );
+ return temp1;
+}
+
+static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ __asm__ volatile (
+ "cmpgdu.lt.qb %[temp1], %[c], %[b] \n\t"
+ "pick.qb %[temp1], %[b], %[c] \n\t"
+ "pick.qb %[temp2], %[c], %[b] \n\t"
+ "cmpgdu.lt.qb %[temp4], %[c], %[a] \n\t"
+ "pick.qb %[temp4], %[a], %[c] \n\t"
+ "pick.qb %[temp5], %[c], %[a] \n\t"
+ "subu.qb %[temp3], %[temp1], %[temp2] \n\t"
+ "subu.qb %[temp0], %[temp4], %[temp5] \n\t"
+ "raddu.w.qb %[temp3], %[temp3] \n\t"
+ "raddu.w.qb %[temp0], %[temp0] \n\t"
+ "subu %[temp3], %[temp3], %[temp0] \n\t"
+ "slti %[temp0], %[temp3], 0x1 \n\t"
+ "movz %[a], %[b], %[temp0] \n\t"
+ : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp0]"=&r"(temp0),
+ [a]"+&r"(a)
+ : [b]"r"(b), [c]"r"(c)
+ );
+ return a;
+}
+
+static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) {
+ __asm__ volatile (
+ "adduh.qb %[a0], %[a0], %[a1] \n\t"
+ : [a0]"+r"(a0)
+ : [a1]"r"(a1)
+ );
+ return a0;
+}
+
+static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) {
+ return Average2(Average2(a0, a2), a1);
+}
+
+static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1,
+ uint32_t a2, uint32_t a3) {
+ return Average2(Average2(a0, a1), Average2(a2, a3));
+}
+
+static uint32_t Predictor5_MIPSdspR2(uint32_t left, const uint32_t* const top) {
+ return Average3(left, top[0], top[1]);
+}
+
+static uint32_t Predictor6_MIPSdspR2(uint32_t left, const uint32_t* const top) {
+ return Average2(left, top[-1]);
+}
+
+static uint32_t Predictor7_MIPSdspR2(uint32_t left, const uint32_t* const top) {
+ return Average2(left, top[0]);
+}
+
+static uint32_t Predictor8_MIPSdspR2(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return Average2(top[-1], top[0]);
+}
+
+static uint32_t Predictor9_MIPSdspR2(uint32_t left, const uint32_t* const top) {
+ (void)left;
+ return Average2(top[0], top[1]);
+}
+
+static uint32_t Predictor10_MIPSdspR2(uint32_t left,
+ const uint32_t* const top) {
+ return Average4(left, top[-1], top[0], top[1]);
+}
+
+static uint32_t Predictor11_MIPSdspR2(uint32_t left,
+ const uint32_t* const top) {
+ return Select(top[0], left, top[-1]);
+}
+
+static uint32_t Predictor12_MIPSdspR2(uint32_t left,
+ const uint32_t* const top) {
+ return ClampedAddSubtractFull(left, top[0], top[-1]);
+}
+
+static uint32_t Predictor13_MIPSdspR2(uint32_t left,
+ const uint32_t* const top) {
+ return ClampedAddSubtractHalf(left, top[0], top[-1]);
+}
+
+// Add green to blue and red channels (i.e. perform the inverse transform of
+// 'subtract green').
+static void AddGreenToBlueAndRed_MIPSdspR2(const uint32_t* src, int num_pixels,
+ uint32_t* dst) {
+ uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
+ const uint32_t* const p_loop1_end = src + (num_pixels & ~3);
+ const uint32_t* const p_loop2_end = src + num_pixels;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[src], %[p_loop1_end], 3f \n\t"
+ " nop \n\t"
+ "0: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "lw %[temp1], 4(%[src]) \n\t"
+ "lw %[temp2], 8(%[src]) \n\t"
+ "lw %[temp3], 12(%[src]) \n\t"
+ "ext %[temp4], %[temp0], 8, 8 \n\t"
+ "ext %[temp5], %[temp1], 8, 8 \n\t"
+ "ext %[temp6], %[temp2], 8, 8 \n\t"
+ "ext %[temp7], %[temp3], 8, 8 \n\t"
+ "addiu %[src], %[src], 16 \n\t"
+ "addiu %[dst], %[dst], 16 \n\t"
+ "replv.ph %[temp4], %[temp4] \n\t"
+ "replv.ph %[temp5], %[temp5] \n\t"
+ "replv.ph %[temp6], %[temp6] \n\t"
+ "replv.ph %[temp7], %[temp7] \n\t"
+ "addu.qb %[temp0], %[temp0], %[temp4] \n\t"
+ "addu.qb %[temp1], %[temp1], %[temp5] \n\t"
+ "addu.qb %[temp2], %[temp2], %[temp6] \n\t"
+ "addu.qb %[temp3], %[temp3], %[temp7] \n\t"
+ "sw %[temp0], -16(%[dst]) \n\t"
+ "sw %[temp1], -12(%[dst]) \n\t"
+ "sw %[temp2], -8(%[dst]) \n\t"
+ "bne %[src], %[p_loop1_end], 0b \n\t"
+ " sw %[temp3], -4(%[dst]) \n\t"
+ "3: \n\t"
+ "beq %[src], %[p_loop2_end], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "addiu %[src], %[src], 4 \n\t"
+ "addiu %[dst], %[dst], 4 \n\t"
+ "ext %[temp4], %[temp0], 8, 8 \n\t"
+ "replv.ph %[temp4], %[temp4] \n\t"
+ "addu.qb %[temp0], %[temp0], %[temp4] \n\t"
+ "bne %[src], %[p_loop2_end], 1b \n\t"
+ " sw %[temp0], -4(%[dst]) \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [dst]"+&r"(dst), [src]"+&r"(src), [temp0]"=&r"(temp0),
+ [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp6]"=&r"(temp6),
+ [temp7]"=&r"(temp7)
+ : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end)
+ : "memory"
+ );
+}
+
+static void TransformColorInverse_MIPSdspR2(const VP8LMultipliers* const m,
+ const uint32_t* src, int num_pixels,
+ uint32_t* dst) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ uint32_t argb, argb1, new_red;
+ const uint32_t G_to_R = m->green_to_red_;
+ const uint32_t G_to_B = m->green_to_blue_;
+ const uint32_t R_to_B = m->red_to_blue_;
+ const uint32_t* const p_loop_end = src + (num_pixels & ~1);
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[src], %[p_loop_end], 1f \n\t"
+ " nop \n\t"
+ "replv.ph %[temp0], %[G_to_R] \n\t"
+ "replv.ph %[temp1], %[G_to_B] \n\t"
+ "replv.ph %[temp2], %[R_to_B] \n\t"
+ "shll.ph %[temp0], %[temp0], 8 \n\t"
+ "shll.ph %[temp1], %[temp1], 8 \n\t"
+ "shll.ph %[temp2], %[temp2], 8 \n\t"
+ "shra.ph %[temp0], %[temp0], 8 \n\t"
+ "shra.ph %[temp1], %[temp1], 8 \n\t"
+ "shra.ph %[temp2], %[temp2], 8 \n\t"
+ "0: \n\t"
+ "lw %[argb], 0(%[src]) \n\t"
+ "lw %[argb1], 4(%[src]) \n\t"
+ "sw %[argb], 0(%[dst]) \n\t"
+ "sw %[argb1], 4(%[dst]) \n\t"
+ "addiu %[src], %[src], 8 \n\t"
+ "addiu %[dst], %[dst], 8 \n\t"
+ "precrq.qb.ph %[temp3], %[argb], %[argb1] \n\t"
+ "preceu.ph.qbra %[temp3], %[temp3] \n\t"
+ "shll.ph %[temp3], %[temp3], 8 \n\t"
+ "shra.ph %[temp3], %[temp3], 8 \n\t"
+ "mul.ph %[temp5], %[temp3], %[temp0] \n\t"
+ "mul.ph %[temp3], %[temp3], %[temp1] \n\t"
+ "precrq.ph.w %[new_red], %[argb], %[argb1] \n\t"
+ "ins %[argb1], %[argb], 16, 16 \n\t"
+ "shra.ph %[temp5], %[temp5], 5 \n\t"
+ "shra.ph %[temp3], %[temp3], 5 \n\t"
+ "addu.ph %[new_red], %[new_red], %[temp5] \n\t"
+ "addu.ph %[argb1], %[argb1], %[temp3] \n\t"
+ "preceu.ph.qbra %[temp5], %[new_red] \n\t"
+ "shll.ph %[temp4], %[temp5], 8 \n\t"
+ "shra.ph %[temp4], %[temp4], 8 \n\t"
+ "mul.ph %[temp4], %[temp4], %[temp2] \n\t"
+ "sb %[temp5], -2(%[dst]) \n\t"
+ "sra %[temp5], %[temp5], 16 \n\t"
+ "shra.ph %[temp4], %[temp4], 5 \n\t"
+ "addu.ph %[argb1], %[argb1], %[temp4] \n\t"
+ "preceu.ph.qbra %[temp3], %[argb1] \n\t"
+ "sb %[temp5], -6(%[dst]) \n\t"
+ "sb %[temp3], -4(%[dst]) \n\t"
+ "sra %[temp3], %[temp3], 16 \n\t"
+ "bne %[src], %[p_loop_end], 0b \n\t"
+ " sb %[temp3], -8(%[dst]) \n\t"
+ "1: \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [new_red]"=&r"(new_red), [argb]"=&r"(argb),
+ [argb1]"=&r"(argb1), [dst]"+&r"(dst), [src]"+&r"(src)
+ : [G_to_R]"r"(G_to_R), [R_to_B]"r"(R_to_B),
+ [G_to_B]"r"(G_to_B), [p_loop_end]"r"(p_loop_end)
+ : "memory", "hi", "lo"
+ );
+
+ // Fall-back to C-version for left-overs.
+ if (num_pixels & 1) VP8LTransformColorInverse_C(m, src, 1, dst);
+}
+
+static void ConvertBGRAToRGB_MIPSdspR2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ int temp0, temp1, temp2, temp3;
+ const uint32_t* const p_loop1_end = src + (num_pixels & ~3);
+ const uint32_t* const p_loop2_end = src + num_pixels;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[src], %[p_loop1_end], 3f \n\t"
+ " nop \n\t"
+ "0: \n\t"
+ "lw %[temp3], 12(%[src]) \n\t"
+ "lw %[temp2], 8(%[src]) \n\t"
+ "lw %[temp1], 4(%[src]) \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "ins %[temp3], %[temp2], 24, 8 \n\t"
+ "sll %[temp2], %[temp2], 8 \n\t"
+ "rotr %[temp3], %[temp3], 16 \n\t"
+ "ins %[temp2], %[temp1], 0, 16 \n\t"
+ "sll %[temp1], %[temp1], 8 \n\t"
+ "wsbh %[temp3], %[temp3] \n\t"
+ "balign %[temp0], %[temp1], 1 \n\t"
+ "wsbh %[temp2], %[temp2] \n\t"
+ "wsbh %[temp0], %[temp0] \n\t"
+ "usw %[temp3], 8(%[dst]) \n\t"
+ "rotr %[temp0], %[temp0], 16 \n\t"
+ "usw %[temp2], 4(%[dst]) \n\t"
+ "addiu %[src], %[src], 16 \n\t"
+ "usw %[temp0], 0(%[dst]) \n\t"
+ "bne %[src], %[p_loop1_end], 0b \n\t"
+ " addiu %[dst], %[dst], 12 \n\t"
+ "3: \n\t"
+ "beq %[src], %[p_loop2_end], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "addiu %[src], %[src], 4 \n\t"
+ "wsbh %[temp1], %[temp0] \n\t"
+ "addiu %[dst], %[dst], 3 \n\t"
+ "ush %[temp1], -2(%[dst]) \n\t"
+ "sra %[temp0], %[temp0], 16 \n\t"
+ "bne %[src], %[p_loop2_end], 1b \n\t"
+ " sb %[temp0], -3(%[dst]) \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [dst]"+&r"(dst), [src]"+&r"(src)
+ : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end)
+ : "memory"
+ );
+}
+
+static void ConvertBGRAToRGBA_MIPSdspR2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ int temp0, temp1, temp2, temp3;
+ const uint32_t* const p_loop1_end = src + (num_pixels & ~3);
+ const uint32_t* const p_loop2_end = src + num_pixels;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[src], %[p_loop1_end], 3f \n\t"
+ " nop \n\t"
+ "0: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "lw %[temp1], 4(%[src]) \n\t"
+ "lw %[temp2], 8(%[src]) \n\t"
+ "lw %[temp3], 12(%[src]) \n\t"
+ "wsbh %[temp0], %[temp0] \n\t"
+ "wsbh %[temp1], %[temp1] \n\t"
+ "wsbh %[temp2], %[temp2] \n\t"
+ "wsbh %[temp3], %[temp3] \n\t"
+ "addiu %[src], %[src], 16 \n\t"
+ "balign %[temp0], %[temp0], 1 \n\t"
+ "balign %[temp1], %[temp1], 1 \n\t"
+ "balign %[temp2], %[temp2], 1 \n\t"
+ "balign %[temp3], %[temp3], 1 \n\t"
+ "usw %[temp0], 0(%[dst]) \n\t"
+ "usw %[temp1], 4(%[dst]) \n\t"
+ "usw %[temp2], 8(%[dst]) \n\t"
+ "usw %[temp3], 12(%[dst]) \n\t"
+ "bne %[src], %[p_loop1_end], 0b \n\t"
+ " addiu %[dst], %[dst], 16 \n\t"
+ "3: \n\t"
+ "beq %[src], %[p_loop2_end], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "wsbh %[temp0], %[temp0] \n\t"
+ "addiu %[src], %[src], 4 \n\t"
+ "balign %[temp0], %[temp0], 1 \n\t"
+ "usw %[temp0], 0(%[dst]) \n\t"
+ "bne %[src], %[p_loop2_end], 1b \n\t"
+ " addiu %[dst], %[dst], 4 \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [dst]"+&r"(dst), [src]"+&r"(src)
+ : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end)
+ : "memory"
+ );
+}
+
+static void ConvertBGRAToRGBA4444_MIPSdspR2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ const uint32_t* const p_loop1_end = src + (num_pixels & ~3);
+ const uint32_t* const p_loop2_end = src + num_pixels;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[src], %[p_loop1_end], 3f \n\t"
+ " nop \n\t"
+ "0: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "lw %[temp1], 4(%[src]) \n\t"
+ "lw %[temp2], 8(%[src]) \n\t"
+ "lw %[temp3], 12(%[src]) \n\t"
+ "ext %[temp4], %[temp0], 28, 4 \n\t"
+ "ext %[temp5], %[temp0], 12, 4 \n\t"
+ "ins %[temp0], %[temp4], 0, 4 \n\t"
+ "ext %[temp4], %[temp1], 28, 4 \n\t"
+ "ins %[temp0], %[temp5], 16, 4 \n\t"
+ "ext %[temp5], %[temp1], 12, 4 \n\t"
+ "ins %[temp1], %[temp4], 0, 4 \n\t"
+ "ext %[temp4], %[temp2], 28, 4 \n\t"
+ "ins %[temp1], %[temp5], 16, 4 \n\t"
+ "ext %[temp5], %[temp2], 12, 4 \n\t"
+ "ins %[temp2], %[temp4], 0, 4 \n\t"
+ "ext %[temp4], %[temp3], 28, 4 \n\t"
+ "ins %[temp2], %[temp5], 16, 4 \n\t"
+ "ext %[temp5], %[temp3], 12, 4 \n\t"
+ "ins %[temp3], %[temp4], 0, 4 \n\t"
+ "precr.qb.ph %[temp1], %[temp1], %[temp0] \n\t"
+ "ins %[temp3], %[temp5], 16, 4 \n\t"
+ "addiu %[src], %[src], 16 \n\t"
+ "precr.qb.ph %[temp3], %[temp3], %[temp2] \n\t"
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ "usw %[temp1], 0(%[dst]) \n\t"
+ "usw %[temp3], 4(%[dst]) \n\t"
+#else
+ "wsbh %[temp1], %[temp1] \n\t"
+ "wsbh %[temp3], %[temp3] \n\t"
+ "usw %[temp1], 0(%[dst]) \n\t"
+ "usw %[temp3], 4(%[dst]) \n\t"
+#endif
+ "bne %[src], %[p_loop1_end], 0b \n\t"
+ " addiu %[dst], %[dst], 8 \n\t"
+ "3: \n\t"
+ "beq %[src], %[p_loop2_end], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "ext %[temp4], %[temp0], 28, 4 \n\t"
+ "ext %[temp5], %[temp0], 12, 4 \n\t"
+ "ins %[temp0], %[temp4], 0, 4 \n\t"
+ "ins %[temp0], %[temp5], 16, 4 \n\t"
+ "addiu %[src], %[src], 4 \n\t"
+ "precr.qb.ph %[temp0], %[temp0], %[temp0] \n\t"
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ "ush %[temp0], 0(%[dst]) \n\t"
+#else
+ "wsbh %[temp0], %[temp0] \n\t"
+ "ush %[temp0], 0(%[dst]) \n\t"
+#endif
+ "bne %[src], %[p_loop2_end], 1b \n\t"
+ " addiu %[dst], %[dst], 2 \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [dst]"+&r"(dst), [src]"+&r"(src)
+ : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end)
+ : "memory"
+ );
+}
+
+static void ConvertBGRAToRGB565_MIPSdspR2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ int temp0, temp1, temp2, temp3, temp4, temp5;
+ const uint32_t* const p_loop1_end = src + (num_pixels & ~3);
+ const uint32_t* const p_loop2_end = src + num_pixels;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[src], %[p_loop1_end], 3f \n\t"
+ " nop \n\t"
+ "0: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "lw %[temp1], 4(%[src]) \n\t"
+ "lw %[temp2], 8(%[src]) \n\t"
+ "lw %[temp3], 12(%[src]) \n\t"
+ "ext %[temp4], %[temp0], 8, 16 \n\t"
+ "ext %[temp5], %[temp0], 5, 11 \n\t"
+ "ext %[temp0], %[temp0], 3, 5 \n\t"
+ "ins %[temp4], %[temp5], 0, 11 \n\t"
+ "ext %[temp5], %[temp1], 5, 11 \n\t"
+ "ins %[temp4], %[temp0], 0, 5 \n\t"
+ "ext %[temp0], %[temp1], 8, 16 \n\t"
+ "ext %[temp1], %[temp1], 3, 5 \n\t"
+ "ins %[temp0], %[temp5], 0, 11 \n\t"
+ "ext %[temp5], %[temp2], 5, 11 \n\t"
+ "ins %[temp0], %[temp1], 0, 5 \n\t"
+ "ext %[temp1], %[temp2], 8, 16 \n\t"
+ "ext %[temp2], %[temp2], 3, 5 \n\t"
+ "ins %[temp1], %[temp5], 0, 11 \n\t"
+ "ext %[temp5], %[temp3], 5, 11 \n\t"
+ "ins %[temp1], %[temp2], 0, 5 \n\t"
+ "ext %[temp2], %[temp3], 8, 16 \n\t"
+ "ext %[temp3], %[temp3], 3, 5 \n\t"
+ "ins %[temp2], %[temp5], 0, 11 \n\t"
+ "append %[temp0], %[temp4], 16 \n\t"
+ "ins %[temp2], %[temp3], 0, 5 \n\t"
+ "addiu %[src], %[src], 16 \n\t"
+ "append %[temp2], %[temp1], 16 \n\t"
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ "usw %[temp0], 0(%[dst]) \n\t"
+ "usw %[temp2], 4(%[dst]) \n\t"
+#else
+ "wsbh %[temp0], %[temp0] \n\t"
+ "wsbh %[temp2], %[temp2] \n\t"
+ "usw %[temp0], 0(%[dst]) \n\t"
+ "usw %[temp2], 4(%[dst]) \n\t"
+#endif
+ "bne %[src], %[p_loop1_end], 0b \n\t"
+ " addiu %[dst], %[dst], 8 \n\t"
+ "3: \n\t"
+ "beq %[src], %[p_loop2_end], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "ext %[temp4], %[temp0], 8, 16 \n\t"
+ "ext %[temp5], %[temp0], 5, 11 \n\t"
+ "ext %[temp0], %[temp0], 3, 5 \n\t"
+ "ins %[temp4], %[temp5], 0, 11 \n\t"
+ "addiu %[src], %[src], 4 \n\t"
+ "ins %[temp4], %[temp0], 0, 5 \n\t"
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ "ush %[temp4], 0(%[dst]) \n\t"
+#else
+ "wsbh %[temp4], %[temp4] \n\t"
+ "ush %[temp4], 0(%[dst]) \n\t"
+#endif
+ "bne %[src], %[p_loop2_end], 1b \n\t"
+ " addiu %[dst], %[dst], 2 \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5),
+ [dst]"+&r"(dst), [src]"+&r"(src)
+ : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end)
+ : "memory"
+ );
+}
+
+static void ConvertBGRAToBGR_MIPSdspR2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ int temp0, temp1, temp2, temp3;
+ const uint32_t* const p_loop1_end = src + (num_pixels & ~3);
+ const uint32_t* const p_loop2_end = src + num_pixels;
+ __asm__ volatile (
+ ".set push \n\t"
+ ".set noreorder \n\t"
+ "beq %[src], %[p_loop1_end], 3f \n\t"
+ " nop \n\t"
+ "0: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "lw %[temp1], 4(%[src]) \n\t"
+ "lw %[temp2], 8(%[src]) \n\t"
+ "lw %[temp3], 12(%[src]) \n\t"
+ "ins %[temp0], %[temp1], 24, 8 \n\t"
+ "sra %[temp1], %[temp1], 8 \n\t"
+ "ins %[temp1], %[temp2], 16, 16 \n\t"
+ "sll %[temp2], %[temp2], 8 \n\t"
+ "balign %[temp3], %[temp2], 1 \n\t"
+ "addiu %[src], %[src], 16 \n\t"
+ "usw %[temp0], 0(%[dst]) \n\t"
+ "usw %[temp1], 4(%[dst]) \n\t"
+ "usw %[temp3], 8(%[dst]) \n\t"
+ "bne %[src], %[p_loop1_end], 0b \n\t"
+ " addiu %[dst], %[dst], 12 \n\t"
+ "3: \n\t"
+ "beq %[src], %[p_loop2_end], 2f \n\t"
+ " nop \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[src]) \n\t"
+ "addiu %[src], %[src], 4 \n\t"
+ "addiu %[dst], %[dst], 3 \n\t"
+ "ush %[temp0], -3(%[dst]) \n\t"
+ "sra %[temp0], %[temp0], 16 \n\t"
+ "bne %[src], %[p_loop2_end], 1b \n\t"
+ " sb %[temp0], -1(%[dst]) \n\t"
+ "2: \n\t"
+ ".set pop \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2),
+ [temp3]"=&r"(temp3), [dst]"+&r"(dst), [src]"+&r"(src)
+ : [p_loop1_end]"r"(p_loop1_end), [p_loop2_end]"r"(p_loop2_end)
+ : "memory"
+ );
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LDspInitMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitMIPSdspR2(void) {
+ VP8LMapColor32b = MapARGB_MIPSdspR2;
+ VP8LMapColor8b = MapAlpha_MIPSdspR2;
+
+ VP8LPredictors[5] = Predictor5_MIPSdspR2;
+ VP8LPredictors[6] = Predictor6_MIPSdspR2;
+ VP8LPredictors[7] = Predictor7_MIPSdspR2;
+ VP8LPredictors[8] = Predictor8_MIPSdspR2;
+ VP8LPredictors[9] = Predictor9_MIPSdspR2;
+ VP8LPredictors[10] = Predictor10_MIPSdspR2;
+ VP8LPredictors[11] = Predictor11_MIPSdspR2;
+ VP8LPredictors[12] = Predictor12_MIPSdspR2;
+ VP8LPredictors[13] = Predictor13_MIPSdspR2;
+
+ VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_MIPSdspR2;
+ VP8LTransformColorInverse = TransformColorInverse_MIPSdspR2;
+
+ VP8LConvertBGRAToRGB = ConvertBGRAToRGB_MIPSdspR2;
+ VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_MIPSdspR2;
+ VP8LConvertBGRAToRGBA4444 = ConvertBGRAToRGBA4444_MIPSdspR2;
+ VP8LConvertBGRAToRGB565 = ConvertBGRAToRGB565_MIPSdspR2;
+ VP8LConvertBGRAToBGR = ConvertBGRAToBGR_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(VP8LDspInitMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/lossless_msa.c b/src/third_party/libwebp/src/dsp/lossless_msa.c
new file mode 100644
index 0000000..9f54720
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_msa.c
@@ -0,0 +1,356 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA variant of methods for lossless decoder
+//
+// Author: Prashant Patil (prashant.patil@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA)
+
+#include "src/dsp/lossless.h"
+#include "src/dsp/msa_macro.h"
+
+//------------------------------------------------------------------------------
+// Colorspace conversion functions
+
+#define CONVERT16_BGRA_XXX(psrc, pdst, m0, m1, m2) do { \
+ v16u8 src0, src1, src2, src3, dst0, dst1, dst2; \
+ LD_UB4(psrc, 16, src0, src1, src2, src3); \
+ VSHF_B2_UB(src0, src1, src1, src2, m0, m1, dst0, dst1); \
+ dst2 = VSHF_UB(src2, src3, m2); \
+ ST_UB2(dst0, dst1, pdst, 16); \
+ ST_UB(dst2, pdst + 32); \
+} while (0)
+
+#define CONVERT12_BGRA_XXX(psrc, pdst, m0, m1, m2) do { \
+ uint32_t pix_w; \
+ v16u8 src0, src1, src2, dst0, dst1, dst2; \
+ LD_UB3(psrc, 16, src0, src1, src2); \
+ VSHF_B2_UB(src0, src1, src1, src2, m0, m1, dst0, dst1); \
+ dst2 = VSHF_UB(src2, src2, m2); \
+ ST_UB2(dst0, dst1, pdst, 16); \
+ pix_w = __msa_copy_s_w((v4i32)dst2, 0); \
+ SW(pix_w, pdst + 32); \
+} while (0)
+
+#define CONVERT8_BGRA_XXX(psrc, pdst, m0, m1) do { \
+ uint64_t pix_d; \
+ v16u8 src0, src1, src2 = { 0 }, dst0, dst1; \
+ LD_UB2(psrc, 16, src0, src1); \
+ VSHF_B2_UB(src0, src1, src1, src2, m0, m1, dst0, dst1); \
+ ST_UB(dst0, pdst); \
+ pix_d = __msa_copy_s_d((v2i64)dst1, 0); \
+ SD(pix_d, pdst + 16); \
+} while (0)
+
+#define CONVERT4_BGRA_XXX(psrc, pdst, m) do { \
+ const v16u8 src0 = LD_UB(psrc); \
+ const v16u8 dst0 = VSHF_UB(src0, src0, m); \
+ uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0); \
+ uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 2); \
+ SD(pix_d, pdst + 0); \
+ SW(pix_w, pdst + 8); \
+} while (0)
+
+#define CONVERT1_BGRA_BGR(psrc, pdst) do { \
+ const int32_t b = (psrc)[0]; \
+ const int32_t g = (psrc)[1]; \
+ const int32_t r = (psrc)[2]; \
+ (pdst)[0] = b; \
+ (pdst)[1] = g; \
+ (pdst)[2] = r; \
+} while (0)
+
+#define CONVERT1_BGRA_RGB(psrc, pdst) do { \
+ const int32_t b = (psrc)[0]; \
+ const int32_t g = (psrc)[1]; \
+ const int32_t r = (psrc)[2]; \
+ (pdst)[0] = r; \
+ (pdst)[1] = g; \
+ (pdst)[2] = b; \
+} while (0)
+
+#define TRANSFORM_COLOR_INVERSE_8(src0, src1, dst0, dst1, \
+ c0, c1, mask0, mask1) do { \
+ v8i16 g0, g1, t0, t1, t2, t3; \
+ v4i32 t4, t5; \
+ VSHF_B2_SH(src0, src0, src1, src1, mask0, mask0, g0, g1); \
+ DOTP_SB2_SH(g0, g1, c0, c0, t0, t1); \
+ SRAI_H2_SH(t0, t1, 5); \
+ t0 = __msa_addv_h(t0, (v8i16)src0); \
+ t1 = __msa_addv_h(t1, (v8i16)src1); \
+ t4 = __msa_srli_w((v4i32)t0, 16); \
+ t5 = __msa_srli_w((v4i32)t1, 16); \
+ DOTP_SB2_SH(t4, t5, c1, c1, t2, t3); \
+ SRAI_H2_SH(t2, t3, 5); \
+ ADD2(t0, t2, t1, t3, t0, t1); \
+ VSHF_B2_UB(src0, t0, src1, t1, mask1, mask1, dst0, dst1); \
+} while (0)
+
+#define TRANSFORM_COLOR_INVERSE_4(src, dst, c0, c1, mask0, mask1) do { \
+ const v16i8 g0 = VSHF_SB(src, src, mask0); \
+ v8i16 t0 = __msa_dotp_s_h(c0, g0); \
+ v8i16 t1; \
+ v4i32 t2; \
+ t0 = SRAI_H(t0, 5); \
+ t0 = __msa_addv_h(t0, (v8i16)src); \
+ t2 = __msa_srli_w((v4i32)t0, 16); \
+ t1 = __msa_dotp_s_h(c1, (v16i8)t2); \
+ t1 = SRAI_H(t1, 5); \
+ t0 = t0 + t1; \
+ dst = VSHF_UB(src, t0, mask1); \
+} while (0)
+
+static void ConvertBGRAToRGBA_MSA(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ int i;
+ const uint8_t* ptemp_src = (const uint8_t*)src;
+ uint8_t* ptemp_dst = (uint8_t*)dst;
+ v16u8 src0, dst0;
+ const v16u8 mask = { 2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15 };
+
+ while (num_pixels >= 8) {
+ v16u8 src1, dst1;
+ LD_UB2(ptemp_src, 16, src0, src1);
+ VSHF_B2_UB(src0, src0, src1, src1, mask, mask, dst0, dst1);
+ ST_UB2(dst0, dst1, ptemp_dst, 16);
+ ptemp_src += 32;
+ ptemp_dst += 32;
+ num_pixels -= 8;
+ }
+ if (num_pixels > 0) {
+ if (num_pixels >= 4) {
+ src0 = LD_UB(ptemp_src);
+ dst0 = VSHF_UB(src0, src0, mask);
+ ST_UB(dst0, ptemp_dst);
+ ptemp_src += 16;
+ ptemp_dst += 16;
+ num_pixels -= 4;
+ }
+ for (i = 0; i < num_pixels; i++) {
+ const uint8_t b = ptemp_src[2];
+ const uint8_t g = ptemp_src[1];
+ const uint8_t r = ptemp_src[0];
+ const uint8_t a = ptemp_src[3];
+ ptemp_dst[0] = b;
+ ptemp_dst[1] = g;
+ ptemp_dst[2] = r;
+ ptemp_dst[3] = a;
+ ptemp_src += 4;
+ ptemp_dst += 4;
+ }
+ }
+}
+
+static void ConvertBGRAToBGR_MSA(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint8_t* ptemp_src = (const uint8_t*)src;
+ uint8_t* ptemp_dst = (uint8_t*)dst;
+ const v16u8 mask0 = { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14,
+ 16, 17, 18, 20 };
+ const v16u8 mask1 = { 5, 6, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20,
+ 21, 22, 24, 25 };
+ const v16u8 mask2 = { 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25,
+ 26, 28, 29, 30 };
+
+ while (num_pixels >= 16) {
+ CONVERT16_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2);
+ ptemp_src += 64;
+ ptemp_dst += 48;
+ num_pixels -= 16;
+ }
+ if (num_pixels > 0) {
+ if (num_pixels >= 12) {
+ CONVERT12_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2);
+ ptemp_src += 48;
+ ptemp_dst += 36;
+ num_pixels -= 12;
+ } else if (num_pixels >= 8) {
+ CONVERT8_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1);
+ ptemp_src += 32;
+ ptemp_dst += 24;
+ num_pixels -= 8;
+ } else if (num_pixels >= 4) {
+ CONVERT4_BGRA_XXX(ptemp_src, ptemp_dst, mask0);
+ ptemp_src += 16;
+ ptemp_dst += 12;
+ num_pixels -= 4;
+ }
+ if (num_pixels == 3) {
+ CONVERT1_BGRA_BGR(ptemp_src + 0, ptemp_dst + 0);
+ CONVERT1_BGRA_BGR(ptemp_src + 4, ptemp_dst + 3);
+ CONVERT1_BGRA_BGR(ptemp_src + 8, ptemp_dst + 6);
+ } else if (num_pixels == 2) {
+ CONVERT1_BGRA_BGR(ptemp_src + 0, ptemp_dst + 0);
+ CONVERT1_BGRA_BGR(ptemp_src + 4, ptemp_dst + 3);
+ } else if (num_pixels == 1) {
+ CONVERT1_BGRA_BGR(ptemp_src, ptemp_dst);
+ }
+ }
+}
+
+static void ConvertBGRAToRGB_MSA(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint8_t* ptemp_src = (const uint8_t*)src;
+ uint8_t* ptemp_dst = (uint8_t*)dst;
+ const v16u8 mask0 = { 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12,
+ 18, 17, 16, 22 };
+ const v16u8 mask1 = { 5, 4, 10, 9, 8, 14, 13, 12, 18, 17, 16, 22,
+ 21, 20, 26, 25 };
+ const v16u8 mask2 = { 8, 14, 13, 12, 18, 17, 16, 22, 21, 20, 26, 25,
+ 24, 30, 29, 28 };
+
+ while (num_pixels >= 16) {
+ CONVERT16_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2);
+ ptemp_src += 64;
+ ptemp_dst += 48;
+ num_pixels -= 16;
+ }
+ if (num_pixels) {
+ if (num_pixels >= 12) {
+ CONVERT12_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1, mask2);
+ ptemp_src += 48;
+ ptemp_dst += 36;
+ num_pixels -= 12;
+ } else if (num_pixels >= 8) {
+ CONVERT8_BGRA_XXX(ptemp_src, ptemp_dst, mask0, mask1);
+ ptemp_src += 32;
+ ptemp_dst += 24;
+ num_pixels -= 8;
+ } else if (num_pixels >= 4) {
+ CONVERT4_BGRA_XXX(ptemp_src, ptemp_dst, mask0);
+ ptemp_src += 16;
+ ptemp_dst += 12;
+ num_pixels -= 4;
+ }
+ if (num_pixels == 3) {
+ CONVERT1_BGRA_RGB(ptemp_src + 0, ptemp_dst + 0);
+ CONVERT1_BGRA_RGB(ptemp_src + 4, ptemp_dst + 3);
+ CONVERT1_BGRA_RGB(ptemp_src + 8, ptemp_dst + 6);
+ } else if (num_pixels == 2) {
+ CONVERT1_BGRA_RGB(ptemp_src + 0, ptemp_dst + 0);
+ CONVERT1_BGRA_RGB(ptemp_src + 4, ptemp_dst + 3);
+ } else if (num_pixels == 1) {
+ CONVERT1_BGRA_RGB(ptemp_src, ptemp_dst);
+ }
+ }
+}
+
+static void AddGreenToBlueAndRed_MSA(const uint32_t* const src, int num_pixels,
+ uint32_t* dst) {
+ int i;
+ const uint8_t* in = (const uint8_t*)src;
+ uint8_t* out = (uint8_t*)dst;
+ v16u8 src0, dst0, tmp0;
+ const v16u8 mask = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255,
+ 13, 255, 13, 255 };
+
+ while (num_pixels >= 8) {
+ v16u8 src1, dst1, tmp1;
+ LD_UB2(in, 16, src0, src1);
+ VSHF_B2_UB(src0, src1, src1, src0, mask, mask, tmp0, tmp1);
+ ADD2(src0, tmp0, src1, tmp1, dst0, dst1);
+ ST_UB2(dst0, dst1, out, 16);
+ in += 32;
+ out += 32;
+ num_pixels -= 8;
+ }
+ if (num_pixels > 0) {
+ if (num_pixels >= 4) {
+ src0 = LD_UB(in);
+ tmp0 = VSHF_UB(src0, src0, mask);
+ dst0 = src0 + tmp0;
+ ST_UB(dst0, out);
+ in += 16;
+ out += 16;
+ num_pixels -= 4;
+ }
+ for (i = 0; i < num_pixels; i++) {
+ const uint8_t b = in[0];
+ const uint8_t g = in[1];
+ const uint8_t r = in[2];
+ out[0] = (b + g) & 0xff;
+ out[1] = g;
+ out[2] = (r + g) & 0xff;
+ out[4] = in[4];
+ out += 4;
+ }
+ }
+}
+
+static void TransformColorInverse_MSA(const VP8LMultipliers* const m,
+ const uint32_t* src, int num_pixels,
+ uint32_t* dst) {
+ v16u8 src0, dst0;
+ const v16i8 g2br = (v16i8)__msa_fill_w(m->green_to_blue_ |
+ (m->green_to_red_ << 16));
+ const v16i8 r2b = (v16i8)__msa_fill_w(m->red_to_blue_);
+ const v16u8 mask0 = { 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255,
+ 13, 255, 13, 255 };
+ const v16u8 mask1 = { 16, 1, 18, 3, 20, 5, 22, 7, 24, 9, 26, 11,
+ 28, 13, 30, 15 };
+
+ while (num_pixels >= 8) {
+ v16u8 src1, dst1;
+ LD_UB2(src, 4, src0, src1);
+ TRANSFORM_COLOR_INVERSE_8(src0, src1, dst0, dst1, g2br, r2b, mask0, mask1);
+ ST_UB2(dst0, dst1, dst, 4);
+ src += 8;
+ dst += 8;
+ num_pixels -= 8;
+ }
+ if (num_pixels > 0) {
+ if (num_pixels >= 4) {
+ src0 = LD_UB(src);
+ TRANSFORM_COLOR_INVERSE_4(src0, dst0, g2br, r2b, mask0, mask1);
+ ST_UB(dst0, dst);
+ src += 4;
+ dst += 4;
+ num_pixels -= 4;
+ }
+ if (num_pixels > 0) {
+ src0 = LD_UB(src);
+ TRANSFORM_COLOR_INVERSE_4(src0, dst0, g2br, r2b, mask0, mask1);
+ if (num_pixels == 3) {
+ const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0);
+ const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 2);
+ SD(pix_d, dst + 0);
+ SW(pix_w, dst + 2);
+ } else if (num_pixels == 2) {
+ const uint64_t pix_d = __msa_copy_s_d((v2i64)dst0, 0);
+ SD(pix_d, dst);
+ } else {
+ const uint32_t pix_w = __msa_copy_s_w((v4i32)dst0, 0);
+ SW(pix_w, dst);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LDspInitMSA(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitMSA(void) {
+ VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_MSA;
+ VP8LConvertBGRAToBGR = ConvertBGRAToBGR_MSA;
+ VP8LConvertBGRAToRGB = ConvertBGRAToRGB_MSA;
+
+ VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_MSA;
+ VP8LTransformColorInverse = TransformColorInverse_MSA;
+}
+
+#else // !WEBP_USE_MSA
+
+WEBP_DSP_INIT_STUB(VP8LDspInitMSA)
+
+#endif // WEBP_USE_MSA
diff --git a/src/third_party/libwebp/src/dsp/lossless_neon.c b/src/third_party/libwebp/src/dsp/lossless_neon.c
new file mode 100644
index 0000000..76a1b6f
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_neon.c
@@ -0,0 +1,641 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// NEON variant of methods for lossless decoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON)
+
+#include <arm_neon.h>
+
+#include "src/dsp/lossless.h"
+#include "src/dsp/neon.h"
+
+//------------------------------------------------------------------------------
+// Colorspace conversion functions
+
+#if !defined(WORK_AROUND_GCC)
+// gcc 4.6.0 had some trouble (NDK-r9) with this code. We only use it for
+// gcc-4.8.x at least.
+static void ConvertBGRAToRGBA_NEON(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const end = src + (num_pixels & ~15);
+ for (; src < end; src += 16) {
+ uint8x16x4_t pixel = vld4q_u8((uint8_t*)src);
+ // swap B and R. (VSWP d0,d2 has no intrinsics equivalent!)
+ const uint8x16_t tmp = pixel.val[0];
+ pixel.val[0] = pixel.val[2];
+ pixel.val[2] = tmp;
+ vst4q_u8(dst, pixel);
+ dst += 64;
+ }
+ VP8LConvertBGRAToRGBA_C(src, num_pixels & 15, dst); // left-overs
+}
+
+static void ConvertBGRAToBGR_NEON(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const end = src + (num_pixels & ~15);
+ for (; src < end; src += 16) {
+ const uint8x16x4_t pixel = vld4q_u8((uint8_t*)src);
+ const uint8x16x3_t tmp = { { pixel.val[0], pixel.val[1], pixel.val[2] } };
+ vst3q_u8(dst, tmp);
+ dst += 48;
+ }
+ VP8LConvertBGRAToBGR_C(src, num_pixels & 15, dst); // left-overs
+}
+
+static void ConvertBGRAToRGB_NEON(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const end = src + (num_pixels & ~15);
+ for (; src < end; src += 16) {
+ const uint8x16x4_t pixel = vld4q_u8((uint8_t*)src);
+ const uint8x16x3_t tmp = { { pixel.val[2], pixel.val[1], pixel.val[0] } };
+ vst3q_u8(dst, tmp);
+ dst += 48;
+ }
+ VP8LConvertBGRAToRGB_C(src, num_pixels & 15, dst); // left-overs
+}
+
+#else // WORK_AROUND_GCC
+
+// gcc-4.6.0 fallback
+
+static const uint8_t kRGBAShuffle[8] = { 2, 1, 0, 3, 6, 5, 4, 7 };
+
+static void ConvertBGRAToRGBA_NEON(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const end = src + (num_pixels & ~1);
+ const uint8x8_t shuffle = vld1_u8(kRGBAShuffle);
+ for (; src < end; src += 2) {
+ const uint8x8_t pixels = vld1_u8((uint8_t*)src);
+ vst1_u8(dst, vtbl1_u8(pixels, shuffle));
+ dst += 8;
+ }
+ VP8LConvertBGRAToRGBA_C(src, num_pixels & 1, dst); // left-overs
+}
+
+static const uint8_t kBGRShuffle[3][8] = {
+ { 0, 1, 2, 4, 5, 6, 8, 9 },
+ { 10, 12, 13, 14, 16, 17, 18, 20 },
+ { 21, 22, 24, 25, 26, 28, 29, 30 }
+};
+
+static void ConvertBGRAToBGR_NEON(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const end = src + (num_pixels & ~7);
+ const uint8x8_t shuffle0 = vld1_u8(kBGRShuffle[0]);
+ const uint8x8_t shuffle1 = vld1_u8(kBGRShuffle[1]);
+ const uint8x8_t shuffle2 = vld1_u8(kBGRShuffle[2]);
+ for (; src < end; src += 8) {
+ uint8x8x4_t pixels;
+ INIT_VECTOR4(pixels,
+ vld1_u8((const uint8_t*)(src + 0)),
+ vld1_u8((const uint8_t*)(src + 2)),
+ vld1_u8((const uint8_t*)(src + 4)),
+ vld1_u8((const uint8_t*)(src + 6)));
+ vst1_u8(dst + 0, vtbl4_u8(pixels, shuffle0));
+ vst1_u8(dst + 8, vtbl4_u8(pixels, shuffle1));
+ vst1_u8(dst + 16, vtbl4_u8(pixels, shuffle2));
+ dst += 8 * 3;
+ }
+ VP8LConvertBGRAToBGR_C(src, num_pixels & 7, dst); // left-overs
+}
+
+static const uint8_t kRGBShuffle[3][8] = {
+ { 2, 1, 0, 6, 5, 4, 10, 9 },
+ { 8, 14, 13, 12, 18, 17, 16, 22 },
+ { 21, 20, 26, 25, 24, 30, 29, 28 }
+};
+
+static void ConvertBGRAToRGB_NEON(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const uint32_t* const end = src + (num_pixels & ~7);
+ const uint8x8_t shuffle0 = vld1_u8(kRGBShuffle[0]);
+ const uint8x8_t shuffle1 = vld1_u8(kRGBShuffle[1]);
+ const uint8x8_t shuffle2 = vld1_u8(kRGBShuffle[2]);
+ for (; src < end; src += 8) {
+ uint8x8x4_t pixels;
+ INIT_VECTOR4(pixels,
+ vld1_u8((const uint8_t*)(src + 0)),
+ vld1_u8((const uint8_t*)(src + 2)),
+ vld1_u8((const uint8_t*)(src + 4)),
+ vld1_u8((const uint8_t*)(src + 6)));
+ vst1_u8(dst + 0, vtbl4_u8(pixels, shuffle0));
+ vst1_u8(dst + 8, vtbl4_u8(pixels, shuffle1));
+ vst1_u8(dst + 16, vtbl4_u8(pixels, shuffle2));
+ dst += 8 * 3;
+ }
+ VP8LConvertBGRAToRGB_C(src, num_pixels & 7, dst); // left-overs
+}
+
+#endif // !WORK_AROUND_GCC
+
+//------------------------------------------------------------------------------
+// Predictor Transform
+
+#define LOAD_U32_AS_U8(IN) vreinterpret_u8_u32(vdup_n_u32((IN)))
+#define LOAD_U32P_AS_U8(IN) vreinterpret_u8_u32(vld1_u32((IN)))
+#define LOADQ_U32_AS_U8(IN) vreinterpretq_u8_u32(vdupq_n_u32((IN)))
+#define LOADQ_U32P_AS_U8(IN) vreinterpretq_u8_u32(vld1q_u32((IN)))
+#define GET_U8_AS_U32(IN) vget_lane_u32(vreinterpret_u32_u8((IN)), 0);
+#define GETQ_U8_AS_U32(IN) vgetq_lane_u32(vreinterpretq_u32_u8((IN)), 0);
+#define STOREQ_U8_AS_U32P(OUT, IN) vst1q_u32((OUT), vreinterpretq_u32_u8((IN)));
+#define ROTATE32_LEFT(L) vextq_u8((L), (L), 12) // D|C|B|A -> C|B|A|D
+
+static WEBP_INLINE uint8x8_t Average2_u8_NEON(uint32_t a0, uint32_t a1) {
+ const uint8x8_t A0 = LOAD_U32_AS_U8(a0);
+ const uint8x8_t A1 = LOAD_U32_AS_U8(a1);
+ return vhadd_u8(A0, A1);
+}
+
+static WEBP_INLINE uint32_t ClampedAddSubtractHalf_NEON(uint32_t c0,
+ uint32_t c1,
+ uint32_t c2) {
+ const uint8x8_t avg = Average2_u8_NEON(c0, c1);
+ // Remove one to c2 when bigger than avg.
+ const uint8x8_t C2 = LOAD_U32_AS_U8(c2);
+ const uint8x8_t cmp = vcgt_u8(C2, avg);
+ const uint8x8_t C2_1 = vadd_u8(C2, cmp);
+ // Compute half of the difference between avg and c2.
+ const int8x8_t diff_avg = vreinterpret_s8_u8(vhsub_u8(avg, C2_1));
+ // Compute the sum with avg and saturate.
+ const int16x8_t avg_16 = vreinterpretq_s16_u16(vmovl_u8(avg));
+ const uint8x8_t res = vqmovun_s16(vaddw_s8(avg_16, diff_avg));
+ const uint32_t output = GET_U8_AS_U32(res);
+ return output;
+}
+
+static WEBP_INLINE uint32_t Average2_NEON(uint32_t a0, uint32_t a1) {
+ const uint8x8_t avg_u8x8 = Average2_u8_NEON(a0, a1);
+ const uint32_t avg = GET_U8_AS_U32(avg_u8x8);
+ return avg;
+}
+
+static WEBP_INLINE uint32_t Average3_NEON(uint32_t a0, uint32_t a1,
+ uint32_t a2) {
+ const uint8x8_t avg0 = Average2_u8_NEON(a0, a2);
+ const uint8x8_t A1 = LOAD_U32_AS_U8(a1);
+ const uint32_t avg = GET_U8_AS_U32(vhadd_u8(avg0, A1));
+ return avg;
+}
+
+static uint32_t Predictor5_NEON(uint32_t left, const uint32_t* const top) {
+ return Average3_NEON(left, top[0], top[1]);
+}
+static uint32_t Predictor6_NEON(uint32_t left, const uint32_t* const top) {
+ return Average2_NEON(left, top[-1]);
+}
+static uint32_t Predictor7_NEON(uint32_t left, const uint32_t* const top) {
+ return Average2_NEON(left, top[0]);
+}
+static uint32_t Predictor13_NEON(uint32_t left, const uint32_t* const top) {
+ return ClampedAddSubtractHalf_NEON(left, top[0], top[-1]);
+}
+
+// Batch versions of those functions.
+
+// Predictor0: ARGB_BLACK.
+static void PredictorAdd0_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ const uint8x16_t black = vreinterpretq_u8_u32(vdupq_n_u32(ARGB_BLACK));
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ const uint8x16_t res = vaddq_u8(src, black);
+ STOREQ_U8_AS_U32P(&out[i], res);
+ }
+ VP8LPredictorsAdd_C[0](in + i, upper + i, num_pixels - i, out + i);
+}
+
+// Predictor1: left.
+static void PredictorAdd1_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ const uint8x16_t zero = LOADQ_U32_AS_U8(0);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ // a | b | c | d
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ // 0 | a | b | c
+ const uint8x16_t shift0 = vextq_u8(zero, src, 12);
+ // a | a + b | b + c | c + d
+ const uint8x16_t sum0 = vaddq_u8(src, shift0);
+ // 0 | 0 | a | a + b
+ const uint8x16_t shift1 = vextq_u8(zero, sum0, 8);
+ // a | a + b | a + b + c | a + b + c + d
+ const uint8x16_t sum1 = vaddq_u8(sum0, shift1);
+ const uint8x16_t prev = LOADQ_U32_AS_U8(out[i - 1]);
+ const uint8x16_t res = vaddq_u8(sum1, prev);
+ STOREQ_U8_AS_U32P(&out[i], res);
+ }
+ VP8LPredictorsAdd_C[1](in + i, upper + i, num_pixels - i, out + i);
+}
+
+// Macro that adds 32-bit integers from IN using mod 256 arithmetic
+// per 8 bit channel.
+#define GENERATE_PREDICTOR_1(X, IN) \
+static void PredictorAdd##X##_NEON(const uint32_t* in, \
+ const uint32_t* upper, int num_pixels, \
+ uint32_t* out) { \
+ int i; \
+ for (i = 0; i + 4 <= num_pixels; i += 4) { \
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); \
+ const uint8x16_t other = LOADQ_U32P_AS_U8(&(IN)); \
+ const uint8x16_t res = vaddq_u8(src, other); \
+ STOREQ_U8_AS_U32P(&out[i], res); \
+ } \
+ VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \
+}
+// Predictor2: Top.
+GENERATE_PREDICTOR_1(2, upper[i])
+// Predictor3: Top-right.
+GENERATE_PREDICTOR_1(3, upper[i + 1])
+// Predictor4: Top-left.
+GENERATE_PREDICTOR_1(4, upper[i - 1])
+#undef GENERATE_PREDICTOR_1
+
+// Predictor5: average(average(left, TR), T)
+#define DO_PRED5(LANE) do { \
+ const uint8x16_t avgLTR = vhaddq_u8(L, TR); \
+ const uint8x16_t avg = vhaddq_u8(avgLTR, T); \
+ const uint8x16_t res = vaddq_u8(avg, src); \
+ vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \
+ L = ROTATE32_LEFT(res); \
+} while (0)
+
+static void PredictorAdd5_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint8x16_t L = LOADQ_U32_AS_U8(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i + 0]);
+ const uint8x16_t TR = LOADQ_U32P_AS_U8(&upper[i + 1]);
+ DO_PRED5(0);
+ DO_PRED5(1);
+ DO_PRED5(2);
+ DO_PRED5(3);
+ }
+ VP8LPredictorsAdd_C[5](in + i, upper + i, num_pixels - i, out + i);
+}
+#undef DO_PRED5
+
+#define DO_PRED67(LANE) do { \
+ const uint8x16_t avg = vhaddq_u8(L, top); \
+ const uint8x16_t res = vaddq_u8(avg, src); \
+ vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \
+ L = ROTATE32_LEFT(res); \
+} while (0)
+
+// Predictor6: average(left, TL)
+static void PredictorAdd6_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint8x16_t L = LOADQ_U32_AS_U8(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ const uint8x16_t top = LOADQ_U32P_AS_U8(&upper[i - 1]);
+ DO_PRED67(0);
+ DO_PRED67(1);
+ DO_PRED67(2);
+ DO_PRED67(3);
+ }
+ VP8LPredictorsAdd_C[6](in + i, upper + i, num_pixels - i, out + i);
+}
+
+// Predictor7: average(left, T)
+static void PredictorAdd7_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint8x16_t L = LOADQ_U32_AS_U8(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ const uint8x16_t top = LOADQ_U32P_AS_U8(&upper[i]);
+ DO_PRED67(0);
+ DO_PRED67(1);
+ DO_PRED67(2);
+ DO_PRED67(3);
+ }
+ VP8LPredictorsAdd_C[7](in + i, upper + i, num_pixels - i, out + i);
+}
+#undef DO_PRED67
+
+#define GENERATE_PREDICTOR_2(X, IN) \
+static void PredictorAdd##X##_NEON(const uint32_t* in, \
+ const uint32_t* upper, int num_pixels, \
+ uint32_t* out) { \
+ int i; \
+ for (i = 0; i + 4 <= num_pixels; i += 4) { \
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]); \
+ const uint8x16_t Tother = LOADQ_U32P_AS_U8(&(IN)); \
+ const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]); \
+ const uint8x16_t avg = vhaddq_u8(T, Tother); \
+ const uint8x16_t res = vaddq_u8(avg, src); \
+ STOREQ_U8_AS_U32P(&out[i], res); \
+ } \
+ VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \
+}
+// Predictor8: average TL T.
+GENERATE_PREDICTOR_2(8, upper[i - 1])
+// Predictor9: average T TR.
+GENERATE_PREDICTOR_2(9, upper[i + 1])
+#undef GENERATE_PREDICTOR_2
+
+// Predictor10: average of (average of (L,TL), average of (T, TR)).
+#define DO_PRED10(LANE) do { \
+ const uint8x16_t avgLTL = vhaddq_u8(L, TL); \
+ const uint8x16_t avg = vhaddq_u8(avgTTR, avgLTL); \
+ const uint8x16_t res = vaddq_u8(avg, src); \
+ vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \
+ L = ROTATE32_LEFT(res); \
+} while (0)
+
+static void PredictorAdd10_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint8x16_t L = LOADQ_U32_AS_U8(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]);
+ const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]);
+ const uint8x16_t TR = LOADQ_U32P_AS_U8(&upper[i + 1]);
+ const uint8x16_t avgTTR = vhaddq_u8(T, TR);
+ DO_PRED10(0);
+ DO_PRED10(1);
+ DO_PRED10(2);
+ DO_PRED10(3);
+ }
+ VP8LPredictorsAdd_C[10](in + i, upper + i, num_pixels - i, out + i);
+}
+#undef DO_PRED10
+
+// Predictor11: select.
+#define DO_PRED11(LANE) do { \
+ const uint8x16_t sumLin = vaddq_u8(L, src); /* in + L */ \
+ const uint8x16_t pLTL = vabdq_u8(L, TL); /* |L - TL| */ \
+ const uint16x8_t sum_LTL = vpaddlq_u8(pLTL); \
+ const uint32x4_t pa = vpaddlq_u16(sum_LTL); \
+ const uint32x4_t mask = vcleq_u32(pa, pb); \
+ const uint8x16_t res = vbslq_u8(vreinterpretq_u8_u32(mask), sumTin, sumLin); \
+ vst1q_lane_u32(&out[i + (LANE)], vreinterpretq_u32_u8(res), (LANE)); \
+ L = ROTATE32_LEFT(res); \
+} while (0)
+
+static void PredictorAdd11_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint8x16_t L = LOADQ_U32_AS_U8(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]);
+ const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]);
+ const uint8x16_t pTTL = vabdq_u8(T, TL); // |T - TL|
+ const uint16x8_t sum_TTL = vpaddlq_u8(pTTL);
+ const uint32x4_t pb = vpaddlq_u16(sum_TTL);
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ const uint8x16_t sumTin = vaddq_u8(T, src); // in + T
+ DO_PRED11(0);
+ DO_PRED11(1);
+ DO_PRED11(2);
+ DO_PRED11(3);
+ }
+ VP8LPredictorsAdd_C[11](in + i, upper + i, num_pixels - i, out + i);
+}
+#undef DO_PRED11
+
+// Predictor12: ClampedAddSubtractFull.
+#define DO_PRED12(DIFF, LANE) do { \
+ const uint8x8_t pred = \
+ vqmovun_s16(vaddq_s16(vreinterpretq_s16_u16(L), (DIFF))); \
+ const uint8x8_t res = \
+ vadd_u8(pred, (LANE <= 1) ? vget_low_u8(src) : vget_high_u8(src)); \
+ const uint16x8_t res16 = vmovl_u8(res); \
+ vst1_lane_u32(&out[i + (LANE)], vreinterpret_u32_u8(res), (LANE) & 1); \
+ /* rotate in the left predictor for next iteration */ \
+ L = vextq_u16(res16, res16, 4); \
+} while (0)
+
+static void PredictorAdd12_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint16x8_t L = vmovl_u8(LOAD_U32_AS_U8(out[-1]));
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ // load four pixels of source
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ // precompute the difference T - TL once for all, stored as s16
+ const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]);
+ const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]);
+ const int16x8_t diff_lo =
+ vreinterpretq_s16_u16(vsubl_u8(vget_low_u8(T), vget_low_u8(TL)));
+ const int16x8_t diff_hi =
+ vreinterpretq_s16_u16(vsubl_u8(vget_high_u8(T), vget_high_u8(TL)));
+ // loop over the four reconstructed pixels
+ DO_PRED12(diff_lo, 0);
+ DO_PRED12(diff_lo, 1);
+ DO_PRED12(diff_hi, 2);
+ DO_PRED12(diff_hi, 3);
+ }
+ VP8LPredictorsAdd_C[12](in + i, upper + i, num_pixels - i, out + i);
+}
+#undef DO_PRED12
+
+// Predictor13: ClampedAddSubtractHalf
+#define DO_PRED13(LANE, LOW_OR_HI) do { \
+ const uint8x16_t avg = vhaddq_u8(L, T); \
+ const uint8x16_t cmp = vcgtq_u8(TL, avg); \
+ const uint8x16_t TL_1 = vaddq_u8(TL, cmp); \
+ /* Compute half of the difference between avg and TL'. */ \
+ const int8x8_t diff_avg = \
+ vreinterpret_s8_u8(LOW_OR_HI(vhsubq_u8(avg, TL_1))); \
+ /* Compute the sum with avg and saturate. */ \
+ const int16x8_t avg_16 = vreinterpretq_s16_u16(vmovl_u8(LOW_OR_HI(avg))); \
+ const uint8x8_t delta = vqmovun_s16(vaddw_s8(avg_16, diff_avg)); \
+ const uint8x8_t res = vadd_u8(LOW_OR_HI(src), delta); \
+ const uint8x16_t res2 = vcombine_u8(res, res); \
+ vst1_lane_u32(&out[i + (LANE)], vreinterpret_u32_u8(res), (LANE) & 1); \
+ L = ROTATE32_LEFT(res2); \
+} while (0)
+
+static void PredictorAdd13_NEON(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ uint8x16_t L = LOADQ_U32_AS_U8(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t src = LOADQ_U32P_AS_U8(&in[i]);
+ const uint8x16_t T = LOADQ_U32P_AS_U8(&upper[i]);
+ const uint8x16_t TL = LOADQ_U32P_AS_U8(&upper[i - 1]);
+ DO_PRED13(0, vget_low_u8);
+ DO_PRED13(1, vget_low_u8);
+ DO_PRED13(2, vget_high_u8);
+ DO_PRED13(3, vget_high_u8);
+ }
+ VP8LPredictorsAdd_C[13](in + i, upper + i, num_pixels - i, out + i);
+}
+#undef DO_PRED13
+
+#undef LOAD_U32_AS_U8
+#undef LOAD_U32P_AS_U8
+#undef LOADQ_U32_AS_U8
+#undef LOADQ_U32P_AS_U8
+#undef GET_U8_AS_U32
+#undef GETQ_U8_AS_U32
+#undef STOREQ_U8_AS_U32P
+#undef ROTATE32_LEFT
+
+//------------------------------------------------------------------------------
+// Subtract-Green Transform
+
+// vtbl?_u8 are marked unavailable for iOS arm64 with Xcode < 6.3, use
+// non-standard versions there.
+#if defined(__APPLE__) && defined(__aarch64__) && \
+ defined(__apple_build_version__) && (__apple_build_version__< 6020037)
+#define USE_VTBLQ
+#endif
+
+#ifdef USE_VTBLQ
+// 255 = byte will be zeroed
+static const uint8_t kGreenShuffle[16] = {
+ 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255
+};
+
+static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb,
+ const uint8x16_t shuffle) {
+ return vcombine_u8(vtbl1q_u8(argb, vget_low_u8(shuffle)),
+ vtbl1q_u8(argb, vget_high_u8(shuffle)));
+}
+#else // !USE_VTBLQ
+// 255 = byte will be zeroed
+static const uint8_t kGreenShuffle[8] = { 1, 255, 1, 255, 5, 255, 5, 255 };
+
+static WEBP_INLINE uint8x16_t DoGreenShuffle_NEON(const uint8x16_t argb,
+ const uint8x8_t shuffle) {
+ return vcombine_u8(vtbl1_u8(vget_low_u8(argb), shuffle),
+ vtbl1_u8(vget_high_u8(argb), shuffle));
+}
+#endif // USE_VTBLQ
+
+static void AddGreenToBlueAndRed_NEON(const uint32_t* src, int num_pixels,
+ uint32_t* dst) {
+ const uint32_t* const end = src + (num_pixels & ~3);
+#ifdef USE_VTBLQ
+ const uint8x16_t shuffle = vld1q_u8(kGreenShuffle);
+#else
+ const uint8x8_t shuffle = vld1_u8(kGreenShuffle);
+#endif
+ for (; src < end; src += 4, dst += 4) {
+ const uint8x16_t argb = vld1q_u8((const uint8_t*)src);
+ const uint8x16_t greens = DoGreenShuffle_NEON(argb, shuffle);
+ vst1q_u8((uint8_t*)dst, vaddq_u8(argb, greens));
+ }
+ // fallthrough and finish off with plain-C
+ VP8LAddGreenToBlueAndRed_C(src, num_pixels & 3, dst);
+}
+
+//------------------------------------------------------------------------------
+// Color Transform
+
+static void TransformColorInverse_NEON(const VP8LMultipliers* const m,
+ const uint32_t* const src,
+ int num_pixels, uint32_t* dst) {
+// sign-extended multiplying constants, pre-shifted by 6.
+#define CST(X) (((int16_t)(m->X << 8)) >> 6)
+ const int16_t rb[8] = {
+ CST(green_to_blue_), CST(green_to_red_),
+ CST(green_to_blue_), CST(green_to_red_),
+ CST(green_to_blue_), CST(green_to_red_),
+ CST(green_to_blue_), CST(green_to_red_)
+ };
+ const int16x8_t mults_rb = vld1q_s16(rb);
+ const int16_t b2[8] = {
+ 0, CST(red_to_blue_), 0, CST(red_to_blue_),
+ 0, CST(red_to_blue_), 0, CST(red_to_blue_),
+ };
+ const int16x8_t mults_b2 = vld1q_s16(b2);
+#undef CST
+#ifdef USE_VTBLQ
+ static const uint8_t kg0g0[16] = {
+ 255, 1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13
+ };
+ const uint8x16_t shuffle = vld1q_u8(kg0g0);
+#else
+ static const uint8_t k0g0g[8] = { 255, 1, 255, 1, 255, 5, 255, 5 };
+ const uint8x8_t shuffle = vld1_u8(k0g0g);
+#endif
+ const uint32x4_t mask_ag = vdupq_n_u32(0xff00ff00u);
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const uint8x16_t in = vld1q_u8((const uint8_t*)(src + i));
+ const uint32x4_t a0g0 = vandq_u32(vreinterpretq_u32_u8(in), mask_ag);
+ // 0 g 0 g
+ const uint8x16_t greens = DoGreenShuffle_NEON(in, shuffle);
+ // x dr x db1
+ const int16x8_t A = vqdmulhq_s16(vreinterpretq_s16_u8(greens), mults_rb);
+ // x r' x b'
+ const int8x16_t B = vaddq_s8(vreinterpretq_s8_u8(in),
+ vreinterpretq_s8_s16(A));
+ // r' 0 b' 0
+ const int16x8_t C = vshlq_n_s16(vreinterpretq_s16_s8(B), 8);
+ // x db2 0 0
+ const int16x8_t D = vqdmulhq_s16(C, mults_b2);
+ // 0 x db2 0
+ const uint32x4_t E = vshrq_n_u32(vreinterpretq_u32_s16(D), 8);
+ // r' x b'' 0
+ const int8x16_t F = vaddq_s8(vreinterpretq_s8_u32(E),
+ vreinterpretq_s8_s16(C));
+ // 0 r' 0 b''
+ const uint16x8_t G = vshrq_n_u16(vreinterpretq_u16_s8(F), 8);
+ const uint32x4_t out = vorrq_u32(vreinterpretq_u32_u16(G), a0g0);
+ vst1q_u32(dst + i, out);
+ }
+ // Fall-back to C-version for left-overs.
+ VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i);
+}
+
+#undef USE_VTBLQ
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LDspInitNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitNEON(void) {
+ VP8LPredictors[5] = Predictor5_NEON;
+ VP8LPredictors[6] = Predictor6_NEON;
+ VP8LPredictors[7] = Predictor7_NEON;
+ VP8LPredictors[13] = Predictor13_NEON;
+
+ VP8LPredictorsAdd[0] = PredictorAdd0_NEON;
+ VP8LPredictorsAdd[1] = PredictorAdd1_NEON;
+ VP8LPredictorsAdd[2] = PredictorAdd2_NEON;
+ VP8LPredictorsAdd[3] = PredictorAdd3_NEON;
+ VP8LPredictorsAdd[4] = PredictorAdd4_NEON;
+ VP8LPredictorsAdd[5] = PredictorAdd5_NEON;
+ VP8LPredictorsAdd[6] = PredictorAdd6_NEON;
+ VP8LPredictorsAdd[7] = PredictorAdd7_NEON;
+ VP8LPredictorsAdd[8] = PredictorAdd8_NEON;
+ VP8LPredictorsAdd[9] = PredictorAdd9_NEON;
+ VP8LPredictorsAdd[10] = PredictorAdd10_NEON;
+ VP8LPredictorsAdd[11] = PredictorAdd11_NEON;
+ VP8LPredictorsAdd[12] = PredictorAdd12_NEON;
+ VP8LPredictorsAdd[13] = PredictorAdd13_NEON;
+
+ VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_NEON;
+ VP8LConvertBGRAToBGR = ConvertBGRAToBGR_NEON;
+ VP8LConvertBGRAToRGB = ConvertBGRAToRGB_NEON;
+
+ VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_NEON;
+ VP8LTransformColorInverse = TransformColorInverse_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(VP8LDspInitNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/lossless_sse2.c b/src/third_party/libwebp/src/dsp/lossless_sse2.c
new file mode 100644
index 0000000..17d7576
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/lossless_sse2.c
@@ -0,0 +1,707 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 variant of methods for lossless decoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+
+#include "src/dsp/common_sse2.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+#include <assert.h>
+#include <emmintrin.h>
+
+//------------------------------------------------------------------------------
+// Predictor Transform
+
+static WEBP_INLINE uint32_t ClampedAddSubtractFull_SSE2(uint32_t c0,
+ uint32_t c1,
+ uint32_t c2) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero);
+ const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero);
+ const __m128i C2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
+ const __m128i V1 = _mm_add_epi16(C0, C1);
+ const __m128i V2 = _mm_sub_epi16(V1, C2);
+ const __m128i b = _mm_packus_epi16(V2, V2);
+ const uint32_t output = _mm_cvtsi128_si32(b);
+ return output;
+}
+
+static WEBP_INLINE uint32_t ClampedAddSubtractHalf_SSE2(uint32_t c0,
+ uint32_t c1,
+ uint32_t c2) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero);
+ const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero);
+ const __m128i B0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
+ const __m128i avg = _mm_add_epi16(C1, C0);
+ const __m128i A0 = _mm_srli_epi16(avg, 1);
+ const __m128i A1 = _mm_sub_epi16(A0, B0);
+ const __m128i BgtA = _mm_cmpgt_epi16(B0, A0);
+ const __m128i A2 = _mm_sub_epi16(A1, BgtA);
+ const __m128i A3 = _mm_srai_epi16(A2, 1);
+ const __m128i A4 = _mm_add_epi16(A0, A3);
+ const __m128i A5 = _mm_packus_epi16(A4, A4);
+ const uint32_t output = _mm_cvtsi128_si32(A5);
+ return output;
+}
+
+static WEBP_INLINE uint32_t Select_SSE2(uint32_t a, uint32_t b, uint32_t c) {
+ int pa_minus_pb;
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i A0 = _mm_cvtsi32_si128(a);
+ const __m128i B0 = _mm_cvtsi32_si128(b);
+ const __m128i C0 = _mm_cvtsi32_si128(c);
+ const __m128i AC0 = _mm_subs_epu8(A0, C0);
+ const __m128i CA0 = _mm_subs_epu8(C0, A0);
+ const __m128i BC0 = _mm_subs_epu8(B0, C0);
+ const __m128i CB0 = _mm_subs_epu8(C0, B0);
+ const __m128i AC = _mm_or_si128(AC0, CA0);
+ const __m128i BC = _mm_or_si128(BC0, CB0);
+ const __m128i pa = _mm_unpacklo_epi8(AC, zero); // |a - c|
+ const __m128i pb = _mm_unpacklo_epi8(BC, zero); // |b - c|
+ const __m128i diff = _mm_sub_epi16(pb, pa);
+ {
+ int16_t out[8];
+ _mm_storeu_si128((__m128i*)out, diff);
+ pa_minus_pb = out[0] + out[1] + out[2] + out[3];
+ }
+ return (pa_minus_pb <= 0) ? a : b;
+}
+
+static WEBP_INLINE void Average2_m128i(const __m128i* const a0,
+ const __m128i* const a1,
+ __m128i* const avg) {
+ // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1)
+ const __m128i ones = _mm_set1_epi8(1);
+ const __m128i avg1 = _mm_avg_epu8(*a0, *a1);
+ const __m128i one = _mm_and_si128(_mm_xor_si128(*a0, *a1), ones);
+ *avg = _mm_sub_epi8(avg1, one);
+}
+
+static WEBP_INLINE void Average2_uint32_SSE2(const uint32_t a0,
+ const uint32_t a1,
+ __m128i* const avg) {
+ // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1)
+ const __m128i ones = _mm_set1_epi8(1);
+ const __m128i A0 = _mm_cvtsi32_si128(a0);
+ const __m128i A1 = _mm_cvtsi32_si128(a1);
+ const __m128i avg1 = _mm_avg_epu8(A0, A1);
+ const __m128i one = _mm_and_si128(_mm_xor_si128(A0, A1), ones);
+ *avg = _mm_sub_epi8(avg1, one);
+}
+
+static WEBP_INLINE __m128i Average2_uint32_16_SSE2(uint32_t a0, uint32_t a1) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i A0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a0), zero);
+ const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero);
+ const __m128i sum = _mm_add_epi16(A1, A0);
+ return _mm_srli_epi16(sum, 1);
+}
+
+static WEBP_INLINE uint32_t Average2_SSE2(uint32_t a0, uint32_t a1) {
+ __m128i output;
+ Average2_uint32_SSE2(a0, a1, &output);
+ return _mm_cvtsi128_si32(output);
+}
+
+static WEBP_INLINE uint32_t Average3_SSE2(uint32_t a0, uint32_t a1,
+ uint32_t a2) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i avg1 = Average2_uint32_16_SSE2(a0, a2);
+ const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero);
+ const __m128i sum = _mm_add_epi16(avg1, A1);
+ const __m128i avg2 = _mm_srli_epi16(sum, 1);
+ const __m128i A2 = _mm_packus_epi16(avg2, avg2);
+ const uint32_t output = _mm_cvtsi128_si32(A2);
+ return output;
+}
+
+static WEBP_INLINE uint32_t Average4_SSE2(uint32_t a0, uint32_t a1,
+ uint32_t a2, uint32_t a3) {
+ const __m128i avg1 = Average2_uint32_16_SSE2(a0, a1);
+ const __m128i avg2 = Average2_uint32_16_SSE2(a2, a3);
+ const __m128i sum = _mm_add_epi16(avg2, avg1);
+ const __m128i avg3 = _mm_srli_epi16(sum, 1);
+ const __m128i A0 = _mm_packus_epi16(avg3, avg3);
+ const uint32_t output = _mm_cvtsi128_si32(A0);
+ return output;
+}
+
+static uint32_t Predictor5_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average3_SSE2(left, top[0], top[1]);
+ return pred;
+}
+static uint32_t Predictor6_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2_SSE2(left, top[-1]);
+ return pred;
+}
+static uint32_t Predictor7_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2_SSE2(left, top[0]);
+ return pred;
+}
+static uint32_t Predictor8_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2_SSE2(top[-1], top[0]);
+ (void)left;
+ return pred;
+}
+static uint32_t Predictor9_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average2_SSE2(top[0], top[1]);
+ (void)left;
+ return pred;
+}
+static uint32_t Predictor10_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Average4_SSE2(left, top[-1], top[0], top[1]);
+ return pred;
+}
+static uint32_t Predictor11_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = Select_SSE2(top[0], left, top[-1]);
+ return pred;
+}
+static uint32_t Predictor12_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractFull_SSE2(left, top[0], top[-1]);
+ return pred;
+}
+static uint32_t Predictor13_SSE2(uint32_t left, const uint32_t* const top) {
+ const uint32_t pred = ClampedAddSubtractHalf_SSE2(left, top[0], top[-1]);
+ return pred;
+}
+
+// Batch versions of those functions.
+
+// Predictor0: ARGB_BLACK.
+static void PredictorAdd0_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ const __m128i black = _mm_set1_epi32(ARGB_BLACK);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ const __m128i res = _mm_add_epi8(src, black);
+ _mm_storeu_si128((__m128i*)&out[i], res);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsAdd_C[0](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+// Predictor1: left.
+static void PredictorAdd1_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ __m128i prev = _mm_set1_epi32(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ // a | b | c | d
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ // 0 | a | b | c
+ const __m128i shift0 = _mm_slli_si128(src, 4);
+ // a | a + b | b + c | c + d
+ const __m128i sum0 = _mm_add_epi8(src, shift0);
+ // 0 | 0 | a | a + b
+ const __m128i shift1 = _mm_slli_si128(sum0, 8);
+ // a | a + b | a + b + c | a + b + c + d
+ const __m128i sum1 = _mm_add_epi8(sum0, shift1);
+ const __m128i res = _mm_add_epi8(sum1, prev);
+ _mm_storeu_si128((__m128i*)&out[i], res);
+ // replicate prev output on the four lanes
+ prev = _mm_shuffle_epi32(res, (3 << 0) | (3 << 2) | (3 << 4) | (3 << 6));
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsAdd_C[1](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+
+// Macro that adds 32-bit integers from IN using mod 256 arithmetic
+// per 8 bit channel.
+#define GENERATE_PREDICTOR_1(X, IN) \
+static void PredictorAdd##X##_SSE2(const uint32_t* in, const uint32_t* upper, \
+ int num_pixels, uint32_t* out) { \
+ int i; \
+ for (i = 0; i + 4 <= num_pixels; i += 4) { \
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \
+ const __m128i other = _mm_loadu_si128((const __m128i*)&(IN)); \
+ const __m128i res = _mm_add_epi8(src, other); \
+ _mm_storeu_si128((__m128i*)&out[i], res); \
+ } \
+ if (i != num_pixels) { \
+ VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \
+ } \
+}
+
+// Predictor2: Top.
+GENERATE_PREDICTOR_1(2, upper[i])
+// Predictor3: Top-right.
+GENERATE_PREDICTOR_1(3, upper[i + 1])
+// Predictor4: Top-left.
+GENERATE_PREDICTOR_1(4, upper[i - 1])
+#undef GENERATE_PREDICTOR_1
+
+// Due to averages with integers, values cannot be accumulated in parallel for
+// predictors 5 to 7.
+GENERATE_PREDICTOR_ADD(Predictor5_SSE2, PredictorAdd5_SSE2)
+GENERATE_PREDICTOR_ADD(Predictor6_SSE2, PredictorAdd6_SSE2)
+GENERATE_PREDICTOR_ADD(Predictor7_SSE2, PredictorAdd7_SSE2)
+
+#define GENERATE_PREDICTOR_2(X, IN) \
+static void PredictorAdd##X##_SSE2(const uint32_t* in, const uint32_t* upper, \
+ int num_pixels, uint32_t* out) { \
+ int i; \
+ for (i = 0; i + 4 <= num_pixels; i += 4) { \
+ const __m128i Tother = _mm_loadu_si128((const __m128i*)&(IN)); \
+ const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]); \
+ const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]); \
+ __m128i avg, res; \
+ Average2_m128i(&T, &Tother, &avg); \
+ res = _mm_add_epi8(avg, src); \
+ _mm_storeu_si128((__m128i*)&out[i], res); \
+ } \
+ if (i != num_pixels) { \
+ VP8LPredictorsAdd_C[(X)](in + i, upper + i, num_pixels - i, out + i); \
+ } \
+}
+// Predictor8: average TL T.
+GENERATE_PREDICTOR_2(8, upper[i - 1])
+// Predictor9: average T TR.
+GENERATE_PREDICTOR_2(9, upper[i + 1])
+#undef GENERATE_PREDICTOR_2
+
+// Predictor10: average of (average of (L,TL), average of (T, TR)).
+#define DO_PRED10(OUT) do { \
+ __m128i avgLTL, avg; \
+ Average2_m128i(&L, &TL, &avgLTL); \
+ Average2_m128i(&avgTTR, &avgLTL, &avg); \
+ L = _mm_add_epi8(avg, src); \
+ out[i + (OUT)] = _mm_cvtsi128_si32(L); \
+} while (0)
+
+#define DO_PRED10_SHIFT do { \
+ /* Rotate the pre-computed values for the next iteration.*/ \
+ avgTTR = _mm_srli_si128(avgTTR, 4); \
+ TL = _mm_srli_si128(TL, 4); \
+ src = _mm_srli_si128(src, 4); \
+} while (0)
+
+static void PredictorAdd10_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ __m128i L = _mm_cvtsi32_si128(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
+ const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
+ const __m128i TR = _mm_loadu_si128((const __m128i*)&upper[i + 1]);
+ __m128i avgTTR;
+ Average2_m128i(&T, &TR, &avgTTR);
+ DO_PRED10(0);
+ DO_PRED10_SHIFT;
+ DO_PRED10(1);
+ DO_PRED10_SHIFT;
+ DO_PRED10(2);
+ DO_PRED10_SHIFT;
+ DO_PRED10(3);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsAdd_C[10](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+#undef DO_PRED10
+#undef DO_PRED10_SHIFT
+
+// Predictor11: select.
+#define DO_PRED11(OUT) do { \
+ const __m128i L_lo = _mm_unpacklo_epi32(L, T); \
+ const __m128i TL_lo = _mm_unpacklo_epi32(TL, T); \
+ const __m128i pb = _mm_sad_epu8(L_lo, TL_lo); /* pb = sum |L-TL|*/ \
+ const __m128i mask = _mm_cmpgt_epi32(pb, pa); \
+ const __m128i A = _mm_and_si128(mask, L); \
+ const __m128i B = _mm_andnot_si128(mask, T); \
+ const __m128i pred = _mm_or_si128(A, B); /* pred = (pa > b)? L : T*/ \
+ L = _mm_add_epi8(src, pred); \
+ out[i + (OUT)] = _mm_cvtsi128_si32(L); \
+} while (0)
+
+#define DO_PRED11_SHIFT do { \
+ /* Shift the pre-computed value for the next iteration.*/ \
+ T = _mm_srli_si128(T, 4); \
+ TL = _mm_srli_si128(TL, 4); \
+ src = _mm_srli_si128(src, 4); \
+ pa = _mm_srli_si128(pa, 4); \
+} while (0)
+
+static void PredictorAdd11_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ __m128i pa;
+ __m128i L = _mm_cvtsi32_si128(out[-1]);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
+ __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
+ __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ {
+ // We can unpack with any value on the upper 32 bits, provided it's the
+ // same on both operands (so that their sum of abs diff is zero). Here we
+ // use T.
+ const __m128i T_lo = _mm_unpacklo_epi32(T, T);
+ const __m128i TL_lo = _mm_unpacklo_epi32(TL, T);
+ const __m128i T_hi = _mm_unpackhi_epi32(T, T);
+ const __m128i TL_hi = _mm_unpackhi_epi32(TL, T);
+ const __m128i s_lo = _mm_sad_epu8(T_lo, TL_lo);
+ const __m128i s_hi = _mm_sad_epu8(T_hi, TL_hi);
+ pa = _mm_packs_epi32(s_lo, s_hi); // pa = sum |T-TL|
+ }
+ DO_PRED11(0);
+ DO_PRED11_SHIFT;
+ DO_PRED11(1);
+ DO_PRED11_SHIFT;
+ DO_PRED11(2);
+ DO_PRED11_SHIFT;
+ DO_PRED11(3);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsAdd_C[11](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+#undef DO_PRED11
+#undef DO_PRED11_SHIFT
+
+// Predictor12: ClampedAddSubtractFull.
+#define DO_PRED12(DIFF, LANE, OUT) do { \
+ const __m128i all = _mm_add_epi16(L, (DIFF)); \
+ const __m128i alls = _mm_packus_epi16(all, all); \
+ const __m128i res = _mm_add_epi8(src, alls); \
+ out[i + (OUT)] = _mm_cvtsi128_si32(res); \
+ L = _mm_unpacklo_epi8(res, zero); \
+} while (0)
+
+#define DO_PRED12_SHIFT(DIFF, LANE) do { \
+ /* Shift the pre-computed value for the next iteration.*/ \
+ if ((LANE) == 0) (DIFF) = _mm_srli_si128((DIFF), 8); \
+ src = _mm_srli_si128(src, 4); \
+} while (0)
+
+static void PredictorAdd12_SSE2(const uint32_t* in, const uint32_t* upper,
+ int num_pixels, uint32_t* out) {
+ int i;
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i L8 = _mm_cvtsi32_si128(out[-1]);
+ __m128i L = _mm_unpacklo_epi8(L8, zero);
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ // Load 4 pixels at a time.
+ __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
+ const __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
+ const __m128i T_lo = _mm_unpacklo_epi8(T, zero);
+ const __m128i T_hi = _mm_unpackhi_epi8(T, zero);
+ const __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
+ const __m128i TL_lo = _mm_unpacklo_epi8(TL, zero);
+ const __m128i TL_hi = _mm_unpackhi_epi8(TL, zero);
+ __m128i diff_lo = _mm_sub_epi16(T_lo, TL_lo);
+ __m128i diff_hi = _mm_sub_epi16(T_hi, TL_hi);
+ DO_PRED12(diff_lo, 0, 0);
+ DO_PRED12_SHIFT(diff_lo, 0);
+ DO_PRED12(diff_lo, 1, 1);
+ DO_PRED12_SHIFT(diff_lo, 1);
+ DO_PRED12(diff_hi, 0, 2);
+ DO_PRED12_SHIFT(diff_hi, 0);
+ DO_PRED12(diff_hi, 1, 3);
+ }
+ if (i != num_pixels) {
+ VP8LPredictorsAdd_C[12](in + i, upper + i, num_pixels - i, out + i);
+ }
+}
+#undef DO_PRED12
+#undef DO_PRED12_SHIFT
+
+// Due to averages with integers, values cannot be accumulated in parallel for
+// predictors 13.
+GENERATE_PREDICTOR_ADD(Predictor13_SSE2, PredictorAdd13_SSE2)
+
+//------------------------------------------------------------------------------
+// Subtract-Green Transform
+
+static void AddGreenToBlueAndRed_SSE2(const uint32_t* const src, int num_pixels,
+ uint32_t* dst) {
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i in = _mm_loadu_si128((const __m128i*)&src[i]); // argb
+ const __m128i A = _mm_srli_epi16(in, 8); // 0 a 0 g
+ const __m128i B = _mm_shufflelo_epi16(A, _MM_SHUFFLE(2, 2, 0, 0));
+ const __m128i C = _mm_shufflehi_epi16(B, _MM_SHUFFLE(2, 2, 0, 0)); // 0g0g
+ const __m128i out = _mm_add_epi8(in, C);
+ _mm_storeu_si128((__m128i*)&dst[i], out);
+ }
+ // fallthrough and finish off with plain-C
+ if (i != num_pixels) {
+ VP8LAddGreenToBlueAndRed_C(src + i, num_pixels - i, dst + i);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Color Transform
+
+static void TransformColorInverse_SSE2(const VP8LMultipliers* const m,
+ const uint32_t* const src,
+ int num_pixels, uint32_t* dst) {
+// sign-extended multiplying constants, pre-shifted by 5.
+#define CST(X) (((int16_t)(m->X << 8)) >> 5) // sign-extend
+#define MK_CST_16(HI, LO) \
+ _mm_set1_epi32((int)(((uint32_t)(HI) << 16) | ((LO) & 0xffff)))
+ const __m128i mults_rb = MK_CST_16(CST(green_to_red_), CST(green_to_blue_));
+ const __m128i mults_b2 = MK_CST_16(CST(red_to_blue_), 0);
+#undef MK_CST_16
+#undef CST
+ const __m128i mask_ag = _mm_set1_epi32(0xff00ff00); // alpha-green masks
+ int i;
+ for (i = 0; i + 4 <= num_pixels; i += 4) {
+ const __m128i in = _mm_loadu_si128((const __m128i*)&src[i]); // argb
+ const __m128i A = _mm_and_si128(in, mask_ag); // a 0 g 0
+ const __m128i B = _mm_shufflelo_epi16(A, _MM_SHUFFLE(2, 2, 0, 0));
+ const __m128i C = _mm_shufflehi_epi16(B, _MM_SHUFFLE(2, 2, 0, 0)); // g0g0
+ const __m128i D = _mm_mulhi_epi16(C, mults_rb); // x dr x db1
+ const __m128i E = _mm_add_epi8(in, D); // x r' x b'
+ const __m128i F = _mm_slli_epi16(E, 8); // r' 0 b' 0
+ const __m128i G = _mm_mulhi_epi16(F, mults_b2); // x db2 0 0
+ const __m128i H = _mm_srli_epi32(G, 8); // 0 x db2 0
+ const __m128i I = _mm_add_epi8(H, F); // r' x b'' 0
+ const __m128i J = _mm_srli_epi16(I, 8); // 0 r' 0 b''
+ const __m128i out = _mm_or_si128(J, A);
+ _mm_storeu_si128((__m128i*)&dst[i], out);
+ }
+ // Fall-back to C-version for left-overs.
+ if (i != num_pixels) {
+ VP8LTransformColorInverse_C(m, src + i, num_pixels - i, dst + i);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Color-space conversion functions
+
+static void ConvertBGRAToRGB_SSE2(const uint32_t* src, int num_pixels,
+ uint8_t* dst) {
+ const __m128i* in = (const __m128i*)src;
+ __m128i* out = (__m128i*)dst;
+
+ while (num_pixels >= 32) {
+ // Load the BGRA buffers.
+ __m128i in0 = _mm_loadu_si128(in + 0);
+ __m128i in1 = _mm_loadu_si128(in + 1);
+ __m128i in2 = _mm_loadu_si128(in + 2);
+ __m128i in3 = _mm_loadu_si128(in + 3);
+ __m128i in4 = _mm_loadu_si128(in + 4);
+ __m128i in5 = _mm_loadu_si128(in + 5);
+ __m128i in6 = _mm_loadu_si128(in + 6);
+ __m128i in7 = _mm_loadu_si128(in + 7);
+ VP8L32bToPlanar_SSE2(&in0, &in1, &in2, &in3);
+ VP8L32bToPlanar_SSE2(&in4, &in5, &in6, &in7);
+ // At this points, in1/in5 contains red only, in2/in6 green only ...
+ // Pack the colors in 24b RGB.
+ VP8PlanarTo24b_SSE2(&in1, &in5, &in2, &in6, &in3, &in7);
+ _mm_storeu_si128(out + 0, in1);
+ _mm_storeu_si128(out + 1, in5);
+ _mm_storeu_si128(out + 2, in2);
+ _mm_storeu_si128(out + 3, in6);
+ _mm_storeu_si128(out + 4, in3);
+ _mm_storeu_si128(out + 5, in7);
+ in += 8;
+ out += 6;
+ num_pixels -= 32;
+ }
+ // left-overs
+ if (num_pixels > 0) {
+ VP8LConvertBGRAToRGB_C((const uint32_t*)in, num_pixels, (uint8_t*)out);
+ }
+}
+
+static void ConvertBGRAToRGBA_SSE2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const __m128i red_blue_mask = _mm_set1_epi32(0x00ff00ffu);
+ const __m128i* in = (const __m128i*)src;
+ __m128i* out = (__m128i*)dst;
+ while (num_pixels >= 8) {
+ const __m128i A1 = _mm_loadu_si128(in++);
+ const __m128i A2 = _mm_loadu_si128(in++);
+ const __m128i B1 = _mm_and_si128(A1, red_blue_mask); // R 0 B 0
+ const __m128i B2 = _mm_and_si128(A2, red_blue_mask); // R 0 B 0
+ const __m128i C1 = _mm_andnot_si128(red_blue_mask, A1); // 0 G 0 A
+ const __m128i C2 = _mm_andnot_si128(red_blue_mask, A2); // 0 G 0 A
+ const __m128i D1 = _mm_shufflelo_epi16(B1, _MM_SHUFFLE(2, 3, 0, 1));
+ const __m128i D2 = _mm_shufflelo_epi16(B2, _MM_SHUFFLE(2, 3, 0, 1));
+ const __m128i E1 = _mm_shufflehi_epi16(D1, _MM_SHUFFLE(2, 3, 0, 1));
+ const __m128i E2 = _mm_shufflehi_epi16(D2, _MM_SHUFFLE(2, 3, 0, 1));
+ const __m128i F1 = _mm_or_si128(E1, C1);
+ const __m128i F2 = _mm_or_si128(E2, C2);
+ _mm_storeu_si128(out++, F1);
+ _mm_storeu_si128(out++, F2);
+ num_pixels -= 8;
+ }
+ // left-overs
+ if (num_pixels > 0) {
+ VP8LConvertBGRAToRGBA_C((const uint32_t*)in, num_pixels, (uint8_t*)out);
+ }
+}
+
+static void ConvertBGRAToRGBA4444_SSE2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const __m128i mask_0x0f = _mm_set1_epi8(0x0f);
+ const __m128i mask_0xf0 = _mm_set1_epi8(0xf0);
+ const __m128i* in = (const __m128i*)src;
+ __m128i* out = (__m128i*)dst;
+ while (num_pixels >= 8) {
+ const __m128i bgra0 = _mm_loadu_si128(in++); // bgra0|bgra1|bgra2|bgra3
+ const __m128i bgra4 = _mm_loadu_si128(in++); // bgra4|bgra5|bgra6|bgra7
+ const __m128i v0l = _mm_unpacklo_epi8(bgra0, bgra4); // b0b4g0g4r0r4a0a4...
+ const __m128i v0h = _mm_unpackhi_epi8(bgra0, bgra4); // b2b6g2g6r2r6a2a6...
+ const __m128i v1l = _mm_unpacklo_epi8(v0l, v0h); // b0b2b4b6g0g2g4g6...
+ const __m128i v1h = _mm_unpackhi_epi8(v0l, v0h); // b1b3b5b7g1g3g5g7...
+ const __m128i v2l = _mm_unpacklo_epi8(v1l, v1h); // b0...b7 | g0...g7
+ const __m128i v2h = _mm_unpackhi_epi8(v1l, v1h); // r0...r7 | a0...a7
+ const __m128i ga0 = _mm_unpackhi_epi64(v2l, v2h); // g0...g7 | a0...a7
+ const __m128i rb0 = _mm_unpacklo_epi64(v2h, v2l); // r0...r7 | b0...b7
+ const __m128i ga1 = _mm_srli_epi16(ga0, 4); // g0-|g1-|...|a6-|a7-
+ const __m128i rb1 = _mm_and_si128(rb0, mask_0xf0); // -r0|-r1|...|-b6|-a7
+ const __m128i ga2 = _mm_and_si128(ga1, mask_0x0f); // g0-|g1-|...|a6-|a7-
+ const __m128i rgba0 = _mm_or_si128(ga2, rb1); // rg0..rg7 | ba0..ba7
+ const __m128i rgba1 = _mm_srli_si128(rgba0, 8); // ba0..ba7 | 0
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ const __m128i rgba = _mm_unpacklo_epi8(rgba1, rgba0); // barg0...barg7
+#else
+ const __m128i rgba = _mm_unpacklo_epi8(rgba0, rgba1); // rgba0...rgba7
+#endif
+ _mm_storeu_si128(out++, rgba);
+ num_pixels -= 8;
+ }
+ // left-overs
+ if (num_pixels > 0) {
+ VP8LConvertBGRAToRGBA4444_C((const uint32_t*)in, num_pixels, (uint8_t*)out);
+ }
+}
+
+static void ConvertBGRAToRGB565_SSE2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const __m128i mask_0xe0 = _mm_set1_epi8(0xe0);
+ const __m128i mask_0xf8 = _mm_set1_epi8(0xf8);
+ const __m128i mask_0x07 = _mm_set1_epi8(0x07);
+ const __m128i* in = (const __m128i*)src;
+ __m128i* out = (__m128i*)dst;
+ while (num_pixels >= 8) {
+ const __m128i bgra0 = _mm_loadu_si128(in++); // bgra0|bgra1|bgra2|bgra3
+ const __m128i bgra4 = _mm_loadu_si128(in++); // bgra4|bgra5|bgra6|bgra7
+ const __m128i v0l = _mm_unpacklo_epi8(bgra0, bgra4); // b0b4g0g4r0r4a0a4...
+ const __m128i v0h = _mm_unpackhi_epi8(bgra0, bgra4); // b2b6g2g6r2r6a2a6...
+ const __m128i v1l = _mm_unpacklo_epi8(v0l, v0h); // b0b2b4b6g0g2g4g6...
+ const __m128i v1h = _mm_unpackhi_epi8(v0l, v0h); // b1b3b5b7g1g3g5g7...
+ const __m128i v2l = _mm_unpacklo_epi8(v1l, v1h); // b0...b7 | g0...g7
+ const __m128i v2h = _mm_unpackhi_epi8(v1l, v1h); // r0...r7 | a0...a7
+ const __m128i ga0 = _mm_unpackhi_epi64(v2l, v2h); // g0...g7 | a0...a7
+ const __m128i rb0 = _mm_unpacklo_epi64(v2h, v2l); // r0...r7 | b0...b7
+ const __m128i rb1 = _mm_and_si128(rb0, mask_0xf8); // -r0..-r7|-b0..-b7
+ const __m128i g_lo1 = _mm_srli_epi16(ga0, 5);
+ const __m128i g_lo2 = _mm_and_si128(g_lo1, mask_0x07); // g0-...g7-|xx (3b)
+ const __m128i g_hi1 = _mm_slli_epi16(ga0, 3);
+ const __m128i g_hi2 = _mm_and_si128(g_hi1, mask_0xe0); // -g0...-g7|xx (3b)
+ const __m128i b0 = _mm_srli_si128(rb1, 8); // -b0...-b7|0
+ const __m128i rg1 = _mm_or_si128(rb1, g_lo2); // gr0...gr7|xx
+ const __m128i b1 = _mm_srli_epi16(b0, 3);
+ const __m128i gb1 = _mm_or_si128(b1, g_hi2); // bg0...bg7|xx
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ const __m128i rgba = _mm_unpacklo_epi8(gb1, rg1); // rggb0...rggb7
+#else
+ const __m128i rgba = _mm_unpacklo_epi8(rg1, gb1); // bgrb0...bgrb7
+#endif
+ _mm_storeu_si128(out++, rgba);
+ num_pixels -= 8;
+ }
+ // left-overs
+ if (num_pixels > 0) {
+ VP8LConvertBGRAToRGB565_C((const uint32_t*)in, num_pixels, (uint8_t*)out);
+ }
+}
+
+static void ConvertBGRAToBGR_SSE2(const uint32_t* src,
+ int num_pixels, uint8_t* dst) {
+ const __m128i mask_l = _mm_set_epi32(0, 0x00ffffff, 0, 0x00ffffff);
+ const __m128i mask_h = _mm_set_epi32(0x00ffffff, 0, 0x00ffffff, 0);
+ const __m128i* in = (const __m128i*)src;
+ const uint8_t* const end = dst + num_pixels * 3;
+ // the last storel_epi64 below writes 8 bytes starting at offset 18
+ while (dst + 26 <= end) {
+ const __m128i bgra0 = _mm_loadu_si128(in++); // bgra0|bgra1|bgra2|bgra3
+ const __m128i bgra4 = _mm_loadu_si128(in++); // bgra4|bgra5|bgra6|bgra7
+ const __m128i a0l = _mm_and_si128(bgra0, mask_l); // bgr0|0|bgr0|0
+ const __m128i a4l = _mm_and_si128(bgra4, mask_l); // bgr0|0|bgr0|0
+ const __m128i a0h = _mm_and_si128(bgra0, mask_h); // 0|bgr0|0|bgr0
+ const __m128i a4h = _mm_and_si128(bgra4, mask_h); // 0|bgr0|0|bgr0
+ const __m128i b0h = _mm_srli_epi64(a0h, 8); // 000b|gr00|000b|gr00
+ const __m128i b4h = _mm_srli_epi64(a4h, 8); // 000b|gr00|000b|gr00
+ const __m128i c0 = _mm_or_si128(a0l, b0h); // rgbrgb00|rgbrgb00
+ const __m128i c4 = _mm_or_si128(a4l, b4h); // rgbrgb00|rgbrgb00
+ const __m128i c2 = _mm_srli_si128(c0, 8);
+ const __m128i c6 = _mm_srli_si128(c4, 8);
+ _mm_storel_epi64((__m128i*)(dst + 0), c0);
+ _mm_storel_epi64((__m128i*)(dst + 6), c2);
+ _mm_storel_epi64((__m128i*)(dst + 12), c4);
+ _mm_storel_epi64((__m128i*)(dst + 18), c6);
+ dst += 24;
+ num_pixels -= 8;
+ }
+ // left-overs
+ if (num_pixels > 0) {
+ VP8LConvertBGRAToBGR_C((const uint32_t*)in, num_pixels, dst);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void VP8LDspInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8LDspInitSSE2(void) {
+ VP8LPredictors[5] = Predictor5_SSE2;
+ VP8LPredictors[6] = Predictor6_SSE2;
+ VP8LPredictors[7] = Predictor7_SSE2;
+ VP8LPredictors[8] = Predictor8_SSE2;
+ VP8LPredictors[9] = Predictor9_SSE2;
+ VP8LPredictors[10] = Predictor10_SSE2;
+ VP8LPredictors[11] = Predictor11_SSE2;
+ VP8LPredictors[12] = Predictor12_SSE2;
+ VP8LPredictors[13] = Predictor13_SSE2;
+
+ VP8LPredictorsAdd[0] = PredictorAdd0_SSE2;
+ VP8LPredictorsAdd[1] = PredictorAdd1_SSE2;
+ VP8LPredictorsAdd[2] = PredictorAdd2_SSE2;
+ VP8LPredictorsAdd[3] = PredictorAdd3_SSE2;
+ VP8LPredictorsAdd[4] = PredictorAdd4_SSE2;
+ VP8LPredictorsAdd[5] = PredictorAdd5_SSE2;
+ VP8LPredictorsAdd[6] = PredictorAdd6_SSE2;
+ VP8LPredictorsAdd[7] = PredictorAdd7_SSE2;
+ VP8LPredictorsAdd[8] = PredictorAdd8_SSE2;
+ VP8LPredictorsAdd[9] = PredictorAdd9_SSE2;
+ VP8LPredictorsAdd[10] = PredictorAdd10_SSE2;
+ VP8LPredictorsAdd[11] = PredictorAdd11_SSE2;
+ VP8LPredictorsAdd[12] = PredictorAdd12_SSE2;
+ VP8LPredictorsAdd[13] = PredictorAdd13_SSE2;
+
+ VP8LAddGreenToBlueAndRed = AddGreenToBlueAndRed_SSE2;
+ VP8LTransformColorInverse = TransformColorInverse_SSE2;
+
+ VP8LConvertBGRAToRGB = ConvertBGRAToRGB_SSE2;
+ VP8LConvertBGRAToRGBA = ConvertBGRAToRGBA_SSE2;
+ VP8LConvertBGRAToRGBA4444 = ConvertBGRAToRGBA4444_SSE2;
+ VP8LConvertBGRAToRGB565 = ConvertBGRAToRGB565_SSE2;
+ VP8LConvertBGRAToBGR = ConvertBGRAToBGR_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(VP8LDspInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/mips_macro.h b/src/third_party/libwebp/src/dsp/mips_macro.h
new file mode 100644
index 0000000..44aba9b
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/mips_macro.h
@@ -0,0 +1,200 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS common macros
+
+#ifndef WEBP_DSP_MIPS_MACRO_H_
+#define WEBP_DSP_MIPS_MACRO_H_
+
+#if defined(__GNUC__) && defined(__ANDROID__) && LOCAL_GCC_VERSION == 0x409
+#define WORK_AROUND_GCC
+#endif
+
+#define STR(s) #s
+#define XSTR(s) STR(s)
+
+// O0[31..16 | 15..0] = I0[31..16 | 15..0] + I1[31..16 | 15..0]
+// O1[31..16 | 15..0] = I0[31..16 | 15..0] - I1[31..16 | 15..0]
+// O - output
+// I - input (macro doesn't change it)
+#define ADD_SUB_HALVES(O0, O1, \
+ I0, I1) \
+ "addq.ph %[" #O0 "], %[" #I0 "], %[" #I1 "] \n\t" \
+ "subq.ph %[" #O1 "], %[" #I0 "], %[" #I1 "] \n\t"
+
+// O - output
+// I - input (macro doesn't change it)
+// I[0/1] - offset in bytes
+#define LOAD_IN_X2(O0, O1, \
+ I0, I1) \
+ "lh %[" #O0 "], " #I0 "(%[in]) \n\t" \
+ "lh %[" #O1 "], " #I1 "(%[in]) \n\t"
+
+// I0 - location
+// I1..I9 - offsets in bytes
+#define LOAD_WITH_OFFSET_X4(O0, O1, O2, O3, \
+ I0, I1, I2, I3, I4, I5, I6, I7, I8, I9) \
+ "ulw %[" #O0 "], " #I1 "+" XSTR(I9) "*" #I5 "(%[" #I0 "]) \n\t" \
+ "ulw %[" #O1 "], " #I2 "+" XSTR(I9) "*" #I6 "(%[" #I0 "]) \n\t" \
+ "ulw %[" #O2 "], " #I3 "+" XSTR(I9) "*" #I7 "(%[" #I0 "]) \n\t" \
+ "ulw %[" #O3 "], " #I4 "+" XSTR(I9) "*" #I8 "(%[" #I0 "]) \n\t"
+
+// O - output
+// IO - input/output
+// I - input (macro doesn't change it)
+#define MUL_SHIFT_SUM(O0, O1, O2, O3, O4, O5, O6, O7, \
+ IO0, IO1, IO2, IO3, \
+ I0, I1, I2, I3, I4, I5, I6, I7) \
+ "mul %[" #O0 "], %[" #I0 "], %[kC2] \n\t" \
+ "mul %[" #O1 "], %[" #I0 "], %[kC1] \n\t" \
+ "mul %[" #O2 "], %[" #I1 "], %[kC2] \n\t" \
+ "mul %[" #O3 "], %[" #I1 "], %[kC1] \n\t" \
+ "mul %[" #O4 "], %[" #I2 "], %[kC2] \n\t" \
+ "mul %[" #O5 "], %[" #I2 "], %[kC1] \n\t" \
+ "mul %[" #O6 "], %[" #I3 "], %[kC2] \n\t" \
+ "mul %[" #O7 "], %[" #I3 "], %[kC1] \n\t" \
+ "sra %[" #O0 "], %[" #O0 "], 16 \n\t" \
+ "sra %[" #O1 "], %[" #O1 "], 16 \n\t" \
+ "sra %[" #O2 "], %[" #O2 "], 16 \n\t" \
+ "sra %[" #O3 "], %[" #O3 "], 16 \n\t" \
+ "sra %[" #O4 "], %[" #O4 "], 16 \n\t" \
+ "sra %[" #O5 "], %[" #O5 "], 16 \n\t" \
+ "sra %[" #O6 "], %[" #O6 "], 16 \n\t" \
+ "sra %[" #O7 "], %[" #O7 "], 16 \n\t" \
+ "addu %[" #IO0 "], %[" #IO0 "], %[" #I4 "] \n\t" \
+ "addu %[" #IO1 "], %[" #IO1 "], %[" #I5 "] \n\t" \
+ "subu %[" #IO2 "], %[" #IO2 "], %[" #I6 "] \n\t" \
+ "subu %[" #IO3 "], %[" #IO3 "], %[" #I7 "] \n\t"
+
+// O - output
+// I - input (macro doesn't change it)
+#define INSERT_HALF_X2(O0, O1, \
+ I0, I1) \
+ "ins %[" #O0 "], %[" #I0 "], 16, 16 \n\t" \
+ "ins %[" #O1 "], %[" #I1 "], 16, 16 \n\t"
+
+// O - output
+// I - input (macro doesn't change it)
+#define SRA_16(O0, O1, O2, O3, \
+ I0, I1, I2, I3) \
+ "sra %[" #O0 "], %[" #I0 "], 16 \n\t" \
+ "sra %[" #O1 "], %[" #I1 "], 16 \n\t" \
+ "sra %[" #O2 "], %[" #I2 "], 16 \n\t" \
+ "sra %[" #O3 "], %[" #I3 "], 16 \n\t"
+
+// temp0[31..16 | 15..0] = temp8[31..16 | 15..0] + temp12[31..16 | 15..0]
+// temp1[31..16 | 15..0] = temp8[31..16 | 15..0] - temp12[31..16 | 15..0]
+// temp0[31..16 | 15..0] = temp0[31..16 >> 3 | 15..0 >> 3]
+// temp1[31..16 | 15..0] = temp1[31..16 >> 3 | 15..0 >> 3]
+// O - output
+// I - input (macro doesn't change it)
+#define SHIFT_R_SUM_X2(O0, O1, O2, O3, O4, O5, O6, O7, \
+ I0, I1, I2, I3, I4, I5, I6, I7) \
+ "addq.ph %[" #O0 "], %[" #I0 "], %[" #I4 "] \n\t" \
+ "subq.ph %[" #O1 "], %[" #I0 "], %[" #I4 "] \n\t" \
+ "addq.ph %[" #O2 "], %[" #I1 "], %[" #I5 "] \n\t" \
+ "subq.ph %[" #O3 "], %[" #I1 "], %[" #I5 "] \n\t" \
+ "addq.ph %[" #O4 "], %[" #I2 "], %[" #I6 "] \n\t" \
+ "subq.ph %[" #O5 "], %[" #I2 "], %[" #I6 "] \n\t" \
+ "addq.ph %[" #O6 "], %[" #I3 "], %[" #I7 "] \n\t" \
+ "subq.ph %[" #O7 "], %[" #I3 "], %[" #I7 "] \n\t" \
+ "shra.ph %[" #O0 "], %[" #O0 "], 3 \n\t" \
+ "shra.ph %[" #O1 "], %[" #O1 "], 3 \n\t" \
+ "shra.ph %[" #O2 "], %[" #O2 "], 3 \n\t" \
+ "shra.ph %[" #O3 "], %[" #O3 "], 3 \n\t" \
+ "shra.ph %[" #O4 "], %[" #O4 "], 3 \n\t" \
+ "shra.ph %[" #O5 "], %[" #O5 "], 3 \n\t" \
+ "shra.ph %[" #O6 "], %[" #O6 "], 3 \n\t" \
+ "shra.ph %[" #O7 "], %[" #O7 "], 3 \n\t"
+
+// precrq.ph.w temp0, temp8, temp2
+// temp0 = temp8[31..16] | temp2[31..16]
+// ins temp2, temp8, 16, 16
+// temp2 = temp8[31..16] | temp2[15..0]
+// O - output
+// IO - input/output
+// I - input (macro doesn't change it)
+#define PACK_2_HALVES_TO_WORD(O0, O1, O2, O3, \
+ IO0, IO1, IO2, IO3, \
+ I0, I1, I2, I3) \
+ "precrq.ph.w %[" #O0 "], %[" #I0 "], %[" #IO0 "] \n\t" \
+ "precrq.ph.w %[" #O1 "], %[" #I1 "], %[" #IO1 "] \n\t" \
+ "ins %[" #IO0 "], %[" #I0 "], 16, 16 \n\t" \
+ "ins %[" #IO1 "], %[" #I1 "], 16, 16 \n\t" \
+ "precrq.ph.w %[" #O2 "], %[" #I2 "], %[" #IO2 "] \n\t" \
+ "precrq.ph.w %[" #O3 "], %[" #I3 "], %[" #IO3 "] \n\t" \
+ "ins %[" #IO2 "], %[" #I2 "], 16, 16 \n\t" \
+ "ins %[" #IO3 "], %[" #I3 "], 16, 16 \n\t"
+
+// preceu.ph.qbr temp0, temp8
+// temp0 = 0 | 0 | temp8[23..16] | temp8[7..0]
+// preceu.ph.qbl temp1, temp8
+// temp1 = temp8[23..16] | temp8[7..0] | 0 | 0
+// O - output
+// I - input (macro doesn't change it)
+#define CONVERT_2_BYTES_TO_HALF(O0, O1, O2, O3, O4, O5, O6, O7, \
+ I0, I1, I2, I3) \
+ "preceu.ph.qbr %[" #O0 "], %[" #I0 "] \n\t" \
+ "preceu.ph.qbl %[" #O1 "], %[" #I0 "] \n\t" \
+ "preceu.ph.qbr %[" #O2 "], %[" #I1 "] \n\t" \
+ "preceu.ph.qbl %[" #O3 "], %[" #I1 "] \n\t" \
+ "preceu.ph.qbr %[" #O4 "], %[" #I2 "] \n\t" \
+ "preceu.ph.qbl %[" #O5 "], %[" #I2 "] \n\t" \
+ "preceu.ph.qbr %[" #O6 "], %[" #I3 "] \n\t" \
+ "preceu.ph.qbl %[" #O7 "], %[" #I3 "] \n\t"
+
+// temp0[31..16 | 15..0] = temp0[31..16 | 15..0] + temp8[31..16 | 15..0]
+// temp0[31..16 | 15..0] = temp0[31..16 <<(s) 7 | 15..0 <<(s) 7]
+// temp1..temp7 same as temp0
+// precrqu_s.qb.ph temp0, temp1, temp0:
+// temp0 = temp1[31..24] | temp1[15..8] | temp0[31..24] | temp0[15..8]
+// store temp0 to dst
+// IO - input/output
+// I - input (macro doesn't change it)
+#define STORE_SAT_SUM_X2(IO0, IO1, IO2, IO3, IO4, IO5, IO6, IO7, \
+ I0, I1, I2, I3, I4, I5, I6, I7, \
+ I8, I9, I10, I11, I12, I13) \
+ "addq.ph %[" #IO0 "], %[" #IO0 "], %[" #I0 "] \n\t" \
+ "addq.ph %[" #IO1 "], %[" #IO1 "], %[" #I1 "] \n\t" \
+ "addq.ph %[" #IO2 "], %[" #IO2 "], %[" #I2 "] \n\t" \
+ "addq.ph %[" #IO3 "], %[" #IO3 "], %[" #I3 "] \n\t" \
+ "addq.ph %[" #IO4 "], %[" #IO4 "], %[" #I4 "] \n\t" \
+ "addq.ph %[" #IO5 "], %[" #IO5 "], %[" #I5 "] \n\t" \
+ "addq.ph %[" #IO6 "], %[" #IO6 "], %[" #I6 "] \n\t" \
+ "addq.ph %[" #IO7 "], %[" #IO7 "], %[" #I7 "] \n\t" \
+ "shll_s.ph %[" #IO0 "], %[" #IO0 "], 7 \n\t" \
+ "shll_s.ph %[" #IO1 "], %[" #IO1 "], 7 \n\t" \
+ "shll_s.ph %[" #IO2 "], %[" #IO2 "], 7 \n\t" \
+ "shll_s.ph %[" #IO3 "], %[" #IO3 "], 7 \n\t" \
+ "shll_s.ph %[" #IO4 "], %[" #IO4 "], 7 \n\t" \
+ "shll_s.ph %[" #IO5 "], %[" #IO5 "], 7 \n\t" \
+ "shll_s.ph %[" #IO6 "], %[" #IO6 "], 7 \n\t" \
+ "shll_s.ph %[" #IO7 "], %[" #IO7 "], 7 \n\t" \
+ "precrqu_s.qb.ph %[" #IO0 "], %[" #IO1 "], %[" #IO0 "] \n\t" \
+ "precrqu_s.qb.ph %[" #IO2 "], %[" #IO3 "], %[" #IO2 "] \n\t" \
+ "precrqu_s.qb.ph %[" #IO4 "], %[" #IO5 "], %[" #IO4 "] \n\t" \
+ "precrqu_s.qb.ph %[" #IO6 "], %[" #IO7 "], %[" #IO6 "] \n\t" \
+ "usw %[" #IO0 "], " XSTR(I13) "*" #I9 "(%[" #I8 "]) \n\t" \
+ "usw %[" #IO2 "], " XSTR(I13) "*" #I10 "(%[" #I8 "]) \n\t" \
+ "usw %[" #IO4 "], " XSTR(I13) "*" #I11 "(%[" #I8 "]) \n\t" \
+ "usw %[" #IO6 "], " XSTR(I13) "*" #I12 "(%[" #I8 "]) \n\t"
+
+#define OUTPUT_EARLY_CLOBBER_REGS_10() \
+ : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), \
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [temp6]"=&r"(temp6), \
+ [temp7]"=&r"(temp7), [temp8]"=&r"(temp8), [temp9]"=&r"(temp9), \
+ [temp10]"=&r"(temp10)
+
+#define OUTPUT_EARLY_CLOBBER_REGS_18() \
+ OUTPUT_EARLY_CLOBBER_REGS_10(), \
+ [temp11]"=&r"(temp11), [temp12]"=&r"(temp12), [temp13]"=&r"(temp13), \
+ [temp14]"=&r"(temp14), [temp15]"=&r"(temp15), [temp16]"=&r"(temp16), \
+ [temp17]"=&r"(temp17), [temp18]"=&r"(temp18)
+
+#endif // WEBP_DSP_MIPS_MACRO_H_
diff --git a/src/third_party/libwebp/src/dsp/msa_macro.h b/src/third_party/libwebp/src/dsp/msa_macro.h
new file mode 100644
index 0000000..dfacda6
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/msa_macro.h
@@ -0,0 +1,1392 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA common macros
+//
+// Author(s): Prashant Patil (prashant.patil@imgtec.com)
+
+#ifndef WEBP_DSP_MSA_MACRO_H_
+#define WEBP_DSP_MSA_MACRO_H_
+
+#include <stdint.h>
+#include <msa.h>
+
+#if defined(__clang__)
+ #define CLANG_BUILD
+#endif
+
+#ifdef CLANG_BUILD
+ #define ALPHAVAL (-1)
+ #define ADDVI_H(a, b) __msa_addvi_h((v8i16)a, b)
+ #define ADDVI_W(a, b) __msa_addvi_w((v4i32)a, b)
+ #define SRAI_B(a, b) __msa_srai_b((v16i8)a, b)
+ #define SRAI_H(a, b) __msa_srai_h((v8i16)a, b)
+ #define SRAI_W(a, b) __msa_srai_w((v4i32)a, b)
+ #define SRLI_H(a, b) __msa_srli_h((v8i16)a, b)
+ #define SLLI_B(a, b) __msa_slli_b((v4i32)a, b)
+ #define ANDI_B(a, b) __msa_andi_b((v16u8)a, b)
+ #define ORI_B(a, b) __msa_ori_b((v16u8)a, b)
+#else
+ #define ALPHAVAL (0xff)
+ #define ADDVI_H(a, b) (a + b)
+ #define ADDVI_W(a, b) (a + b)
+ #define SRAI_B(a, b) (a >> b)
+ #define SRAI_H(a, b) (a >> b)
+ #define SRAI_W(a, b) (a >> b)
+ #define SRLI_H(a, b) (a << b)
+ #define SLLI_B(a, b) (a << b)
+ #define ANDI_B(a, b) (a & b)
+ #define ORI_B(a, b) (a | b)
+#endif
+
+#define LD_B(RTYPE, psrc) *((RTYPE*)(psrc))
+#define LD_UB(...) LD_B(v16u8, __VA_ARGS__)
+#define LD_SB(...) LD_B(v16i8, __VA_ARGS__)
+
+#define LD_H(RTYPE, psrc) *((RTYPE*)(psrc))
+#define LD_UH(...) LD_H(v8u16, __VA_ARGS__)
+#define LD_SH(...) LD_H(v8i16, __VA_ARGS__)
+
+#define LD_W(RTYPE, psrc) *((RTYPE*)(psrc))
+#define LD_UW(...) LD_W(v4u32, __VA_ARGS__)
+#define LD_SW(...) LD_W(v4i32, __VA_ARGS__)
+
+#define ST_B(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in
+#define ST_UB(...) ST_B(v16u8, __VA_ARGS__)
+#define ST_SB(...) ST_B(v16i8, __VA_ARGS__)
+
+#define ST_H(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in
+#define ST_UH(...) ST_H(v8u16, __VA_ARGS__)
+#define ST_SH(...) ST_H(v8i16, __VA_ARGS__)
+
+#define ST_W(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in
+#define ST_UW(...) ST_W(v4u32, __VA_ARGS__)
+#define ST_SW(...) ST_W(v4i32, __VA_ARGS__)
+
+#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \
+ static inline TYPE FUNC_NAME(const void* const psrc) { \
+ const uint8_t* const psrc_m = (const uint8_t*)psrc; \
+ TYPE val_m; \
+ asm volatile ( \
+ "" #INSTR " %[val_m], %[psrc_m] \n\t" \
+ : [val_m] "=r" (val_m) \
+ : [psrc_m] "m" (*psrc_m)); \
+ return val_m; \
+ }
+
+#define MSA_LOAD(psrc, FUNC_NAME) FUNC_NAME(psrc)
+
+#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \
+ static inline void FUNC_NAME(TYPE val, void* const pdst) { \
+ uint8_t* const pdst_m = (uint8_t*)pdst; \
+ TYPE val_m = val; \
+ asm volatile ( \
+ " " #INSTR " %[val_m], %[pdst_m] \n\t" \
+ : [pdst_m] "=m" (*pdst_m) \
+ : [val_m] "r" (val_m)); \
+ }
+
+#define MSA_STORE(val, pdst, FUNC_NAME) FUNC_NAME(val, pdst)
+
+#if (__mips_isa_rev >= 6)
+ MSA_LOAD_FUNC(uint16_t, lh, msa_lh);
+ #define LH(psrc) MSA_LOAD(psrc, msa_lh)
+ MSA_LOAD_FUNC(uint32_t, lw, msa_lw);
+ #define LW(psrc) MSA_LOAD(psrc, msa_lw)
+ #if (__mips == 64)
+ MSA_LOAD_FUNC(uint64_t, ld, msa_ld);
+ #define LD(psrc) MSA_LOAD(psrc, msa_ld)
+ #else // !(__mips == 64)
+ #define LD(psrc) ((((uint64_t)MSA_LOAD(psrc + 4, msa_lw)) << 32) | \
+ MSA_LOAD(psrc, msa_lw))
+ #endif // (__mips == 64)
+
+ MSA_STORE_FUNC(uint16_t, sh, msa_sh);
+ #define SH(val, pdst) MSA_STORE(val, pdst, msa_sh)
+ MSA_STORE_FUNC(uint32_t, sw, msa_sw);
+ #define SW(val, pdst) MSA_STORE(val, pdst, msa_sw)
+ MSA_STORE_FUNC(uint64_t, sd, msa_sd);
+ #define SD(val, pdst) MSA_STORE(val, pdst, msa_sd)
+#else // !(__mips_isa_rev >= 6)
+ MSA_LOAD_FUNC(uint16_t, ulh, msa_ulh);
+ #define LH(psrc) MSA_LOAD(psrc, msa_ulh)
+ MSA_LOAD_FUNC(uint32_t, ulw, msa_ulw);
+ #define LW(psrc) MSA_LOAD(psrc, msa_ulw)
+ #if (__mips == 64)
+ MSA_LOAD_FUNC(uint64_t, uld, msa_uld);
+ #define LD(psrc) MSA_LOAD(psrc, msa_uld)
+ #else // !(__mips == 64)
+ #define LD(psrc) ((((uint64_t)MSA_LOAD(psrc + 4, msa_ulw)) << 32) | \
+ MSA_LOAD(psrc, msa_ulw))
+ #endif // (__mips == 64)
+
+ MSA_STORE_FUNC(uint16_t, ush, msa_ush);
+ #define SH(val, pdst) MSA_STORE(val, pdst, msa_ush)
+ MSA_STORE_FUNC(uint32_t, usw, msa_usw);
+ #define SW(val, pdst) MSA_STORE(val, pdst, msa_usw)
+ #define SD(val, pdst) do { \
+ uint8_t* const pdst_sd_m = (uint8_t*)(pdst); \
+ const uint32_t val0_m = (uint32_t)(val & 0x00000000FFFFFFFF); \
+ const uint32_t val1_m = (uint32_t)((val >> 32) & 0x00000000FFFFFFFF); \
+ SW(val0_m, pdst_sd_m); \
+ SW(val1_m, pdst_sd_m + 4); \
+ } while (0)
+#endif // (__mips_isa_rev >= 6)
+
+/* Description : Load 4 words with stride
+ * Arguments : Inputs - psrc, stride
+ * Outputs - out0, out1, out2, out3
+ * Details : Load word in 'out0' from (psrc)
+ * Load word in 'out1' from (psrc + stride)
+ * Load word in 'out2' from (psrc + 2 * stride)
+ * Load word in 'out3' from (psrc + 3 * stride)
+ */
+#define LW4(psrc, stride, out0, out1, out2, out3) do { \
+ const uint8_t* ptmp = (const uint8_t*)psrc; \
+ out0 = LW(ptmp); \
+ ptmp += stride; \
+ out1 = LW(ptmp); \
+ ptmp += stride; \
+ out2 = LW(ptmp); \
+ ptmp += stride; \
+ out3 = LW(ptmp); \
+} while (0)
+
+/* Description : Store words with stride
+ * Arguments : Inputs - in0, in1, in2, in3, pdst, stride
+ * Details : Store word from 'in0' to (pdst)
+ * Store word from 'in1' to (pdst + stride)
+ * Store word from 'in2' to (pdst + 2 * stride)
+ * Store word from 'in3' to (pdst + 3 * stride)
+ */
+#define SW4(in0, in1, in2, in3, pdst, stride) do { \
+ uint8_t* ptmp = (uint8_t*)pdst; \
+ SW(in0, ptmp); \
+ ptmp += stride; \
+ SW(in1, ptmp); \
+ ptmp += stride; \
+ SW(in2, ptmp); \
+ ptmp += stride; \
+ SW(in3, ptmp); \
+} while (0)
+
+#define SW3(in0, in1, in2, pdst, stride) do { \
+ uint8_t* ptmp = (uint8_t*)pdst; \
+ SW(in0, ptmp); \
+ ptmp += stride; \
+ SW(in1, ptmp); \
+ ptmp += stride; \
+ SW(in2, ptmp); \
+} while (0)
+
+#define SW2(in0, in1, pdst, stride) do { \
+ uint8_t* ptmp = (uint8_t*)pdst; \
+ SW(in0, ptmp); \
+ ptmp += stride; \
+ SW(in1, ptmp); \
+} while (0)
+
+/* Description : Store 4 double words with stride
+ * Arguments : Inputs - in0, in1, in2, in3, pdst, stride
+ * Details : Store double word from 'in0' to (pdst)
+ * Store double word from 'in1' to (pdst + stride)
+ * Store double word from 'in2' to (pdst + 2 * stride)
+ * Store double word from 'in3' to (pdst + 3 * stride)
+ */
+#define SD4(in0, in1, in2, in3, pdst, stride) do { \
+ uint8_t* ptmp = (uint8_t*)pdst; \
+ SD(in0, ptmp); \
+ ptmp += stride; \
+ SD(in1, ptmp); \
+ ptmp += stride; \
+ SD(in2, ptmp); \
+ ptmp += stride; \
+ SD(in3, ptmp); \
+} while (0)
+
+/* Description : Load vectors with 16 byte elements with stride
+ * Arguments : Inputs - psrc, stride
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Load 16 byte elements in 'out0' from (psrc)
+ * Load 16 byte elements in 'out1' from (psrc + stride)
+ */
+#define LD_B2(RTYPE, psrc, stride, out0, out1) do { \
+ out0 = LD_B(RTYPE, psrc); \
+ out1 = LD_B(RTYPE, psrc + stride); \
+} while (0)
+#define LD_UB2(...) LD_B2(v16u8, __VA_ARGS__)
+#define LD_SB2(...) LD_B2(v16i8, __VA_ARGS__)
+
+#define LD_B3(RTYPE, psrc, stride, out0, out1, out2) do { \
+ LD_B2(RTYPE, psrc, stride, out0, out1); \
+ out2 = LD_B(RTYPE, psrc + 2 * stride); \
+} while (0)
+#define LD_UB3(...) LD_B3(v16u8, __VA_ARGS__)
+#define LD_SB3(...) LD_B3(v16i8, __VA_ARGS__)
+
+#define LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3) do { \
+ LD_B2(RTYPE, psrc, stride, out0, out1); \
+ LD_B2(RTYPE, psrc + 2 * stride , stride, out2, out3); \
+} while (0)
+#define LD_UB4(...) LD_B4(v16u8, __VA_ARGS__)
+#define LD_SB4(...) LD_B4(v16i8, __VA_ARGS__)
+
+#define LD_B8(RTYPE, psrc, stride, \
+ out0, out1, out2, out3, out4, out5, out6, out7) do { \
+ LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3); \
+ LD_B4(RTYPE, psrc + 4 * stride, stride, out4, out5, out6, out7); \
+} while (0)
+#define LD_UB8(...) LD_B8(v16u8, __VA_ARGS__)
+#define LD_SB8(...) LD_B8(v16i8, __VA_ARGS__)
+
+/* Description : Load vectors with 8 halfword elements with stride
+ * Arguments : Inputs - psrc, stride
+ * Outputs - out0, out1
+ * Details : Load 8 halfword elements in 'out0' from (psrc)
+ * Load 8 halfword elements in 'out1' from (psrc + stride)
+ */
+#define LD_H2(RTYPE, psrc, stride, out0, out1) do { \
+ out0 = LD_H(RTYPE, psrc); \
+ out1 = LD_H(RTYPE, psrc + stride); \
+} while (0)
+#define LD_UH2(...) LD_H2(v8u16, __VA_ARGS__)
+#define LD_SH2(...) LD_H2(v8i16, __VA_ARGS__)
+
+/* Description : Load vectors with 4 word elements with stride
+ * Arguments : Inputs - psrc, stride
+ * Outputs - out0, out1, out2, out3
+ * Details : Load 4 word elements in 'out0' from (psrc + 0 * stride)
+ * Load 4 word elements in 'out1' from (psrc + 1 * stride)
+ * Load 4 word elements in 'out2' from (psrc + 2 * stride)
+ * Load 4 word elements in 'out3' from (psrc + 3 * stride)
+ */
+#define LD_W2(RTYPE, psrc, stride, out0, out1) do { \
+ out0 = LD_W(RTYPE, psrc); \
+ out1 = LD_W(RTYPE, psrc + stride); \
+} while (0)
+#define LD_UW2(...) LD_W2(v4u32, __VA_ARGS__)
+#define LD_SW2(...) LD_W2(v4i32, __VA_ARGS__)
+
+#define LD_W3(RTYPE, psrc, stride, out0, out1, out2) do { \
+ LD_W2(RTYPE, psrc, stride, out0, out1); \
+ out2 = LD_W(RTYPE, psrc + 2 * stride); \
+} while (0)
+#define LD_UW3(...) LD_W3(v4u32, __VA_ARGS__)
+#define LD_SW3(...) LD_W3(v4i32, __VA_ARGS__)
+
+#define LD_W4(RTYPE, psrc, stride, out0, out1, out2, out3) do { \
+ LD_W2(RTYPE, psrc, stride, out0, out1); \
+ LD_W2(RTYPE, psrc + 2 * stride, stride, out2, out3); \
+} while (0)
+#define LD_UW4(...) LD_W4(v4u32, __VA_ARGS__)
+#define LD_SW4(...) LD_W4(v4i32, __VA_ARGS__)
+
+/* Description : Store vectors of 16 byte elements with stride
+ * Arguments : Inputs - in0, in1, pdst, stride
+ * Details : Store 16 byte elements from 'in0' to (pdst)
+ * Store 16 byte elements from 'in1' to (pdst + stride)
+ */
+#define ST_B2(RTYPE, in0, in1, pdst, stride) do { \
+ ST_B(RTYPE, in0, pdst); \
+ ST_B(RTYPE, in1, pdst + stride); \
+} while (0)
+#define ST_UB2(...) ST_B2(v16u8, __VA_ARGS__)
+#define ST_SB2(...) ST_B2(v16i8, __VA_ARGS__)
+
+#define ST_B4(RTYPE, in0, in1, in2, in3, pdst, stride) do { \
+ ST_B2(RTYPE, in0, in1, pdst, stride); \
+ ST_B2(RTYPE, in2, in3, pdst + 2 * stride, stride); \
+} while (0)
+#define ST_UB4(...) ST_B4(v16u8, __VA_ARGS__)
+#define ST_SB4(...) ST_B4(v16i8, __VA_ARGS__)
+
+#define ST_B8(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \
+ pdst, stride) do { \
+ ST_B4(RTYPE, in0, in1, in2, in3, pdst, stride); \
+ ST_B4(RTYPE, in4, in5, in6, in7, pdst + 4 * stride, stride); \
+} while (0)
+#define ST_UB8(...) ST_B8(v16u8, __VA_ARGS__)
+
+/* Description : Store vectors of 4 word elements with stride
+ * Arguments : Inputs - in0, in1, in2, in3, pdst, stride
+ * Details : Store 4 word elements from 'in0' to (pdst + 0 * stride)
+ * Store 4 word elements from 'in1' to (pdst + 1 * stride)
+ * Store 4 word elements from 'in2' to (pdst + 2 * stride)
+ * Store 4 word elements from 'in3' to (pdst + 3 * stride)
+ */
+#define ST_W2(RTYPE, in0, in1, pdst, stride) do { \
+ ST_W(RTYPE, in0, pdst); \
+ ST_W(RTYPE, in1, pdst + stride); \
+} while (0)
+#define ST_UW2(...) ST_W2(v4u32, __VA_ARGS__)
+#define ST_SW2(...) ST_W2(v4i32, __VA_ARGS__)
+
+#define ST_W3(RTYPE, in0, in1, in2, pdst, stride) do { \
+ ST_W2(RTYPE, in0, in1, pdst, stride); \
+ ST_W(RTYPE, in2, pdst + 2 * stride); \
+} while (0)
+#define ST_UW3(...) ST_W3(v4u32, __VA_ARGS__)
+#define ST_SW3(...) ST_W3(v4i32, __VA_ARGS__)
+
+#define ST_W4(RTYPE, in0, in1, in2, in3, pdst, stride) do { \
+ ST_W2(RTYPE, in0, in1, pdst, stride); \
+ ST_W2(RTYPE, in2, in3, pdst + 2 * stride, stride); \
+} while (0)
+#define ST_UW4(...) ST_W4(v4u32, __VA_ARGS__)
+#define ST_SW4(...) ST_W4(v4i32, __VA_ARGS__)
+
+/* Description : Store vectors of 8 halfword elements with stride
+ * Arguments : Inputs - in0, in1, pdst, stride
+ * Details : Store 8 halfword elements from 'in0' to (pdst)
+ * Store 8 halfword elements from 'in1' to (pdst + stride)
+ */
+#define ST_H2(RTYPE, in0, in1, pdst, stride) do { \
+ ST_H(RTYPE, in0, pdst); \
+ ST_H(RTYPE, in1, pdst + stride); \
+} while (0)
+#define ST_UH2(...) ST_H2(v8u16, __VA_ARGS__)
+#define ST_SH2(...) ST_H2(v8i16, __VA_ARGS__)
+
+/* Description : Store 2x4 byte block to destination memory from input vector
+ * Arguments : Inputs - in, stidx, pdst, stride
+ * Details : Index 'stidx' halfword element from 'in' vector is copied to
+ * the GP register and stored to (pdst)
+ * Index 'stidx+1' halfword element from 'in' vector is copied to
+ * the GP register and stored to (pdst + stride)
+ * Index 'stidx+2' halfword element from 'in' vector is copied to
+ * the GP register and stored to (pdst + 2 * stride)
+ * Index 'stidx+3' halfword element from 'in' vector is copied to
+ * the GP register and stored to (pdst + 3 * stride)
+ */
+#define ST2x4_UB(in, stidx, pdst, stride) do { \
+ uint8_t* pblk_2x4_m = (uint8_t*)pdst; \
+ const uint16_t out0_m = __msa_copy_s_h((v8i16)in, stidx); \
+ const uint16_t out1_m = __msa_copy_s_h((v8i16)in, stidx + 1); \
+ const uint16_t out2_m = __msa_copy_s_h((v8i16)in, stidx + 2); \
+ const uint16_t out3_m = __msa_copy_s_h((v8i16)in, stidx + 3); \
+ SH(out0_m, pblk_2x4_m); \
+ pblk_2x4_m += stride; \
+ SH(out1_m, pblk_2x4_m); \
+ pblk_2x4_m += stride; \
+ SH(out2_m, pblk_2x4_m); \
+ pblk_2x4_m += stride; \
+ SH(out3_m, pblk_2x4_m); \
+} while (0)
+
+/* Description : Store 4x4 byte block to destination memory from input vector
+ * Arguments : Inputs - in0, in1, pdst, stride
+ * Details : 'Idx0' word element from input vector 'in0' is copied to the
+ * GP register and stored to (pdst)
+ * 'Idx1' word element from input vector 'in0' is copied to the
+ * GP register and stored to (pdst + stride)
+ * 'Idx2' word element from input vector 'in0' is copied to the
+ * GP register and stored to (pdst + 2 * stride)
+ * 'Idx3' word element from input vector 'in0' is copied to the
+ * GP register and stored to (pdst + 3 * stride)
+ */
+#define ST4x4_UB(in0, in1, idx0, idx1, idx2, idx3, pdst, stride) do { \
+ uint8_t* const pblk_4x4_m = (uint8_t*)pdst; \
+ const uint32_t out0_m = __msa_copy_s_w((v4i32)in0, idx0); \
+ const uint32_t out1_m = __msa_copy_s_w((v4i32)in0, idx1); \
+ const uint32_t out2_m = __msa_copy_s_w((v4i32)in1, idx2); \
+ const uint32_t out3_m = __msa_copy_s_w((v4i32)in1, idx3); \
+ SW4(out0_m, out1_m, out2_m, out3_m, pblk_4x4_m, stride); \
+} while (0)
+
+#define ST4x8_UB(in0, in1, pdst, stride) do { \
+ uint8_t* const pblk_4x8 = (uint8_t*)pdst; \
+ ST4x4_UB(in0, in0, 0, 1, 2, 3, pblk_4x8, stride); \
+ ST4x4_UB(in1, in1, 0, 1, 2, 3, pblk_4x8 + 4 * stride, stride); \
+} while (0)
+
+/* Description : Immediate number of elements to slide
+ * Arguments : Inputs - in0, in1, slide_val
+ * Outputs - out
+ * Return Type - as per RTYPE
+ * Details : Byte elements from 'in1' vector are slid into 'in0' by
+ * value specified in the 'slide_val'
+ */
+#define SLDI_B(RTYPE, in0, in1, slide_val) \
+ (RTYPE)__msa_sldi_b((v16i8)in0, (v16i8)in1, slide_val) \
+
+#define SLDI_UB(...) SLDI_B(v16u8, __VA_ARGS__)
+#define SLDI_SB(...) SLDI_B(v16i8, __VA_ARGS__)
+#define SLDI_SH(...) SLDI_B(v8i16, __VA_ARGS__)
+
+/* Description : Shuffle byte vector elements as per mask vector
+ * Arguments : Inputs - in0, in1, in2, in3, mask0, mask1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Byte elements from 'in0' & 'in1' are copied selectively to
+ * 'out0' as per control vector 'mask0'
+ */
+#define VSHF_B(RTYPE, in0, in1, mask) \
+ (RTYPE)__msa_vshf_b((v16i8)mask, (v16i8)in1, (v16i8)in0)
+
+#define VSHF_UB(...) VSHF_B(v16u8, __VA_ARGS__)
+#define VSHF_SB(...) VSHF_B(v16i8, __VA_ARGS__)
+#define VSHF_UH(...) VSHF_B(v8u16, __VA_ARGS__)
+#define VSHF_SH(...) VSHF_B(v8i16, __VA_ARGS__)
+
+#define VSHF_B2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) do { \
+ out0 = VSHF_B(RTYPE, in0, in1, mask0); \
+ out1 = VSHF_B(RTYPE, in2, in3, mask1); \
+} while (0)
+#define VSHF_B2_UB(...) VSHF_B2(v16u8, __VA_ARGS__)
+#define VSHF_B2_SB(...) VSHF_B2(v16i8, __VA_ARGS__)
+#define VSHF_B2_UH(...) VSHF_B2(v8u16, __VA_ARGS__)
+#define VSHF_B2_SH(...) VSHF_B2(v8i16, __VA_ARGS__)
+
+/* Description : Shuffle halfword vector elements as per mask vector
+ * Arguments : Inputs - in0, in1, in2, in3, mask0, mask1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : halfword elements from 'in0' & 'in1' are copied selectively to
+ * 'out0' as per control vector 'mask0'
+ */
+#define VSHF_H2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) do { \
+ out0 = (RTYPE)__msa_vshf_h((v8i16)mask0, (v8i16)in1, (v8i16)in0); \
+ out1 = (RTYPE)__msa_vshf_h((v8i16)mask1, (v8i16)in3, (v8i16)in2); \
+} while (0)
+#define VSHF_H2_UH(...) VSHF_H2(v8u16, __VA_ARGS__)
+#define VSHF_H2_SH(...) VSHF_H2(v8i16, __VA_ARGS__)
+
+/* Description : Dot product of byte vector elements
+ * Arguments : Inputs - mult0, mult1, cnst0, cnst1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Signed byte elements from 'mult0' are multiplied with
+ * signed byte elements from 'cnst0' producing a result
+ * twice the size of input i.e. signed halfword.
+ * The multiplication result of adjacent odd-even elements
+ * are added together and written to the 'out0' vector
+*/
+#define DOTP_SB2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \
+ out0 = (RTYPE)__msa_dotp_s_h((v16i8)mult0, (v16i8)cnst0); \
+ out1 = (RTYPE)__msa_dotp_s_h((v16i8)mult1, (v16i8)cnst1); \
+} while (0)
+#define DOTP_SB2_SH(...) DOTP_SB2(v8i16, __VA_ARGS__)
+
+/* Description : Dot product of halfword vector elements
+ * Arguments : Inputs - mult0, mult1, cnst0, cnst1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Signed halfword elements from 'mult0' are multiplied with
+ * signed halfword elements from 'cnst0' producing a result
+ * twice the size of input i.e. signed word.
+ * The multiplication result of adjacent odd-even elements
+ * are added together and written to the 'out0' vector
+ */
+#define DOTP_SH2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \
+ out0 = (RTYPE)__msa_dotp_s_w((v8i16)mult0, (v8i16)cnst0); \
+ out1 = (RTYPE)__msa_dotp_s_w((v8i16)mult1, (v8i16)cnst1); \
+} while (0)
+#define DOTP_SH2_SW(...) DOTP_SH2(v4i32, __VA_ARGS__)
+
+/* Description : Dot product of unsigned word vector elements
+ * Arguments : Inputs - mult0, mult1, cnst0, cnst1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Unsigned word elements from 'mult0' are multiplied with
+ * unsigned word elements from 'cnst0' producing a result
+ * twice the size of input i.e. unsigned double word.
+ * The multiplication result of adjacent odd-even elements
+ * are added together and written to the 'out0' vector
+ */
+#define DOTP_UW2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \
+ out0 = (RTYPE)__msa_dotp_u_d((v4u32)mult0, (v4u32)cnst0); \
+ out1 = (RTYPE)__msa_dotp_u_d((v4u32)mult1, (v4u32)cnst1); \
+} while (0)
+#define DOTP_UW2_UD(...) DOTP_UW2(v2u64, __VA_ARGS__)
+
+/* Description : Dot product & addition of halfword vector elements
+ * Arguments : Inputs - mult0, mult1, cnst0, cnst1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Signed halfword elements from 'mult0' are multiplied with
+ * signed halfword elements from 'cnst0' producing a result
+ * twice the size of input i.e. signed word.
+ * The multiplication result of adjacent odd-even elements
+ * are added to the 'out0' vector
+ */
+#define DPADD_SH2(RTYPE, mult0, mult1, cnst0, cnst1, out0, out1) do { \
+ out0 = (RTYPE)__msa_dpadd_s_w((v4i32)out0, (v8i16)mult0, (v8i16)cnst0); \
+ out1 = (RTYPE)__msa_dpadd_s_w((v4i32)out1, (v8i16)mult1, (v8i16)cnst1); \
+} while (0)
+#define DPADD_SH2_SW(...) DPADD_SH2(v4i32, __VA_ARGS__)
+
+/* Description : Clips all signed halfword elements of input vector
+ * between 0 & 255
+ * Arguments : Input/output - val
+ * Return Type - signed halfword
+ */
+#define CLIP_SH_0_255(val) do { \
+ const v8i16 max_m = __msa_ldi_h(255); \
+ val = __msa_maxi_s_h((v8i16)val, 0); \
+ val = __msa_min_s_h(max_m, (v8i16)val); \
+} while (0)
+
+#define CLIP_SH2_0_255(in0, in1) do { \
+ CLIP_SH_0_255(in0); \
+ CLIP_SH_0_255(in1); \
+} while (0)
+
+#define CLIP_SH4_0_255(in0, in1, in2, in3) do { \
+ CLIP_SH2_0_255(in0, in1); \
+ CLIP_SH2_0_255(in2, in3); \
+} while (0)
+
+/* Description : Clips all unsigned halfword elements of input vector
+ * between 0 & 255
+ * Arguments : Input - in
+ * Output - out_m
+ * Return Type - unsigned halfword
+ */
+#define CLIP_UH_0_255(in) do { \
+ const v8u16 max_m = (v8u16)__msa_ldi_h(255); \
+ in = __msa_maxi_u_h((v8u16) in, 0); \
+ in = __msa_min_u_h((v8u16) max_m, (v8u16) in); \
+} while (0)
+
+#define CLIP_UH2_0_255(in0, in1) do { \
+ CLIP_UH_0_255(in0); \
+ CLIP_UH_0_255(in1); \
+} while (0)
+
+/* Description : Clips all signed word elements of input vector
+ * between 0 & 255
+ * Arguments : Input/output - val
+ * Return Type - signed word
+ */
+#define CLIP_SW_0_255(val) do { \
+ const v4i32 max_m = __msa_ldi_w(255); \
+ val = __msa_maxi_s_w((v4i32)val, 0); \
+ val = __msa_min_s_w(max_m, (v4i32)val); \
+} while (0)
+
+#define CLIP_SW4_0_255(in0, in1, in2, in3) do { \
+ CLIP_SW_0_255(in0); \
+ CLIP_SW_0_255(in1); \
+ CLIP_SW_0_255(in2); \
+ CLIP_SW_0_255(in3); \
+} while (0)
+
+/* Description : Horizontal addition of 4 signed word elements of input vector
+ * Arguments : Input - in (signed word vector)
+ * Output - sum_m (i32 sum)
+ * Return Type - signed word (GP)
+ * Details : 4 signed word elements of 'in' vector are added together and
+ * the resulting integer sum is returned
+ */
+static WEBP_INLINE int32_t func_hadd_sw_s32(v4i32 in) {
+ const v2i64 res0_m = __msa_hadd_s_d((v4i32)in, (v4i32)in);
+ const v2i64 res1_m = __msa_splati_d(res0_m, 1);
+ const v2i64 out = res0_m + res1_m;
+ int32_t sum_m = __msa_copy_s_w((v4i32)out, 0);
+ return sum_m;
+}
+#define HADD_SW_S32(in) func_hadd_sw_s32(in)
+
+/* Description : Horizontal addition of 8 signed halfword elements
+ * Arguments : Input - in (signed halfword vector)
+ * Output - sum_m (s32 sum)
+ * Return Type - signed word
+ * Details : 8 signed halfword elements of input vector are added
+ * together and the resulting integer sum is returned
+ */
+static WEBP_INLINE int32_t func_hadd_sh_s32(v8i16 in) {
+ const v4i32 res = __msa_hadd_s_w(in, in);
+ const v2i64 res0 = __msa_hadd_s_d(res, res);
+ const v2i64 res1 = __msa_splati_d(res0, 1);
+ const v2i64 res2 = res0 + res1;
+ const int32_t sum_m = __msa_copy_s_w((v4i32)res2, 0);
+ return sum_m;
+}
+#define HADD_SH_S32(in) func_hadd_sh_s32(in)
+
+/* Description : Horizontal addition of 8 unsigned halfword elements
+ * Arguments : Input - in (unsigned halfword vector)
+ * Output - sum_m (u32 sum)
+ * Return Type - unsigned word
+ * Details : 8 unsigned halfword elements of input vector are added
+ * together and the resulting integer sum is returned
+ */
+static WEBP_INLINE uint32_t func_hadd_uh_u32(v8u16 in) {
+ uint32_t sum_m;
+ const v4u32 res_m = __msa_hadd_u_w(in, in);
+ v2u64 res0_m = __msa_hadd_u_d(res_m, res_m);
+ v2u64 res1_m = (v2u64)__msa_splati_d((v2i64)res0_m, 1);
+ res0_m = res0_m + res1_m;
+ sum_m = __msa_copy_s_w((v4i32)res0_m, 0);
+ return sum_m;
+}
+#define HADD_UH_U32(in) func_hadd_uh_u32(in)
+
+/* Description : Horizontal addition of signed half word vector elements
+ Arguments : Inputs - in0, in1
+ Outputs - out0, out1
+ Return Type - as per RTYPE
+ Details : Each signed odd half word element from 'in0' is added to
+ even signed half word element from 'in0' (pairwise) and the
+ halfword result is written in 'out0'
+*/
+#define HADD_SH2(RTYPE, in0, in1, out0, out1) do { \
+ out0 = (RTYPE)__msa_hadd_s_w((v8i16)in0, (v8i16)in0); \
+ out1 = (RTYPE)__msa_hadd_s_w((v8i16)in1, (v8i16)in1); \
+} while (0)
+#define HADD_SH2_SW(...) HADD_SH2(v4i32, __VA_ARGS__)
+
+#define HADD_SH4(RTYPE, in0, in1, in2, in3, out0, out1, out2, out3) do { \
+ HADD_SH2(RTYPE, in0, in1, out0, out1); \
+ HADD_SH2(RTYPE, in2, in3, out2, out3); \
+} while (0)
+#define HADD_SH4_SW(...) HADD_SH4(v4i32, __VA_ARGS__)
+
+/* Description : Horizontal subtraction of unsigned byte vector elements
+ * Arguments : Inputs - in0, in1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Each unsigned odd byte element from 'in0' is subtracted from
+ * even unsigned byte element from 'in0' (pairwise) and the
+ * halfword result is written to 'out0'
+ */
+#define HSUB_UB2(RTYPE, in0, in1, out0, out1) do { \
+ out0 = (RTYPE)__msa_hsub_u_h((v16u8)in0, (v16u8)in0); \
+ out1 = (RTYPE)__msa_hsub_u_h((v16u8)in1, (v16u8)in1); \
+} while (0)
+#define HSUB_UB2_UH(...) HSUB_UB2(v8u16, __VA_ARGS__)
+#define HSUB_UB2_SH(...) HSUB_UB2(v8i16, __VA_ARGS__)
+#define HSUB_UB2_SW(...) HSUB_UB2(v4i32, __VA_ARGS__)
+
+/* Description : Set element n input vector to GPR value
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Output - out
+ * Return Type - as per RTYPE
+ * Details : Set element 0 in vector 'out' to value specified in 'in0'
+ */
+#define INSERT_W2(RTYPE, in0, in1, out) do { \
+ out = (RTYPE)__msa_insert_w((v4i32)out, 0, in0); \
+ out = (RTYPE)__msa_insert_w((v4i32)out, 1, in1); \
+} while (0)
+#define INSERT_W2_UB(...) INSERT_W2(v16u8, __VA_ARGS__)
+#define INSERT_W2_SB(...) INSERT_W2(v16i8, __VA_ARGS__)
+
+#define INSERT_W4(RTYPE, in0, in1, in2, in3, out) do { \
+ out = (RTYPE)__msa_insert_w((v4i32)out, 0, in0); \
+ out = (RTYPE)__msa_insert_w((v4i32)out, 1, in1); \
+ out = (RTYPE)__msa_insert_w((v4i32)out, 2, in2); \
+ out = (RTYPE)__msa_insert_w((v4i32)out, 3, in3); \
+} while (0)
+#define INSERT_W4_UB(...) INSERT_W4(v16u8, __VA_ARGS__)
+#define INSERT_W4_SB(...) INSERT_W4(v16i8, __VA_ARGS__)
+#define INSERT_W4_SW(...) INSERT_W4(v4i32, __VA_ARGS__)
+
+/* Description : Set element n of double word input vector to GPR value
+ * Arguments : Inputs - in0, in1
+ * Output - out
+ * Return Type - as per RTYPE
+ * Details : Set element 0 in vector 'out' to GPR value specified in 'in0'
+ * Set element 1 in vector 'out' to GPR value specified in 'in1'
+ */
+#define INSERT_D2(RTYPE, in0, in1, out) do { \
+ out = (RTYPE)__msa_insert_d((v2i64)out, 0, in0); \
+ out = (RTYPE)__msa_insert_d((v2i64)out, 1, in1); \
+} while (0)
+#define INSERT_D2_UB(...) INSERT_D2(v16u8, __VA_ARGS__)
+#define INSERT_D2_SB(...) INSERT_D2(v16i8, __VA_ARGS__)
+
+/* Description : Interleave even byte elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even byte elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ */
+#define ILVEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvev_b((v16i8)in1, (v16i8)in0); \
+ out1 = (RTYPE)__msa_ilvev_b((v16i8)in3, (v16i8)in2); \
+} while (0)
+#define ILVEV_B2_UB(...) ILVEV_B2(v16u8, __VA_ARGS__)
+#define ILVEV_B2_SB(...) ILVEV_B2(v16i8, __VA_ARGS__)
+#define ILVEV_B2_UH(...) ILVEV_B2(v8u16, __VA_ARGS__)
+#define ILVEV_B2_SH(...) ILVEV_B2(v8i16, __VA_ARGS__)
+#define ILVEV_B2_SD(...) ILVEV_B2(v2i64, __VA_ARGS__)
+
+/* Description : Interleave odd byte elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Odd byte elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ */
+#define ILVOD_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvod_b((v16i8)in1, (v16i8)in0); \
+ out1 = (RTYPE)__msa_ilvod_b((v16i8)in3, (v16i8)in2); \
+} while (0)
+#define ILVOD_B2_UB(...) ILVOD_B2(v16u8, __VA_ARGS__)
+#define ILVOD_B2_SB(...) ILVOD_B2(v16i8, __VA_ARGS__)
+#define ILVOD_B2_UH(...) ILVOD_B2(v8u16, __VA_ARGS__)
+#define ILVOD_B2_SH(...) ILVOD_B2(v8i16, __VA_ARGS__)
+#define ILVOD_B2_SD(...) ILVOD_B2(v2i64, __VA_ARGS__)
+
+/* Description : Interleave even halfword elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even halfword elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ */
+#define ILVEV_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvev_h((v8i16)in1, (v8i16)in0); \
+ out1 = (RTYPE)__msa_ilvev_h((v8i16)in3, (v8i16)in2); \
+} while (0)
+#define ILVEV_H2_UB(...) ILVEV_H2(v16u8, __VA_ARGS__)
+#define ILVEV_H2_UH(...) ILVEV_H2(v8u16, __VA_ARGS__)
+#define ILVEV_H2_SH(...) ILVEV_H2(v8i16, __VA_ARGS__)
+#define ILVEV_H2_SW(...) ILVEV_H2(v4i32, __VA_ARGS__)
+
+/* Description : Interleave odd halfword elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Odd halfword elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ */
+#define ILVOD_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvod_h((v8i16)in1, (v8i16)in0); \
+ out1 = (RTYPE)__msa_ilvod_h((v8i16)in3, (v8i16)in2); \
+} while (0)
+#define ILVOD_H2_UB(...) ILVOD_H2(v16u8, __VA_ARGS__)
+#define ILVOD_H2_UH(...) ILVOD_H2(v8u16, __VA_ARGS__)
+#define ILVOD_H2_SH(...) ILVOD_H2(v8i16, __VA_ARGS__)
+#define ILVOD_H2_SW(...) ILVOD_H2(v4i32, __VA_ARGS__)
+
+/* Description : Interleave even word elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even word elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ */
+#define ILVEV_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvev_w((v4i32)in1, (v4i32)in0); \
+ out1 = (RTYPE)__msa_ilvev_w((v4i32)in3, (v4i32)in2); \
+} while (0)
+#define ILVEV_W2_UB(...) ILVEV_W2(v16u8, __VA_ARGS__)
+#define ILVEV_W2_SB(...) ILVEV_W2(v16i8, __VA_ARGS__)
+#define ILVEV_W2_UH(...) ILVEV_W2(v8u16, __VA_ARGS__)
+#define ILVEV_W2_SD(...) ILVEV_W2(v2i64, __VA_ARGS__)
+
+/* Description : Interleave even-odd word elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even word elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ * Odd word elements of 'in2' and 'in3' are interleaved
+ * and written to 'out1'
+ */
+#define ILVEVOD_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvev_w((v4i32)in1, (v4i32)in0); \
+ out1 = (RTYPE)__msa_ilvod_w((v4i32)in3, (v4i32)in2); \
+} while (0)
+#define ILVEVOD_W2_UB(...) ILVEVOD_W2(v16u8, __VA_ARGS__)
+#define ILVEVOD_W2_UH(...) ILVEVOD_W2(v8u16, __VA_ARGS__)
+#define ILVEVOD_W2_SH(...) ILVEVOD_W2(v8i16, __VA_ARGS__)
+#define ILVEVOD_W2_SW(...) ILVEVOD_W2(v4i32, __VA_ARGS__)
+
+/* Description : Interleave even-odd half-word elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even half-word elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ * Odd half-word elements of 'in2' and 'in3' are interleaved
+ * and written to 'out1'
+ */
+#define ILVEVOD_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvev_h((v8i16)in1, (v8i16)in0); \
+ out1 = (RTYPE)__msa_ilvod_h((v8i16)in3, (v8i16)in2); \
+} while (0)
+#define ILVEVOD_H2_UB(...) ILVEVOD_H2(v16u8, __VA_ARGS__)
+#define ILVEVOD_H2_UH(...) ILVEVOD_H2(v8u16, __VA_ARGS__)
+#define ILVEVOD_H2_SH(...) ILVEVOD_H2(v8i16, __VA_ARGS__)
+#define ILVEVOD_H2_SW(...) ILVEVOD_H2(v4i32, __VA_ARGS__)
+
+/* Description : Interleave even double word elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even double word elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'
+ */
+#define ILVEV_D2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvev_d((v2i64)in1, (v2i64)in0); \
+ out1 = (RTYPE)__msa_ilvev_d((v2i64)in3, (v2i64)in2); \
+} while (0)
+#define ILVEV_D2_UB(...) ILVEV_D2(v16u8, __VA_ARGS__)
+#define ILVEV_D2_SB(...) ILVEV_D2(v16i8, __VA_ARGS__)
+#define ILVEV_D2_SW(...) ILVEV_D2(v4i32, __VA_ARGS__)
+#define ILVEV_D2_SD(...) ILVEV_D2(v2i64, __VA_ARGS__)
+
+/* Description : Interleave left half of byte elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Left half of byte elements of 'in0' and 'in1' are interleaved
+ * and written to 'out0'.
+ */
+#define ILVL_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvl_b((v16i8)in0, (v16i8)in1); \
+ out1 = (RTYPE)__msa_ilvl_b((v16i8)in2, (v16i8)in3); \
+} while (0)
+#define ILVL_B2_UB(...) ILVL_B2(v16u8, __VA_ARGS__)
+#define ILVL_B2_SB(...) ILVL_B2(v16i8, __VA_ARGS__)
+#define ILVL_B2_UH(...) ILVL_B2(v8u16, __VA_ARGS__)
+#define ILVL_B2_SH(...) ILVL_B2(v8i16, __VA_ARGS__)
+#define ILVL_B2_SW(...) ILVL_B2(v4i32, __VA_ARGS__)
+
+/* Description : Interleave right half of byte elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Right half of byte elements of 'in0' and 'in1' are interleaved
+ * and written to out0.
+ */
+#define ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvr_b((v16i8)in0, (v16i8)in1); \
+ out1 = (RTYPE)__msa_ilvr_b((v16i8)in2, (v16i8)in3); \
+} while (0)
+#define ILVR_B2_UB(...) ILVR_B2(v16u8, __VA_ARGS__)
+#define ILVR_B2_SB(...) ILVR_B2(v16i8, __VA_ARGS__)
+#define ILVR_B2_UH(...) ILVR_B2(v8u16, __VA_ARGS__)
+#define ILVR_B2_SH(...) ILVR_B2(v8i16, __VA_ARGS__)
+#define ILVR_B2_SW(...) ILVR_B2(v4i32, __VA_ARGS__)
+
+#define ILVR_B4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \
+ out0, out1, out2, out3) do { \
+ ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ ILVR_B2(RTYPE, in4, in5, in6, in7, out2, out3); \
+} while (0)
+#define ILVR_B4_UB(...) ILVR_B4(v16u8, __VA_ARGS__)
+#define ILVR_B4_SB(...) ILVR_B4(v16i8, __VA_ARGS__)
+#define ILVR_B4_UH(...) ILVR_B4(v8u16, __VA_ARGS__)
+#define ILVR_B4_SH(...) ILVR_B4(v8i16, __VA_ARGS__)
+#define ILVR_B4_SW(...) ILVR_B4(v4i32, __VA_ARGS__)
+
+/* Description : Interleave right half of halfword elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Right half of halfword elements of 'in0' and 'in1' are
+ * interleaved and written to 'out0'.
+ */
+#define ILVR_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \
+ out1 = (RTYPE)__msa_ilvr_h((v8i16)in2, (v8i16)in3); \
+} while (0)
+#define ILVR_H2_UB(...) ILVR_H2(v16u8, __VA_ARGS__)
+#define ILVR_H2_SH(...) ILVR_H2(v8i16, __VA_ARGS__)
+#define ILVR_H2_SW(...) ILVR_H2(v4i32, __VA_ARGS__)
+
+#define ILVR_H4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \
+ out0, out1, out2, out3) do { \
+ ILVR_H2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ ILVR_H2(RTYPE, in4, in5, in6, in7, out2, out3); \
+} while (0)
+#define ILVR_H4_UB(...) ILVR_H4(v16u8, __VA_ARGS__)
+#define ILVR_H4_SH(...) ILVR_H4(v8i16, __VA_ARGS__)
+#define ILVR_H4_SW(...) ILVR_H4(v4i32, __VA_ARGS__)
+
+/* Description : Interleave right half of double word elements from vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Right half of double word elements of 'in0' and 'in1' are
+ * interleaved and written to 'out0'.
+ */
+#define ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvr_d((v2i64)in0, (v2i64)in1); \
+ out1 = (RTYPE)__msa_ilvr_d((v2i64)in2, (v2i64)in3); \
+} while (0)
+#define ILVR_D2_UB(...) ILVR_D2(v16u8, __VA_ARGS__)
+#define ILVR_D2_SB(...) ILVR_D2(v16i8, __VA_ARGS__)
+#define ILVR_D2_SH(...) ILVR_D2(v8i16, __VA_ARGS__)
+
+#define ILVR_D4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \
+ out0, out1, out2, out3) do { \
+ ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ ILVR_D2(RTYPE, in4, in5, in6, in7, out2, out3); \
+} while (0)
+#define ILVR_D4_SB(...) ILVR_D4(v16i8, __VA_ARGS__)
+#define ILVR_D4_UB(...) ILVR_D4(v16u8, __VA_ARGS__)
+
+/* Description : Interleave both left and right half of input vectors
+ * Arguments : Inputs - in0, in1
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Right half of byte elements from 'in0' and 'in1' are
+ * interleaved and written to 'out0'
+ */
+#define ILVRL_B2(RTYPE, in0, in1, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvr_b((v16i8)in0, (v16i8)in1); \
+ out1 = (RTYPE)__msa_ilvl_b((v16i8)in0, (v16i8)in1); \
+} while (0)
+#define ILVRL_B2_UB(...) ILVRL_B2(v16u8, __VA_ARGS__)
+#define ILVRL_B2_SB(...) ILVRL_B2(v16i8, __VA_ARGS__)
+#define ILVRL_B2_UH(...) ILVRL_B2(v8u16, __VA_ARGS__)
+#define ILVRL_B2_SH(...) ILVRL_B2(v8i16, __VA_ARGS__)
+#define ILVRL_B2_SW(...) ILVRL_B2(v4i32, __VA_ARGS__)
+
+#define ILVRL_H2(RTYPE, in0, in1, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \
+ out1 = (RTYPE)__msa_ilvl_h((v8i16)in0, (v8i16)in1); \
+} while (0)
+#define ILVRL_H2_UB(...) ILVRL_H2(v16u8, __VA_ARGS__)
+#define ILVRL_H2_SB(...) ILVRL_H2(v16i8, __VA_ARGS__)
+#define ILVRL_H2_SH(...) ILVRL_H2(v8i16, __VA_ARGS__)
+#define ILVRL_H2_SW(...) ILVRL_H2(v4i32, __VA_ARGS__)
+#define ILVRL_H2_UW(...) ILVRL_H2(v4u32, __VA_ARGS__)
+
+#define ILVRL_W2(RTYPE, in0, in1, out0, out1) do { \
+ out0 = (RTYPE)__msa_ilvr_w((v4i32)in0, (v4i32)in1); \
+ out1 = (RTYPE)__msa_ilvl_w((v4i32)in0, (v4i32)in1); \
+} while (0)
+#define ILVRL_W2_UB(...) ILVRL_W2(v16u8, __VA_ARGS__)
+#define ILVRL_W2_SH(...) ILVRL_W2(v8i16, __VA_ARGS__)
+#define ILVRL_W2_SW(...) ILVRL_W2(v4i32, __VA_ARGS__)
+#define ILVRL_W2_UW(...) ILVRL_W2(v4u32, __VA_ARGS__)
+
+/* Description : Pack even byte elements of vector pairs
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even byte elements of 'in0' are copied to the left half of
+ * 'out0' & even byte elements of 'in1' are copied to the right
+ * half of 'out0'.
+ */
+#define PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_pckev_b((v16i8)in0, (v16i8)in1); \
+ out1 = (RTYPE)__msa_pckev_b((v16i8)in2, (v16i8)in3); \
+} while (0)
+#define PCKEV_B2_SB(...) PCKEV_B2(v16i8, __VA_ARGS__)
+#define PCKEV_B2_UB(...) PCKEV_B2(v16u8, __VA_ARGS__)
+#define PCKEV_B2_SH(...) PCKEV_B2(v8i16, __VA_ARGS__)
+#define PCKEV_B2_SW(...) PCKEV_B2(v4i32, __VA_ARGS__)
+
+#define PCKEV_B4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \
+ out0, out1, out2, out3) do { \
+ PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1); \
+ PCKEV_B2(RTYPE, in4, in5, in6, in7, out2, out3); \
+} while (0)
+#define PCKEV_B4_SB(...) PCKEV_B4(v16i8, __VA_ARGS__)
+#define PCKEV_B4_UB(...) PCKEV_B4(v16u8, __VA_ARGS__)
+#define PCKEV_B4_SH(...) PCKEV_B4(v8i16, __VA_ARGS__)
+#define PCKEV_B4_SW(...) PCKEV_B4(v4i32, __VA_ARGS__)
+
+/* Description : Pack even halfword elements of vector pairs
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even halfword elements of 'in0' are copied to the left half of
+ * 'out0' & even halfword elements of 'in1' are copied to the
+ * right half of 'out0'.
+ */
+#define PCKEV_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_pckev_h((v8i16)in0, (v8i16)in1); \
+ out1 = (RTYPE)__msa_pckev_h((v8i16)in2, (v8i16)in3); \
+} while (0)
+#define PCKEV_H2_UH(...) PCKEV_H2(v8u16, __VA_ARGS__)
+#define PCKEV_H2_SH(...) PCKEV_H2(v8i16, __VA_ARGS__)
+#define PCKEV_H2_SW(...) PCKEV_H2(v4i32, __VA_ARGS__)
+#define PCKEV_H2_UW(...) PCKEV_H2(v4u32, __VA_ARGS__)
+
+/* Description : Pack even word elements of vector pairs
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Even word elements of 'in0' are copied to the left half of
+ * 'out0' & even word elements of 'in1' are copied to the
+ * right half of 'out0'.
+ */
+#define PCKEV_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_pckev_w((v4i32)in0, (v4i32)in1); \
+ out1 = (RTYPE)__msa_pckev_w((v4i32)in2, (v4i32)in3); \
+} while (0)
+#define PCKEV_W2_UH(...) PCKEV_W2(v8u16, __VA_ARGS__)
+#define PCKEV_W2_SH(...) PCKEV_W2(v8i16, __VA_ARGS__)
+#define PCKEV_W2_SW(...) PCKEV_W2(v4i32, __VA_ARGS__)
+#define PCKEV_W2_UW(...) PCKEV_W2(v4u32, __VA_ARGS__)
+
+/* Description : Pack odd halfword elements of vector pairs
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Odd halfword elements of 'in0' are copied to the left half of
+ * 'out0' & odd halfword elements of 'in1' are copied to the
+ * right half of 'out0'.
+ */
+#define PCKOD_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_pckod_h((v8i16)in0, (v8i16)in1); \
+ out1 = (RTYPE)__msa_pckod_h((v8i16)in2, (v8i16)in3); \
+} while (0)
+#define PCKOD_H2_UH(...) PCKOD_H2(v8u16, __VA_ARGS__)
+#define PCKOD_H2_SH(...) PCKOD_H2(v8i16, __VA_ARGS__)
+#define PCKOD_H2_SW(...) PCKOD_H2(v4i32, __VA_ARGS__)
+#define PCKOD_H2_UW(...) PCKOD_H2(v4u32, __VA_ARGS__)
+
+/* Description : Arithmetic immediate shift right all elements of word vector
+ * Arguments : Inputs - in0, in1, shift
+ * Outputs - in place operation
+ * Return Type - as per input vector RTYPE
+ * Details : Each element of vector 'in0' is right shifted by 'shift' and
+ * the result is written in-place. 'shift' is a GP variable.
+ */
+#define SRAI_W2(RTYPE, in0, in1, shift_val) do { \
+ in0 = (RTYPE)SRAI_W(in0, shift_val); \
+ in1 = (RTYPE)SRAI_W(in1, shift_val); \
+} while (0)
+#define SRAI_W2_SW(...) SRAI_W2(v4i32, __VA_ARGS__)
+#define SRAI_W2_UW(...) SRAI_W2(v4u32, __VA_ARGS__)
+
+#define SRAI_W4(RTYPE, in0, in1, in2, in3, shift_val) do { \
+ SRAI_W2(RTYPE, in0, in1, shift_val); \
+ SRAI_W2(RTYPE, in2, in3, shift_val); \
+} while (0)
+#define SRAI_W4_SW(...) SRAI_W4(v4i32, __VA_ARGS__)
+#define SRAI_W4_UW(...) SRAI_W4(v4u32, __VA_ARGS__)
+
+/* Description : Arithmetic shift right all elements of half-word vector
+ * Arguments : Inputs - in0, in1, shift
+ * Outputs - in place operation
+ * Return Type - as per input vector RTYPE
+ * Details : Each element of vector 'in0' is right shifted by 'shift' and
+ * the result is written in-place. 'shift' is a GP variable.
+ */
+#define SRAI_H2(RTYPE, in0, in1, shift_val) do { \
+ in0 = (RTYPE)SRAI_H(in0, shift_val); \
+ in1 = (RTYPE)SRAI_H(in1, shift_val); \
+} while (0)
+#define SRAI_H2_SH(...) SRAI_H2(v8i16, __VA_ARGS__)
+#define SRAI_H2_UH(...) SRAI_H2(v8u16, __VA_ARGS__)
+
+/* Description : Arithmetic rounded shift right all elements of word vector
+ * Arguments : Inputs - in0, in1, shift
+ * Outputs - in place operation
+ * Return Type - as per input vector RTYPE
+ * Details : Each element of vector 'in0' is right shifted by 'shift' and
+ * the result is written in-place. 'shift' is a GP variable.
+ */
+#define SRARI_W2(RTYPE, in0, in1, shift) do { \
+ in0 = (RTYPE)__msa_srari_w((v4i32)in0, shift); \
+ in1 = (RTYPE)__msa_srari_w((v4i32)in1, shift); \
+} while (0)
+#define SRARI_W2_SW(...) SRARI_W2(v4i32, __VA_ARGS__)
+
+#define SRARI_W4(RTYPE, in0, in1, in2, in3, shift) do { \
+ SRARI_W2(RTYPE, in0, in1, shift); \
+ SRARI_W2(RTYPE, in2, in3, shift); \
+} while (0)
+#define SRARI_W4_SH(...) SRARI_W4(v8i16, __VA_ARGS__)
+#define SRARI_W4_UW(...) SRARI_W4(v4u32, __VA_ARGS__)
+#define SRARI_W4_SW(...) SRARI_W4(v4i32, __VA_ARGS__)
+
+/* Description : Shift right arithmetic rounded double words
+ * Arguments : Inputs - in0, in1, shift
+ * Outputs - in place operation
+ * Return Type - as per RTYPE
+ * Details : Each element of vector 'in0' is shifted right arithmetically by
+ * the number of bits in the corresponding element in the vector
+ * 'shift'. The last discarded bit is added to shifted value for
+ * rounding and the result is written in-place.
+ * 'shift' is a vector.
+ */
+#define SRAR_D2(RTYPE, in0, in1, shift) do { \
+ in0 = (RTYPE)__msa_srar_d((v2i64)in0, (v2i64)shift); \
+ in1 = (RTYPE)__msa_srar_d((v2i64)in1, (v2i64)shift); \
+} while (0)
+#define SRAR_D2_SW(...) SRAR_D2(v4i32, __VA_ARGS__)
+#define SRAR_D2_SD(...) SRAR_D2(v2i64, __VA_ARGS__)
+#define SRAR_D2_UD(...) SRAR_D2(v2u64, __VA_ARGS__)
+
+#define SRAR_D4(RTYPE, in0, in1, in2, in3, shift) do { \
+ SRAR_D2(RTYPE, in0, in1, shift); \
+ SRAR_D2(RTYPE, in2, in3, shift); \
+} while (0)
+#define SRAR_D4_SD(...) SRAR_D4(v2i64, __VA_ARGS__)
+#define SRAR_D4_UD(...) SRAR_D4(v2u64, __VA_ARGS__)
+
+/* Description : Addition of 2 pairs of half-word vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Details : Each element in 'in0' is added to 'in1' and result is written
+ * to 'out0'.
+ */
+#define ADDVI_H2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)ADDVI_H(in0, in1); \
+ out1 = (RTYPE)ADDVI_H(in2, in3); \
+} while (0)
+#define ADDVI_H2_SH(...) ADDVI_H2(v8i16, __VA_ARGS__)
+#define ADDVI_H2_UH(...) ADDVI_H2(v8u16, __VA_ARGS__)
+
+/* Description : Addition of 2 pairs of word vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Details : Each element in 'in0' is added to 'in1' and result is written
+ * to 'out0'.
+ */
+#define ADDVI_W2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)ADDVI_W(in0, in1); \
+ out1 = (RTYPE)ADDVI_W(in2, in3); \
+} while (0)
+#define ADDVI_W2_SW(...) ADDVI_W2(v4i32, __VA_ARGS__)
+
+/* Description : Fill 2 pairs of word vectors with GP registers
+ * Arguments : Inputs - in0, in1
+ * Outputs - out0, out1
+ * Details : GP register in0 is replicated in each word element of out0
+ * GP register in1 is replicated in each word element of out1
+ */
+#define FILL_W2(RTYPE, in0, in1, out0, out1) do { \
+ out0 = (RTYPE)__msa_fill_w(in0); \
+ out1 = (RTYPE)__msa_fill_w(in1); \
+} while (0)
+#define FILL_W2_SW(...) FILL_W2(v4i32, __VA_ARGS__)
+
+/* Description : Addition of 2 pairs of vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Details : Each element in 'in0' is added to 'in1' and result is written
+ * to 'out0'.
+ */
+#define ADD2(in0, in1, in2, in3, out0, out1) do { \
+ out0 = in0 + in1; \
+ out1 = in2 + in3; \
+} while (0)
+
+#define ADD4(in0, in1, in2, in3, in4, in5, in6, in7, \
+ out0, out1, out2, out3) do { \
+ ADD2(in0, in1, in2, in3, out0, out1); \
+ ADD2(in4, in5, in6, in7, out2, out3); \
+} while (0)
+
+/* Description : Subtraction of 2 pairs of vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Details : Each element in 'in1' is subtracted from 'in0' and result is
+ * written to 'out0'.
+ */
+#define SUB2(in0, in1, in2, in3, out0, out1) do { \
+ out0 = in0 - in1; \
+ out1 = in2 - in3; \
+} while (0)
+
+#define SUB3(in0, in1, in2, in3, in4, in5, out0, out1, out2) do { \
+ out0 = in0 - in1; \
+ out1 = in2 - in3; \
+ out2 = in4 - in5; \
+} while (0)
+
+#define SUB4(in0, in1, in2, in3, in4, in5, in6, in7, \
+ out0, out1, out2, out3) do { \
+ out0 = in0 - in1; \
+ out1 = in2 - in3; \
+ out2 = in4 - in5; \
+ out3 = in6 - in7; \
+} while (0)
+
+/* Description : Addition - Subtraction of input vectors
+ * Arguments : Inputs - in0, in1
+ * Outputs - out0, out1
+ * Details : Each element in 'in1' is added to 'in0' and result is
+ * written to 'out0'.
+ * Each element in 'in1' is subtracted from 'in0' and result is
+ * written to 'out1'.
+ */
+#define ADDSUB2(in0, in1, out0, out1) do { \
+ out0 = in0 + in1; \
+ out1 = in0 - in1; \
+} while (0)
+
+/* Description : Multiplication of pairs of vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1
+ * Details : Each element from 'in0' is multiplied with elements from 'in1'
+ * and the result is written to 'out0'
+ */
+#define MUL2(in0, in1, in2, in3, out0, out1) do { \
+ out0 = in0 * in1; \
+ out1 = in2 * in3; \
+} while (0)
+
+#define MUL4(in0, in1, in2, in3, in4, in5, in6, in7, \
+ out0, out1, out2, out3) do { \
+ MUL2(in0, in1, in2, in3, out0, out1); \
+ MUL2(in4, in5, in6, in7, out2, out3); \
+} while (0)
+
+/* Description : Sign extend halfword elements from right half of the vector
+ * Arguments : Input - in (halfword vector)
+ * Output - out (sign extended word vector)
+ * Return Type - signed word
+ * Details : Sign bit of halfword elements from input vector 'in' is
+ * extracted and interleaved with same vector 'in0' to generate
+ * 4 word elements keeping sign intact
+ */
+#define UNPCK_R_SH_SW(in, out) do { \
+ const v8i16 sign_m = __msa_clti_s_h((v8i16)in, 0); \
+ out = (v4i32)__msa_ilvr_h(sign_m, (v8i16)in); \
+} while (0)
+
+/* Description : Sign extend halfword elements from input vector and return
+ * the result in pair of vectors
+ * Arguments : Input - in (halfword vector)
+ * Outputs - out0, out1 (sign extended word vectors)
+ * Return Type - signed word
+ * Details : Sign bit of halfword elements from input vector 'in' is
+ * extracted and interleaved right with same vector 'in0' to
+ * generate 4 signed word elements in 'out0'
+ * Then interleaved left with same vector 'in0' to
+ * generate 4 signed word elements in 'out1'
+ */
+#define UNPCK_SH_SW(in, out0, out1) do { \
+ const v8i16 tmp_m = __msa_clti_s_h((v8i16)in, 0); \
+ ILVRL_H2_SW(tmp_m, in, out0, out1); \
+} while (0)
+
+/* Description : Butterfly of 4 input vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1, out2, out3
+ * Details : Butterfly operation
+ */
+#define BUTTERFLY_4(in0, in1, in2, in3, out0, out1, out2, out3) do { \
+ out0 = in0 + in3; \
+ out1 = in1 + in2; \
+ out2 = in1 - in2; \
+ out3 = in0 - in3; \
+} while (0)
+
+/* Description : Transpose 16x4 block into 4x16 with byte elements in vectors
+ * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7,
+ * in8, in9, in10, in11, in12, in13, in14, in15
+ * Outputs - out0, out1, out2, out3
+ * Return Type - unsigned byte
+ */
+#define TRANSPOSE16x4_UB_UB(in0, in1, in2, in3, in4, in5, in6, in7, \
+ in8, in9, in10, in11, in12, in13, in14, in15, \
+ out0, out1, out2, out3) do { \
+ v2i64 tmp0_m, tmp1_m, tmp2_m, tmp3_m, tmp4_m, tmp5_m; \
+ ILVEV_W2_SD(in0, in4, in8, in12, tmp2_m, tmp3_m); \
+ ILVEV_W2_SD(in1, in5, in9, in13, tmp0_m, tmp1_m); \
+ ILVEV_D2_UB(tmp2_m, tmp3_m, tmp0_m, tmp1_m, out1, out3); \
+ ILVEV_W2_SD(in2, in6, in10, in14, tmp4_m, tmp5_m); \
+ ILVEV_W2_SD(in3, in7, in11, in15, tmp0_m, tmp1_m); \
+ ILVEV_D2_SD(tmp4_m, tmp5_m, tmp0_m, tmp1_m, tmp2_m, tmp3_m); \
+ ILVEV_B2_SD(out1, out3, tmp2_m, tmp3_m, tmp0_m, tmp1_m); \
+ ILVEVOD_H2_UB(tmp0_m, tmp1_m, tmp0_m, tmp1_m, out0, out2); \
+ ILVOD_B2_SD(out1, out3, tmp2_m, tmp3_m, tmp0_m, tmp1_m); \
+ ILVEVOD_H2_UB(tmp0_m, tmp1_m, tmp0_m, tmp1_m, out1, out3); \
+} while (0)
+
+/* Description : Transpose 16x8 block into 8x16 with byte elements in vectors
+ * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7,
+ * in8, in9, in10, in11, in12, in13, in14, in15
+ * Outputs - out0, out1, out2, out3, out4, out5, out6, out7
+ * Return Type - unsigned byte
+ */
+#define TRANSPOSE16x8_UB_UB(in0, in1, in2, in3, in4, in5, in6, in7, \
+ in8, in9, in10, in11, in12, in13, in14, in15, \
+ out0, out1, out2, out3, out4, out5, \
+ out6, out7) do { \
+ v8i16 tmp0_m, tmp1_m, tmp4_m, tmp5_m, tmp6_m, tmp7_m; \
+ v4i32 tmp2_m, tmp3_m; \
+ ILVEV_D2_UB(in0, in8, in1, in9, out7, out6); \
+ ILVEV_D2_UB(in2, in10, in3, in11, out5, out4); \
+ ILVEV_D2_UB(in4, in12, in5, in13, out3, out2); \
+ ILVEV_D2_UB(in6, in14, in7, in15, out1, out0); \
+ ILVEV_B2_SH(out7, out6, out5, out4, tmp0_m, tmp1_m); \
+ ILVOD_B2_SH(out7, out6, out5, out4, tmp4_m, tmp5_m); \
+ ILVEV_B2_UB(out3, out2, out1, out0, out5, out7); \
+ ILVOD_B2_SH(out3, out2, out1, out0, tmp6_m, tmp7_m); \
+ ILVEV_H2_SW(tmp0_m, tmp1_m, out5, out7, tmp2_m, tmp3_m); \
+ ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out0, out4); \
+ ILVOD_H2_SW(tmp0_m, tmp1_m, out5, out7, tmp2_m, tmp3_m); \
+ ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out2, out6); \
+ ILVEV_H2_SW(tmp4_m, tmp5_m, tmp6_m, tmp7_m, tmp2_m, tmp3_m); \
+ ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out1, out5); \
+ ILVOD_H2_SW(tmp4_m, tmp5_m, tmp6_m, tmp7_m, tmp2_m, tmp3_m); \
+ ILVEVOD_W2_UB(tmp2_m, tmp3_m, tmp2_m, tmp3_m, out3, out7); \
+} while (0)
+
+/* Description : Transpose 4x4 block with word elements in vectors
+ * Arguments : Inputs - in0, in1, in2, in3
+ * Outputs - out0, out1, out2, out3
+ * Return Type - as per RTYPE
+ */
+#define TRANSPOSE4x4_W(RTYPE, in0, in1, in2, in3, \
+ out0, out1, out2, out3) do { \
+ v4i32 s0_m, s1_m, s2_m, s3_m; \
+ ILVRL_W2_SW(in1, in0, s0_m, s1_m); \
+ ILVRL_W2_SW(in3, in2, s2_m, s3_m); \
+ out0 = (RTYPE)__msa_ilvr_d((v2i64)s2_m, (v2i64)s0_m); \
+ out1 = (RTYPE)__msa_ilvl_d((v2i64)s2_m, (v2i64)s0_m); \
+ out2 = (RTYPE)__msa_ilvr_d((v2i64)s3_m, (v2i64)s1_m); \
+ out3 = (RTYPE)__msa_ilvl_d((v2i64)s3_m, (v2i64)s1_m); \
+} while (0)
+#define TRANSPOSE4x4_SW_SW(...) TRANSPOSE4x4_W(v4i32, __VA_ARGS__)
+
+/* Description : Add block 4x4
+ * Arguments : Inputs - in0, in1, in2, in3, pdst, stride
+ * Details : Least significant 4 bytes from each input vector are added to
+ * the destination bytes, clipped between 0-255 and stored.
+ */
+#define ADDBLK_ST4x4_UB(in0, in1, in2, in3, pdst, stride) do { \
+ uint32_t src0_m, src1_m, src2_m, src3_m; \
+ v8i16 inp0_m, inp1_m, res0_m, res1_m; \
+ v16i8 dst0_m = { 0 }; \
+ v16i8 dst1_m = { 0 }; \
+ const v16i8 zero_m = { 0 }; \
+ ILVR_D2_SH(in1, in0, in3, in2, inp0_m, inp1_m); \
+ LW4(pdst, stride, src0_m, src1_m, src2_m, src3_m); \
+ INSERT_W2_SB(src0_m, src1_m, dst0_m); \
+ INSERT_W2_SB(src2_m, src3_m, dst1_m); \
+ ILVR_B2_SH(zero_m, dst0_m, zero_m, dst1_m, res0_m, res1_m); \
+ ADD2(res0_m, inp0_m, res1_m, inp1_m, res0_m, res1_m); \
+ CLIP_SH2_0_255(res0_m, res1_m); \
+ PCKEV_B2_SB(res0_m, res0_m, res1_m, res1_m, dst0_m, dst1_m); \
+ ST4x4_UB(dst0_m, dst1_m, 0, 1, 0, 1, pdst, stride); \
+} while (0)
+
+/* Description : Pack even byte elements, extract 0 & 2 index words from pair
+ * of results and store 4 words in destination memory as per
+ * stride
+ * Arguments : Inputs - in0, in1, in2, in3, pdst, stride
+ */
+#define PCKEV_ST4x4_UB(in0, in1, in2, in3, pdst, stride) do { \
+ v16i8 tmp0_m, tmp1_m; \
+ PCKEV_B2_SB(in1, in0, in3, in2, tmp0_m, tmp1_m); \
+ ST4x4_UB(tmp0_m, tmp1_m, 0, 2, 0, 2, pdst, stride); \
+} while (0)
+
+/* Description : average with rounding (in0 + in1 + 1) / 2.
+ * Arguments : Inputs - in0, in1, in2, in3,
+ * Outputs - out0, out1
+ * Return Type - as per RTYPE
+ * Details : Each unsigned byte element from 'in0' vector is added with
+ * each unsigned byte element from 'in1' vector. Then the average
+ * with rounding is calculated and written to 'out0'
+ */
+#define AVER_UB2(RTYPE, in0, in1, in2, in3, out0, out1) do { \
+ out0 = (RTYPE)__msa_aver_u_b((v16u8)in0, (v16u8)in1); \
+ out1 = (RTYPE)__msa_aver_u_b((v16u8)in2, (v16u8)in3); \
+} while (0)
+#define AVER_UB2_UB(...) AVER_UB2(v16u8, __VA_ARGS__)
+
+#endif /* WEBP_DSP_MSA_MACRO_H_ */
diff --git a/src/third_party/libwebp/src/dsp/neon.h b/src/third_party/libwebp/src/dsp/neon.h
new file mode 100644
index 0000000..aa1dea1
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/neon.h
@@ -0,0 +1,101 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// NEON common code.
+
+#ifndef WEBP_DSP_NEON_H_
+#define WEBP_DSP_NEON_H_
+
+#include <arm_neon.h>
+
+#include "src/dsp/dsp.h"
+
+// Right now, some intrinsics functions seem slower, so we disable them
+// everywhere except newer clang/gcc or aarch64 where the inline assembly is
+// incompatible.
+#if LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,9) || defined(__aarch64__)
+#define WEBP_USE_INTRINSICS // use intrinsics when possible
+#endif
+
+#define INIT_VECTOR2(v, a, b) do { \
+ v.val[0] = a; \
+ v.val[1] = b; \
+} while (0)
+
+#define INIT_VECTOR3(v, a, b, c) do { \
+ v.val[0] = a; \
+ v.val[1] = b; \
+ v.val[2] = c; \
+} while (0)
+
+#define INIT_VECTOR4(v, a, b, c, d) do { \
+ v.val[0] = a; \
+ v.val[1] = b; \
+ v.val[2] = c; \
+ v.val[3] = d; \
+} while (0)
+
+// if using intrinsics, this flag avoids some functions that make gcc-4.6.3
+// crash ("internal compiler error: in immed_double_const, at emit-rtl.").
+// (probably similar to gcc.gnu.org/bugzilla/show_bug.cgi?id=48183)
+#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__))
+#define WORK_AROUND_GCC
+#endif
+
+static WEBP_INLINE int32x4x4_t Transpose4x4_NEON(const int32x4x4_t rows) {
+ uint64x2x2_t row01, row23;
+
+ row01.val[0] = vreinterpretq_u64_s32(rows.val[0]);
+ row01.val[1] = vreinterpretq_u64_s32(rows.val[1]);
+ row23.val[0] = vreinterpretq_u64_s32(rows.val[2]);
+ row23.val[1] = vreinterpretq_u64_s32(rows.val[3]);
+ // Transpose 64-bit values (there's no vswp equivalent)
+ {
+ const uint64x1_t row0h = vget_high_u64(row01.val[0]);
+ const uint64x1_t row2l = vget_low_u64(row23.val[0]);
+ const uint64x1_t row1h = vget_high_u64(row01.val[1]);
+ const uint64x1_t row3l = vget_low_u64(row23.val[1]);
+ row01.val[0] = vcombine_u64(vget_low_u64(row01.val[0]), row2l);
+ row23.val[0] = vcombine_u64(row0h, vget_high_u64(row23.val[0]));
+ row01.val[1] = vcombine_u64(vget_low_u64(row01.val[1]), row3l);
+ row23.val[1] = vcombine_u64(row1h, vget_high_u64(row23.val[1]));
+ }
+ {
+ const int32x4x2_t out01 = vtrnq_s32(vreinterpretq_s32_u64(row01.val[0]),
+ vreinterpretq_s32_u64(row01.val[1]));
+ const int32x4x2_t out23 = vtrnq_s32(vreinterpretq_s32_u64(row23.val[0]),
+ vreinterpretq_s32_u64(row23.val[1]));
+ int32x4x4_t out;
+ out.val[0] = out01.val[0];
+ out.val[1] = out01.val[1];
+ out.val[2] = out23.val[0];
+ out.val[3] = out23.val[1];
+ return out;
+ }
+}
+
+#if 0 // Useful debug macro.
+#include <stdio.h>
+#define PRINT_REG(REG, SIZE) do { \
+ int i; \
+ printf("%s \t[%d]: 0x", #REG, SIZE); \
+ if (SIZE == 8) { \
+ uint8_t _tmp[8]; \
+ vst1_u8(_tmp, (REG)); \
+ for (i = 0; i < 8; ++i) printf("%.2x ", _tmp[i]); \
+ } else if (SIZE == 16) { \
+ uint16_t _tmp[4]; \
+ vst1_u16(_tmp, (REG)); \
+ for (i = 0; i < 4; ++i) printf("%.4x ", _tmp[i]); \
+ } \
+ printf("\n"); \
+} while (0)
+#endif
+
+#endif // WEBP_DSP_NEON_H_
diff --git a/src/third_party/libwebp/src/dsp/rescaler.c b/src/third_party/libwebp/src/dsp/rescaler.c
new file mode 100644
index 0000000..bd3e143
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/rescaler.c
@@ -0,0 +1,256 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Rescaling functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#else
+#include <assert.h>
+#endif
+
+#include "src/dsp/dsp.h"
+#include "src/utils/rescaler_utils.h"
+
+//------------------------------------------------------------------------------
+// Implementations of critical functions ImportRow / ExportRow
+
+#define ROUNDER (WEBP_RESCALER_ONE >> 1)
+#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
+
+//------------------------------------------------------------------------------
+// Row import
+
+void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk,
+ const uint8_t* src) {
+ const int x_stride = wrk->num_channels;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ int channel;
+ assert(!WebPRescalerInputDone(wrk));
+ assert(wrk->x_expand);
+ for (channel = 0; channel < x_stride; ++channel) {
+ int x_in = channel;
+ int x_out = channel;
+ // simple bilinear interpolation
+ int accum = wrk->x_add;
+ int left = src[x_in];
+ int right = (wrk->src_width > 1) ? src[x_in + x_stride] : left;
+ x_in += x_stride;
+ while (1) {
+ wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
+ x_out += x_stride;
+ if (x_out >= x_out_max) break;
+ accum -= wrk->x_sub;
+ if (accum < 0) {
+ left = right;
+ x_in += x_stride;
+ assert(x_in < wrk->src_width * x_stride);
+ right = src[x_in];
+ accum += wrk->x_add;
+ }
+ }
+ assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
+ }
+}
+
+void WebPRescalerImportRowShrink_C(WebPRescaler* const wrk,
+ const uint8_t* src) {
+ const int x_stride = wrk->num_channels;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ int channel;
+ assert(!WebPRescalerInputDone(wrk));
+ assert(!wrk->x_expand);
+ for (channel = 0; channel < x_stride; ++channel) {
+ int x_in = channel;
+ int x_out = channel;
+ uint32_t sum = 0;
+ int accum = 0;
+ while (x_out < x_out_max) {
+ uint32_t base = 0;
+ accum += wrk->x_add;
+ while (accum > 0) {
+ accum -= wrk->x_sub;
+ assert(x_in < wrk->src_width * x_stride);
+ base = src[x_in];
+ sum += base;
+ x_in += x_stride;
+ }
+ { // Emit next horizontal pixel.
+ const rescaler_t frac = base * (-accum);
+ wrk->frow[x_out] = sum * wrk->x_sub - frac;
+ // fresh fractional start for next pixel
+ sum = (int)MULT_FIX(frac, wrk->fx_scale);
+ }
+ x_out += x_stride;
+ }
+ assert(accum == 0);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Row export
+
+void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
+ int x_out;
+ uint8_t* const dst = wrk->dst;
+ rescaler_t* const irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* const frow = wrk->frow;
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(wrk->y_expand);
+ assert(wrk->y_sub != 0);
+ if (wrk->y_accum == 0) {
+ for (x_out = 0; x_out < x_out_max; ++x_out) {
+ const uint32_t J = frow[x_out];
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ } else {
+ const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
+ const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
+ for (x_out = 0; x_out < x_out_max; ++x_out) {
+ const uint64_t I = (uint64_t)A * frow[x_out]
+ + (uint64_t)B * irow[x_out];
+ const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ }
+}
+
+void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) {
+ int x_out;
+ uint8_t* const dst = wrk->dst;
+ rescaler_t* const irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* const frow = wrk->frow;
+ const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(!wrk->y_expand);
+ if (yscale) {
+ for (x_out = 0; x_out < x_out_max; ++x_out) {
+ const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale);
+ const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = frac; // new fractional start
+ }
+ } else {
+ for (x_out = 0; x_out < x_out_max; ++x_out) {
+ const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = 0;
+ }
+ }
+}
+
+#undef MULT_FIX
+#undef ROUNDER
+
+//------------------------------------------------------------------------------
+// Main entry calls
+
+void WebPRescalerImportRow(WebPRescaler* const wrk, const uint8_t* src) {
+ assert(!WebPRescalerInputDone(wrk));
+ if (!wrk->x_expand) {
+ WebPRescalerImportRowShrink(wrk, src);
+ } else {
+ WebPRescalerImportRowExpand(wrk, src);
+ }
+}
+
+void WebPRescalerExportRow(WebPRescaler* const wrk) {
+ if (wrk->y_accum <= 0) {
+ assert(!WebPRescalerOutputDone(wrk));
+ if (wrk->y_expand) {
+ WebPRescalerExportRowExpand(wrk);
+ } else if (wrk->fxy_scale) {
+ WebPRescalerExportRowShrink(wrk);
+ } else { // special case
+ int i;
+ assert(wrk->src_height == wrk->dst_height && wrk->x_add == 1);
+ assert(wrk->src_width == 1 && wrk->dst_width <= 2);
+ for (i = 0; i < wrk->num_channels * wrk->dst_width; ++i) {
+ wrk->dst[i] = wrk->irow[i];
+ wrk->irow[i] = 0;
+ }
+ }
+ wrk->y_accum += wrk->y_add;
+ wrk->dst += wrk->dst_stride;
+ ++wrk->dst_y;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
+WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
+
+WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
+WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
+
+extern void WebPRescalerDspInitSSE2(void);
+extern void WebPRescalerDspInitMIPS32(void);
+extern void WebPRescalerDspInitMIPSdspR2(void);
+extern void WebPRescalerDspInitMSA(void);
+extern void WebPRescalerDspInitNEON(void);
+
+WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
+#if !defined(WEBP_REDUCE_SIZE)
+#if !WEBP_NEON_OMIT_C_CODE
+ WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C;
+ WebPRescalerExportRowShrink = WebPRescalerExportRowShrink_C;
+#endif
+
+ WebPRescalerImportRowExpand = WebPRescalerImportRowExpand_C;
+ WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C;
+
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ WebPRescalerDspInitSSE2();
+ }
+#endif
+#if defined(WEBP_USE_MIPS32)
+ if (VP8GetCPUInfo(kMIPS32)) {
+ WebPRescalerDspInitMIPS32();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ WebPRescalerDspInitMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_MSA)
+ if (VP8GetCPUInfo(kMSA)) {
+ WebPRescalerDspInitMSA();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ WebPRescalerDspInitNEON();
+ }
+#endif
+
+ assert(WebPRescalerExportRowExpand != NULL);
+ assert(WebPRescalerExportRowShrink != NULL);
+ assert(WebPRescalerImportRowExpand != NULL);
+ assert(WebPRescalerImportRowShrink != NULL);
+#endif // WEBP_REDUCE_SIZE
+}
diff --git a/src/third_party/libwebp/src/dsp/rescaler_mips32.c b/src/third_party/libwebp/src/dsp/rescaler_mips32.c
new file mode 100644
index 0000000..542f7e5
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/rescaler_mips32.c
@@ -0,0 +1,293 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of rescaling functions
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS32) && !defined(WEBP_REDUCE_SIZE)
+
+#include <assert.h>
+#include "src/utils/rescaler_utils.h"
+
+//------------------------------------------------------------------------------
+// Row import
+
+static void ImportRowShrink_MIPS32(WebPRescaler* const wrk,
+ const uint8_t* src) {
+ const int x_stride = wrk->num_channels;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const int fx_scale = wrk->fx_scale;
+ const int x_add = wrk->x_add;
+ const int x_sub = wrk->x_sub;
+ const int x_stride1 = x_stride << 2;
+ int channel;
+ assert(!wrk->x_expand);
+ assert(!WebPRescalerInputDone(wrk));
+
+ for (channel = 0; channel < x_stride; ++channel) {
+ const uint8_t* src1 = src + channel;
+ rescaler_t* frow = wrk->frow + channel;
+ int temp1, temp2, temp3;
+ int base, frac, sum;
+ int accum, accum1;
+ int loop_c = x_out_max - channel;
+
+ __asm__ volatile (
+ "li %[temp1], 0x8000 \n\t"
+ "li %[temp2], 0x10000 \n\t"
+ "li %[sum], 0 \n\t"
+ "li %[accum], 0 \n\t"
+ "1: \n\t"
+ "addu %[accum], %[accum], %[x_add] \n\t"
+ "li %[base], 0 \n\t"
+ "blez %[accum], 3f \n\t"
+ "2: \n\t"
+ "lbu %[base], 0(%[src1]) \n\t"
+ "subu %[accum], %[accum], %[x_sub] \n\t"
+ "addu %[src1], %[src1], %[x_stride] \n\t"
+ "addu %[sum], %[sum], %[base] \n\t"
+ "bgtz %[accum], 2b \n\t"
+ "3: \n\t"
+ "negu %[accum1], %[accum] \n\t"
+ "mul %[frac], %[base], %[accum1] \n\t"
+ "mul %[temp3], %[sum], %[x_sub] \n\t"
+ "subu %[loop_c], %[loop_c], %[x_stride] \n\t"
+ "mult %[temp1], %[temp2] \n\t"
+ "maddu %[frac], %[fx_scale] \n\t"
+ "mfhi %[sum] \n\t"
+ "subu %[temp3], %[temp3], %[frac] \n\t"
+ "sw %[temp3], 0(%[frow]) \n\t"
+ "addu %[frow], %[frow], %[x_stride1] \n\t"
+ "bgtz %[loop_c], 1b \n\t"
+ : [accum]"=&r"(accum), [src1]"+r"(src1), [temp3]"=&r"(temp3),
+ [sum]"=&r"(sum), [base]"=&r"(base), [frac]"=&r"(frac),
+ [frow]"+r"(frow), [accum1]"=&r"(accum1),
+ [temp2]"=&r"(temp2), [temp1]"=&r"(temp1)
+ : [x_stride]"r"(x_stride), [fx_scale]"r"(fx_scale),
+ [x_sub]"r"(x_sub), [x_add]"r"(x_add),
+ [loop_c]"r"(loop_c), [x_stride1]"r"(x_stride1)
+ : "memory", "hi", "lo"
+ );
+ assert(accum == 0);
+ }
+}
+
+static void ImportRowExpand_MIPS32(WebPRescaler* const wrk,
+ const uint8_t* src) {
+ const int x_stride = wrk->num_channels;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const int x_add = wrk->x_add;
+ const int x_sub = wrk->x_sub;
+ const int src_width = wrk->src_width;
+ const int x_stride1 = x_stride << 2;
+ int channel;
+ assert(wrk->x_expand);
+ assert(!WebPRescalerInputDone(wrk));
+
+ for (channel = 0; channel < x_stride; ++channel) {
+ const uint8_t* src1 = src + channel;
+ rescaler_t* frow = wrk->frow + channel;
+ int temp1, temp2, temp3, temp4;
+ int frac;
+ int accum;
+ int x_out = channel;
+
+ __asm__ volatile (
+ "addiu %[temp3], %[src_width], -1 \n\t"
+ "lbu %[temp2], 0(%[src1]) \n\t"
+ "addu %[src1], %[src1], %[x_stride] \n\t"
+ "bgtz %[temp3], 0f \n\t"
+ "addiu %[temp1], %[temp2], 0 \n\t"
+ "b 3f \n\t"
+ "0: \n\t"
+ "lbu %[temp1], 0(%[src1]) \n\t"
+ "3: \n\t"
+ "addiu %[accum], %[x_add], 0 \n\t"
+ "1: \n\t"
+ "subu %[temp3], %[temp2], %[temp1] \n\t"
+ "mul %[temp3], %[temp3], %[accum] \n\t"
+ "mul %[temp4], %[temp1], %[x_add] \n\t"
+ "addu %[temp3], %[temp4], %[temp3] \n\t"
+ "sw %[temp3], 0(%[frow]) \n\t"
+ "addu %[frow], %[frow], %[x_stride1] \n\t"
+ "addu %[x_out], %[x_out], %[x_stride] \n\t"
+ "subu %[temp3], %[x_out], %[x_out_max] \n\t"
+ "bgez %[temp3], 2f \n\t"
+ "subu %[accum], %[accum], %[x_sub] \n\t"
+ "bgez %[accum], 4f \n\t"
+ "addiu %[temp2], %[temp1], 0 \n\t"
+ "addu %[src1], %[src1], %[x_stride] \n\t"
+ "lbu %[temp1], 0(%[src1]) \n\t"
+ "addu %[accum], %[accum], %[x_add] \n\t"
+ "4: \n\t"
+ "b 1b \n\t"
+ "2: \n\t"
+ : [src1]"+r"(src1), [accum]"=&r"(accum), [temp1]"=&r"(temp1),
+ [temp2]"=&r"(temp2), [temp3]"=&r"(temp3), [temp4]"=&r"(temp4),
+ [x_out]"+r"(x_out), [frac]"=&r"(frac), [frow]"+r"(frow)
+ : [x_stride]"r"(x_stride), [x_add]"r"(x_add), [x_sub]"r"(x_sub),
+ [x_stride1]"r"(x_stride1), [src_width]"r"(src_width),
+ [x_out_max]"r"(x_out_max)
+ : "memory", "hi", "lo"
+ );
+ assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Row export
+
+static void ExportRowExpand_MIPS32(WebPRescaler* const wrk) {
+ uint8_t* dst = wrk->dst;
+ rescaler_t* irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* frow = wrk->frow;
+ int temp0, temp1, temp3, temp4, temp5, loop_end;
+ const int temp2 = (int)wrk->fy_scale;
+ const int temp6 = x_out_max << 2;
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(wrk->y_expand);
+ assert(wrk->y_sub != 0);
+ if (wrk->y_accum == 0) {
+ __asm__ volatile (
+ "li %[temp3], 0x10000 \n\t"
+ "li %[temp4], 0x8000 \n\t"
+ "addu %[loop_end], %[frow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[frow]) \n\t"
+ "addiu %[dst], %[dst], 1 \n\t"
+ "addiu %[frow], %[frow], 4 \n\t"
+ "mult %[temp3], %[temp4] \n\t"
+ "maddu %[temp0], %[temp2] \n\t"
+ "mfhi %[temp5] \n\t"
+ "sb %[temp5], -1(%[dst]) \n\t"
+ "bne %[frow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow),
+ [dst]"+r"(dst), [loop_end]"=&r"(loop_end)
+ : [temp2]"r"(temp2), [temp6]"r"(temp6)
+ : "memory", "hi", "lo"
+ );
+ } else {
+ const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
+ const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
+ __asm__ volatile (
+ "li %[temp3], 0x10000 \n\t"
+ "li %[temp4], 0x8000 \n\t"
+ "addu %[loop_end], %[frow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[frow]) \n\t"
+ "lw %[temp1], 0(%[irow]) \n\t"
+ "addiu %[dst], %[dst], 1 \n\t"
+ "mult %[temp3], %[temp4] \n\t"
+ "maddu %[A], %[temp0] \n\t"
+ "maddu %[B], %[temp1] \n\t"
+ "addiu %[frow], %[frow], 4 \n\t"
+ "addiu %[irow], %[irow], 4 \n\t"
+ "mfhi %[temp5] \n\t"
+ "mult %[temp3], %[temp4] \n\t"
+ "maddu %[temp5], %[temp2] \n\t"
+ "mfhi %[temp5] \n\t"
+ "sb %[temp5], -1(%[dst]) \n\t"
+ "bne %[frow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow),
+ [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end)
+ : [temp2]"r"(temp2), [temp6]"r"(temp6), [A]"r"(A), [B]"r"(B)
+ : "memory", "hi", "lo"
+ );
+ }
+}
+
+static void ExportRowShrink_MIPS32(WebPRescaler* const wrk) {
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ uint8_t* dst = wrk->dst;
+ rescaler_t* irow = wrk->irow;
+ const rescaler_t* frow = wrk->frow;
+ const int yscale = wrk->fy_scale * (-wrk->y_accum);
+ int temp0, temp1, temp3, temp4, temp5, loop_end;
+ const int temp2 = (int)wrk->fxy_scale;
+ const int temp6 = x_out_max << 2;
+
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(!wrk->y_expand);
+ assert(wrk->fxy_scale != 0);
+ if (yscale) {
+ __asm__ volatile (
+ "li %[temp3], 0x10000 \n\t"
+ "li %[temp4], 0x8000 \n\t"
+ "addu %[loop_end], %[frow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[frow]) \n\t"
+ "mult %[temp3], %[temp4] \n\t"
+ "addiu %[frow], %[frow], 4 \n\t"
+ "maddu %[temp0], %[yscale] \n\t"
+ "mfhi %[temp1] \n\t"
+ "lw %[temp0], 0(%[irow]) \n\t"
+ "addiu %[dst], %[dst], 1 \n\t"
+ "addiu %[irow], %[irow], 4 \n\t"
+ "subu %[temp0], %[temp0], %[temp1] \n\t"
+ "mult %[temp3], %[temp4] \n\t"
+ "maddu %[temp0], %[temp2] \n\t"
+ "mfhi %[temp5] \n\t"
+ "sw %[temp1], -4(%[irow]) \n\t"
+ "sb %[temp5], -1(%[dst]) \n\t"
+ "bne %[frow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow),
+ [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end)
+ : [temp2]"r"(temp2), [yscale]"r"(yscale), [temp6]"r"(temp6)
+ : "memory", "hi", "lo"
+ );
+ } else {
+ __asm__ volatile (
+ "li %[temp3], 0x10000 \n\t"
+ "li %[temp4], 0x8000 \n\t"
+ "addu %[loop_end], %[irow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[irow]) \n\t"
+ "addiu %[dst], %[dst], 1 \n\t"
+ "addiu %[irow], %[irow], 4 \n\t"
+ "mult %[temp3], %[temp4] \n\t"
+ "maddu %[temp0], %[temp2] \n\t"
+ "mfhi %[temp5] \n\t"
+ "sw $zero, -4(%[irow]) \n\t"
+ "sb %[temp5], -1(%[dst]) \n\t"
+ "bne %[irow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [irow]"+r"(irow),
+ [dst]"+r"(dst), [loop_end]"=&r"(loop_end)
+ : [temp2]"r"(temp2), [temp6]"r"(temp6)
+ : "memory", "hi", "lo"
+ );
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPRescalerDspInitMIPS32(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMIPS32(void) {
+ WebPRescalerImportRowExpand = ImportRowExpand_MIPS32;
+ WebPRescalerImportRowShrink = ImportRowShrink_MIPS32;
+ WebPRescalerExportRowExpand = ExportRowExpand_MIPS32;
+ WebPRescalerExportRowShrink = ExportRowShrink_MIPS32;
+}
+
+#else // !WEBP_USE_MIPS32
+
+WEBP_DSP_INIT_STUB(WebPRescalerDspInitMIPS32)
+
+#endif // WEBP_USE_MIPS32
diff --git a/src/third_party/libwebp/src/dsp/rescaler_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/rescaler_mips_dsp_r2.c
new file mode 100644
index 0000000..b78aac1
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/rescaler_mips_dsp_r2.c
@@ -0,0 +1,314 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of rescaling functions
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2) && !defined(WEBP_REDUCE_SIZE)
+
+#include <assert.h>
+#include "src/utils/rescaler_utils.h"
+
+#define ROUNDER (WEBP_RESCALER_ONE >> 1)
+#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
+
+//------------------------------------------------------------------------------
+// Row export
+
+static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
+ int i;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ uint8_t* dst = wrk->dst;
+ rescaler_t* irow = wrk->irow;
+ const rescaler_t* frow = wrk->frow;
+ const int yscale = wrk->fy_scale * (-wrk->y_accum);
+ int temp0, temp1, temp2, temp3, temp4, temp5, loop_end;
+ const int temp7 = (int)wrk->fxy_scale;
+ const int temp6 = (x_out_max & ~0x3) << 2;
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(!wrk->y_expand);
+ assert(wrk->fxy_scale != 0);
+ if (yscale) {
+ if (x_out_max >= 4) {
+ int temp8, temp9, temp10, temp11;
+ __asm__ volatile (
+ "li %[temp3], 0x10000 \n\t"
+ "li %[temp4], 0x8000 \n\t"
+ "addu %[loop_end], %[frow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[frow]) \n\t"
+ "lw %[temp1], 4(%[frow]) \n\t"
+ "lw %[temp2], 8(%[frow]) \n\t"
+ "lw %[temp5], 12(%[frow]) \n\t"
+ "mult $ac0, %[temp3], %[temp4] \n\t"
+ "maddu $ac0, %[temp0], %[yscale] \n\t"
+ "mult $ac1, %[temp3], %[temp4] \n\t"
+ "maddu $ac1, %[temp1], %[yscale] \n\t"
+ "mult $ac2, %[temp3], %[temp4] \n\t"
+ "maddu $ac2, %[temp2], %[yscale] \n\t"
+ "mult $ac3, %[temp3], %[temp4] \n\t"
+ "maddu $ac3, %[temp5], %[yscale] \n\t"
+ "addiu %[frow], %[frow], 16 \n\t"
+ "mfhi %[temp0], $ac0 \n\t"
+ "mfhi %[temp1], $ac1 \n\t"
+ "mfhi %[temp2], $ac2 \n\t"
+ "mfhi %[temp5], $ac3 \n\t"
+ "lw %[temp8], 0(%[irow]) \n\t"
+ "lw %[temp9], 4(%[irow]) \n\t"
+ "lw %[temp10], 8(%[irow]) \n\t"
+ "lw %[temp11], 12(%[irow]) \n\t"
+ "addiu %[dst], %[dst], 4 \n\t"
+ "addiu %[irow], %[irow], 16 \n\t"
+ "subu %[temp8], %[temp8], %[temp0] \n\t"
+ "subu %[temp9], %[temp9], %[temp1] \n\t"
+ "subu %[temp10], %[temp10], %[temp2] \n\t"
+ "subu %[temp11], %[temp11], %[temp5] \n\t"
+ "mult $ac0, %[temp3], %[temp4] \n\t"
+ "maddu $ac0, %[temp8], %[temp7] \n\t"
+ "mult $ac1, %[temp3], %[temp4] \n\t"
+ "maddu $ac1, %[temp9], %[temp7] \n\t"
+ "mult $ac2, %[temp3], %[temp4] \n\t"
+ "maddu $ac2, %[temp10], %[temp7] \n\t"
+ "mult $ac3, %[temp3], %[temp4] \n\t"
+ "maddu $ac3, %[temp11], %[temp7] \n\t"
+ "mfhi %[temp8], $ac0 \n\t"
+ "mfhi %[temp9], $ac1 \n\t"
+ "mfhi %[temp10], $ac2 \n\t"
+ "mfhi %[temp11], $ac3 \n\t"
+ "sw %[temp0], -16(%[irow]) \n\t"
+ "sw %[temp1], -12(%[irow]) \n\t"
+ "sw %[temp2], -8(%[irow]) \n\t"
+ "sw %[temp5], -4(%[irow]) \n\t"
+ "sb %[temp8], -4(%[dst]) \n\t"
+ "sb %[temp9], -3(%[dst]) \n\t"
+ "sb %[temp10], -2(%[dst]) \n\t"
+ "sb %[temp11], -1(%[dst]) \n\t"
+ "bne %[frow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow),
+ [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end),
+ [temp8]"=&r"(temp8), [temp9]"=&r"(temp9), [temp10]"=&r"(temp10),
+ [temp11]"=&r"(temp11), [temp2]"=&r"(temp2)
+ : [temp7]"r"(temp7), [yscale]"r"(yscale), [temp6]"r"(temp6)
+ : "memory", "hi", "lo", "$ac1hi", "$ac1lo",
+ "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo"
+ );
+ }
+ for (i = 0; i < (x_out_max & 0x3); ++i) {
+ const uint32_t frac = (uint32_t)MULT_FIX(*frow++, yscale);
+ const int v = (int)MULT_FIX(*irow - frac, wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ *dst++ = v;
+ *irow++ = frac; // new fractional start
+ }
+ } else {
+ if (x_out_max >= 4) {
+ __asm__ volatile (
+ "li %[temp3], 0x10000 \n\t"
+ "li %[temp4], 0x8000 \n\t"
+ "addu %[loop_end], %[irow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[irow]) \n\t"
+ "lw %[temp1], 4(%[irow]) \n\t"
+ "lw %[temp2], 8(%[irow]) \n\t"
+ "lw %[temp5], 12(%[irow]) \n\t"
+ "addiu %[dst], %[dst], 4 \n\t"
+ "addiu %[irow], %[irow], 16 \n\t"
+ "mult $ac0, %[temp3], %[temp4] \n\t"
+ "maddu $ac0, %[temp0], %[temp7] \n\t"
+ "mult $ac1, %[temp3], %[temp4] \n\t"
+ "maddu $ac1, %[temp1], %[temp7] \n\t"
+ "mult $ac2, %[temp3], %[temp4] \n\t"
+ "maddu $ac2, %[temp2], %[temp7] \n\t"
+ "mult $ac3, %[temp3], %[temp4] \n\t"
+ "maddu $ac3, %[temp5], %[temp7] \n\t"
+ "mfhi %[temp0], $ac0 \n\t"
+ "mfhi %[temp1], $ac1 \n\t"
+ "mfhi %[temp2], $ac2 \n\t"
+ "mfhi %[temp5], $ac3 \n\t"
+ "sw $zero, -16(%[irow]) \n\t"
+ "sw $zero, -12(%[irow]) \n\t"
+ "sw $zero, -8(%[irow]) \n\t"
+ "sw $zero, -4(%[irow]) \n\t"
+ "sb %[temp0], -4(%[dst]) \n\t"
+ "sb %[temp1], -3(%[dst]) \n\t"
+ "sb %[temp2], -2(%[dst]) \n\t"
+ "sb %[temp5], -1(%[dst]) \n\t"
+ "bne %[irow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [irow]"+r"(irow),
+ [dst]"+r"(dst), [loop_end]"=&r"(loop_end), [temp2]"=&r"(temp2)
+ : [temp7]"r"(temp7), [temp6]"r"(temp6)
+ : "memory", "hi", "lo", "$ac1hi", "$ac1lo",
+ "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo"
+ );
+ }
+ for (i = 0; i < (x_out_max & 0x3); ++i) {
+ const int v = (int)MULT_FIX(*irow, wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ *dst++ = v;
+ *irow++ = 0;
+ }
+ }
+}
+
+static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
+ int i;
+ uint8_t* dst = wrk->dst;
+ rescaler_t* irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* frow = wrk->frow;
+ int temp0, temp1, temp2, temp3, temp4, temp5, loop_end;
+ const int temp6 = (x_out_max & ~0x3) << 2;
+ const int temp7 = (int)wrk->fy_scale;
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(wrk->y_expand);
+ assert(wrk->y_sub != 0);
+ if (wrk->y_accum == 0) {
+ if (x_out_max >= 4) {
+ __asm__ volatile (
+ "li %[temp4], 0x10000 \n\t"
+ "li %[temp5], 0x8000 \n\t"
+ "addu %[loop_end], %[frow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[frow]) \n\t"
+ "lw %[temp1], 4(%[frow]) \n\t"
+ "lw %[temp2], 8(%[frow]) \n\t"
+ "lw %[temp3], 12(%[frow]) \n\t"
+ "addiu %[dst], %[dst], 4 \n\t"
+ "addiu %[frow], %[frow], 16 \n\t"
+ "mult $ac0, %[temp4], %[temp5] \n\t"
+ "maddu $ac0, %[temp0], %[temp7] \n\t"
+ "mult $ac1, %[temp4], %[temp5] \n\t"
+ "maddu $ac1, %[temp1], %[temp7] \n\t"
+ "mult $ac2, %[temp4], %[temp5] \n\t"
+ "maddu $ac2, %[temp2], %[temp7] \n\t"
+ "mult $ac3, %[temp4], %[temp5] \n\t"
+ "maddu $ac3, %[temp3], %[temp7] \n\t"
+ "mfhi %[temp0], $ac0 \n\t"
+ "mfhi %[temp1], $ac1 \n\t"
+ "mfhi %[temp2], $ac2 \n\t"
+ "mfhi %[temp3], $ac3 \n\t"
+ "sb %[temp0], -4(%[dst]) \n\t"
+ "sb %[temp1], -3(%[dst]) \n\t"
+ "sb %[temp2], -2(%[dst]) \n\t"
+ "sb %[temp3], -1(%[dst]) \n\t"
+ "bne %[frow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow),
+ [dst]"+r"(dst), [loop_end]"=&r"(loop_end), [temp2]"=&r"(temp2)
+ : [temp7]"r"(temp7), [temp6]"r"(temp6)
+ : "memory", "hi", "lo", "$ac1hi", "$ac1lo",
+ "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo"
+ );
+ }
+ for (i = 0; i < (x_out_max & 0x3); ++i) {
+ const uint32_t J = *frow++;
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ *dst++ = v;
+ }
+ } else {
+ const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
+ const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
+ if (x_out_max >= 4) {
+ int temp8, temp9, temp10, temp11;
+ __asm__ volatile (
+ "li %[temp8], 0x10000 \n\t"
+ "li %[temp9], 0x8000 \n\t"
+ "addu %[loop_end], %[frow], %[temp6] \n\t"
+ "1: \n\t"
+ "lw %[temp0], 0(%[frow]) \n\t"
+ "lw %[temp1], 4(%[frow]) \n\t"
+ "lw %[temp2], 8(%[frow]) \n\t"
+ "lw %[temp3], 12(%[frow]) \n\t"
+ "lw %[temp4], 0(%[irow]) \n\t"
+ "lw %[temp5], 4(%[irow]) \n\t"
+ "lw %[temp10], 8(%[irow]) \n\t"
+ "lw %[temp11], 12(%[irow]) \n\t"
+ "addiu %[dst], %[dst], 4 \n\t"
+ "mult $ac0, %[temp8], %[temp9] \n\t"
+ "maddu $ac0, %[A], %[temp0] \n\t"
+ "maddu $ac0, %[B], %[temp4] \n\t"
+ "mult $ac1, %[temp8], %[temp9] \n\t"
+ "maddu $ac1, %[A], %[temp1] \n\t"
+ "maddu $ac1, %[B], %[temp5] \n\t"
+ "mult $ac2, %[temp8], %[temp9] \n\t"
+ "maddu $ac2, %[A], %[temp2] \n\t"
+ "maddu $ac2, %[B], %[temp10] \n\t"
+ "mult $ac3, %[temp8], %[temp9] \n\t"
+ "maddu $ac3, %[A], %[temp3] \n\t"
+ "maddu $ac3, %[B], %[temp11] \n\t"
+ "addiu %[frow], %[frow], 16 \n\t"
+ "addiu %[irow], %[irow], 16 \n\t"
+ "mfhi %[temp0], $ac0 \n\t"
+ "mfhi %[temp1], $ac1 \n\t"
+ "mfhi %[temp2], $ac2 \n\t"
+ "mfhi %[temp3], $ac3 \n\t"
+ "mult $ac0, %[temp8], %[temp9] \n\t"
+ "maddu $ac0, %[temp0], %[temp7] \n\t"
+ "mult $ac1, %[temp8], %[temp9] \n\t"
+ "maddu $ac1, %[temp1], %[temp7] \n\t"
+ "mult $ac2, %[temp8], %[temp9] \n\t"
+ "maddu $ac2, %[temp2], %[temp7] \n\t"
+ "mult $ac3, %[temp8], %[temp9] \n\t"
+ "maddu $ac3, %[temp3], %[temp7] \n\t"
+ "mfhi %[temp0], $ac0 \n\t"
+ "mfhi %[temp1], $ac1 \n\t"
+ "mfhi %[temp2], $ac2 \n\t"
+ "mfhi %[temp3], $ac3 \n\t"
+ "sb %[temp0], -4(%[dst]) \n\t"
+ "sb %[temp1], -3(%[dst]) \n\t"
+ "sb %[temp2], -2(%[dst]) \n\t"
+ "sb %[temp3], -1(%[dst]) \n\t"
+ "bne %[frow], %[loop_end], 1b \n\t"
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp3]"=&r"(temp3),
+ [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), [frow]"+r"(frow),
+ [irow]"+r"(irow), [dst]"+r"(dst), [loop_end]"=&r"(loop_end),
+ [temp8]"=&r"(temp8), [temp9]"=&r"(temp9), [temp10]"=&r"(temp10),
+ [temp11]"=&r"(temp11), [temp2]"=&r"(temp2)
+ : [temp7]"r"(temp7), [temp6]"r"(temp6), [A]"r"(A), [B]"r"(B)
+ : "memory", "hi", "lo", "$ac1hi", "$ac1lo",
+ "$ac2hi", "$ac2lo", "$ac3hi", "$ac3lo"
+ );
+ }
+ for (i = 0; i < (x_out_max & 0x3); ++i) {
+ const uint64_t I = (uint64_t)A * *frow++
+ + (uint64_t)B * *irow++;
+ const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ *dst++ = v;
+ }
+ }
+}
+
+#undef MULT_FIX
+#undef ROUNDER
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPRescalerDspInitMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMIPSdspR2(void) {
+ WebPRescalerExportRowExpand = ExportRowExpand_MIPSdspR2;
+ WebPRescalerExportRowShrink = ExportRowShrink_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(WebPRescalerDspInitMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/rescaler_msa.c b/src/third_party/libwebp/src/dsp/rescaler_msa.c
new file mode 100644
index 0000000..f3bc99f
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/rescaler_msa.c
@@ -0,0 +1,444 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA version of rescaling functions
+//
+// Author: Prashant Patil (prashant.patil@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA) && !defined(WEBP_REDUCE_SIZE)
+
+#include <assert.h>
+
+#include "src/utils/rescaler_utils.h"
+#include "src/dsp/msa_macro.h"
+
+#define ROUNDER (WEBP_RESCALER_ONE >> 1)
+#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
+
+#define CALC_MULT_FIX_16(in0, in1, in2, in3, scale, shift, dst) do { \
+ v4u32 tmp0, tmp1, tmp2, tmp3; \
+ v16u8 t0, t1, t2, t3, t4, t5; \
+ v2u64 out0, out1, out2, out3; \
+ ILVRL_W2_UW(zero, in0, tmp0, tmp1); \
+ ILVRL_W2_UW(zero, in1, tmp2, tmp3); \
+ DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \
+ DOTP_UW2_UD(tmp2, tmp3, scale, scale, out2, out3); \
+ SRAR_D4_UD(out0, out1, out2, out3, shift); \
+ PCKEV_B2_UB(out1, out0, out3, out2, t0, t1); \
+ ILVRL_W2_UW(zero, in2, tmp0, tmp1); \
+ ILVRL_W2_UW(zero, in3, tmp2, tmp3); \
+ DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \
+ DOTP_UW2_UD(tmp2, tmp3, scale, scale, out2, out3); \
+ SRAR_D4_UD(out0, out1, out2, out3, shift); \
+ PCKEV_B2_UB(out1, out0, out3, out2, t2, t3); \
+ PCKEV_B2_UB(t1, t0, t3, t2, t4, t5); \
+ dst = (v16u8)__msa_pckev_b((v16i8)t5, (v16i8)t4); \
+} while (0)
+
+#define CALC_MULT_FIX_4(in0, scale, shift, dst) do { \
+ v4u32 tmp0, tmp1; \
+ v16i8 t0, t1; \
+ v2u64 out0, out1; \
+ ILVRL_W2_UW(zero, in0, tmp0, tmp1); \
+ DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \
+ SRAR_D2_UD(out0, out1, shift); \
+ t0 = __msa_pckev_b((v16i8)out1, (v16i8)out0); \
+ t1 = __msa_pckev_b(t0, t0); \
+ t0 = __msa_pckev_b(t1, t1); \
+ dst = __msa_copy_s_w((v4i32)t0, 0); \
+} while (0)
+
+#define CALC_MULT_FIX1_16(in0, in1, in2, in3, fyscale, shift, \
+ dst0, dst1, dst2, dst3) do { \
+ v4u32 tmp0, tmp1, tmp2, tmp3; \
+ v2u64 out0, out1, out2, out3; \
+ ILVRL_W2_UW(zero, in0, tmp0, tmp1); \
+ ILVRL_W2_UW(zero, in1, tmp2, tmp3); \
+ DOTP_UW2_UD(tmp0, tmp1, fyscale, fyscale, out0, out1); \
+ DOTP_UW2_UD(tmp2, tmp3, fyscale, fyscale, out2, out3); \
+ SRAR_D4_UD(out0, out1, out2, out3, shift); \
+ PCKEV_W2_UW(out1, out0, out3, out2, dst0, dst1); \
+ ILVRL_W2_UW(zero, in2, tmp0, tmp1); \
+ ILVRL_W2_UW(zero, in3, tmp2, tmp3); \
+ DOTP_UW2_UD(tmp0, tmp1, fyscale, fyscale, out0, out1); \
+ DOTP_UW2_UD(tmp2, tmp3, fyscale, fyscale, out2, out3); \
+ SRAR_D4_UD(out0, out1, out2, out3, shift); \
+ PCKEV_W2_UW(out1, out0, out3, out2, dst2, dst3); \
+} while (0)
+
+#define CALC_MULT_FIX1_4(in0, scale, shift, dst) do { \
+ v4u32 tmp0, tmp1; \
+ v2u64 out0, out1; \
+ ILVRL_W2_UW(zero, in0, tmp0, tmp1); \
+ DOTP_UW2_UD(tmp0, tmp1, scale, scale, out0, out1); \
+ SRAR_D2_UD(out0, out1, shift); \
+ dst = (v4u32)__msa_pckev_w((v4i32)out1, (v4i32)out0); \
+} while (0)
+
+#define CALC_MULT_FIX2_16(in0, in1, in2, in3, mult, scale, shift, \
+ dst0, dst1) do { \
+ v4u32 tmp0, tmp1, tmp2, tmp3; \
+ v2u64 out0, out1, out2, out3; \
+ ILVRL_W2_UW(in0, in2, tmp0, tmp1); \
+ ILVRL_W2_UW(in1, in3, tmp2, tmp3); \
+ DOTP_UW2_UD(tmp0, tmp1, mult, mult, out0, out1); \
+ DOTP_UW2_UD(tmp2, tmp3, mult, mult, out2, out3); \
+ SRAR_D4_UD(out0, out1, out2, out3, shift); \
+ DOTP_UW2_UD(out0, out1, scale, scale, out0, out1); \
+ DOTP_UW2_UD(out2, out3, scale, scale, out2, out3); \
+ SRAR_D4_UD(out0, out1, out2, out3, shift); \
+ PCKEV_B2_UB(out1, out0, out3, out2, dst0, dst1); \
+} while (0)
+
+#define CALC_MULT_FIX2_4(in0, in1, mult, scale, shift, dst) do { \
+ v4u32 tmp0, tmp1; \
+ v2u64 out0, out1; \
+ v16i8 t0, t1; \
+ ILVRL_W2_UW(in0, in1, tmp0, tmp1); \
+ DOTP_UW2_UD(tmp0, tmp1, mult, mult, out0, out1); \
+ SRAR_D2_UD(out0, out1, shift); \
+ DOTP_UW2_UD(out0, out1, scale, scale, out0, out1); \
+ SRAR_D2_UD(out0, out1, shift); \
+ t0 = __msa_pckev_b((v16i8)out1, (v16i8)out0); \
+ t1 = __msa_pckev_b(t0, t0); \
+ t0 = __msa_pckev_b(t1, t1); \
+ dst = __msa_copy_s_w((v4i32)t0, 0); \
+} while (0)
+
+static WEBP_INLINE void ExportRowExpand_0(const uint32_t* frow, uint8_t* dst,
+ int length,
+ WebPRescaler* const wrk) {
+ const v4u32 scale = (v4u32)__msa_fill_w(wrk->fy_scale);
+ const v4u32 shift = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX);
+ const v4i32 zero = { 0 };
+
+ while (length >= 16) {
+ v4u32 src0, src1, src2, src3;
+ v16u8 out;
+ LD_UW4(frow, 4, src0, src1, src2, src3);
+ CALC_MULT_FIX_16(src0, src1, src2, src3, scale, shift, out);
+ ST_UB(out, dst);
+ length -= 16;
+ frow += 16;
+ dst += 16;
+ }
+ if (length > 0) {
+ int x_out;
+ if (length >= 12) {
+ uint32_t val0_m, val1_m, val2_m;
+ v4u32 src0, src1, src2;
+ LD_UW3(frow, 4, src0, src1, src2);
+ CALC_MULT_FIX_4(src0, scale, shift, val0_m);
+ CALC_MULT_FIX_4(src1, scale, shift, val1_m);
+ CALC_MULT_FIX_4(src2, scale, shift, val2_m);
+ SW3(val0_m, val1_m, val2_m, dst, 4);
+ length -= 12;
+ frow += 12;
+ dst += 12;
+ } else if (length >= 8) {
+ uint32_t val0_m, val1_m;
+ v4u32 src0, src1;
+ LD_UW2(frow, 4, src0, src1);
+ CALC_MULT_FIX_4(src0, scale, shift, val0_m);
+ CALC_MULT_FIX_4(src1, scale, shift, val1_m);
+ SW2(val0_m, val1_m, dst, 4);
+ length -= 8;
+ frow += 8;
+ dst += 8;
+ } else if (length >= 4) {
+ uint32_t val0_m;
+ const v4u32 src0 = LD_UW(frow);
+ CALC_MULT_FIX_4(src0, scale, shift, val0_m);
+ SW(val0_m, dst);
+ length -= 4;
+ frow += 4;
+ dst += 4;
+ }
+ for (x_out = 0; x_out < length; ++x_out) {
+ const uint32_t J = frow[x_out];
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ }
+}
+
+static WEBP_INLINE void ExportRowExpand_1(const uint32_t* frow, uint32_t* irow,
+ uint8_t* dst, int length,
+ WebPRescaler* const wrk) {
+ const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
+ const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
+ const v4i32 B1 = __msa_fill_w(B);
+ const v4i32 A1 = __msa_fill_w(A);
+ const v4i32 AB = __msa_ilvr_w(A1, B1);
+ const v4u32 scale = (v4u32)__msa_fill_w(wrk->fy_scale);
+ const v4u32 shift = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX);
+
+ while (length >= 16) {
+ v4u32 frow0, frow1, frow2, frow3, irow0, irow1, irow2, irow3;
+ v16u8 t0, t1, t2, t3, t4, t5;
+ LD_UW4(frow, 4, frow0, frow1, frow2, frow3);
+ LD_UW4(irow, 4, irow0, irow1, irow2, irow3);
+ CALC_MULT_FIX2_16(frow0, frow1, irow0, irow1, AB, scale, shift, t0, t1);
+ CALC_MULT_FIX2_16(frow2, frow3, irow2, irow3, AB, scale, shift, t2, t3);
+ PCKEV_B2_UB(t1, t0, t3, t2, t4, t5);
+ t0 = (v16u8)__msa_pckev_b((v16i8)t5, (v16i8)t4);
+ ST_UB(t0, dst);
+ frow += 16;
+ irow += 16;
+ dst += 16;
+ length -= 16;
+ }
+ if (length > 0) {
+ int x_out;
+ if (length >= 12) {
+ uint32_t val0_m, val1_m, val2_m;
+ v4u32 frow0, frow1, frow2, irow0, irow1, irow2;
+ LD_UW3(frow, 4, frow0, frow1, frow2);
+ LD_UW3(irow, 4, irow0, irow1, irow2);
+ CALC_MULT_FIX2_4(frow0, irow0, AB, scale, shift, val0_m);
+ CALC_MULT_FIX2_4(frow1, irow1, AB, scale, shift, val1_m);
+ CALC_MULT_FIX2_4(frow2, irow2, AB, scale, shift, val2_m);
+ SW3(val0_m, val1_m, val2_m, dst, 4);
+ frow += 12;
+ irow += 12;
+ dst += 12;
+ length -= 12;
+ } else if (length >= 8) {
+ uint32_t val0_m, val1_m;
+ v4u32 frow0, frow1, irow0, irow1;
+ LD_UW2(frow, 4, frow0, frow1);
+ LD_UW2(irow, 4, irow0, irow1);
+ CALC_MULT_FIX2_4(frow0, irow0, AB, scale, shift, val0_m);
+ CALC_MULT_FIX2_4(frow1, irow1, AB, scale, shift, val1_m);
+ SW2(val0_m, val1_m, dst, 4);
+ frow += 4;
+ irow += 4;
+ dst += 4;
+ length -= 4;
+ } else if (length >= 4) {
+ uint32_t val0_m;
+ const v4u32 frow0 = LD_UW(frow + 0);
+ const v4u32 irow0 = LD_UW(irow + 0);
+ CALC_MULT_FIX2_4(frow0, irow0, AB, scale, shift, val0_m);
+ SW(val0_m, dst);
+ frow += 4;
+ irow += 4;
+ dst += 4;
+ length -= 4;
+ }
+ for (x_out = 0; x_out < length; ++x_out) {
+ const uint64_t I = (uint64_t)A * frow[x_out]
+ + (uint64_t)B * irow[x_out];
+ const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ }
+}
+
+static void RescalerExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
+ uint8_t* dst = wrk->dst;
+ rescaler_t* irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* frow = wrk->frow;
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(wrk->y_expand);
+ assert(wrk->y_sub != 0);
+ if (wrk->y_accum == 0) {
+ ExportRowExpand_0(frow, dst, x_out_max, wrk);
+ } else {
+ ExportRowExpand_1(frow, irow, dst, x_out_max, wrk);
+ }
+}
+
+static WEBP_INLINE void ExportRowShrink_0(const uint32_t* frow, uint32_t* irow,
+ uint8_t* dst, int length,
+ const uint32_t yscale,
+ WebPRescaler* const wrk) {
+ const v4u32 y_scale = (v4u32)__msa_fill_w(yscale);
+ const v4u32 fxyscale = (v4u32)__msa_fill_w(wrk->fxy_scale);
+ const v4u32 shiftval = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX);
+ const v4i32 zero = { 0 };
+
+ while (length >= 16) {
+ v4u32 src0, src1, src2, src3, frac0, frac1, frac2, frac3;
+ v16u8 out;
+ LD_UW4(frow, 4, src0, src1, src2, src3);
+ CALC_MULT_FIX1_16(src0, src1, src2, src3, y_scale, shiftval,
+ frac0, frac1, frac2, frac3);
+ LD_UW4(irow, 4, src0, src1, src2, src3);
+ SUB4(src0, frac0, src1, frac1, src2, frac2, src3, frac3,
+ src0, src1, src2, src3);
+ CALC_MULT_FIX_16(src0, src1, src2, src3, fxyscale, shiftval, out);
+ ST_UB(out, dst);
+ ST_UW4(frac0, frac1, frac2, frac3, irow, 4);
+ frow += 16;
+ irow += 16;
+ dst += 16;
+ length -= 16;
+ }
+ if (length > 0) {
+ int x_out;
+ if (length >= 12) {
+ uint32_t val0_m, val1_m, val2_m;
+ v4u32 src0, src1, src2, frac0, frac1, frac2;
+ LD_UW3(frow, 4, src0, src1, src2);
+ CALC_MULT_FIX1_4(src0, y_scale, shiftval, frac0);
+ CALC_MULT_FIX1_4(src1, y_scale, shiftval, frac1);
+ CALC_MULT_FIX1_4(src2, y_scale, shiftval, frac2);
+ LD_UW3(irow, 4, src0, src1, src2);
+ SUB3(src0, frac0, src1, frac1, src2, frac2, src0, src1, src2);
+ CALC_MULT_FIX_4(src0, fxyscale, shiftval, val0_m);
+ CALC_MULT_FIX_4(src1, fxyscale, shiftval, val1_m);
+ CALC_MULT_FIX_4(src2, fxyscale, shiftval, val2_m);
+ SW3(val0_m, val1_m, val2_m, dst, 4);
+ ST_UW3(frac0, frac1, frac2, irow, 4);
+ frow += 12;
+ irow += 12;
+ dst += 12;
+ length -= 12;
+ } else if (length >= 8) {
+ uint32_t val0_m, val1_m;
+ v4u32 src0, src1, frac0, frac1;
+ LD_UW2(frow, 4, src0, src1);
+ CALC_MULT_FIX1_4(src0, y_scale, shiftval, frac0);
+ CALC_MULT_FIX1_4(src1, y_scale, shiftval, frac1);
+ LD_UW2(irow, 4, src0, src1);
+ SUB2(src0, frac0, src1, frac1, src0, src1);
+ CALC_MULT_FIX_4(src0, fxyscale, shiftval, val0_m);
+ CALC_MULT_FIX_4(src1, fxyscale, shiftval, val1_m);
+ SW2(val0_m, val1_m, dst, 4);
+ ST_UW2(frac0, frac1, irow, 4);
+ frow += 8;
+ irow += 8;
+ dst += 8;
+ length -= 8;
+ } else if (length >= 4) {
+ uint32_t val0_m;
+ v4u32 frac0;
+ v4u32 src0 = LD_UW(frow);
+ CALC_MULT_FIX1_4(src0, y_scale, shiftval, frac0);
+ src0 = LD_UW(irow);
+ src0 = src0 - frac0;
+ CALC_MULT_FIX_4(src0, fxyscale, shiftval, val0_m);
+ SW(val0_m, dst);
+ ST_UW(frac0, irow);
+ frow += 4;
+ irow += 4;
+ dst += 4;
+ length -= 4;
+ }
+ for (x_out = 0; x_out < length; ++x_out) {
+ const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale);
+ const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = frac;
+ }
+ }
+}
+
+static WEBP_INLINE void ExportRowShrink_1(uint32_t* irow, uint8_t* dst,
+ int length,
+ WebPRescaler* const wrk) {
+ const v4u32 scale = (v4u32)__msa_fill_w(wrk->fxy_scale);
+ const v4u32 shift = (v4u32)__msa_fill_w(WEBP_RESCALER_RFIX);
+ const v4i32 zero = { 0 };
+
+ while (length >= 16) {
+ v4u32 src0, src1, src2, src3;
+ v16u8 dst0;
+ LD_UW4(irow, 4, src0, src1, src2, src3);
+ CALC_MULT_FIX_16(src0, src1, src2, src3, scale, shift, dst0);
+ ST_UB(dst0, dst);
+ ST_SW4(zero, zero, zero, zero, irow, 4);
+ length -= 16;
+ irow += 16;
+ dst += 16;
+ }
+ if (length > 0) {
+ int x_out;
+ if (length >= 12) {
+ uint32_t val0_m, val1_m, val2_m;
+ v4u32 src0, src1, src2;
+ LD_UW3(irow, 4, src0, src1, src2);
+ CALC_MULT_FIX_4(src0, scale, shift, val0_m);
+ CALC_MULT_FIX_4(src1, scale, shift, val1_m);
+ CALC_MULT_FIX_4(src2, scale, shift, val2_m);
+ SW3(val0_m, val1_m, val2_m, dst, 4);
+ ST_SW3(zero, zero, zero, irow, 4);
+ length -= 12;
+ irow += 12;
+ dst += 12;
+ } else if (length >= 8) {
+ uint32_t val0_m, val1_m;
+ v4u32 src0, src1;
+ LD_UW2(irow, 4, src0, src1);
+ CALC_MULT_FIX_4(src0, scale, shift, val0_m);
+ CALC_MULT_FIX_4(src1, scale, shift, val1_m);
+ SW2(val0_m, val1_m, dst, 4);
+ ST_SW2(zero, zero, irow, 4);
+ length -= 8;
+ irow += 8;
+ dst += 8;
+ } else if (length >= 4) {
+ uint32_t val0_m;
+ const v4u32 src0 = LD_UW(irow + 0);
+ CALC_MULT_FIX_4(src0, scale, shift, val0_m);
+ SW(val0_m, dst);
+ ST_SW(zero, irow);
+ length -= 4;
+ irow += 4;
+ dst += 4;
+ }
+ for (x_out = 0; x_out < length; ++x_out) {
+ const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = 0;
+ }
+ }
+}
+
+static void RescalerExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
+ uint8_t* dst = wrk->dst;
+ rescaler_t* irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* frow = wrk->frow;
+ const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(!wrk->y_expand);
+ if (yscale) {
+ ExportRowShrink_0(frow, irow, dst, x_out_max, yscale, wrk);
+ } else {
+ ExportRowShrink_1(irow, dst, x_out_max, wrk);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPRescalerDspInitMSA(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitMSA(void) {
+ WebPRescalerExportRowExpand = RescalerExportRowExpand_MIPSdspR2;
+ WebPRescalerExportRowShrink = RescalerExportRowShrink_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MSA
+
+WEBP_DSP_INIT_STUB(WebPRescalerDspInitMSA)
+
+#endif // WEBP_USE_MSA
diff --git a/src/third_party/libwebp/src/dsp/rescaler_neon.c b/src/third_party/libwebp/src/dsp/rescaler_neon.c
new file mode 100644
index 0000000..3eff9fb
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/rescaler_neon.c
@@ -0,0 +1,186 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// NEON version of rescaling functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON) && !defined(WEBP_REDUCE_SIZE)
+
+#include <arm_neon.h>
+#include <assert.h>
+#include "src/dsp/neon.h"
+#include "src/utils/rescaler_utils.h"
+
+#define ROUNDER (WEBP_RESCALER_ONE >> 1)
+#define MULT_FIX_C(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
+
+#define LOAD_32x4(SRC, DST) const uint32x4_t DST = vld1q_u32((SRC))
+#define LOAD_32x8(SRC, DST0, DST1) \
+ LOAD_32x4(SRC + 0, DST0); \
+ LOAD_32x4(SRC + 4, DST1)
+
+#define STORE_32x8(SRC0, SRC1, DST) do { \
+ vst1q_u32((DST) + 0, SRC0); \
+ vst1q_u32((DST) + 4, SRC1); \
+} while (0);
+
+#if (WEBP_RESCALER_RFIX == 32)
+#define MAKE_HALF_CST(C) vdupq_n_s32((int32_t)((C) >> 1))
+#define MULT_FIX(A, B) /* note: B is actualy scale>>1. See MAKE_HALF_CST */ \
+ vreinterpretq_u32_s32(vqrdmulhq_s32(vreinterpretq_s32_u32((A)), (B)))
+#else
+#error "MULT_FIX/WEBP_RESCALER_RFIX need some more work"
+#endif
+
+static uint32x4_t Interpolate_NEON(const rescaler_t* const frow,
+ const rescaler_t* const irow,
+ uint32_t A, uint32_t B) {
+ LOAD_32x4(frow, A0);
+ LOAD_32x4(irow, B0);
+ const uint64x2_t C0 = vmull_n_u32(vget_low_u32(A0), A);
+ const uint64x2_t C1 = vmull_n_u32(vget_high_u32(A0), A);
+ const uint64x2_t D0 = vmlal_n_u32(C0, vget_low_u32(B0), B);
+ const uint64x2_t D1 = vmlal_n_u32(C1, vget_high_u32(B0), B);
+ const uint32x4_t E = vcombine_u32(
+ vrshrn_n_u64(D0, WEBP_RESCALER_RFIX),
+ vrshrn_n_u64(D1, WEBP_RESCALER_RFIX));
+ return E;
+}
+
+static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) {
+ int x_out;
+ uint8_t* const dst = wrk->dst;
+ rescaler_t* const irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const int max_span = x_out_max & ~7;
+ const rescaler_t* const frow = wrk->frow;
+ const uint32_t fy_scale = wrk->fy_scale;
+ const int32x4_t fy_scale_half = MAKE_HALF_CST(fy_scale);
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(wrk->y_expand);
+ assert(wrk->y_sub != 0);
+ if (wrk->y_accum == 0) {
+ for (x_out = 0; x_out < max_span; x_out += 8) {
+ LOAD_32x4(frow + x_out + 0, A0);
+ LOAD_32x4(frow + x_out + 4, A1);
+ const uint32x4_t B0 = MULT_FIX(A0, fy_scale_half);
+ const uint32x4_t B1 = MULT_FIX(A1, fy_scale_half);
+ const uint16x4_t C0 = vmovn_u32(B0);
+ const uint16x4_t C1 = vmovn_u32(B1);
+ const uint8x8_t D = vmovn_u16(vcombine_u16(C0, C1));
+ vst1_u8(dst + x_out, D);
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const uint32_t J = frow[x_out];
+ const int v = (int)MULT_FIX_C(J, fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ } else {
+ const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
+ const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
+ for (x_out = 0; x_out < max_span; x_out += 8) {
+ const uint32x4_t C0 =
+ Interpolate_NEON(frow + x_out + 0, irow + x_out + 0, A, B);
+ const uint32x4_t C1 =
+ Interpolate_NEON(frow + x_out + 4, irow + x_out + 4, A, B);
+ const uint32x4_t D0 = MULT_FIX(C0, fy_scale_half);
+ const uint32x4_t D1 = MULT_FIX(C1, fy_scale_half);
+ const uint16x4_t E0 = vmovn_u32(D0);
+ const uint16x4_t E1 = vmovn_u32(D1);
+ const uint8x8_t F = vmovn_u16(vcombine_u16(E0, E1));
+ vst1_u8(dst + x_out, F);
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const uint64_t I = (uint64_t)A * frow[x_out]
+ + (uint64_t)B * irow[x_out];
+ const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
+ const int v = (int)MULT_FIX_C(J, fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ }
+}
+
+static void RescalerExportRowShrink_NEON(WebPRescaler* const wrk) {
+ int x_out;
+ uint8_t* const dst = wrk->dst;
+ rescaler_t* const irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const int max_span = x_out_max & ~7;
+ const rescaler_t* const frow = wrk->frow;
+ const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
+ const uint32_t fxy_scale = wrk->fxy_scale;
+ const uint32x4_t zero = vdupq_n_u32(0);
+ const int32x4_t yscale_half = MAKE_HALF_CST(yscale);
+ const int32x4_t fxy_scale_half = MAKE_HALF_CST(fxy_scale);
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(!wrk->y_expand);
+ if (yscale) {
+ for (x_out = 0; x_out < max_span; x_out += 8) {
+ LOAD_32x8(frow + x_out, in0, in1);
+ LOAD_32x8(irow + x_out, in2, in3);
+ const uint32x4_t A0 = MULT_FIX(in0, yscale_half);
+ const uint32x4_t A1 = MULT_FIX(in1, yscale_half);
+ const uint32x4_t B0 = vqsubq_u32(in2, A0);
+ const uint32x4_t B1 = vqsubq_u32(in3, A1);
+ const uint32x4_t C0 = MULT_FIX(B0, fxy_scale_half);
+ const uint32x4_t C1 = MULT_FIX(B1, fxy_scale_half);
+ const uint16x4_t D0 = vmovn_u32(C0);
+ const uint16x4_t D1 = vmovn_u32(C1);
+ const uint8x8_t E = vmovn_u16(vcombine_u16(D0, D1));
+ vst1_u8(dst + x_out, E);
+ STORE_32x8(A0, A1, irow + x_out);
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const uint32_t frac = (uint32_t)MULT_FIX_C(frow[x_out], yscale);
+ const int v = (int)MULT_FIX_C(irow[x_out] - frac, wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = frac; // new fractional start
+ }
+ } else {
+ for (x_out = 0; x_out < max_span; x_out += 8) {
+ LOAD_32x8(irow + x_out, in0, in1);
+ const uint32x4_t A0 = MULT_FIX(in0, fxy_scale_half);
+ const uint32x4_t A1 = MULT_FIX(in1, fxy_scale_half);
+ const uint16x4_t B0 = vmovn_u32(A0);
+ const uint16x4_t B1 = vmovn_u32(A1);
+ const uint8x8_t C = vmovn_u16(vcombine_u16(B0, B1));
+ vst1_u8(dst + x_out, C);
+ STORE_32x8(zero, zero, irow + x_out);
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const int v = (int)MULT_FIX_C(irow[x_out], fxy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = 0;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+extern void WebPRescalerDspInitNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitNEON(void) {
+ WebPRescalerExportRowExpand = RescalerExportRowExpand_NEON;
+ WebPRescalerExportRowShrink = RescalerExportRowShrink_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(WebPRescalerDspInitNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/rescaler_sse2.c b/src/third_party/libwebp/src/dsp/rescaler_sse2.c
new file mode 100644
index 0000000..64c50de
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/rescaler_sse2.c
@@ -0,0 +1,373 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 Rescaling functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2) && !defined(WEBP_REDUCE_SIZE)
+#include <emmintrin.h>
+
+#include <assert.h>
+#include "src/utils/rescaler_utils.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// Implementations of critical functions ImportRow / ExportRow
+
+#define ROUNDER (WEBP_RESCALER_ONE >> 1)
+#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
+
+// input: 8 bytes ABCDEFGH -> output: A0E0B0F0C0G0D0H0
+static void LoadTwoPixels_SSE2(const uint8_t* const src, __m128i* out) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i A = _mm_loadl_epi64((const __m128i*)(src)); // ABCDEFGH
+ const __m128i B = _mm_unpacklo_epi8(A, zero); // A0B0C0D0E0F0G0H0
+ const __m128i C = _mm_srli_si128(B, 8); // E0F0G0H0
+ *out = _mm_unpacklo_epi16(B, C);
+}
+
+// input: 8 bytes ABCDEFGH -> output: A0B0C0D0E0F0G0H0
+static void LoadEightPixels_SSE2(const uint8_t* const src, __m128i* out) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i A = _mm_loadl_epi64((const __m128i*)(src)); // ABCDEFGH
+ *out = _mm_unpacklo_epi8(A, zero);
+}
+
+static void RescalerImportRowExpand_SSE2(WebPRescaler* const wrk,
+ const uint8_t* src) {
+ rescaler_t* frow = wrk->frow;
+ const rescaler_t* const frow_end = frow + wrk->dst_width * wrk->num_channels;
+ const int x_add = wrk->x_add;
+ int accum = x_add;
+ __m128i cur_pixels;
+
+ // SSE2 implementation only works with 16b signed arithmetic at max.
+ if (wrk->src_width < 8 || accum >= (1 << 15)) {
+ WebPRescalerImportRowExpand_C(wrk, src);
+ return;
+ }
+
+ assert(!WebPRescalerInputDone(wrk));
+ assert(wrk->x_expand);
+ if (wrk->num_channels == 4) {
+ LoadTwoPixels_SSE2(src, &cur_pixels);
+ src += 4;
+ while (1) {
+ const __m128i mult = _mm_set1_epi32(((x_add - accum) << 16) | accum);
+ const __m128i out = _mm_madd_epi16(cur_pixels, mult);
+ _mm_storeu_si128((__m128i*)frow, out);
+ frow += 4;
+ if (frow >= frow_end) break;
+ accum -= wrk->x_sub;
+ if (accum < 0) {
+ LoadTwoPixels_SSE2(src, &cur_pixels);
+ src += 4;
+ accum += x_add;
+ }
+ }
+ } else {
+ int left;
+ const uint8_t* const src_limit = src + wrk->src_width - 8;
+ LoadEightPixels_SSE2(src, &cur_pixels);
+ src += 7;
+ left = 7;
+ while (1) {
+ const __m128i mult = _mm_cvtsi32_si128(((x_add - accum) << 16) | accum);
+ const __m128i out = _mm_madd_epi16(cur_pixels, mult);
+ assert(sizeof(*frow) == sizeof(uint32_t));
+ WebPUint32ToMem((uint8_t*)frow, _mm_cvtsi128_si32(out));
+ frow += 1;
+ if (frow >= frow_end) break;
+ accum -= wrk->x_sub;
+ if (accum < 0) {
+ if (--left) {
+ cur_pixels = _mm_srli_si128(cur_pixels, 2);
+ } else if (src <= src_limit) {
+ LoadEightPixels_SSE2(src, &cur_pixels);
+ src += 7;
+ left = 7;
+ } else { // tail
+ cur_pixels = _mm_srli_si128(cur_pixels, 2);
+ cur_pixels = _mm_insert_epi16(cur_pixels, src[1], 1);
+ src += 1;
+ left = 1;
+ }
+ accum += x_add;
+ }
+ }
+ }
+ assert(accum == 0);
+}
+
+static void RescalerImportRowShrink_SSE2(WebPRescaler* const wrk,
+ const uint8_t* src) {
+ const int x_sub = wrk->x_sub;
+ int accum = 0;
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i mult0 = _mm_set1_epi16(x_sub);
+ const __m128i mult1 = _mm_set1_epi32(wrk->fx_scale);
+ const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
+ __m128i sum = zero;
+ rescaler_t* frow = wrk->frow;
+ const rescaler_t* const frow_end = wrk->frow + 4 * wrk->dst_width;
+
+ if (wrk->num_channels != 4 || wrk->x_add > (x_sub << 7)) {
+ WebPRescalerImportRowShrink_C(wrk, src);
+ return;
+ }
+ assert(!WebPRescalerInputDone(wrk));
+ assert(!wrk->x_expand);
+
+ for (; frow < frow_end; frow += 4) {
+ __m128i base = zero;
+ accum += wrk->x_add;
+ while (accum > 0) {
+ const __m128i A = _mm_cvtsi32_si128(WebPMemToUint32(src));
+ src += 4;
+ base = _mm_unpacklo_epi8(A, zero);
+ // To avoid overflow, we need: base * x_add / x_sub < 32768
+ // => x_add < x_sub << 7. That's a 1/128 reduction ratio limit.
+ sum = _mm_add_epi16(sum, base);
+ accum -= x_sub;
+ }
+ { // Emit next horizontal pixel.
+ const __m128i mult = _mm_set1_epi16(-accum);
+ const __m128i frac0 = _mm_mullo_epi16(base, mult); // 16b x 16b -> 32b
+ const __m128i frac1 = _mm_mulhi_epu16(base, mult);
+ const __m128i frac = _mm_unpacklo_epi16(frac0, frac1); // frac is 32b
+ const __m128i A0 = _mm_mullo_epi16(sum, mult0);
+ const __m128i A1 = _mm_mulhi_epu16(sum, mult0);
+ const __m128i B0 = _mm_unpacklo_epi16(A0, A1); // sum * x_sub
+ const __m128i frow_out = _mm_sub_epi32(B0, frac); // sum * x_sub - frac
+ const __m128i D0 = _mm_srli_epi64(frac, 32);
+ const __m128i D1 = _mm_mul_epu32(frac, mult1); // 32b x 16b -> 64b
+ const __m128i D2 = _mm_mul_epu32(D0, mult1);
+ const __m128i E1 = _mm_add_epi64(D1, rounder);
+ const __m128i E2 = _mm_add_epi64(D2, rounder);
+ const __m128i F1 = _mm_shuffle_epi32(E1, 1 | (3 << 2));
+ const __m128i F2 = _mm_shuffle_epi32(E2, 1 | (3 << 2));
+ const __m128i G = _mm_unpacklo_epi32(F1, F2);
+ sum = _mm_packs_epi32(G, zero);
+ _mm_storeu_si128((__m128i*)frow, frow_out);
+ }
+ }
+ assert(accum == 0);
+}
+
+//------------------------------------------------------------------------------
+// Row export
+
+// load *src as epi64, multiply by mult and store result in [out0 ... out3]
+static WEBP_INLINE void LoadDispatchAndMult_SSE2(const rescaler_t* const src,
+ const __m128i* const mult,
+ __m128i* const out0,
+ __m128i* const out1,
+ __m128i* const out2,
+ __m128i* const out3) {
+ const __m128i A0 = _mm_loadu_si128((const __m128i*)(src + 0));
+ const __m128i A1 = _mm_loadu_si128((const __m128i*)(src + 4));
+ const __m128i A2 = _mm_srli_epi64(A0, 32);
+ const __m128i A3 = _mm_srli_epi64(A1, 32);
+ if (mult != NULL) {
+ *out0 = _mm_mul_epu32(A0, *mult);
+ *out1 = _mm_mul_epu32(A1, *mult);
+ *out2 = _mm_mul_epu32(A2, *mult);
+ *out3 = _mm_mul_epu32(A3, *mult);
+ } else {
+ *out0 = A0;
+ *out1 = A1;
+ *out2 = A2;
+ *out3 = A3;
+ }
+}
+
+static WEBP_INLINE void ProcessRow_SSE2(const __m128i* const A0,
+ const __m128i* const A1,
+ const __m128i* const A2,
+ const __m128i* const A3,
+ const __m128i* const mult,
+ uint8_t* const dst) {
+ const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
+ const __m128i mask = _mm_set_epi32(0xffffffffu, 0, 0xffffffffu, 0);
+ const __m128i B0 = _mm_mul_epu32(*A0, *mult);
+ const __m128i B1 = _mm_mul_epu32(*A1, *mult);
+ const __m128i B2 = _mm_mul_epu32(*A2, *mult);
+ const __m128i B3 = _mm_mul_epu32(*A3, *mult);
+ const __m128i C0 = _mm_add_epi64(B0, rounder);
+ const __m128i C1 = _mm_add_epi64(B1, rounder);
+ const __m128i C2 = _mm_add_epi64(B2, rounder);
+ const __m128i C3 = _mm_add_epi64(B3, rounder);
+ const __m128i D0 = _mm_srli_epi64(C0, WEBP_RESCALER_RFIX);
+ const __m128i D1 = _mm_srli_epi64(C1, WEBP_RESCALER_RFIX);
+#if (WEBP_RESCALER_RFIX < 32)
+ const __m128i D2 =
+ _mm_and_si128(_mm_slli_epi64(C2, 32 - WEBP_RESCALER_RFIX), mask);
+ const __m128i D3 =
+ _mm_and_si128(_mm_slli_epi64(C3, 32 - WEBP_RESCALER_RFIX), mask);
+#else
+ const __m128i D2 = _mm_and_si128(C2, mask);
+ const __m128i D3 = _mm_and_si128(C3, mask);
+#endif
+ const __m128i E0 = _mm_or_si128(D0, D2);
+ const __m128i E1 = _mm_or_si128(D1, D3);
+ const __m128i F = _mm_packs_epi32(E0, E1);
+ const __m128i G = _mm_packus_epi16(F, F);
+ _mm_storel_epi64((__m128i*)dst, G);
+}
+
+static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) {
+ int x_out;
+ uint8_t* const dst = wrk->dst;
+ rescaler_t* const irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* const frow = wrk->frow;
+ const __m128i mult = _mm_set_epi32(0, wrk->fy_scale, 0, wrk->fy_scale);
+
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0 && wrk->y_sub + wrk->y_accum >= 0);
+ assert(wrk->y_expand);
+ if (wrk->y_accum == 0) {
+ for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) {
+ __m128i A0, A1, A2, A3;
+ LoadDispatchAndMult_SSE2(frow + x_out, NULL, &A0, &A1, &A2, &A3);
+ ProcessRow_SSE2(&A0, &A1, &A2, &A3, &mult, dst + x_out);
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const uint32_t J = frow[x_out];
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ } else {
+ const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
+ const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
+ const __m128i mA = _mm_set_epi32(0, A, 0, A);
+ const __m128i mB = _mm_set_epi32(0, B, 0, B);
+ const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
+ for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) {
+ __m128i A0, A1, A2, A3, B0, B1, B2, B3;
+ LoadDispatchAndMult_SSE2(frow + x_out, &mA, &A0, &A1, &A2, &A3);
+ LoadDispatchAndMult_SSE2(irow + x_out, &mB, &B0, &B1, &B2, &B3);
+ {
+ const __m128i C0 = _mm_add_epi64(A0, B0);
+ const __m128i C1 = _mm_add_epi64(A1, B1);
+ const __m128i C2 = _mm_add_epi64(A2, B2);
+ const __m128i C3 = _mm_add_epi64(A3, B3);
+ const __m128i D0 = _mm_add_epi64(C0, rounder);
+ const __m128i D1 = _mm_add_epi64(C1, rounder);
+ const __m128i D2 = _mm_add_epi64(C2, rounder);
+ const __m128i D3 = _mm_add_epi64(C3, rounder);
+ const __m128i E0 = _mm_srli_epi64(D0, WEBP_RESCALER_RFIX);
+ const __m128i E1 = _mm_srli_epi64(D1, WEBP_RESCALER_RFIX);
+ const __m128i E2 = _mm_srli_epi64(D2, WEBP_RESCALER_RFIX);
+ const __m128i E3 = _mm_srli_epi64(D3, WEBP_RESCALER_RFIX);
+ ProcessRow_SSE2(&E0, &E1, &E2, &E3, &mult, dst + x_out);
+ }
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const uint64_t I = (uint64_t)A * frow[x_out]
+ + (uint64_t)B * irow[x_out];
+ const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
+ const int v = (int)MULT_FIX(J, wrk->fy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ }
+ }
+}
+
+static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
+ int x_out;
+ uint8_t* const dst = wrk->dst;
+ rescaler_t* const irow = wrk->irow;
+ const int x_out_max = wrk->dst_width * wrk->num_channels;
+ const rescaler_t* const frow = wrk->frow;
+ const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
+ assert(!WebPRescalerOutputDone(wrk));
+ assert(wrk->y_accum <= 0);
+ assert(!wrk->y_expand);
+ if (yscale) {
+ const int scale_xy = wrk->fxy_scale;
+ const __m128i mult_xy = _mm_set_epi32(0, scale_xy, 0, scale_xy);
+ const __m128i mult_y = _mm_set_epi32(0, yscale, 0, yscale);
+ const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
+ for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) {
+ __m128i A0, A1, A2, A3, B0, B1, B2, B3;
+ LoadDispatchAndMult_SSE2(irow + x_out, NULL, &A0, &A1, &A2, &A3);
+ LoadDispatchAndMult_SSE2(frow + x_out, &mult_y, &B0, &B1, &B2, &B3);
+ {
+ const __m128i C0 = _mm_add_epi64(B0, rounder);
+ const __m128i C1 = _mm_add_epi64(B1, rounder);
+ const __m128i C2 = _mm_add_epi64(B2, rounder);
+ const __m128i C3 = _mm_add_epi64(B3, rounder);
+ const __m128i D0 = _mm_srli_epi64(C0, WEBP_RESCALER_RFIX); // = frac
+ const __m128i D1 = _mm_srli_epi64(C1, WEBP_RESCALER_RFIX);
+ const __m128i D2 = _mm_srli_epi64(C2, WEBP_RESCALER_RFIX);
+ const __m128i D3 = _mm_srli_epi64(C3, WEBP_RESCALER_RFIX);
+ const __m128i E0 = _mm_sub_epi64(A0, D0); // irow[x] - frac
+ const __m128i E1 = _mm_sub_epi64(A1, D1);
+ const __m128i E2 = _mm_sub_epi64(A2, D2);
+ const __m128i E3 = _mm_sub_epi64(A3, D3);
+ const __m128i F2 = _mm_slli_epi64(D2, 32);
+ const __m128i F3 = _mm_slli_epi64(D3, 32);
+ const __m128i G0 = _mm_or_si128(D0, F2);
+ const __m128i G1 = _mm_or_si128(D1, F3);
+ _mm_storeu_si128((__m128i*)(irow + x_out + 0), G0);
+ _mm_storeu_si128((__m128i*)(irow + x_out + 4), G1);
+ ProcessRow_SSE2(&E0, &E1, &E2, &E3, &mult_xy, dst + x_out);
+ }
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const uint32_t frac = (int)MULT_FIX(frow[x_out], yscale);
+ const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = frac; // new fractional start
+ }
+ } else {
+ const uint32_t scale = wrk->fxy_scale;
+ const __m128i mult = _mm_set_epi32(0, scale, 0, scale);
+ const __m128i zero = _mm_setzero_si128();
+ for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) {
+ __m128i A0, A1, A2, A3;
+ LoadDispatchAndMult_SSE2(irow + x_out, NULL, &A0, &A1, &A2, &A3);
+ _mm_storeu_si128((__m128i*)(irow + x_out + 0), zero);
+ _mm_storeu_si128((__m128i*)(irow + x_out + 4), zero);
+ ProcessRow_SSE2(&A0, &A1, &A2, &A3, &mult, dst + x_out);
+ }
+ for (; x_out < x_out_max; ++x_out) {
+ const int v = (int)MULT_FIX(irow[x_out], scale);
+ assert(v >= 0 && v <= 255);
+ dst[x_out] = v;
+ irow[x_out] = 0;
+ }
+ }
+}
+
+#undef MULT_FIX
+#undef ROUNDER
+
+//------------------------------------------------------------------------------
+
+extern void WebPRescalerDspInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInitSSE2(void) {
+ WebPRescalerImportRowExpand = RescalerImportRowExpand_SSE2;
+ WebPRescalerImportRowShrink = RescalerImportRowShrink_SSE2;
+ WebPRescalerExportRowExpand = RescalerExportRowExpand_SSE2;
+ WebPRescalerExportRowShrink = RescalerExportRowShrink_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(WebPRescalerDspInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/ssim.c b/src/third_party/libwebp/src/dsp/ssim.c
new file mode 100644
index 0000000..989ce82
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/ssim.c
@@ -0,0 +1,159 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// distortion calculation
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h> // for abs()
+
+#include "src/dsp/dsp.h"
+
+#if !defined(WEBP_REDUCE_SIZE)
+
+//------------------------------------------------------------------------------
+// SSIM / PSNR
+
+// hat-shaped filter. Sum of coefficients is equal to 16.
+static const uint32_t kWeight[2 * VP8_SSIM_KERNEL + 1] = {
+ 1, 2, 3, 4, 3, 2, 1
+};
+static const uint32_t kWeightSum = 16 * 16; // sum{kWeight}^2
+
+static WEBP_INLINE double SSIMCalculation(
+ const VP8DistoStats* const stats, uint32_t N /*num samples*/) {
+ const uint32_t w2 = N * N;
+ const uint32_t C1 = 20 * w2;
+ const uint32_t C2 = 60 * w2;
+ const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6
+ const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
+ const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
+ if (xmxm + ymym >= C3) {
+ const int64_t xmym = (int64_t)stats->xm * stats->ym;
+ const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
+ const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
+ const uint64_t syy = (uint64_t)stats->yym * N - ymym;
+ // we descale by 8 to prevent overflow during the fnum/fden multiply.
+ const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
+ const uint64_t den_S = (sxx + syy + C2) >> 8;
+ const uint64_t fnum = (2 * xmym + C1) * num_S;
+ const uint64_t fden = (xmxm + ymym + C1) * den_S;
+ const double r = (double)fnum / fden;
+ assert(r >= 0. && r <= 1.0);
+ return r;
+ }
+ return 1.; // area is too dark to contribute meaningfully
+}
+
+double VP8SSIMFromStats(const VP8DistoStats* const stats) {
+ return SSIMCalculation(stats, kWeightSum);
+}
+
+double VP8SSIMFromStatsClipped(const VP8DistoStats* const stats) {
+ return SSIMCalculation(stats, stats->w);
+}
+
+static double SSIMGetClipped_C(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2,
+ int xo, int yo, int W, int H) {
+ VP8DistoStats stats = { 0, 0, 0, 0, 0, 0 };
+ const int ymin = (yo - VP8_SSIM_KERNEL < 0) ? 0 : yo - VP8_SSIM_KERNEL;
+ const int ymax = (yo + VP8_SSIM_KERNEL > H - 1) ? H - 1
+ : yo + VP8_SSIM_KERNEL;
+ const int xmin = (xo - VP8_SSIM_KERNEL < 0) ? 0 : xo - VP8_SSIM_KERNEL;
+ const int xmax = (xo + VP8_SSIM_KERNEL > W - 1) ? W - 1
+ : xo + VP8_SSIM_KERNEL;
+ int x, y;
+ src1 += ymin * stride1;
+ src2 += ymin * stride2;
+ for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
+ for (x = xmin; x <= xmax; ++x) {
+ const uint32_t w = kWeight[VP8_SSIM_KERNEL + x - xo]
+ * kWeight[VP8_SSIM_KERNEL + y - yo];
+ const uint32_t s1 = src1[x];
+ const uint32_t s2 = src2[x];
+ stats.w += w;
+ stats.xm += w * s1;
+ stats.ym += w * s2;
+ stats.xxm += w * s1 * s1;
+ stats.xym += w * s1 * s2;
+ stats.yym += w * s2 * s2;
+ }
+ }
+ return VP8SSIMFromStatsClipped(&stats);
+}
+
+static double SSIMGet_C(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2) {
+ VP8DistoStats stats = { 0, 0, 0, 0, 0, 0 };
+ int x, y;
+ for (y = 0; y <= 2 * VP8_SSIM_KERNEL; ++y, src1 += stride1, src2 += stride2) {
+ for (x = 0; x <= 2 * VP8_SSIM_KERNEL; ++x) {
+ const uint32_t w = kWeight[x] * kWeight[y];
+ const uint32_t s1 = src1[x];
+ const uint32_t s2 = src2[x];
+ stats.xm += w * s1;
+ stats.ym += w * s2;
+ stats.xxm += w * s1 * s1;
+ stats.xym += w * s1 * s2;
+ stats.yym += w * s2 * s2;
+ }
+ }
+ return VP8SSIMFromStats(&stats);
+}
+
+#endif // !defined(WEBP_REDUCE_SIZE)
+
+//------------------------------------------------------------------------------
+
+#if !defined(WEBP_DISABLE_STATS)
+static uint32_t AccumulateSSE_C(const uint8_t* src1,
+ const uint8_t* src2, int len) {
+ int i;
+ uint32_t sse2 = 0;
+ assert(len <= 65535); // to ensure that accumulation fits within uint32_t
+ for (i = 0; i < len; ++i) {
+ const int32_t diff = src1[i] - src2[i];
+ sse2 += diff * diff;
+ }
+ return sse2;
+}
+#endif
+
+//------------------------------------------------------------------------------
+
+#if !defined(WEBP_REDUCE_SIZE)
+VP8SSIMGetFunc VP8SSIMGet;
+VP8SSIMGetClippedFunc VP8SSIMGetClipped;
+#endif
+#if !defined(WEBP_DISABLE_STATS)
+VP8AccumulateSSEFunc VP8AccumulateSSE;
+#endif
+
+extern void VP8SSIMDspInitSSE2(void);
+
+WEBP_DSP_INIT_FUNC(VP8SSIMDspInit) {
+#if !defined(WEBP_REDUCE_SIZE)
+ VP8SSIMGetClipped = SSIMGetClipped_C;
+ VP8SSIMGet = SSIMGet_C;
+#endif
+
+#if !defined(WEBP_DISABLE_STATS)
+ VP8AccumulateSSE = AccumulateSSE_C;
+#endif
+
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ VP8SSIMDspInitSSE2();
+ }
+#endif
+ }
+}
diff --git a/src/third_party/libwebp/src/dsp/ssim_sse2.c b/src/third_party/libwebp/src/dsp/ssim_sse2.c
new file mode 100644
index 0000000..1dcb0eb
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/ssim_sse2.c
@@ -0,0 +1,165 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 version of distortion calculation
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+
+#include <assert.h>
+#include <emmintrin.h>
+
+#include "src/dsp/common_sse2.h"
+
+#if !defined(WEBP_DISABLE_STATS)
+
+// Helper function
+static WEBP_INLINE void SubtractAndSquare_SSE2(const __m128i a, const __m128i b,
+ __m128i* const sum) {
+ // take abs(a-b) in 8b
+ const __m128i a_b = _mm_subs_epu8(a, b);
+ const __m128i b_a = _mm_subs_epu8(b, a);
+ const __m128i abs_a_b = _mm_or_si128(a_b, b_a);
+ // zero-extend to 16b
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i C0 = _mm_unpacklo_epi8(abs_a_b, zero);
+ const __m128i C1 = _mm_unpackhi_epi8(abs_a_b, zero);
+ // multiply with self
+ const __m128i sum1 = _mm_madd_epi16(C0, C0);
+ const __m128i sum2 = _mm_madd_epi16(C1, C1);
+ *sum = _mm_add_epi32(sum1, sum2);
+}
+
+//------------------------------------------------------------------------------
+// SSIM / PSNR entry point
+
+static uint32_t AccumulateSSE_SSE2(const uint8_t* src1,
+ const uint8_t* src2, int len) {
+ int i = 0;
+ uint32_t sse2 = 0;
+ if (len >= 16) {
+ const int limit = len - 32;
+ int32_t tmp[4];
+ __m128i sum1;
+ __m128i sum = _mm_setzero_si128();
+ __m128i a0 = _mm_loadu_si128((const __m128i*)&src1[i]);
+ __m128i b0 = _mm_loadu_si128((const __m128i*)&src2[i]);
+ i += 16;
+ while (i <= limit) {
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)&src1[i]);
+ const __m128i b1 = _mm_loadu_si128((const __m128i*)&src2[i]);
+ __m128i sum2;
+ i += 16;
+ SubtractAndSquare_SSE2(a0, b0, &sum1);
+ sum = _mm_add_epi32(sum, sum1);
+ a0 = _mm_loadu_si128((const __m128i*)&src1[i]);
+ b0 = _mm_loadu_si128((const __m128i*)&src2[i]);
+ i += 16;
+ SubtractAndSquare_SSE2(a1, b1, &sum2);
+ sum = _mm_add_epi32(sum, sum2);
+ }
+ SubtractAndSquare_SSE2(a0, b0, &sum1);
+ sum = _mm_add_epi32(sum, sum1);
+ _mm_storeu_si128((__m128i*)tmp, sum);
+ sse2 += (tmp[3] + tmp[2] + tmp[1] + tmp[0]);
+ }
+
+ for (; i < len; ++i) {
+ const int32_t diff = src1[i] - src2[i];
+ sse2 += diff * diff;
+ }
+ return sse2;
+}
+#endif // !defined(WEBP_DISABLE_STATS)
+
+#if !defined(WEBP_REDUCE_SIZE)
+
+static uint32_t HorizontalAdd16b_SSE2(const __m128i* const m) {
+ uint16_t tmp[8];
+ const __m128i a = _mm_srli_si128(*m, 8);
+ const __m128i b = _mm_add_epi16(*m, a);
+ _mm_storeu_si128((__m128i*)tmp, b);
+ return (uint32_t)tmp[3] + tmp[2] + tmp[1] + tmp[0];
+}
+
+static uint32_t HorizontalAdd32b_SSE2(const __m128i* const m) {
+ const __m128i a = _mm_srli_si128(*m, 8);
+ const __m128i b = _mm_add_epi32(*m, a);
+ const __m128i c = _mm_add_epi32(b, _mm_srli_si128(b, 4));
+ return (uint32_t)_mm_cvtsi128_si32(c);
+}
+
+static const uint16_t kWeight[] = { 1, 2, 3, 4, 3, 2, 1, 0 };
+
+#define ACCUMULATE_ROW(WEIGHT) do { \
+ /* compute row weight (Wx * Wy) */ \
+ const __m128i Wy = _mm_set1_epi16((WEIGHT)); \
+ const __m128i W = _mm_mullo_epi16(Wx, Wy); \
+ /* process 8 bytes at a time (7 bytes, actually) */ \
+ const __m128i a0 = _mm_loadl_epi64((const __m128i*)src1); \
+ const __m128i b0 = _mm_loadl_epi64((const __m128i*)src2); \
+ /* convert to 16b and multiply by weight */ \
+ const __m128i a1 = _mm_unpacklo_epi8(a0, zero); \
+ const __m128i b1 = _mm_unpacklo_epi8(b0, zero); \
+ const __m128i wa1 = _mm_mullo_epi16(a1, W); \
+ const __m128i wb1 = _mm_mullo_epi16(b1, W); \
+ /* accumulate */ \
+ xm = _mm_add_epi16(xm, wa1); \
+ ym = _mm_add_epi16(ym, wb1); \
+ xxm = _mm_add_epi32(xxm, _mm_madd_epi16(a1, wa1)); \
+ xym = _mm_add_epi32(xym, _mm_madd_epi16(a1, wb1)); \
+ yym = _mm_add_epi32(yym, _mm_madd_epi16(b1, wb1)); \
+ src1 += stride1; \
+ src2 += stride2; \
+} while (0)
+
+static double SSIMGet_SSE2(const uint8_t* src1, int stride1,
+ const uint8_t* src2, int stride2) {
+ VP8DistoStats stats;
+ const __m128i zero = _mm_setzero_si128();
+ __m128i xm = zero, ym = zero; // 16b accums
+ __m128i xxm = zero, yym = zero, xym = zero; // 32b accum
+ const __m128i Wx = _mm_loadu_si128((const __m128i*)kWeight);
+ assert(2 * VP8_SSIM_KERNEL + 1 == 7);
+ ACCUMULATE_ROW(1);
+ ACCUMULATE_ROW(2);
+ ACCUMULATE_ROW(3);
+ ACCUMULATE_ROW(4);
+ ACCUMULATE_ROW(3);
+ ACCUMULATE_ROW(2);
+ ACCUMULATE_ROW(1);
+ stats.xm = HorizontalAdd16b_SSE2(&xm);
+ stats.ym = HorizontalAdd16b_SSE2(&ym);
+ stats.xxm = HorizontalAdd32b_SSE2(&xxm);
+ stats.xym = HorizontalAdd32b_SSE2(&xym);
+ stats.yym = HorizontalAdd32b_SSE2(&yym);
+ return VP8SSIMFromStats(&stats);
+}
+
+#endif // !defined(WEBP_REDUCE_SIZE)
+
+extern void VP8SSIMDspInitSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void VP8SSIMDspInitSSE2(void) {
+#if !defined(WEBP_DISABLE_STATS)
+ VP8AccumulateSSE = AccumulateSSE_SSE2;
+#endif
+#if !defined(WEBP_REDUCE_SIZE)
+ VP8SSIMGet = SSIMGet_SSE2;
+#endif
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(VP8SSIMDspInitSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/upsampling.c b/src/third_party/libwebp/src/dsp/upsampling.c
new file mode 100644
index 0000000..9b60da5
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/upsampling.c
@@ -0,0 +1,327 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// YUV to RGB upsampling functions.
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#include "src/dsp/dsp.h"
+#include "src/dsp/yuv.h"
+
+#include <assert.h>
+
+//------------------------------------------------------------------------------
+// Fancy upsampler
+
+#ifdef FANCY_UPSAMPLING
+
+// Fancy upsampling functions to convert YUV to RGB
+WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST];
+
+// Given samples laid out in a square as:
+// [a b]
+// [c d]
+// we interpolate u/v as:
+// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16
+// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16
+
+// We process u and v together stashed into 32bit (16bit each).
+#define LOAD_UV(u, v) ((u) | ((v) << 16))
+
+#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
+ const uint8_t* top_u, const uint8_t* top_v, \
+ const uint8_t* cur_u, const uint8_t* cur_v, \
+ uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
+ int x; \
+ const int last_pixel_pair = (len - 1) >> 1; \
+ uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \
+ uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \
+ assert(top_y != NULL); \
+ { \
+ const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
+ FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \
+ } \
+ if (bottom_y != NULL) { \
+ const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
+ FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \
+ } \
+ for (x = 1; x <= last_pixel_pair; ++x) { \
+ const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \
+ const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \
+ /* precompute invariant values associated with first and second diagonals*/\
+ const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \
+ const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \
+ const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \
+ { \
+ const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \
+ const uint32_t uv1 = (diag_03 + t_uv) >> 1; \
+ FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
+ top_dst + (2 * x - 1) * (XSTEP)); \
+ FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \
+ top_dst + (2 * x - 0) * (XSTEP)); \
+ } \
+ if (bottom_y != NULL) { \
+ const uint32_t uv0 = (diag_03 + l_uv) >> 1; \
+ const uint32_t uv1 = (diag_12 + uv) >> 1; \
+ FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
+ bottom_dst + (2 * x - 1) * (XSTEP)); \
+ FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \
+ bottom_dst + (2 * x + 0) * (XSTEP)); \
+ } \
+ tl_uv = t_uv; \
+ l_uv = uv; \
+ } \
+ if (!(len & 1)) { \
+ { \
+ const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
+ FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
+ top_dst + (len - 1) * (XSTEP)); \
+ } \
+ if (bottom_y != NULL) { \
+ const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
+ FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
+ bottom_dst + (len - 1) * (XSTEP)); \
+ } \
+ } \
+}
+
+// All variants implemented.
+#if !WEBP_NEON_OMIT_C_CODE
+UPSAMPLE_FUNC(UpsampleRgbaLinePair_C, VP8YuvToRgba, 4)
+UPSAMPLE_FUNC(UpsampleBgraLinePair_C, VP8YuvToBgra, 4)
+#if !defined(WEBP_REDUCE_CSP)
+UPSAMPLE_FUNC(UpsampleArgbLinePair_C, VP8YuvToArgb, 4)
+UPSAMPLE_FUNC(UpsampleRgbLinePair_C, VP8YuvToRgb, 3)
+UPSAMPLE_FUNC(UpsampleBgrLinePair_C, VP8YuvToBgr, 3)
+UPSAMPLE_FUNC(UpsampleRgba4444LinePair_C, VP8YuvToRgba4444, 2)
+UPSAMPLE_FUNC(UpsampleRgb565LinePair_C, VP8YuvToRgb565, 2)
+#else
+static void EmptyUpsampleFunc(const uint8_t* top_y, const uint8_t* bottom_y,
+ const uint8_t* top_u, const uint8_t* top_v,
+ const uint8_t* cur_u, const uint8_t* cur_v,
+ uint8_t* top_dst, uint8_t* bottom_dst, int len) {
+ (void)top_y;
+ (void)bottom_y;
+ (void)top_u;
+ (void)top_v;
+ (void)cur_u;
+ (void)cur_v;
+ (void)top_dst;
+ (void)bottom_dst;
+ (void)len;
+ assert(0); // COLORSPACE SUPPORT NOT COMPILED
+}
+#define UpsampleArgbLinePair_C EmptyUpsampleFunc
+#define UpsampleRgbLinePair_C EmptyUpsampleFunc
+#define UpsampleBgrLinePair_C EmptyUpsampleFunc
+#define UpsampleRgba4444LinePair_C EmptyUpsampleFunc
+#define UpsampleRgb565LinePair_C EmptyUpsampleFunc
+#endif // WEBP_REDUCE_CSP
+
+#endif
+
+#undef LOAD_UV
+#undef UPSAMPLE_FUNC
+
+#endif // FANCY_UPSAMPLING
+
+//------------------------------------------------------------------------------
+
+#if !defined(FANCY_UPSAMPLING)
+#define DUAL_SAMPLE_FUNC(FUNC_NAME, FUNC) \
+static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \
+ const uint8_t* top_u, const uint8_t* top_v, \
+ const uint8_t* bot_u, const uint8_t* bot_v, \
+ uint8_t* top_dst, uint8_t* bot_dst, int len) { \
+ const int half_len = len >> 1; \
+ int x; \
+ assert(top_dst != NULL); \
+ { \
+ for (x = 0; x < half_len; ++x) { \
+ FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x + 0); \
+ FUNC(top_y[2 * x + 1], top_u[x], top_v[x], top_dst + 8 * x + 4); \
+ } \
+ if (len & 1) FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x); \
+ } \
+ if (bot_dst != NULL) { \
+ for (x = 0; x < half_len; ++x) { \
+ FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x + 0); \
+ FUNC(bot_y[2 * x + 1], bot_u[x], bot_v[x], bot_dst + 8 * x + 4); \
+ } \
+ if (len & 1) FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x); \
+ } \
+}
+
+DUAL_SAMPLE_FUNC(DualLineSamplerBGRA, VP8YuvToBgra)
+DUAL_SAMPLE_FUNC(DualLineSamplerARGB, VP8YuvToArgb)
+#undef DUAL_SAMPLE_FUNC
+
+#endif // !FANCY_UPSAMPLING
+
+WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) {
+ WebPInitUpsamplers();
+#ifdef FANCY_UPSAMPLING
+ return WebPUpsamplers[alpha_is_last ? MODE_BGRA : MODE_ARGB];
+#else
+ return (alpha_is_last ? DualLineSamplerBGRA : DualLineSamplerARGB);
+#endif
+}
+
+//------------------------------------------------------------------------------
+// YUV444 converter
+
+#define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \
+extern void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len); \
+void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ int i; \
+ for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * (XSTEP)]); \
+}
+
+YUV444_FUNC(WebPYuv444ToRgba_C, VP8YuvToRgba, 4)
+YUV444_FUNC(WebPYuv444ToBgra_C, VP8YuvToBgra, 4)
+#if !defined(WEBP_REDUCE_CSP)
+YUV444_FUNC(WebPYuv444ToRgb_C, VP8YuvToRgb, 3)
+YUV444_FUNC(WebPYuv444ToBgr_C, VP8YuvToBgr, 3)
+YUV444_FUNC(WebPYuv444ToArgb_C, VP8YuvToArgb, 4)
+YUV444_FUNC(WebPYuv444ToRgba4444_C, VP8YuvToRgba4444, 2)
+YUV444_FUNC(WebPYuv444ToRgb565_C, VP8YuvToRgb565, 2)
+#else
+static void EmptyYuv444Func(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ (void)y;
+ (void)u;
+ (void)v;
+ (void)dst;
+ (void)len;
+}
+#define WebPYuv444ToRgb_C EmptyYuv444Func
+#define WebPYuv444ToBgr_C EmptyYuv444Func
+#define WebPYuv444ToArgb_C EmptyYuv444Func
+#define WebPYuv444ToRgba4444_C EmptyYuv444Func
+#define WebPYuv444ToRgb565_C EmptyYuv444Func
+#endif // WEBP_REDUCE_CSP
+
+#undef YUV444_FUNC
+
+WebPYUV444Converter WebPYUV444Converters[MODE_LAST];
+
+extern void WebPInitYUV444ConvertersMIPSdspR2(void);
+extern void WebPInitYUV444ConvertersSSE2(void);
+extern void WebPInitYUV444ConvertersSSE41(void);
+
+WEBP_DSP_INIT_FUNC(WebPInitYUV444Converters) {
+ WebPYUV444Converters[MODE_RGBA] = WebPYuv444ToRgba_C;
+ WebPYUV444Converters[MODE_BGRA] = WebPYuv444ToBgra_C;
+ WebPYUV444Converters[MODE_RGB] = WebPYuv444ToRgb_C;
+ WebPYUV444Converters[MODE_BGR] = WebPYuv444ToBgr_C;
+ WebPYUV444Converters[MODE_ARGB] = WebPYuv444ToArgb_C;
+ WebPYUV444Converters[MODE_RGBA_4444] = WebPYuv444ToRgba4444_C;
+ WebPYUV444Converters[MODE_RGB_565] = WebPYuv444ToRgb565_C;
+ WebPYUV444Converters[MODE_rgbA] = WebPYuv444ToRgba_C;
+ WebPYUV444Converters[MODE_bgrA] = WebPYuv444ToBgra_C;
+ WebPYUV444Converters[MODE_Argb] = WebPYuv444ToArgb_C;
+ WebPYUV444Converters[MODE_rgbA_4444] = WebPYuv444ToRgba4444_C;
+
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ WebPInitYUV444ConvertersSSE2();
+ }
+#endif
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitYUV444ConvertersSSE41();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ WebPInitYUV444ConvertersMIPSdspR2();
+ }
+#endif
+ }
+}
+
+//------------------------------------------------------------------------------
+// Main calls
+
+extern void WebPInitUpsamplersSSE2(void);
+extern void WebPInitUpsamplersSSE41(void);
+extern void WebPInitUpsamplersNEON(void);
+extern void WebPInitUpsamplersMIPSdspR2(void);
+extern void WebPInitUpsamplersMSA(void);
+
+WEBP_DSP_INIT_FUNC(WebPInitUpsamplers) {
+#ifdef FANCY_UPSAMPLING
+#if !WEBP_NEON_OMIT_C_CODE
+ WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair_C;
+ WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair_C;
+ WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair_C;
+ WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair_C;
+ WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_C;
+ WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_C;
+ WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair_C;
+ WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair_C;
+ WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair_C;
+ WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair_C;
+ WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair_C;
+#endif
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ WebPInitUpsamplersSSE2();
+ }
+#endif
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitUpsamplersSSE41();
+ }
+#endif
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ WebPInitUpsamplersMIPSdspR2();
+ }
+#endif
+#if defined(WEBP_USE_MSA)
+ if (VP8GetCPUInfo(kMSA)) {
+ WebPInitUpsamplersMSA();
+ }
+#endif
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ WebPInitUpsamplersNEON();
+ }
+#endif
+
+ assert(WebPUpsamplers[MODE_RGBA] != NULL);
+ assert(WebPUpsamplers[MODE_BGRA] != NULL);
+ assert(WebPUpsamplers[MODE_rgbA] != NULL);
+ assert(WebPUpsamplers[MODE_bgrA] != NULL);
+#if !defined(WEBP_REDUCE_CSP) || !WEBP_NEON_OMIT_C_CODE
+ assert(WebPUpsamplers[MODE_RGB] != NULL);
+ assert(WebPUpsamplers[MODE_BGR] != NULL);
+ assert(WebPUpsamplers[MODE_ARGB] != NULL);
+ assert(WebPUpsamplers[MODE_RGBA_4444] != NULL);
+ assert(WebPUpsamplers[MODE_RGB_565] != NULL);
+ assert(WebPUpsamplers[MODE_Argb] != NULL);
+ assert(WebPUpsamplers[MODE_rgbA_4444] != NULL);
+#endif
+
+#endif // FANCY_UPSAMPLING
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/dsp/upsampling_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/upsampling_mips_dsp_r2.c
new file mode 100644
index 0000000..10d499d
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/upsampling_mips_dsp_r2.c
@@ -0,0 +1,291 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// YUV to RGB upsampling functions.
+//
+// Author(s): Branimir Vasic (branimir.vasic@imgtec.com)
+// Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#include <assert.h>
+#include "src/dsp/yuv.h"
+
+#define YUV_TO_RGB(Y, U, V, R, G, B) do { \
+ const int t1 = MultHi(Y, 19077); \
+ const int t2 = MultHi(V, 13320); \
+ R = MultHi(V, 26149); \
+ G = MultHi(U, 6419); \
+ B = MultHi(U, 33050); \
+ R = t1 + R; \
+ G = t1 - G; \
+ B = t1 + B; \
+ R = R - 14234; \
+ G = G - t2 + 8708; \
+ B = B - 17685; \
+ __asm__ volatile ( \
+ "shll_s.w %[" #R "], %[" #R "], 17 \n\t" \
+ "shll_s.w %[" #G "], %[" #G "], 17 \n\t" \
+ "shll_s.w %[" #B "], %[" #B "], 17 \n\t" \
+ "precrqu_s.qb.ph %[" #R "], %[" #R "], $zero \n\t" \
+ "precrqu_s.qb.ph %[" #G "], %[" #G "], $zero \n\t" \
+ "precrqu_s.qb.ph %[" #B "], %[" #B "], $zero \n\t" \
+ "srl %[" #R "], %[" #R "], 24 \n\t" \
+ "srl %[" #G "], %[" #G "], 24 \n\t" \
+ "srl %[" #B "], %[" #B "], 24 \n\t" \
+ : [R]"+r"(R), [G]"+r"(G), [B]"+r"(B) \
+ : \
+ ); \
+ } while (0)
+
+#if !defined(WEBP_REDUCE_CSP)
+static WEBP_INLINE void YuvToRgb(int y, int u, int v, uint8_t* const rgb) {
+ int r, g, b;
+ YUV_TO_RGB(y, u, v, r, g, b);
+ rgb[0] = r;
+ rgb[1] = g;
+ rgb[2] = b;
+}
+static WEBP_INLINE void YuvToBgr(int y, int u, int v, uint8_t* const bgr) {
+ int r, g, b;
+ YUV_TO_RGB(y, u, v, r, g, b);
+ bgr[0] = b;
+ bgr[1] = g;
+ bgr[2] = r;
+}
+static WEBP_INLINE void YuvToRgb565(int y, int u, int v, uint8_t* const rgb) {
+ int r, g, b;
+ YUV_TO_RGB(y, u, v, r, g, b);
+ {
+ const int rg = (r & 0xf8) | (g >> 5);
+ const int gb = ((g << 3) & 0xe0) | (b >> 3);
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ rgb[0] = gb;
+ rgb[1] = rg;
+#else
+ rgb[0] = rg;
+ rgb[1] = gb;
+#endif
+ }
+}
+static WEBP_INLINE void YuvToRgba4444(int y, int u, int v,
+ uint8_t* const argb) {
+ int r, g, b;
+ YUV_TO_RGB(y, u, v, r, g, b);
+ {
+ const int rg = (r & 0xf0) | (g >> 4);
+ const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ argb[0] = ba;
+ argb[1] = rg;
+#else
+ argb[0] = rg;
+ argb[1] = ba;
+#endif
+ }
+}
+#endif // WEBP_REDUCE_CSP
+
+//-----------------------------------------------------------------------------
+// Alpha handling variants
+
+#if !defined(WEBP_REDUCE_CSP)
+static WEBP_INLINE void YuvToArgb(uint8_t y, uint8_t u, uint8_t v,
+ uint8_t* const argb) {
+ int r, g, b;
+ YUV_TO_RGB(y, u, v, r, g, b);
+ argb[0] = 0xff;
+ argb[1] = r;
+ argb[2] = g;
+ argb[3] = b;
+}
+#endif // WEBP_REDUCE_CSP
+static WEBP_INLINE void YuvToBgra(uint8_t y, uint8_t u, uint8_t v,
+ uint8_t* const bgra) {
+ int r, g, b;
+ YUV_TO_RGB(y, u, v, r, g, b);
+ bgra[0] = b;
+ bgra[1] = g;
+ bgra[2] = r;
+ bgra[3] = 0xff;
+}
+static WEBP_INLINE void YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
+ uint8_t* const rgba) {
+ int r, g, b;
+ YUV_TO_RGB(y, u, v, r, g, b);
+ rgba[0] = r;
+ rgba[1] = g;
+ rgba[2] = b;
+ rgba[3] = 0xff;
+}
+
+//------------------------------------------------------------------------------
+// Fancy upsampler
+
+#ifdef FANCY_UPSAMPLING
+
+// Given samples laid out in a square as:
+// [a b]
+// [c d]
+// we interpolate u/v as:
+// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16
+// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16
+
+// We process u and v together stashed into 32bit (16bit each).
+#define LOAD_UV(u, v) ((u) | ((v) << 16))
+
+#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
+ const uint8_t* top_u, const uint8_t* top_v, \
+ const uint8_t* cur_u, const uint8_t* cur_v, \
+ uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
+ int x; \
+ const int last_pixel_pair = (len - 1) >> 1; \
+ uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \
+ uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \
+ assert(top_y != NULL); \
+ { \
+ const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
+ FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \
+ } \
+ if (bottom_y != NULL) { \
+ const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
+ FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \
+ } \
+ for (x = 1; x <= last_pixel_pair; ++x) { \
+ const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \
+ const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \
+ /* precompute invariant values associated with first and second diagonals*/\
+ const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \
+ const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \
+ const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \
+ { \
+ const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \
+ const uint32_t uv1 = (diag_03 + t_uv) >> 1; \
+ FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
+ top_dst + (2 * x - 1) * XSTEP); \
+ FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \
+ top_dst + (2 * x - 0) * XSTEP); \
+ } \
+ if (bottom_y != NULL) { \
+ const uint32_t uv0 = (diag_03 + l_uv) >> 1; \
+ const uint32_t uv1 = (diag_12 + uv) >> 1; \
+ FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
+ bottom_dst + (2 * x - 1) * XSTEP); \
+ FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \
+ bottom_dst + (2 * x + 0) * XSTEP); \
+ } \
+ tl_uv = t_uv; \
+ l_uv = uv; \
+ } \
+ if (!(len & 1)) { \
+ { \
+ const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
+ FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
+ top_dst + (len - 1) * XSTEP); \
+ } \
+ if (bottom_y != NULL) { \
+ const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
+ FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
+ bottom_dst + (len - 1) * XSTEP); \
+ } \
+ } \
+}
+
+// All variants implemented.
+UPSAMPLE_FUNC(UpsampleRgbaLinePair, YuvToRgba, 4)
+UPSAMPLE_FUNC(UpsampleBgraLinePair, YuvToBgra, 4)
+#if !defined(WEBP_REDUCE_CSP)
+UPSAMPLE_FUNC(UpsampleRgbLinePair, YuvToRgb, 3)
+UPSAMPLE_FUNC(UpsampleBgrLinePair, YuvToBgr, 3)
+UPSAMPLE_FUNC(UpsampleArgbLinePair, YuvToArgb, 4)
+UPSAMPLE_FUNC(UpsampleRgba4444LinePair, YuvToRgba4444, 2)
+UPSAMPLE_FUNC(UpsampleRgb565LinePair, YuvToRgb565, 2)
+#endif // WEBP_REDUCE_CSP
+
+#undef LOAD_UV
+#undef UPSAMPLE_FUNC
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitUpsamplersMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersMIPSdspR2(void) {
+ WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair;
+ WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair;
+ WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair;
+ WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair;
+#if !defined(WEBP_REDUCE_CSP)
+ WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair;
+ WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair;
+ WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair;
+ WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair;
+ WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair;
+ WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair;
+ WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair;
+#endif // WEBP_REDUCE_CSP
+}
+
+#endif // FANCY_UPSAMPLING
+
+//------------------------------------------------------------------------------
+// YUV444 converter
+
+#define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ int i; \
+ for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * XSTEP]); \
+}
+
+YUV444_FUNC(Yuv444ToRgba, YuvToRgba, 4)
+YUV444_FUNC(Yuv444ToBgra, YuvToBgra, 4)
+#if !defined(WEBP_REDUCE_CSP)
+YUV444_FUNC(Yuv444ToRgb, YuvToRgb, 3)
+YUV444_FUNC(Yuv444ToBgr, YuvToBgr, 3)
+YUV444_FUNC(Yuv444ToArgb, YuvToArgb, 4)
+YUV444_FUNC(Yuv444ToRgba4444, YuvToRgba4444, 2)
+YUV444_FUNC(Yuv444ToRgb565, YuvToRgb565, 2)
+#endif // WEBP_REDUCE_CSP
+
+#undef YUV444_FUNC
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitYUV444ConvertersMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersMIPSdspR2(void) {
+ WebPYUV444Converters[MODE_RGBA] = Yuv444ToRgba;
+ WebPYUV444Converters[MODE_BGRA] = Yuv444ToBgra;
+ WebPYUV444Converters[MODE_rgbA] = Yuv444ToRgba;
+ WebPYUV444Converters[MODE_bgrA] = Yuv444ToBgra;
+#if !defined(WEBP_REDUCE_CSP)
+ WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb;
+ WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr;
+ WebPYUV444Converters[MODE_ARGB] = Yuv444ToArgb;
+ WebPYUV444Converters[MODE_RGBA_4444] = Yuv444ToRgba4444;
+ WebPYUV444Converters[MODE_RGB_565] = Yuv444ToRgb565;
+ WebPYUV444Converters[MODE_Argb] = Yuv444ToArgb;
+ WebPYUV444Converters[MODE_rgbA_4444] = Yuv444ToRgba4444;
+#endif // WEBP_REDUCE_CSP
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
+
+#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_MIPS_DSP_R2))
+WEBP_DSP_INIT_STUB(WebPInitUpsamplersMIPSdspR2)
+#endif
diff --git a/src/third_party/libwebp/src/dsp/upsampling_msa.c b/src/third_party/libwebp/src/dsp/upsampling_msa.c
new file mode 100644
index 0000000..d5bf4db
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/upsampling_msa.c
@@ -0,0 +1,692 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MSA version of YUV to RGB upsampling functions.
+//
+// Author: Prashant Patil (prashant.patil@imgtec.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <string.h>
+#endif
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MSA)
+
+#include "src/dsp/msa_macro.h"
+#include "src/dsp/yuv.h"
+
+#ifdef FANCY_UPSAMPLING
+
+#define ILVR_UW2(in, out0, out1) do { \
+ const v8i16 t0 = (v8i16)__msa_ilvr_b((v16i8)zero, (v16i8)in); \
+ out0 = (v4u32)__msa_ilvr_h((v8i16)zero, t0); \
+ out1 = (v4u32)__msa_ilvl_h((v8i16)zero, t0); \
+} while (0)
+
+#define ILVRL_UW4(in, out0, out1, out2, out3) do { \
+ v16u8 t0, t1; \
+ ILVRL_B2_UB(zero, in, t0, t1); \
+ ILVRL_H2_UW(zero, t0, out0, out1); \
+ ILVRL_H2_UW(zero, t1, out2, out3); \
+} while (0)
+
+#define MULTHI_16(in0, in1, in2, in3, cnst, out0, out1) do { \
+ const v4i32 const0 = (v4i32)__msa_fill_w(cnst * 256); \
+ v4u32 temp0, temp1, temp2, temp3; \
+ MUL4(in0, const0, in1, const0, in2, const0, in3, const0, \
+ temp0, temp1, temp2, temp3); \
+ PCKOD_H2_UH(temp1, temp0, temp3, temp2, out0, out1); \
+} while (0)
+
+#define MULTHI_8(in0, in1, cnst, out0) do { \
+ const v4i32 const0 = (v4i32)__msa_fill_w(cnst * 256); \
+ v4u32 temp0, temp1; \
+ MUL2(in0, const0, in1, const0, temp0, temp1); \
+ out0 = (v8u16)__msa_pckod_h((v8i16)temp1, (v8i16)temp0); \
+} while (0)
+
+#define CALC_R16(y0, y1, v0, v1, dst) do { \
+ const v8i16 const_a = (v8i16)__msa_fill_h(14234); \
+ const v8i16 a0 = __msa_adds_s_h((v8i16)y0, (v8i16)v0); \
+ const v8i16 a1 = __msa_adds_s_h((v8i16)y1, (v8i16)v1); \
+ v8i16 b0 = __msa_subs_s_h(a0, const_a); \
+ v8i16 b1 = __msa_subs_s_h(a1, const_a); \
+ SRAI_H2_SH(b0, b1, 6); \
+ CLIP_SH2_0_255(b0, b1); \
+ dst = (v16u8)__msa_pckev_b((v16i8)b1, (v16i8)b0); \
+} while (0)
+
+#define CALC_R8(y0, v0, dst) do { \
+ const v8i16 const_a = (v8i16)__msa_fill_h(14234); \
+ const v8i16 a0 = __msa_adds_s_h((v8i16)y0, (v8i16)v0); \
+ v8i16 b0 = __msa_subs_s_h(a0, const_a); \
+ b0 = SRAI_H(b0, 6); \
+ CLIP_SH_0_255(b0); \
+ dst = (v16u8)__msa_pckev_b((v16i8)b0, (v16i8)b0); \
+} while (0)
+
+#define CALC_G16(y0, y1, u0, u1, v0, v1, dst) do { \
+ const v8i16 const_a = (v8i16)__msa_fill_h(8708); \
+ v8i16 a0 = __msa_subs_s_h((v8i16)y0, (v8i16)u0); \
+ v8i16 a1 = __msa_subs_s_h((v8i16)y1, (v8i16)u1); \
+ const v8i16 b0 = __msa_subs_s_h(a0, (v8i16)v0); \
+ const v8i16 b1 = __msa_subs_s_h(a1, (v8i16)v1); \
+ a0 = __msa_adds_s_h(b0, const_a); \
+ a1 = __msa_adds_s_h(b1, const_a); \
+ SRAI_H2_SH(a0, a1, 6); \
+ CLIP_SH2_0_255(a0, a1); \
+ dst = (v16u8)__msa_pckev_b((v16i8)a1, (v16i8)a0); \
+} while (0)
+
+#define CALC_G8(y0, u0, v0, dst) do { \
+ const v8i16 const_a = (v8i16)__msa_fill_h(8708); \
+ v8i16 a0 = __msa_subs_s_h((v8i16)y0, (v8i16)u0); \
+ const v8i16 b0 = __msa_subs_s_h(a0, (v8i16)v0); \
+ a0 = __msa_adds_s_h(b0, const_a); \
+ a0 = SRAI_H(a0, 6); \
+ CLIP_SH_0_255(a0); \
+ dst = (v16u8)__msa_pckev_b((v16i8)a0, (v16i8)a0); \
+} while (0)
+
+#define CALC_B16(y0, y1, u0, u1, dst) do { \
+ const v8u16 const_a = (v8u16)__msa_fill_h(17685); \
+ const v8u16 a0 = __msa_adds_u_h((v8u16)y0, u0); \
+ const v8u16 a1 = __msa_adds_u_h((v8u16)y1, u1); \
+ v8u16 b0 = __msa_subs_u_h(a0, const_a); \
+ v8u16 b1 = __msa_subs_u_h(a1, const_a); \
+ SRAI_H2_UH(b0, b1, 6); \
+ CLIP_UH2_0_255(b0, b1); \
+ dst = (v16u8)__msa_pckev_b((v16i8)b1, (v16i8)b0); \
+} while (0)
+
+#define CALC_B8(y0, u0, dst) do { \
+ const v8u16 const_a = (v8u16)__msa_fill_h(17685); \
+ const v8u16 a0 = __msa_adds_u_h((v8u16)y0, u0); \
+ v8u16 b0 = __msa_subs_u_h(a0, const_a); \
+ b0 = SRAI_H(b0, 6); \
+ CLIP_UH_0_255(b0); \
+ dst = (v16u8)__msa_pckev_b((v16i8)b0, (v16i8)b0); \
+} while (0)
+
+#define CALC_RGB16(y, u, v, R, G, B) do { \
+ const v16u8 zero = { 0 }; \
+ v8u16 y0, y1, u0, u1, v0, v1; \
+ v4u32 p0, p1, p2, p3; \
+ const v16u8 in_y = LD_UB(y); \
+ const v16u8 in_u = LD_UB(u); \
+ const v16u8 in_v = LD_UB(v); \
+ ILVRL_UW4(in_y, p0, p1, p2, p3); \
+ MULTHI_16(p0, p1, p2, p3, 19077, y0, y1); \
+ ILVRL_UW4(in_v, p0, p1, p2, p3); \
+ MULTHI_16(p0, p1, p2, p3, 26149, v0, v1); \
+ CALC_R16(y0, y1, v0, v1, R); \
+ MULTHI_16(p0, p1, p2, p3, 13320, v0, v1); \
+ ILVRL_UW4(in_u, p0, p1, p2, p3); \
+ MULTHI_16(p0, p1, p2, p3, 6419, u0, u1); \
+ CALC_G16(y0, y1, u0, u1, v0, v1, G); \
+ MULTHI_16(p0, p1, p2, p3, 33050, u0, u1); \
+ CALC_B16(y0, y1, u0, u1, B); \
+} while (0)
+
+#define CALC_RGB8(y, u, v, R, G, B) do { \
+ const v16u8 zero = { 0 }; \
+ v8u16 y0, u0, v0; \
+ v4u32 p0, p1; \
+ const v16u8 in_y = LD_UB(y); \
+ const v16u8 in_u = LD_UB(u); \
+ const v16u8 in_v = LD_UB(v); \
+ ILVR_UW2(in_y, p0, p1); \
+ MULTHI_8(p0, p1, 19077, y0); \
+ ILVR_UW2(in_v, p0, p1); \
+ MULTHI_8(p0, p1, 26149, v0); \
+ CALC_R8(y0, v0, R); \
+ MULTHI_8(p0, p1, 13320, v0); \
+ ILVR_UW2(in_u, p0, p1); \
+ MULTHI_8(p0, p1, 6419, u0); \
+ CALC_G8(y0, u0, v0, G); \
+ MULTHI_8(p0, p1, 33050, u0); \
+ CALC_B8(y0, u0, B); \
+} while (0)
+
+#define STORE16_3(a0, a1, a2, dst) do { \
+ const v16u8 mask0 = { 0, 1, 16, 2, 3, 17, 4, 5, 18, 6, 7, 19, \
+ 8, 9, 20, 10 }; \
+ const v16u8 mask1 = { 0, 21, 1, 2, 22, 3, 4, 23, 5, 6, 24, 7, \
+ 8, 25, 9, 10 }; \
+ const v16u8 mask2 = { 26, 0, 1, 27, 2, 3, 28, 4, 5, 29, 6, 7, \
+ 30, 8, 9, 31 }; \
+ v16u8 out0, out1, out2, tmp0, tmp1, tmp2; \
+ ILVRL_B2_UB(a1, a0, tmp0, tmp1); \
+ out0 = VSHF_UB(tmp0, a2, mask0); \
+ tmp2 = SLDI_UB(tmp1, tmp0, 11); \
+ out1 = VSHF_UB(tmp2, a2, mask1); \
+ tmp2 = SLDI_UB(tmp1, tmp1, 6); \
+ out2 = VSHF_UB(tmp2, a2, mask2); \
+ ST_UB(out0, dst + 0); \
+ ST_UB(out1, dst + 16); \
+ ST_UB(out2, dst + 32); \
+} while (0)
+
+#define STORE8_3(a0, a1, a2, dst) do { \
+ int64_t out_m; \
+ const v16u8 mask0 = { 0, 1, 16, 2, 3, 17, 4, 5, 18, 6, 7, 19, \
+ 8, 9, 20, 10 }; \
+ const v16u8 mask1 = { 11, 21, 12, 13, 22, 14, 15, 23, \
+ 255, 255, 255, 255, 255, 255, 255, 255 }; \
+ const v16u8 tmp0 = (v16u8)__msa_ilvr_b((v16i8)a1, (v16i8)a0); \
+ v16u8 out0, out1; \
+ VSHF_B2_UB(tmp0, a2, tmp0, a2, mask0, mask1, out0, out1); \
+ ST_UB(out0, dst); \
+ out_m = __msa_copy_s_d((v2i64)out1, 0); \
+ SD(out_m, dst + 16); \
+} while (0)
+
+#define STORE16_4(a0, a1, a2, a3, dst) do { \
+ v16u8 tmp0, tmp1, tmp2, tmp3; \
+ v16u8 out0, out1, out2, out3; \
+ ILVRL_B2_UB(a1, a0, tmp0, tmp1); \
+ ILVRL_B2_UB(a3, a2, tmp2, tmp3); \
+ ILVRL_H2_UB(tmp2, tmp0, out0, out1); \
+ ILVRL_H2_UB(tmp3, tmp1, out2, out3); \
+ ST_UB(out0, dst + 0); \
+ ST_UB(out1, dst + 16); \
+ ST_UB(out2, dst + 32); \
+ ST_UB(out3, dst + 48); \
+} while (0)
+
+#define STORE8_4(a0, a1, a2, a3, dst) do { \
+ v16u8 tmp0, tmp1, tmp2, tmp3; \
+ ILVR_B2_UB(a1, a0, a3, a2, tmp0, tmp1); \
+ ILVRL_H2_UB(tmp1, tmp0, tmp2, tmp3); \
+ ST_UB(tmp2, dst + 0); \
+ ST_UB(tmp3, dst + 16); \
+} while (0)
+
+#define STORE2_16(a0, a1, dst) do { \
+ v16u8 out0, out1; \
+ ILVRL_B2_UB(a1, a0, out0, out1); \
+ ST_UB(out0, dst + 0); \
+ ST_UB(out1, dst + 16); \
+} while (0)
+
+#define STORE2_8(a0, a1, dst) do { \
+ const v16u8 out0 = (v16u8)__msa_ilvr_b((v16i8)a1, (v16i8)a0); \
+ ST_UB(out0, dst); \
+} while (0)
+
+#define CALC_RGBA4444(y, u, v, out0, out1, N, dst) do { \
+ CALC_RGB##N(y, u, v, R, G, B); \
+ tmp0 = ANDI_B(R, 0xf0); \
+ tmp1 = SRAI_B(G, 4); \
+ RG = tmp0 | tmp1; \
+ tmp0 = ANDI_B(B, 0xf0); \
+ BA = ORI_B(tmp0, 0x0f); \
+ STORE2_##N(out0, out1, dst); \
+} while (0)
+
+#define CALC_RGB565(y, u, v, out0, out1, N, dst) do { \
+ CALC_RGB##N(y, u, v, R, G, B); \
+ tmp0 = ANDI_B(R, 0xf8); \
+ tmp1 = SRAI_B(G, 5); \
+ RG = tmp0 | tmp1; \
+ tmp0 = SLLI_B(G, 3); \
+ tmp1 = ANDI_B(tmp0, 0xe0); \
+ tmp0 = SRAI_B(B, 3); \
+ GB = tmp0 | tmp1; \
+ STORE2_##N(out0, out1, dst); \
+} while (0)
+
+static WEBP_INLINE int Clip8(int v) {
+ return v < 0 ? 0 : v > 255 ? 255 : v;
+}
+
+static void YuvToRgb(int y, int u, int v, uint8_t* const rgb) {
+ const int y1 = MultHi(y, 19077);
+ const int r1 = y1 + MultHi(v, 26149) - 14234;
+ const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708;
+ const int b1 = y1 + MultHi(u, 33050) - 17685;
+ rgb[0] = Clip8(r1 >> 6);
+ rgb[1] = Clip8(g1 >> 6);
+ rgb[2] = Clip8(b1 >> 6);
+}
+
+static void YuvToBgr(int y, int u, int v, uint8_t* const bgr) {
+ const int y1 = MultHi(y, 19077);
+ const int r1 = y1 + MultHi(v, 26149) - 14234;
+ const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708;
+ const int b1 = y1 + MultHi(u, 33050) - 17685;
+ bgr[0] = Clip8(b1 >> 6);
+ bgr[1] = Clip8(g1 >> 6);
+ bgr[2] = Clip8(r1 >> 6);
+}
+
+#if !defined(WEBP_REDUCE_CSP)
+static void YuvToRgb565(int y, int u, int v, uint8_t* const rgb) {
+ const int y1 = MultHi(y, 19077);
+ const int r1 = y1 + MultHi(v, 26149) - 14234;
+ const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708;
+ const int b1 = y1 + MultHi(u, 33050) - 17685;
+ const int r = Clip8(r1 >> 6);
+ const int g = Clip8(g1 >> 6);
+ const int b = Clip8(b1 >> 6);
+ const int rg = (r & 0xf8) | (g >> 5);
+ const int gb = ((g << 3) & 0xe0) | (b >> 3);
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ rgb[0] = gb;
+ rgb[1] = rg;
+#else
+ rgb[0] = rg;
+ rgb[1] = gb;
+#endif
+}
+
+static void YuvToRgba4444(int y, int u, int v, uint8_t* const argb) {
+ const int y1 = MultHi(y, 19077);
+ const int r1 = y1 + MultHi(v, 26149) - 14234;
+ const int g1 = y1 - MultHi(u, 6419) - MultHi(v, 13320) + 8708;
+ const int b1 = y1 + MultHi(u, 33050) - 17685;
+ const int r = Clip8(r1 >> 6);
+ const int g = Clip8(g1 >> 6);
+ const int b = Clip8(b1 >> 6);
+ const int rg = (r & 0xf0) | (g >> 4);
+ const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ argb[0] = ba;
+ argb[1] = rg;
+#else
+ argb[0] = rg;
+ argb[1] = ba;
+#endif
+}
+
+static void YuvToArgb(uint8_t y, uint8_t u, uint8_t v, uint8_t* const argb) {
+ argb[0] = 0xff;
+ YuvToRgb(y, u, v, argb + 1);
+}
+#endif // WEBP_REDUCE_CSP
+
+static void YuvToBgra(uint8_t y, uint8_t u, uint8_t v, uint8_t* const bgra) {
+ YuvToBgr(y, u, v, bgra);
+ bgra[3] = 0xff;
+}
+
+static void YuvToRgba(uint8_t y, uint8_t u, uint8_t v, uint8_t* const rgba) {
+ YuvToRgb(y, u, v, rgba);
+ rgba[3] = 0xff;
+}
+
+#if !defined(WEBP_REDUCE_CSP)
+static void YuvToRgbLine(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst, int length) {
+ v16u8 R, G, B;
+ while (length >= 16) {
+ CALC_RGB16(y, u, v, R, G, B);
+ STORE16_3(R, G, B, dst);
+ y += 16;
+ u += 16;
+ v += 16;
+ dst += 16 * 3;
+ length -= 16;
+ }
+ if (length > 8) {
+ uint8_t temp[3 * 16] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB16(temp, u, v, R, G, B);
+ STORE16_3(R, G, B, temp);
+ memcpy(dst, temp, length * 3 * sizeof(*dst));
+ } else if (length > 0) {
+ uint8_t temp[3 * 8] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB8(temp, u, v, R, G, B);
+ STORE8_3(R, G, B, temp);
+ memcpy(dst, temp, length * 3 * sizeof(*dst));
+ }
+}
+
+static void YuvToBgrLine(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst, int length) {
+ v16u8 R, G, B;
+ while (length >= 16) {
+ CALC_RGB16(y, u, v, R, G, B);
+ STORE16_3(B, G, R, dst);
+ y += 16;
+ u += 16;
+ v += 16;
+ dst += 16 * 3;
+ length -= 16;
+ }
+ if (length > 8) {
+ uint8_t temp[3 * 16] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB16(temp, u, v, R, G, B);
+ STORE16_3(B, G, R, temp);
+ memcpy(dst, temp, length * 3 * sizeof(*dst));
+ } else if (length > 0) {
+ uint8_t temp[3 * 8] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB8(temp, u, v, R, G, B);
+ STORE8_3(B, G, R, temp);
+ memcpy(dst, temp, length * 3 * sizeof(*dst));
+ }
+}
+#endif // WEBP_REDUCE_CSP
+
+static void YuvToRgbaLine(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst, int length) {
+ v16u8 R, G, B;
+ const v16u8 A = (v16u8)__msa_ldi_b(ALPHAVAL);
+ while (length >= 16) {
+ CALC_RGB16(y, u, v, R, G, B);
+ STORE16_4(R, G, B, A, dst);
+ y += 16;
+ u += 16;
+ v += 16;
+ dst += 16 * 4;
+ length -= 16;
+ }
+ if (length > 8) {
+ uint8_t temp[4 * 16] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB16(&temp[0], u, v, R, G, B);
+ STORE16_4(R, G, B, A, temp);
+ memcpy(dst, temp, length * 4 * sizeof(*dst));
+ } else if (length > 0) {
+ uint8_t temp[4 * 8] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB8(temp, u, v, R, G, B);
+ STORE8_4(R, G, B, A, temp);
+ memcpy(dst, temp, length * 4 * sizeof(*dst));
+ }
+}
+
+static void YuvToBgraLine(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst, int length) {
+ v16u8 R, G, B;
+ const v16u8 A = (v16u8)__msa_ldi_b(ALPHAVAL);
+ while (length >= 16) {
+ CALC_RGB16(y, u, v, R, G, B);
+ STORE16_4(B, G, R, A, dst);
+ y += 16;
+ u += 16;
+ v += 16;
+ dst += 16 * 4;
+ length -= 16;
+ }
+ if (length > 8) {
+ uint8_t temp[4 * 16] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB16(temp, u, v, R, G, B);
+ STORE16_4(B, G, R, A, temp);
+ memcpy(dst, temp, length * 4 * sizeof(*dst));
+ } else if (length > 0) {
+ uint8_t temp[4 * 8] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB8(temp, u, v, R, G, B);
+ STORE8_4(B, G, R, A, temp);
+ memcpy(dst, temp, length * 4 * sizeof(*dst));
+ }
+}
+
+#if !defined(WEBP_REDUCE_CSP)
+static void YuvToArgbLine(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst, int length) {
+ v16u8 R, G, B;
+ const v16u8 A = (v16u8)__msa_ldi_b(ALPHAVAL);
+ while (length >= 16) {
+ CALC_RGB16(y, u, v, R, G, B);
+ STORE16_4(A, R, G, B, dst);
+ y += 16;
+ u += 16;
+ v += 16;
+ dst += 16 * 4;
+ length -= 16;
+ }
+ if (length > 8) {
+ uint8_t temp[4 * 16] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB16(temp, u, v, R, G, B);
+ STORE16_4(A, R, G, B, temp);
+ memcpy(dst, temp, length * 4 * sizeof(*dst));
+ } else if (length > 0) {
+ uint8_t temp[4 * 8] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+ CALC_RGB8(temp, u, v, R, G, B);
+ STORE8_4(A, R, G, B, temp);
+ memcpy(dst, temp, length * 4 * sizeof(*dst));
+ }
+}
+
+static void YuvToRgba4444Line(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst, int length) {
+ v16u8 R, G, B, RG, BA, tmp0, tmp1;
+ while (length >= 16) {
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ CALC_RGBA4444(y, u, v, BA, RG, 16, dst);
+#else
+ CALC_RGBA4444(y, u, v, RG, BA, 16, dst);
+#endif
+ y += 16;
+ u += 16;
+ v += 16;
+ dst += 16 * 2;
+ length -= 16;
+ }
+ if (length > 8) {
+ uint8_t temp[2 * 16] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ CALC_RGBA4444(temp, u, v, BA, RG, 16, temp);
+#else
+ CALC_RGBA4444(temp, u, v, RG, BA, 16, temp);
+#endif
+ memcpy(dst, temp, length * 2 * sizeof(*dst));
+ } else if (length > 0) {
+ uint8_t temp[2 * 8] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ CALC_RGBA4444(temp, u, v, BA, RG, 8, temp);
+#else
+ CALC_RGBA4444(temp, u, v, RG, BA, 8, temp);
+#endif
+ memcpy(dst, temp, length * 2 * sizeof(*dst));
+ }
+}
+
+static void YuvToRgb565Line(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst, int length) {
+ v16u8 R, G, B, RG, GB, tmp0, tmp1;
+ while (length >= 16) {
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ CALC_RGB565(y, u, v, GB, RG, 16, dst);
+#else
+ CALC_RGB565(y, u, v, RG, GB, 16, dst);
+#endif
+ y += 16;
+ u += 16;
+ v += 16;
+ dst += 16 * 2;
+ length -= 16;
+ }
+ if (length > 8) {
+ uint8_t temp[2 * 16] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ CALC_RGB565(temp, u, v, GB, RG, 16, temp);
+#else
+ CALC_RGB565(temp, u, v, RG, GB, 16, temp);
+#endif
+ memcpy(dst, temp, length * 2 * sizeof(*dst));
+ } else if (length > 0) {
+ uint8_t temp[2 * 8] = { 0 };
+ memcpy(temp, y, length * sizeof(*temp));
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ CALC_RGB565(temp, u, v, GB, RG, 8, temp);
+#else
+ CALC_RGB565(temp, u, v, RG, GB, 8, temp);
+#endif
+ memcpy(dst, temp, length * 2 * sizeof(*dst));
+ }
+}
+#endif // WEBP_REDUCE_CSP
+
+#define UPSAMPLE_32PIXELS(a, b, c, d) do { \
+ v16u8 s = __msa_aver_u_b(a, d); \
+ v16u8 t = __msa_aver_u_b(b, c); \
+ const v16u8 st = s ^ t; \
+ v16u8 ad = a ^ d; \
+ v16u8 bc = b ^ c; \
+ v16u8 t0 = ad | bc; \
+ v16u8 t1 = t0 | st; \
+ v16u8 t2 = ANDI_B(t1, 1); \
+ v16u8 t3 = __msa_aver_u_b(s, t); \
+ const v16u8 k = t3 - t2; \
+ v16u8 diag1, diag2; \
+ AVER_UB2_UB(t, k, s, k, t0, t1); \
+ bc = bc & st; \
+ ad = ad & st; \
+ t = t ^ k; \
+ s = s ^ k; \
+ t2 = bc | t; \
+ t3 = ad | s; \
+ t2 = ANDI_B(t2, 1); \
+ t3 = ANDI_B(t3, 1); \
+ SUB2(t0, t2, t1, t3, diag1, diag2); \
+ AVER_UB2_UB(a, diag1, b, diag2, t0, t1); \
+ ILVRL_B2_UB(t1, t0, a, b); \
+ if (pbot_y != NULL) { \
+ AVER_UB2_UB(c, diag2, d, diag1, t0, t1); \
+ ILVRL_B2_UB(t1, t0, c, d); \
+ } \
+} while (0)
+
+#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \
+ const uint8_t* top_u, const uint8_t* top_v, \
+ const uint8_t* cur_u, const uint8_t* cur_v, \
+ uint8_t* top_dst, uint8_t* bot_dst, int len) \
+{ \
+ int size = (len - 1) >> 1; \
+ uint8_t temp_u[64]; \
+ uint8_t temp_v[64]; \
+ const uint32_t tl_uv = ((top_u[0]) | ((top_v[0]) << 16)); \
+ const uint32_t l_uv = ((cur_u[0]) | ((cur_v[0]) << 16)); \
+ const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
+ const uint8_t* ptop_y = &top_y[1]; \
+ uint8_t *ptop_dst = top_dst + XSTEP; \
+ const uint8_t* pbot_y = &bot_y[1]; \
+ uint8_t *pbot_dst = bot_dst + XSTEP; \
+ \
+ FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \
+ if (bot_y != NULL) { \
+ const uint32_t uv1 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
+ FUNC(bot_y[0], uv1 & 0xff, (uv1 >> 16), bot_dst); \
+ } \
+ while (size >= 16) { \
+ v16u8 tu0, tu1, tv0, tv1, cu0, cu1, cv0, cv1; \
+ LD_UB2(top_u, 1, tu0, tu1); \
+ LD_UB2(cur_u, 1, cu0, cu1); \
+ LD_UB2(top_v, 1, tv0, tv1); \
+ LD_UB2(cur_v, 1, cv0, cv1); \
+ UPSAMPLE_32PIXELS(tu0, tu1, cu0, cu1); \
+ UPSAMPLE_32PIXELS(tv0, tv1, cv0, cv1); \
+ ST_UB4(tu0, tu1, cu0, cu1, &temp_u[0], 16); \
+ ST_UB4(tv0, tv1, cv0, cv1, &temp_v[0], 16); \
+ FUNC##Line(ptop_y, &temp_u[ 0], &temp_v[0], ptop_dst, 32); \
+ if (bot_y != NULL) { \
+ FUNC##Line(pbot_y, &temp_u[32], &temp_v[32], pbot_dst, 32); \
+ } \
+ ptop_y += 32; \
+ pbot_y += 32; \
+ ptop_dst += XSTEP * 32; \
+ pbot_dst += XSTEP * 32; \
+ top_u += 16; \
+ top_v += 16; \
+ cur_u += 16; \
+ cur_v += 16; \
+ size -= 16; \
+ } \
+ if (size > 0) { \
+ v16u8 tu0, tu1, tv0, tv1, cu0, cu1, cv0, cv1; \
+ memcpy(&temp_u[ 0], top_u, 17 * sizeof(uint8_t)); \
+ memcpy(&temp_u[32], cur_u, 17 * sizeof(uint8_t)); \
+ memcpy(&temp_v[ 0], top_v, 17 * sizeof(uint8_t)); \
+ memcpy(&temp_v[32], cur_v, 17 * sizeof(uint8_t)); \
+ LD_UB2(&temp_u[ 0], 1, tu0, tu1); \
+ LD_UB2(&temp_u[32], 1, cu0, cu1); \
+ LD_UB2(&temp_v[ 0], 1, tv0, tv1); \
+ LD_UB2(&temp_v[32], 1, cv0, cv1); \
+ UPSAMPLE_32PIXELS(tu0, tu1, cu0, cu1); \
+ UPSAMPLE_32PIXELS(tv0, tv1, cv0, cv1); \
+ ST_UB4(tu0, tu1, cu0, cu1, &temp_u[0], 16); \
+ ST_UB4(tv0, tv1, cv0, cv1, &temp_v[0], 16); \
+ FUNC##Line(ptop_y, &temp_u[ 0], &temp_v[0], ptop_dst, size * 2); \
+ if (bot_y != NULL) { \
+ FUNC##Line(pbot_y, &temp_u[32], &temp_v[32], pbot_dst, size * 2); \
+ } \
+ top_u += size; \
+ top_v += size; \
+ cur_u += size; \
+ cur_v += size; \
+ } \
+ if (!(len & 1)) { \
+ const uint32_t t0 = ((top_u[0]) | ((top_v[0]) << 16)); \
+ const uint32_t c0 = ((cur_u[0]) | ((cur_v[0]) << 16)); \
+ const uint32_t tmp0 = (3 * t0 + c0 + 0x00020002u) >> 2; \
+ FUNC(top_y[len - 1], tmp0 & 0xff, (tmp0 >> 16), \
+ top_dst + (len - 1) * XSTEP); \
+ if (bot_y != NULL) { \
+ const uint32_t tmp1 = (3 * c0 + t0 + 0x00020002u) >> 2; \
+ FUNC(bot_y[len - 1], tmp1 & 0xff, (tmp1 >> 16), \
+ bot_dst + (len - 1) * XSTEP); \
+ } \
+ } \
+}
+
+UPSAMPLE_FUNC(UpsampleRgbaLinePair, YuvToRgba, 4)
+UPSAMPLE_FUNC(UpsampleBgraLinePair, YuvToBgra, 4)
+#if !defined(WEBP_REDUCE_CSP)
+UPSAMPLE_FUNC(UpsampleRgbLinePair, YuvToRgb, 3)
+UPSAMPLE_FUNC(UpsampleBgrLinePair, YuvToBgr, 3)
+UPSAMPLE_FUNC(UpsampleArgbLinePair, YuvToArgb, 4)
+UPSAMPLE_FUNC(UpsampleRgba4444LinePair, YuvToRgba4444, 2)
+UPSAMPLE_FUNC(UpsampleRgb565LinePair, YuvToRgb565, 2)
+#endif // WEBP_REDUCE_CSP
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
+
+extern void WebPInitUpsamplersMSA(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersMSA(void) {
+ WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair;
+ WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair;
+ WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair;
+ WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair;
+#if !defined(WEBP_REDUCE_CSP)
+ WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair;
+ WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair;
+ WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair;
+ WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair;
+ WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair;
+ WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair;
+ WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair;
+#endif // WEBP_REDUCE_CSP
+}
+
+#endif // FANCY_UPSAMPLING
+
+#endif // WEBP_USE_MSA
+
+#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_MSA))
+WEBP_DSP_INIT_STUB(WebPInitUpsamplersMSA)
+#endif
diff --git a/src/third_party/libwebp/src/dsp/upsampling_neon.c b/src/third_party/libwebp/src/dsp/upsampling_neon.c
new file mode 100644
index 0000000..ff05bab
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/upsampling_neon.c
@@ -0,0 +1,291 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// NEON version of YUV to RGB upsampling functions.
+//
+// Author: mans@mansr.com (Mans Rullgard)
+// Based on SSE code by: somnath@google.com (Somnath Banerjee)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_NEON)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <string.h>
+#endif
+
+#include <arm_neon.h>
+#include "src/dsp/neon.h"
+#include "src/dsp/yuv.h"
+
+#ifdef FANCY_UPSAMPLING
+
+//-----------------------------------------------------------------------------
+// U/V upsampling
+
+// Loads 9 pixels each from rows r1 and r2 and generates 16 pixels.
+#define UPSAMPLE_16PIXELS(r1, r2, out) do { \
+ const uint8x8_t a = vld1_u8(r1 + 0); \
+ const uint8x8_t b = vld1_u8(r1 + 1); \
+ const uint8x8_t c = vld1_u8(r2 + 0); \
+ const uint8x8_t d = vld1_u8(r2 + 1); \
+ /* a + b + c + d */ \
+ const uint16x8_t ad = vaddl_u8(a, d); \
+ const uint16x8_t bc = vaddl_u8(b, c); \
+ const uint16x8_t abcd = vaddq_u16(ad, bc); \
+ /* 3a + b + c + 3d */ \
+ const uint16x8_t al = vaddq_u16(abcd, vshlq_n_u16(ad, 1)); \
+ /* a + 3b + 3c + d */ \
+ const uint16x8_t bl = vaddq_u16(abcd, vshlq_n_u16(bc, 1)); \
+ \
+ const uint8x8_t diag2 = vshrn_n_u16(al, 3); \
+ const uint8x8_t diag1 = vshrn_n_u16(bl, 3); \
+ \
+ const uint8x8_t A = vrhadd_u8(a, diag1); \
+ const uint8x8_t B = vrhadd_u8(b, diag2); \
+ const uint8x8_t C = vrhadd_u8(c, diag2); \
+ const uint8x8_t D = vrhadd_u8(d, diag1); \
+ \
+ uint8x8x2_t A_B, C_D; \
+ INIT_VECTOR2(A_B, A, B); \
+ INIT_VECTOR2(C_D, C, D); \
+ vst2_u8(out + 0, A_B); \
+ vst2_u8(out + 32, C_D); \
+} while (0)
+
+// Turn the macro into a function for reducing code-size when non-critical
+static void Upsample16Pixels_NEON(const uint8_t *r1, const uint8_t *r2,
+ uint8_t *out) {
+ UPSAMPLE_16PIXELS(r1, r2, out);
+}
+
+#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \
+ uint8_t r1[9], r2[9]; \
+ memcpy(r1, (tb), (num_pixels)); \
+ memcpy(r2, (bb), (num_pixels)); \
+ /* replicate last byte */ \
+ memset(r1 + (num_pixels), r1[(num_pixels) - 1], 9 - (num_pixels)); \
+ memset(r2 + (num_pixels), r2[(num_pixels) - 1], 9 - (num_pixels)); \
+ Upsample16Pixels_NEON(r1, r2, out); \
+}
+
+//-----------------------------------------------------------------------------
+// YUV->RGB conversion
+
+// note: we represent the 33050 large constant as 32768 + 282
+static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 };
+
+#define v255 vdup_n_u8(255)
+
+#define STORE_Rgb(out, r, g, b) do { \
+ uint8x8x3_t r_g_b; \
+ INIT_VECTOR3(r_g_b, r, g, b); \
+ vst3_u8(out, r_g_b); \
+} while (0)
+
+#define STORE_Bgr(out, r, g, b) do { \
+ uint8x8x3_t b_g_r; \
+ INIT_VECTOR3(b_g_r, b, g, r); \
+ vst3_u8(out, b_g_r); \
+} while (0)
+
+#define STORE_Rgba(out, r, g, b) do { \
+ uint8x8x4_t r_g_b_v255; \
+ INIT_VECTOR4(r_g_b_v255, r, g, b, v255); \
+ vst4_u8(out, r_g_b_v255); \
+} while (0)
+
+#define STORE_Bgra(out, r, g, b) do { \
+ uint8x8x4_t b_g_r_v255; \
+ INIT_VECTOR4(b_g_r_v255, b, g, r, v255); \
+ vst4_u8(out, b_g_r_v255); \
+} while (0)
+
+#define STORE_Argb(out, r, g, b) do { \
+ uint8x8x4_t v255_r_g_b; \
+ INIT_VECTOR4(v255_r_g_b, v255, r, g, b); \
+ vst4_u8(out, v255_r_g_b); \
+} while (0)
+
+#if !defined(WEBP_SWAP_16BIT_CSP)
+#define ZIP_U8(lo, hi) vzip_u8((lo), (hi))
+#else
+#define ZIP_U8(lo, hi) vzip_u8((hi), (lo))
+#endif
+
+#define STORE_Rgba4444(out, r, g, b) do { \
+ const uint8x8_t rg = vsri_n_u8(r, g, 4); /* shift g, insert r */ \
+ const uint8x8_t ba = vsri_n_u8(b, v255, 4); /* shift a, insert b */ \
+ const uint8x8x2_t rgba4444 = ZIP_U8(rg, ba); \
+ vst1q_u8(out, vcombine_u8(rgba4444.val[0], rgba4444.val[1])); \
+} while (0)
+
+#define STORE_Rgb565(out, r, g, b) do { \
+ const uint8x8_t rg = vsri_n_u8(r, g, 5); /* shift g and insert r */ \
+ const uint8x8_t g1 = vshl_n_u8(g, 3); /* pre-shift g: 3bits */ \
+ const uint8x8_t gb = vsri_n_u8(g1, b, 3); /* shift b and insert g */ \
+ const uint8x8x2_t rgb565 = ZIP_U8(rg, gb); \
+ vst1q_u8(out, vcombine_u8(rgb565.val[0], rgb565.val[1])); \
+} while (0)
+
+#define CONVERT8(FMT, XSTEP, N, src_y, src_uv, out, cur_x) do { \
+ int i; \
+ for (i = 0; i < N; i += 8) { \
+ const int off = ((cur_x) + i) * XSTEP; \
+ const uint8x8_t y = vld1_u8((src_y) + (cur_x) + i); \
+ const uint8x8_t u = vld1_u8((src_uv) + i + 0); \
+ const uint8x8_t v = vld1_u8((src_uv) + i + 16); \
+ const int16x8_t Y0 = vreinterpretq_s16_u16(vshll_n_u8(y, 7)); \
+ const int16x8_t U0 = vreinterpretq_s16_u16(vshll_n_u8(u, 7)); \
+ const int16x8_t V0 = vreinterpretq_s16_u16(vshll_n_u8(v, 7)); \
+ const int16x8_t Y1 = vqdmulhq_lane_s16(Y0, coeff1, 0); \
+ const int16x8_t R0 = vqdmulhq_lane_s16(V0, coeff1, 1); \
+ const int16x8_t G0 = vqdmulhq_lane_s16(U0, coeff1, 2); \
+ const int16x8_t G1 = vqdmulhq_lane_s16(V0, coeff1, 3); \
+ const int16x8_t B0 = vqdmulhq_n_s16(U0, 282); \
+ const int16x8_t R1 = vqaddq_s16(Y1, R_Rounder); \
+ const int16x8_t G2 = vqaddq_s16(Y1, G_Rounder); \
+ const int16x8_t B1 = vqaddq_s16(Y1, B_Rounder); \
+ const int16x8_t R2 = vqaddq_s16(R0, R1); \
+ const int16x8_t G3 = vqaddq_s16(G0, G1); \
+ const int16x8_t B2 = vqaddq_s16(B0, B1); \
+ const int16x8_t G4 = vqsubq_s16(G2, G3); \
+ const int16x8_t B3 = vqaddq_s16(B2, U0); \
+ const uint8x8_t R = vqshrun_n_s16(R2, YUV_FIX2); \
+ const uint8x8_t G = vqshrun_n_s16(G4, YUV_FIX2); \
+ const uint8x8_t B = vqshrun_n_s16(B3, YUV_FIX2); \
+ STORE_ ## FMT(out + off, R, G, B); \
+ } \
+} while (0)
+
+#define CONVERT1(FUNC, XSTEP, N, src_y, src_uv, rgb, cur_x) { \
+ int i; \
+ for (i = 0; i < N; i++) { \
+ const int off = ((cur_x) + i) * XSTEP; \
+ const int y = src_y[(cur_x) + i]; \
+ const int u = (src_uv)[i]; \
+ const int v = (src_uv)[i + 16]; \
+ FUNC(y, u, v, rgb + off); \
+ } \
+}
+
+#define CONVERT2RGB_8(FMT, XSTEP, top_y, bottom_y, uv, \
+ top_dst, bottom_dst, cur_x, len) { \
+ CONVERT8(FMT, XSTEP, len, top_y, uv, top_dst, cur_x); \
+ if (bottom_y != NULL) { \
+ CONVERT8(FMT, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x); \
+ } \
+}
+
+#define CONVERT2RGB_1(FUNC, XSTEP, top_y, bottom_y, uv, \
+ top_dst, bottom_dst, cur_x, len) { \
+ CONVERT1(FUNC, XSTEP, len, top_y, uv, top_dst, cur_x); \
+ if (bottom_y != NULL) { \
+ CONVERT1(FUNC, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x); \
+ } \
+}
+
+#define NEON_UPSAMPLE_FUNC(FUNC_NAME, FMT, XSTEP) \
+static void FUNC_NAME(const uint8_t *top_y, const uint8_t *bottom_y, \
+ const uint8_t *top_u, const uint8_t *top_v, \
+ const uint8_t *cur_u, const uint8_t *cur_v, \
+ uint8_t *top_dst, uint8_t *bottom_dst, int len) { \
+ int block; \
+ /* 16 byte aligned array to cache reconstructed u and v */ \
+ uint8_t uv_buf[2 * 32 + 15]; \
+ uint8_t *const r_uv = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
+ const int uv_len = (len + 1) >> 1; \
+ /* 9 pixels must be read-able for each block */ \
+ const int num_blocks = (uv_len - 1) >> 3; \
+ const int leftover = uv_len - num_blocks * 8; \
+ const int last_pos = 1 + 16 * num_blocks; \
+ \
+ const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
+ const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
+ \
+ const int16x4_t coeff1 = vld1_s16(kCoeffs1); \
+ const int16x8_t R_Rounder = vdupq_n_s16(-14234); \
+ const int16x8_t G_Rounder = vdupq_n_s16(8708); \
+ const int16x8_t B_Rounder = vdupq_n_s16(-17685); \
+ \
+ /* Treat the first pixel in regular way */ \
+ assert(top_y != NULL); \
+ { \
+ const int u0 = (top_u[0] + u_diag) >> 1; \
+ const int v0 = (top_v[0] + v_diag) >> 1; \
+ VP8YuvTo ## FMT(top_y[0], u0, v0, top_dst); \
+ } \
+ if (bottom_y != NULL) { \
+ const int u0 = (cur_u[0] + u_diag) >> 1; \
+ const int v0 = (cur_v[0] + v_diag) >> 1; \
+ VP8YuvTo ## FMT(bottom_y[0], u0, v0, bottom_dst); \
+ } \
+ \
+ for (block = 0; block < num_blocks; ++block) { \
+ UPSAMPLE_16PIXELS(top_u, cur_u, r_uv); \
+ UPSAMPLE_16PIXELS(top_v, cur_v, r_uv + 16); \
+ CONVERT2RGB_8(FMT, XSTEP, top_y, bottom_y, r_uv, \
+ top_dst, bottom_dst, 16 * block + 1, 16); \
+ top_u += 8; \
+ cur_u += 8; \
+ top_v += 8; \
+ cur_v += 8; \
+ } \
+ \
+ UPSAMPLE_LAST_BLOCK(top_u, cur_u, leftover, r_uv); \
+ UPSAMPLE_LAST_BLOCK(top_v, cur_v, leftover, r_uv + 16); \
+ CONVERT2RGB_1(VP8YuvTo ## FMT, XSTEP, top_y, bottom_y, r_uv, \
+ top_dst, bottom_dst, last_pos, len - last_pos); \
+}
+
+// NEON variants of the fancy upsampler.
+NEON_UPSAMPLE_FUNC(UpsampleRgbaLinePair_NEON, Rgba, 4)
+NEON_UPSAMPLE_FUNC(UpsampleBgraLinePair_NEON, Bgra, 4)
+#if !defined(WEBP_REDUCE_CSP)
+NEON_UPSAMPLE_FUNC(UpsampleRgbLinePair_NEON, Rgb, 3)
+NEON_UPSAMPLE_FUNC(UpsampleBgrLinePair_NEON, Bgr, 3)
+NEON_UPSAMPLE_FUNC(UpsampleArgbLinePair_NEON, Argb, 4)
+NEON_UPSAMPLE_FUNC(UpsampleRgba4444LinePair_NEON, Rgba4444, 2)
+NEON_UPSAMPLE_FUNC(UpsampleRgb565LinePair_NEON, Rgb565, 2)
+#endif // WEBP_REDUCE_CSP
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
+
+extern void WebPInitUpsamplersNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersNEON(void) {
+ WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair_NEON;
+ WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair_NEON;
+ WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair_NEON;
+ WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair_NEON;
+#if !defined(WEBP_REDUCE_CSP)
+ WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_NEON;
+ WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_NEON;
+ WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair_NEON;
+ WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair_NEON;
+ WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair_NEON;
+ WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair_NEON;
+ WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair_NEON;
+#endif // WEBP_REDUCE_CSP
+}
+
+#endif // FANCY_UPSAMPLING
+
+#endif // WEBP_USE_NEON
+
+#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_NEON))
+WEBP_DSP_INIT_STUB(WebPInitUpsamplersNEON)
+#endif
diff --git a/src/third_party/libwebp/src/dsp/upsampling_sse2.c b/src/third_party/libwebp/src/dsp/upsampling_sse2.c
new file mode 100644
index 0000000..45a3746
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/upsampling_sse2.c
@@ -0,0 +1,273 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE2 version of YUV to RGB upsampling functions.
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <string.h>
+#endif
+
+#include <emmintrin.h>
+#include "src/dsp/yuv.h"
+
+#ifdef FANCY_UPSAMPLING
+
+// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows
+// u = (9*a + 3*b + 3*c + d + 8) / 16
+// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2
+// = (a + m + 1) / 2
+// where m = (a + 3*b + 3*c + d) / 8
+// = ((a + b + c + d) / 2 + b + c) / 4
+//
+// Let's say k = (a + b + c + d) / 4.
+// We can compute k as
+// k = (s + t + 1) / 2 - ((a^d) | (b^c) | (s^t)) & 1
+// where s = (a + d + 1) / 2 and t = (b + c + 1) / 2
+//
+// Then m can be written as
+// m = (k + t + 1) / 2 - (((b^c) & (s^t)) | (k^t)) & 1
+
+// Computes out = (k + in + 1) / 2 - ((ij & (s^t)) | (k^in)) & 1
+#define GET_M(ij, in, out) do { \
+ const __m128i tmp0 = _mm_avg_epu8(k, (in)); /* (k + in + 1) / 2 */ \
+ const __m128i tmp1 = _mm_and_si128((ij), st); /* (ij) & (s^t) */ \
+ const __m128i tmp2 = _mm_xor_si128(k, (in)); /* (k^in) */ \
+ const __m128i tmp3 = _mm_or_si128(tmp1, tmp2); /* ((ij) & (s^t)) | (k^in) */\
+ const __m128i tmp4 = _mm_and_si128(tmp3, one); /* & 1 -> lsb_correction */ \
+ (out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \
+} while (0)
+
+// pack and store two alternating pixel rows
+#define PACK_AND_STORE(a, b, da, db, out) do { \
+ const __m128i t_a = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \
+ const __m128i t_b = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \
+ const __m128i t_1 = _mm_unpacklo_epi8(t_a, t_b); \
+ const __m128i t_2 = _mm_unpackhi_epi8(t_a, t_b); \
+ _mm_store_si128(((__m128i*)(out)) + 0, t_1); \
+ _mm_store_si128(((__m128i*)(out)) + 1, t_2); \
+} while (0)
+
+// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
+#define UPSAMPLE_32PIXELS(r1, r2, out) { \
+ const __m128i one = _mm_set1_epi8(1); \
+ const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
+ const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
+ const __m128i c = _mm_loadu_si128((const __m128i*)&(r2)[0]); \
+ const __m128i d = _mm_loadu_si128((const __m128i*)&(r2)[1]); \
+ \
+ const __m128i s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \
+ const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \
+ const __m128i st = _mm_xor_si128(s, t); /* st = s^t */ \
+ \
+ const __m128i ad = _mm_xor_si128(a, d); /* ad = a^d */ \
+ const __m128i bc = _mm_xor_si128(b, c); /* bc = b^c */ \
+ \
+ const __m128i t1 = _mm_or_si128(ad, bc); /* (a^d) | (b^c) */ \
+ const __m128i t2 = _mm_or_si128(t1, st); /* (a^d) | (b^c) | (s^t) */ \
+ const __m128i t3 = _mm_and_si128(t2, one); /* (a^d) | (b^c) | (s^t) & 1 */ \
+ const __m128i t4 = _mm_avg_epu8(s, t); \
+ const __m128i k = _mm_sub_epi8(t4, t3); /* k = (a + b + c + d) / 4 */ \
+ __m128i diag1, diag2; \
+ \
+ GET_M(bc, t, diag1); /* diag1 = (a + 3b + 3c + d) / 8 */ \
+ GET_M(ad, s, diag2); /* diag2 = (3a + b + c + 3d) / 8 */ \
+ \
+ /* pack the alternate pixels */ \
+ PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \
+ PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \
+}
+
+// Turn the macro into a function for reducing code-size when non-critical
+static void Upsample32Pixels_SSE2(const uint8_t r1[], const uint8_t r2[],
+ uint8_t* const out) {
+ UPSAMPLE_32PIXELS(r1, r2, out);
+}
+
+#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \
+ uint8_t r1[17], r2[17]; \
+ memcpy(r1, (tb), (num_pixels)); \
+ memcpy(r2, (bb), (num_pixels)); \
+ /* replicate last byte */ \
+ memset(r1 + (num_pixels), r1[(num_pixels) - 1], 17 - (num_pixels)); \
+ memset(r2 + (num_pixels), r2[(num_pixels) - 1], 17 - (num_pixels)); \
+ /* using the shared function instead of the macro saves ~3k code size */ \
+ Upsample32Pixels_SSE2(r1, r2, out); \
+}
+
+#define CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, \
+ top_dst, bottom_dst, cur_x) do { \
+ FUNC##32_SSE2((top_y) + (cur_x), r_u, r_v, (top_dst) + (cur_x) * (XSTEP)); \
+ if ((bottom_y) != NULL) { \
+ FUNC##32_SSE2((bottom_y) + (cur_x), r_u + 64, r_v + 64, \
+ (bottom_dst) + (cur_x) * (XSTEP)); \
+ } \
+} while (0)
+
+#define SSE2_UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
+ const uint8_t* top_u, const uint8_t* top_v, \
+ const uint8_t* cur_u, const uint8_t* cur_v, \
+ uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
+ int uv_pos, pos; \
+ /* 16byte-aligned array to cache reconstructed u and v */ \
+ uint8_t uv_buf[14 * 32 + 15] = { 0 }; \
+ uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
+ uint8_t* const r_v = r_u + 32; \
+ \
+ assert(top_y != NULL); \
+ { /* Treat the first pixel in regular way */ \
+ const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
+ const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
+ const int u0_t = (top_u[0] + u_diag) >> 1; \
+ const int v0_t = (top_v[0] + v_diag) >> 1; \
+ FUNC(top_y[0], u0_t, v0_t, top_dst); \
+ if (bottom_y != NULL) { \
+ const int u0_b = (cur_u[0] + u_diag) >> 1; \
+ const int v0_b = (cur_v[0] + v_diag) >> 1; \
+ FUNC(bottom_y[0], u0_b, v0_b, bottom_dst); \
+ } \
+ } \
+ /* For UPSAMPLE_32PIXELS, 17 u/v values must be read-able for each block */ \
+ for (pos = 1, uv_pos = 0; pos + 32 + 1 <= len; pos += 32, uv_pos += 16) { \
+ UPSAMPLE_32PIXELS(top_u + uv_pos, cur_u + uv_pos, r_u); \
+ UPSAMPLE_32PIXELS(top_v + uv_pos, cur_v + uv_pos, r_v); \
+ CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, pos); \
+ } \
+ if (len > 1) { \
+ const int left_over = ((len + 1) >> 1) - (pos >> 1); \
+ uint8_t* const tmp_top_dst = r_u + 4 * 32; \
+ uint8_t* const tmp_bottom_dst = tmp_top_dst + 4 * 32; \
+ uint8_t* const tmp_top = tmp_bottom_dst + 4 * 32; \
+ uint8_t* const tmp_bottom = (bottom_y == NULL) ? NULL : tmp_top + 32; \
+ assert(left_over > 0); \
+ UPSAMPLE_LAST_BLOCK(top_u + uv_pos, cur_u + uv_pos, left_over, r_u); \
+ UPSAMPLE_LAST_BLOCK(top_v + uv_pos, cur_v + uv_pos, left_over, r_v); \
+ memcpy(tmp_top, top_y + pos, len - pos); \
+ if (bottom_y != NULL) memcpy(tmp_bottom, bottom_y + pos, len - pos); \
+ CONVERT2RGB_32(FUNC, XSTEP, tmp_top, tmp_bottom, tmp_top_dst, \
+ tmp_bottom_dst, 0); \
+ memcpy(top_dst + pos * (XSTEP), tmp_top_dst, (len - pos) * (XSTEP)); \
+ if (bottom_y != NULL) { \
+ memcpy(bottom_dst + pos * (XSTEP), tmp_bottom_dst, \
+ (len - pos) * (XSTEP)); \
+ } \
+ } \
+}
+
+// SSE2 variants of the fancy upsampler.
+SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePair_SSE2, VP8YuvToRgba, 4)
+SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePair_SSE2, VP8YuvToBgra, 4)
+
+#if !defined(WEBP_REDUCE_CSP)
+SSE2_UPSAMPLE_FUNC(UpsampleRgbLinePair_SSE2, VP8YuvToRgb, 3)
+SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePair_SSE2, VP8YuvToBgr, 3)
+SSE2_UPSAMPLE_FUNC(UpsampleArgbLinePair_SSE2, VP8YuvToArgb, 4)
+SSE2_UPSAMPLE_FUNC(UpsampleRgba4444LinePair_SSE2, VP8YuvToRgba4444, 2)
+SSE2_UPSAMPLE_FUNC(UpsampleRgb565LinePair_SSE2, VP8YuvToRgb565, 2)
+#endif // WEBP_REDUCE_CSP
+
+#undef GET_M
+#undef PACK_AND_STORE
+#undef UPSAMPLE_32PIXELS
+#undef UPSAMPLE_LAST_BLOCK
+#undef CONVERT2RGB
+#undef CONVERT2RGB_32
+#undef SSE2_UPSAMPLE_FUNC
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
+
+extern void WebPInitUpsamplersSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersSSE2(void) {
+ WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair_SSE2;
+ WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair_SSE2;
+ WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair_SSE2;
+ WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair_SSE2;
+#if !defined(WEBP_REDUCE_CSP)
+ WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_SSE2;
+ WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_SSE2;
+ WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair_SSE2;
+ WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair_SSE2;
+ WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair_SSE2;
+ WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair_SSE2;
+ WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair_SSE2;
+#endif // WEBP_REDUCE_CSP
+}
+
+#endif // FANCY_UPSAMPLING
+
+//------------------------------------------------------------------------------
+
+extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
+extern void WebPInitYUV444ConvertersSSE2(void);
+
+#define YUV444_FUNC(FUNC_NAME, CALL, CALL_C, XSTEP) \
+extern void CALL_C(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len); \
+static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ int i; \
+ const int max_len = len & ~31; \
+ for (i = 0; i < max_len; i += 32) { \
+ CALL(y + i, u + i, v + i, dst + i * (XSTEP)); \
+ } \
+ if (i < len) { /* C-fallback */ \
+ CALL_C(y + i, u + i, v + i, dst + i * (XSTEP), len - i); \
+ } \
+}
+
+YUV444_FUNC(Yuv444ToRgba_SSE2, VP8YuvToRgba32_SSE2, WebPYuv444ToRgba_C, 4);
+YUV444_FUNC(Yuv444ToBgra_SSE2, VP8YuvToBgra32_SSE2, WebPYuv444ToBgra_C, 4);
+#if !defined(WEBP_REDUCE_CSP)
+YUV444_FUNC(Yuv444ToRgb_SSE2, VP8YuvToRgb32_SSE2, WebPYuv444ToRgb_C, 3);
+YUV444_FUNC(Yuv444ToBgr_SSE2, VP8YuvToBgr32_SSE2, WebPYuv444ToBgr_C, 3);
+YUV444_FUNC(Yuv444ToArgb_SSE2, VP8YuvToArgb32_SSE2, WebPYuv444ToArgb_C, 4)
+YUV444_FUNC(Yuv444ToRgba4444_SSE2, VP8YuvToRgba444432_SSE2, \
+ WebPYuv444ToRgba4444_C, 2)
+YUV444_FUNC(Yuv444ToRgb565_SSE2, VP8YuvToRgb56532_SSE2, WebPYuv444ToRgb565_C, 2)
+#endif // WEBP_REDUCE_CSP
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE2(void) {
+ WebPYUV444Converters[MODE_RGBA] = Yuv444ToRgba_SSE2;
+ WebPYUV444Converters[MODE_BGRA] = Yuv444ToBgra_SSE2;
+ WebPYUV444Converters[MODE_rgbA] = Yuv444ToRgba_SSE2;
+ WebPYUV444Converters[MODE_bgrA] = Yuv444ToBgra_SSE2;
+#if !defined(WEBP_REDUCE_CSP)
+ WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb_SSE2;
+ WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr_SSE2;
+ WebPYUV444Converters[MODE_ARGB] = Yuv444ToArgb_SSE2;
+ WebPYUV444Converters[MODE_RGBA_4444] = Yuv444ToRgba4444_SSE2;
+ WebPYUV444Converters[MODE_RGB_565] = Yuv444ToRgb565_SSE2;
+ WebPYUV444Converters[MODE_Argb] = Yuv444ToArgb_SSE2;
+ WebPYUV444Converters[MODE_rgbA_4444] = Yuv444ToRgba4444_SSE2;
+#endif // WEBP_REDUCE_CSP
+}
+
+#else
+
+WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersSSE2)
+
+#endif // WEBP_USE_SSE2
+
+#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_SSE2))
+WEBP_DSP_INIT_STUB(WebPInitUpsamplersSSE2)
+#endif
diff --git a/src/third_party/libwebp/src/dsp/upsampling_sse41.c b/src/third_party/libwebp/src/dsp/upsampling_sse41.c
new file mode 100644
index 0000000..cfc4de8
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/upsampling_sse41.c
@@ -0,0 +1,244 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// SSE41 version of YUV to RGB upsampling functions.
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_SSE41)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <string.h>
+#endif
+#include <smmintrin.h>
+#include "src/dsp/yuv.h"
+
+#ifdef FANCY_UPSAMPLING
+
+#if !defined(WEBP_REDUCE_CSP)
+
+// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows
+// u = (9*a + 3*b + 3*c + d + 8) / 16
+// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2
+// = (a + m + 1) / 2
+// where m = (a + 3*b + 3*c + d) / 8
+// = ((a + b + c + d) / 2 + b + c) / 4
+//
+// Let's say k = (a + b + c + d) / 4.
+// We can compute k as
+// k = (s + t + 1) / 2 - ((a^d) | (b^c) | (s^t)) & 1
+// where s = (a + d + 1) / 2 and t = (b + c + 1) / 2
+//
+// Then m can be written as
+// m = (k + t + 1) / 2 - (((b^c) & (s^t)) | (k^t)) & 1
+
+// Computes out = (k + in + 1) / 2 - ((ij & (s^t)) | (k^in)) & 1
+#define GET_M(ij, in, out) do { \
+ const __m128i tmp0 = _mm_avg_epu8(k, (in)); /* (k + in + 1) / 2 */ \
+ const __m128i tmp1 = _mm_and_si128((ij), st); /* (ij) & (s^t) */ \
+ const __m128i tmp2 = _mm_xor_si128(k, (in)); /* (k^in) */ \
+ const __m128i tmp3 = _mm_or_si128(tmp1, tmp2); /* ((ij) & (s^t)) | (k^in) */\
+ const __m128i tmp4 = _mm_and_si128(tmp3, one); /* & 1 -> lsb_correction */ \
+ (out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \
+} while (0)
+
+// pack and store two alternating pixel rows
+#define PACK_AND_STORE(a, b, da, db, out) do { \
+ const __m128i t_a = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \
+ const __m128i t_b = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \
+ const __m128i t_1 = _mm_unpacklo_epi8(t_a, t_b); \
+ const __m128i t_2 = _mm_unpackhi_epi8(t_a, t_b); \
+ _mm_store_si128(((__m128i*)(out)) + 0, t_1); \
+ _mm_store_si128(((__m128i*)(out)) + 1, t_2); \
+} while (0)
+
+// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
+#define UPSAMPLE_32PIXELS(r1, r2, out) { \
+ const __m128i one = _mm_set1_epi8(1); \
+ const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
+ const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
+ const __m128i c = _mm_loadu_si128((const __m128i*)&(r2)[0]); \
+ const __m128i d = _mm_loadu_si128((const __m128i*)&(r2)[1]); \
+ \
+ const __m128i s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \
+ const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \
+ const __m128i st = _mm_xor_si128(s, t); /* st = s^t */ \
+ \
+ const __m128i ad = _mm_xor_si128(a, d); /* ad = a^d */ \
+ const __m128i bc = _mm_xor_si128(b, c); /* bc = b^c */ \
+ \
+ const __m128i t1 = _mm_or_si128(ad, bc); /* (a^d) | (b^c) */ \
+ const __m128i t2 = _mm_or_si128(t1, st); /* (a^d) | (b^c) | (s^t) */ \
+ const __m128i t3 = _mm_and_si128(t2, one); /* (a^d) | (b^c) | (s^t) & 1 */ \
+ const __m128i t4 = _mm_avg_epu8(s, t); \
+ const __m128i k = _mm_sub_epi8(t4, t3); /* k = (a + b + c + d) / 4 */ \
+ __m128i diag1, diag2; \
+ \
+ GET_M(bc, t, diag1); /* diag1 = (a + 3b + 3c + d) / 8 */ \
+ GET_M(ad, s, diag2); /* diag2 = (3a + b + c + 3d) / 8 */ \
+ \
+ /* pack the alternate pixels */ \
+ PACK_AND_STORE(a, b, diag1, diag2, (out) + 0); /* store top */ \
+ PACK_AND_STORE(c, d, diag2, diag1, (out) + 2 * 32); /* store bottom */ \
+}
+
+// Turn the macro into a function for reducing code-size when non-critical
+static void Upsample32Pixels_SSE41(const uint8_t r1[], const uint8_t r2[],
+ uint8_t* const out) {
+ UPSAMPLE_32PIXELS(r1, r2, out);
+}
+
+#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \
+ uint8_t r1[17], r2[17]; \
+ memcpy(r1, (tb), (num_pixels)); \
+ memcpy(r2, (bb), (num_pixels)); \
+ /* replicate last byte */ \
+ memset(r1 + (num_pixels), r1[(num_pixels) - 1], 17 - (num_pixels)); \
+ memset(r2 + (num_pixels), r2[(num_pixels) - 1], 17 - (num_pixels)); \
+ /* using the shared function instead of the macro saves ~3k code size */ \
+ Upsample32Pixels_SSE41(r1, r2, out); \
+}
+
+#define CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, \
+ top_dst, bottom_dst, cur_x) do { \
+ FUNC##32_SSE41((top_y) + (cur_x), r_u, r_v, (top_dst) + (cur_x) * (XSTEP)); \
+ if ((bottom_y) != NULL) { \
+ FUNC##32_SSE41((bottom_y) + (cur_x), r_u + 64, r_v + 64, \
+ (bottom_dst) + (cur_x) * (XSTEP)); \
+ } \
+} while (0)
+
+#define SSE4_UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
+ const uint8_t* top_u, const uint8_t* top_v, \
+ const uint8_t* cur_u, const uint8_t* cur_v, \
+ uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
+ int uv_pos, pos; \
+ /* 16byte-aligned array to cache reconstructed u and v */ \
+ uint8_t uv_buf[14 * 32 + 15] = { 0 }; \
+ uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
+ uint8_t* const r_v = r_u + 32; \
+ \
+ assert(top_y != NULL); \
+ { /* Treat the first pixel in regular way */ \
+ const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
+ const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
+ const int u0_t = (top_u[0] + u_diag) >> 1; \
+ const int v0_t = (top_v[0] + v_diag) >> 1; \
+ FUNC(top_y[0], u0_t, v0_t, top_dst); \
+ if (bottom_y != NULL) { \
+ const int u0_b = (cur_u[0] + u_diag) >> 1; \
+ const int v0_b = (cur_v[0] + v_diag) >> 1; \
+ FUNC(bottom_y[0], u0_b, v0_b, bottom_dst); \
+ } \
+ } \
+ /* For UPSAMPLE_32PIXELS, 17 u/v values must be read-able for each block */ \
+ for (pos = 1, uv_pos = 0; pos + 32 + 1 <= len; pos += 32, uv_pos += 16) { \
+ UPSAMPLE_32PIXELS(top_u + uv_pos, cur_u + uv_pos, r_u); \
+ UPSAMPLE_32PIXELS(top_v + uv_pos, cur_v + uv_pos, r_v); \
+ CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, pos); \
+ } \
+ if (len > 1) { \
+ const int left_over = ((len + 1) >> 1) - (pos >> 1); \
+ uint8_t* const tmp_top_dst = r_u + 4 * 32; \
+ uint8_t* const tmp_bottom_dst = tmp_top_dst + 4 * 32; \
+ uint8_t* const tmp_top = tmp_bottom_dst + 4 * 32; \
+ uint8_t* const tmp_bottom = (bottom_y == NULL) ? NULL : tmp_top + 32; \
+ assert(left_over > 0); \
+ UPSAMPLE_LAST_BLOCK(top_u + uv_pos, cur_u + uv_pos, left_over, r_u); \
+ UPSAMPLE_LAST_BLOCK(top_v + uv_pos, cur_v + uv_pos, left_over, r_v); \
+ memcpy(tmp_top, top_y + pos, len - pos); \
+ if (bottom_y != NULL) memcpy(tmp_bottom, bottom_y + pos, len - pos); \
+ CONVERT2RGB_32(FUNC, XSTEP, tmp_top, tmp_bottom, tmp_top_dst, \
+ tmp_bottom_dst, 0); \
+ memcpy(top_dst + pos * (XSTEP), tmp_top_dst, (len - pos) * (XSTEP)); \
+ if (bottom_y != NULL) { \
+ memcpy(bottom_dst + pos * (XSTEP), tmp_bottom_dst, \
+ (len - pos) * (XSTEP)); \
+ } \
+ } \
+}
+
+// SSE4 variants of the fancy upsampler.
+SSE4_UPSAMPLE_FUNC(UpsampleRgbLinePair_SSE41, VP8YuvToRgb, 3)
+SSE4_UPSAMPLE_FUNC(UpsampleBgrLinePair_SSE41, VP8YuvToBgr, 3)
+
+#undef GET_M
+#undef PACK_AND_STORE
+#undef UPSAMPLE_32PIXELS
+#undef UPSAMPLE_LAST_BLOCK
+#undef CONVERT2RGB
+#undef CONVERT2RGB_32
+#undef SSE4_UPSAMPLE_FUNC
+
+#endif // WEBP_REDUCE_CSP
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
+
+extern void WebPInitUpsamplersSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersSSE41(void) {
+#if !defined(WEBP_REDUCE_CSP)
+ WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair_SSE41;
+ WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair_SSE41;
+#endif // WEBP_REDUCE_CSP
+}
+
+#endif // FANCY_UPSAMPLING
+
+//------------------------------------------------------------------------------
+
+extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
+extern void WebPInitYUV444ConvertersSSE41(void);
+
+#define YUV444_FUNC(FUNC_NAME, CALL, CALL_C, XSTEP) \
+extern void CALL_C(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len); \
+static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ int i; \
+ const int max_len = len & ~31; \
+ for (i = 0; i < max_len; i += 32) { \
+ CALL(y + i, u + i, v + i, dst + i * (XSTEP)); \
+ } \
+ if (i < len) { /* C-fallback */ \
+ CALL_C(y + i, u + i, v + i, dst + i * (XSTEP), len - i); \
+ } \
+}
+
+#if !defined(WEBP_REDUCE_CSP)
+YUV444_FUNC(Yuv444ToRgb_SSE41, VP8YuvToRgb32_SSE41, WebPYuv444ToRgb_C, 3);
+YUV444_FUNC(Yuv444ToBgr_SSE41, VP8YuvToBgr32_SSE41, WebPYuv444ToBgr_C, 3);
+#endif // WEBP_REDUCE_CSP
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE41(void) {
+#if !defined(WEBP_REDUCE_CSP)
+ WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb_SSE41;
+ WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr_SSE41;
+#endif // WEBP_REDUCE_CSP
+}
+
+#else
+
+WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersSSE41)
+
+#endif // WEBP_USE_SSE41
+
+#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_SSE41))
+WEBP_DSP_INIT_STUB(WebPInitUpsamplersSSE41)
+#endif
diff --git a/src/third_party/libwebp/src/dsp/yuv.c b/src/third_party/libwebp/src/dsp/yuv.c
new file mode 100644
index 0000000..0567aaa
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/yuv.c
@@ -0,0 +1,316 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// YUV->RGB conversion functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/yuv.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+//-----------------------------------------------------------------------------
+// Plain-C version
+
+#define ROW_FUNC(FUNC_NAME, FUNC, XSTEP) \
+static void FUNC_NAME(const uint8_t* y, \
+ const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ const uint8_t* const end = dst + (len & ~1) * (XSTEP); \
+ while (dst != end) { \
+ FUNC(y[0], u[0], v[0], dst); \
+ FUNC(y[1], u[0], v[0], dst + (XSTEP)); \
+ y += 2; \
+ ++u; \
+ ++v; \
+ dst += 2 * (XSTEP); \
+ } \
+ if (len & 1) { \
+ FUNC(y[0], u[0], v[0], dst); \
+ } \
+} \
+
+// All variants implemented.
+ROW_FUNC(YuvToRgbRow, VP8YuvToRgb, 3)
+ROW_FUNC(YuvToBgrRow, VP8YuvToBgr, 3)
+ROW_FUNC(YuvToRgbaRow, VP8YuvToRgba, 4)
+ROW_FUNC(YuvToBgraRow, VP8YuvToBgra, 4)
+ROW_FUNC(YuvToArgbRow, VP8YuvToArgb, 4)
+ROW_FUNC(YuvToRgba4444Row, VP8YuvToRgba4444, 2)
+ROW_FUNC(YuvToRgb565Row, VP8YuvToRgb565, 2)
+
+#undef ROW_FUNC
+
+// Main call for processing a plane with a WebPSamplerRowFunc function:
+void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
+ const uint8_t* u, const uint8_t* v, int uv_stride,
+ uint8_t* dst, int dst_stride,
+ int width, int height, WebPSamplerRowFunc func) {
+ int j;
+ for (j = 0; j < height; ++j) {
+ func(y, u, v, dst, width);
+ y += y_stride;
+ if (j & 1) {
+ u += uv_stride;
+ v += uv_stride;
+ }
+ dst += dst_stride;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Main call
+
+WebPSamplerRowFunc WebPSamplers[MODE_LAST];
+
+extern void WebPInitSamplersSSE2(void);
+extern void WebPInitSamplersSSE41(void);
+extern void WebPInitSamplersMIPS32(void);
+extern void WebPInitSamplersMIPSdspR2(void);
+
+WEBP_DSP_INIT_FUNC(WebPInitSamplers) {
+ WebPSamplers[MODE_RGB] = YuvToRgbRow;
+ WebPSamplers[MODE_RGBA] = YuvToRgbaRow;
+ WebPSamplers[MODE_BGR] = YuvToBgrRow;
+ WebPSamplers[MODE_BGRA] = YuvToBgraRow;
+ WebPSamplers[MODE_ARGB] = YuvToArgbRow;
+ WebPSamplers[MODE_RGBA_4444] = YuvToRgba4444Row;
+ WebPSamplers[MODE_RGB_565] = YuvToRgb565Row;
+ WebPSamplers[MODE_rgbA] = YuvToRgbaRow;
+ WebPSamplers[MODE_bgrA] = YuvToBgraRow;
+ WebPSamplers[MODE_Argb] = YuvToArgbRow;
+ WebPSamplers[MODE_rgbA_4444] = YuvToRgba4444Row;
+
+ // If defined, use CPUInfo() to overwrite some pointers with faster versions.
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ WebPInitSamplersSSE2();
+ }
+#endif // WEBP_USE_SSE2
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitSamplersSSE41();
+ }
+#endif // WEBP_USE_SSE41
+#if defined(WEBP_USE_MIPS32)
+ if (VP8GetCPUInfo(kMIPS32)) {
+ WebPInitSamplersMIPS32();
+ }
+#endif // WEBP_USE_MIPS32
+#if !defined(STARBOARD)
+ // Unfortunately, on at least one Starboard MIPS platform the MIPS DSP (r2)
+ // code was found to produce erroneous results (most pixels end up either
+ // white or purple). It is disabled for now out of lack of time and
+ // priority for investigation, but if additional performance is required,
+ // the issue should be investigated and fixed properly.
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+ if (VP8GetCPUInfo(kMIPSdspR2)) {
+ WebPInitSamplersMIPSdspR2();
+ }
+#endif // WEBP_USE_MIPS_DSP_R2
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ARGB -> YUV converters
+
+static void ConvertARGBToY_C(const uint32_t* argb, uint8_t* y, int width) {
+ int i;
+ for (i = 0; i < width; ++i) {
+ const uint32_t p = argb[i];
+ y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff,
+ YUV_HALF);
+ }
+}
+
+void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v,
+ int src_width, int do_store) {
+ // No rounding. Last pixel is dealt with separately.
+ const int uv_width = src_width >> 1;
+ int i;
+ for (i = 0; i < uv_width; ++i) {
+ const uint32_t v0 = argb[2 * i + 0];
+ const uint32_t v1 = argb[2 * i + 1];
+ // VP8RGBToU/V expects four accumulated pixels. Hence we need to
+ // scale r/g/b value by a factor 2. We just shift v0/v1 one bit less.
+ const int r = ((v0 >> 15) & 0x1fe) + ((v1 >> 15) & 0x1fe);
+ const int g = ((v0 >> 7) & 0x1fe) + ((v1 >> 7) & 0x1fe);
+ const int b = ((v0 << 1) & 0x1fe) + ((v1 << 1) & 0x1fe);
+ const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2);
+ const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2);
+ if (do_store) {
+ u[i] = tmp_u;
+ v[i] = tmp_v;
+ } else {
+ // Approximated average-of-four. But it's an acceptable diff.
+ u[i] = (u[i] + tmp_u + 1) >> 1;
+ v[i] = (v[i] + tmp_v + 1) >> 1;
+ }
+ }
+ if (src_width & 1) { // last pixel
+ const uint32_t v0 = argb[2 * i + 0];
+ const int r = (v0 >> 14) & 0x3fc;
+ const int g = (v0 >> 6) & 0x3fc;
+ const int b = (v0 << 2) & 0x3fc;
+ const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2);
+ const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2);
+ if (do_store) {
+ u[i] = tmp_u;
+ v[i] = tmp_v;
+ } else {
+ u[i] = (u[i] + tmp_u + 1) >> 1;
+ v[i] = (v[i] + tmp_v + 1) >> 1;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static void ConvertRGB24ToY_C(const uint8_t* rgb, uint8_t* y, int width) {
+ int i;
+ for (i = 0; i < width; ++i, rgb += 3) {
+ y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF);
+ }
+}
+
+static void ConvertBGR24ToY_C(const uint8_t* bgr, uint8_t* y, int width) {
+ int i;
+ for (i = 0; i < width; ++i, bgr += 3) {
+ y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF);
+ }
+}
+
+void WebPConvertRGBA32ToUV_C(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width) {
+ int i;
+ for (i = 0; i < width; i += 1, rgb += 4) {
+ const int r = rgb[0], g = rgb[1], b = rgb[2];
+ u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2);
+ v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+#if !WEBP_NEON_OMIT_C_CODE
+#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic
+static uint16_t clip_y(int v) {
+ return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
+}
+
+static uint64_t SharpYUVUpdateY_C(const uint16_t* ref, const uint16_t* src,
+ uint16_t* dst, int len) {
+ uint64_t diff = 0;
+ int i;
+ for (i = 0; i < len; ++i) {
+ const int diff_y = ref[i] - src[i];
+ const int new_y = (int)dst[i] + diff_y;
+ dst[i] = clip_y(new_y);
+ diff += (uint64_t)abs(diff_y);
+ }
+ return diff;
+}
+
+static void SharpYUVUpdateRGB_C(const int16_t* ref, const int16_t* src,
+ int16_t* dst, int len) {
+ int i;
+ for (i = 0; i < len; ++i) {
+ const int diff_uv = ref[i] - src[i];
+ dst[i] += diff_uv;
+ }
+}
+
+static void SharpYUVFilterRow_C(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out) {
+ int i;
+ for (i = 0; i < len; ++i, ++A, ++B) {
+ const int v0 = (A[0] * 9 + A[1] * 3 + B[0] * 3 + B[1] + 8) >> 4;
+ const int v1 = (A[1] * 9 + A[0] * 3 + B[1] * 3 + B[0] + 8) >> 4;
+ out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0);
+ out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1);
+ }
+}
+#endif // !WEBP_NEON_OMIT_C_CODE
+
+#undef MAX_Y
+
+//-----------------------------------------------------------------------------
+
+void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width);
+void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width);
+void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width);
+
+void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width);
+void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v,
+ int src_width, int do_store);
+
+uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* ref, const uint16_t* src,
+ uint16_t* dst, int len);
+void (*WebPSharpYUVUpdateRGB)(const int16_t* ref, const int16_t* src,
+ int16_t* dst, int len);
+void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out);
+
+extern void WebPInitConvertARGBToYUVSSE2(void);
+extern void WebPInitConvertARGBToYUVSSE41(void);
+extern void WebPInitConvertARGBToYUVNEON(void);
+extern void WebPInitSharpYUVSSE2(void);
+extern void WebPInitSharpYUVNEON(void);
+
+WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
+ WebPConvertARGBToY = ConvertARGBToY_C;
+ WebPConvertARGBToUV = WebPConvertARGBToUV_C;
+
+ WebPConvertRGB24ToY = ConvertRGB24ToY_C;
+ WebPConvertBGR24ToY = ConvertBGR24ToY_C;
+
+ WebPConvertRGBA32ToUV = WebPConvertRGBA32ToUV_C;
+
+#if !WEBP_NEON_OMIT_C_CODE
+ WebPSharpYUVUpdateY = SharpYUVUpdateY_C;
+ WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_C;
+ WebPSharpYUVFilterRow = SharpYUVFilterRow_C;
+#endif
+
+ if (VP8GetCPUInfo != NULL) {
+#if defined(WEBP_USE_SSE2)
+ if (VP8GetCPUInfo(kSSE2)) {
+ WebPInitConvertARGBToYUVSSE2();
+ WebPInitSharpYUVSSE2();
+ }
+#endif // WEBP_USE_SSE2
+#if defined(WEBP_USE_SSE41)
+ if (VP8GetCPUInfo(kSSE4_1)) {
+ WebPInitConvertARGBToYUVSSE41();
+ }
+#endif // WEBP_USE_SSE41
+ }
+
+#if defined(WEBP_USE_NEON)
+ if (WEBP_NEON_OMIT_C_CODE ||
+ (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
+ WebPInitConvertARGBToYUVNEON();
+ WebPInitSharpYUVNEON();
+ }
+#endif // WEBP_USE_NEON
+
+ assert(WebPConvertARGBToY != NULL);
+ assert(WebPConvertARGBToUV != NULL);
+ assert(WebPConvertRGB24ToY != NULL);
+ assert(WebPConvertBGR24ToY != NULL);
+ assert(WebPConvertRGBA32ToUV != NULL);
+ assert(WebPSharpYUVUpdateY != NULL);
+ assert(WebPSharpYUVUpdateRGB != NULL);
+ assert(WebPSharpYUVFilterRow != NULL);
+}
diff --git a/src/third_party/libwebp/src/dsp/yuv.h b/src/third_party/libwebp/src/dsp/yuv.h
new file mode 100644
index 0000000..eb78727
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/yuv.h
@@ -0,0 +1,210 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// inline YUV<->RGB conversion function
+//
+// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
+// More information at: http://en.wikipedia.org/wiki/YCbCr
+// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
+// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
+// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
+// We use 16bit fixed point operations for RGB->YUV conversion (YUV_FIX).
+//
+// For the Y'CbCr to RGB conversion, the BT.601 specification reads:
+// R = 1.164 * (Y-16) + 1.596 * (V-128)
+// G = 1.164 * (Y-16) - 0.813 * (V-128) - 0.391 * (U-128)
+// B = 1.164 * (Y-16) + 2.018 * (U-128)
+// where Y is in the [16,235] range, and U/V in the [16,240] range.
+//
+// The fixed-point implementation used here is:
+// R = (19077 . y + 26149 . v - 14234) >> 6
+// G = (19077 . y - 6419 . u - 13320 . v + 8708) >> 6
+// B = (19077 . y + 33050 . u - 17685) >> 6
+// where the '.' operator is the mulhi_epu16 variant:
+// a . b = ((a << 8) * b) >> 16
+// that preserves 8 bits of fractional precision before final descaling.
+
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_DSP_YUV_H_
+#define WEBP_DSP_YUV_H_
+
+#include "src/dsp/dsp.h"
+#include "src/dec/vp8_dec.h"
+
+//------------------------------------------------------------------------------
+// YUV -> RGB conversion
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ YUV_FIX = 16, // fixed-point precision for RGB->YUV
+ YUV_HALF = 1 << (YUV_FIX - 1),
+
+ YUV_FIX2 = 6, // fixed-point precision for YUV->RGB
+ YUV_MASK2 = (256 << YUV_FIX2) - 1
+};
+
+//------------------------------------------------------------------------------
+// slower on x86 by ~7-8%, but bit-exact with the SSE2/NEON version
+
+static WEBP_INLINE int MultHi(int v, int coeff) { // _mm_mulhi_epu16 emulation
+ return (v * coeff) >> 8;
+}
+
+static WEBP_INLINE int VP8Clip8(int v) {
+ return ((v & ~YUV_MASK2) == 0) ? (v >> YUV_FIX2) : (v < 0) ? 0 : 255;
+}
+
+static WEBP_INLINE int VP8YUVToR(int y, int v) {
+ return VP8Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234);
+}
+
+static WEBP_INLINE int VP8YUVToG(int y, int u, int v) {
+ return VP8Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v, 13320) + 8708);
+}
+
+static WEBP_INLINE int VP8YUVToB(int y, int u) {
+ return VP8Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685);
+}
+
+static WEBP_INLINE void VP8YuvToRgb(int y, int u, int v,
+ uint8_t* const rgb) {
+ rgb[0] = VP8YUVToR(y, v);
+ rgb[1] = VP8YUVToG(y, u, v);
+ rgb[2] = VP8YUVToB(y, u);
+}
+
+static WEBP_INLINE void VP8YuvToBgr(int y, int u, int v,
+ uint8_t* const bgr) {
+ bgr[0] = VP8YUVToB(y, u);
+ bgr[1] = VP8YUVToG(y, u, v);
+ bgr[2] = VP8YUVToR(y, v);
+}
+
+static WEBP_INLINE void VP8YuvToRgb565(int y, int u, int v,
+ uint8_t* const rgb) {
+ const int r = VP8YUVToR(y, v); // 5 usable bits
+ const int g = VP8YUVToG(y, u, v); // 6 usable bits
+ const int b = VP8YUVToB(y, u); // 5 usable bits
+ const int rg = (r & 0xf8) | (g >> 5);
+ const int gb = ((g << 3) & 0xe0) | (b >> 3);
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ rgb[0] = gb;
+ rgb[1] = rg;
+#else
+ rgb[0] = rg;
+ rgb[1] = gb;
+#endif
+}
+
+static WEBP_INLINE void VP8YuvToRgba4444(int y, int u, int v,
+ uint8_t* const argb) {
+ const int r = VP8YUVToR(y, v); // 4 usable bits
+ const int g = VP8YUVToG(y, u, v); // 4 usable bits
+ const int b = VP8YUVToB(y, u); // 4 usable bits
+ const int rg = (r & 0xf0) | (g >> 4);
+ const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits
+#if (WEBP_SWAP_16BIT_CSP == 1)
+ argb[0] = ba;
+ argb[1] = rg;
+#else
+ argb[0] = rg;
+ argb[1] = ba;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Alpha handling variants
+
+static WEBP_INLINE void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v,
+ uint8_t* const argb) {
+ argb[0] = 0xff;
+ VP8YuvToRgb(y, u, v, argb + 1);
+}
+
+static WEBP_INLINE void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v,
+ uint8_t* const bgra) {
+ VP8YuvToBgr(y, u, v, bgra);
+ bgra[3] = 0xff;
+}
+
+static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
+ uint8_t* const rgba) {
+ VP8YuvToRgb(y, u, v, rgba);
+ rgba[3] = 0xff;
+}
+
+//-----------------------------------------------------------------------------
+// SSE2 extra functions (mostly for upsampling_sse2.c)
+
+#if defined(WEBP_USE_SSE2)
+
+// Process 32 pixels and store the result (16b, 24b or 32b per pixel) in *dst.
+void VP8YuvToRgba32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+void VP8YuvToRgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+void VP8YuvToBgra32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+void VP8YuvToBgr32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+void VP8YuvToArgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+void VP8YuvToRgba444432_SSE2(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst);
+void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+
+#endif // WEBP_USE_SSE2
+
+//-----------------------------------------------------------------------------
+// SSE41 extra functions (mostly for upsampling_sse41.c)
+
+#if defined(WEBP_USE_SSE41)
+
+// Process 32 pixels and store the result (16b, 24b or 32b per pixel) in *dst.
+void VP8YuvToRgb32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+void VP8YuvToBgr32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst);
+
+#endif // WEBP_USE_SSE41
+
+//------------------------------------------------------------------------------
+// RGB -> YUV conversion
+
+// Stub functions that can be called with various rounding values:
+static WEBP_INLINE int VP8ClipUV(int uv, int rounding) {
+ uv = (uv + rounding + (128 << (YUV_FIX + 2))) >> (YUV_FIX + 2);
+ return ((uv & ~0xff) == 0) ? uv : (uv < 0) ? 0 : 255;
+}
+
+static WEBP_INLINE int VP8RGBToY(int r, int g, int b, int rounding) {
+ const int luma = 16839 * r + 33059 * g + 6420 * b;
+ return (luma + rounding + (16 << YUV_FIX)) >> YUV_FIX; // no need to clip
+}
+
+static WEBP_INLINE int VP8RGBToU(int r, int g, int b, int rounding) {
+ const int u = -9719 * r - 19081 * g + 28800 * b;
+ return VP8ClipUV(u, rounding);
+}
+
+static WEBP_INLINE int VP8RGBToV(int r, int g, int b, int rounding) {
+ const int v = +28800 * r - 24116 * g - 4684 * b;
+ return VP8ClipUV(v, rounding);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_DSP_YUV_H_ */
diff --git a/src/third_party/libwebp/src/dsp/yuv_mips32.c b/src/third_party/libwebp/src/dsp/yuv_mips32.c
new file mode 100644
index 0000000..9d0a887
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/yuv_mips32.c
@@ -0,0 +1,103 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS version of YUV to RGB upsampling functions.
+//
+// Author(s): Djordje Pesut (djordje.pesut@imgtec.com)
+// Jovan Zelincevic (jovan.zelincevic@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS32)
+
+#include "src/dsp/yuv.h"
+
+//------------------------------------------------------------------------------
+// simple point-sampling
+
+#define ROW_FUNC(FUNC_NAME, XSTEP, R, G, B, A) \
+static void FUNC_NAME(const uint8_t* y, \
+ const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ int i, r, g, b; \
+ int temp0, temp1, temp2, temp3, temp4; \
+ for (i = 0; i < (len >> 1); i++) { \
+ temp1 = MultHi(v[0], 26149); \
+ temp3 = MultHi(v[0], 13320); \
+ temp2 = MultHi(u[0], 6419); \
+ temp4 = MultHi(u[0], 33050); \
+ temp0 = MultHi(y[0], 19077); \
+ temp1 -= 14234; \
+ temp3 -= 8708; \
+ temp2 += temp3; \
+ temp4 -= 17685; \
+ r = VP8Clip8(temp0 + temp1); \
+ g = VP8Clip8(temp0 - temp2); \
+ b = VP8Clip8(temp0 + temp4); \
+ temp0 = MultHi(y[1], 19077); \
+ dst[R] = r; \
+ dst[G] = g; \
+ dst[B] = b; \
+ if (A) dst[A] = 0xff; \
+ r = VP8Clip8(temp0 + temp1); \
+ g = VP8Clip8(temp0 - temp2); \
+ b = VP8Clip8(temp0 + temp4); \
+ dst[R + XSTEP] = r; \
+ dst[G + XSTEP] = g; \
+ dst[B + XSTEP] = b; \
+ if (A) dst[A + XSTEP] = 0xff; \
+ y += 2; \
+ ++u; \
+ ++v; \
+ dst += 2 * XSTEP; \
+ } \
+ if (len & 1) { \
+ temp1 = MultHi(v[0], 26149); \
+ temp3 = MultHi(v[0], 13320); \
+ temp2 = MultHi(u[0], 6419); \
+ temp4 = MultHi(u[0], 33050); \
+ temp0 = MultHi(y[0], 19077); \
+ temp1 -= 14234; \
+ temp3 -= 8708; \
+ temp2 += temp3; \
+ temp4 -= 17685; \
+ r = VP8Clip8(temp0 + temp1); \
+ g = VP8Clip8(temp0 - temp2); \
+ b = VP8Clip8(temp0 + temp4); \
+ dst[R] = r; \
+ dst[G] = g; \
+ dst[B] = b; \
+ if (A) dst[A] = 0xff; \
+ } \
+}
+
+ROW_FUNC(YuvToRgbRow_MIPS32, 3, 0, 1, 2, 0)
+ROW_FUNC(YuvToRgbaRow_MIPS32, 4, 0, 1, 2, 3)
+ROW_FUNC(YuvToBgrRow_MIPS32, 3, 2, 1, 0, 0)
+ROW_FUNC(YuvToBgraRow_MIPS32, 4, 2, 1, 0, 3)
+
+#undef ROW_FUNC
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitSamplersMIPS32(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersMIPS32(void) {
+ WebPSamplers[MODE_RGB] = YuvToRgbRow_MIPS32;
+ WebPSamplers[MODE_RGBA] = YuvToRgbaRow_MIPS32;
+ WebPSamplers[MODE_BGR] = YuvToBgrRow_MIPS32;
+ WebPSamplers[MODE_BGRA] = YuvToBgraRow_MIPS32;
+}
+
+#else // !WEBP_USE_MIPS32
+
+WEBP_DSP_INIT_STUB(WebPInitSamplersMIPS32)
+
+#endif // WEBP_USE_MIPS32
diff --git a/src/third_party/libwebp/src/dsp/yuv_mips_dsp_r2.c b/src/third_party/libwebp/src/dsp/yuv_mips_dsp_r2.c
new file mode 100644
index 0000000..cc8afcc
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/yuv_mips_dsp_r2.c
@@ -0,0 +1,134 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// MIPS DSPr2 version of YUV to RGB upsampling functions.
+//
+// Author(s): Branimir Vasic (branimir.vasic@imgtec.com)
+// Djordje Pesut (djordje.pesut@imgtec.com)
+
+#include "src/dsp/dsp.h"
+
+#if defined(WEBP_USE_MIPS_DSP_R2)
+
+#include "src/dsp/yuv.h"
+
+//------------------------------------------------------------------------------
+// simple point-sampling
+
+#define ROW_FUNC_PART_1() \
+ "lbu %[temp3], 0(%[v]) \n\t" \
+ "lbu %[temp4], 0(%[u]) \n\t" \
+ "lbu %[temp0], 0(%[y]) \n\t" \
+ "mul %[temp1], %[t_con_1], %[temp3] \n\t" \
+ "mul %[temp3], %[t_con_2], %[temp3] \n\t" \
+ "mul %[temp2], %[t_con_3], %[temp4] \n\t" \
+ "mul %[temp4], %[t_con_4], %[temp4] \n\t" \
+ "mul %[temp0], %[t_con_5], %[temp0] \n\t" \
+ "subu %[temp1], %[temp1], %[t_con_6] \n\t" \
+ "subu %[temp3], %[temp3], %[t_con_7] \n\t" \
+ "addu %[temp2], %[temp2], %[temp3] \n\t" \
+ "subu %[temp4], %[temp4], %[t_con_8] \n\t" \
+
+#define ROW_FUNC_PART_2(R, G, B, K) \
+ "addu %[temp5], %[temp0], %[temp1] \n\t" \
+ "subu %[temp6], %[temp0], %[temp2] \n\t" \
+ "addu %[temp7], %[temp0], %[temp4] \n\t" \
+".if " #K " \n\t" \
+ "lbu %[temp0], 1(%[y]) \n\t" \
+".endif \n\t" \
+ "shll_s.w %[temp5], %[temp5], 17 \n\t" \
+ "shll_s.w %[temp6], %[temp6], 17 \n\t" \
+".if " #K " \n\t" \
+ "mul %[temp0], %[t_con_5], %[temp0] \n\t" \
+".endif \n\t" \
+ "shll_s.w %[temp7], %[temp7], 17 \n\t" \
+ "precrqu_s.qb.ph %[temp5], %[temp5], $zero \n\t" \
+ "precrqu_s.qb.ph %[temp6], %[temp6], $zero \n\t" \
+ "precrqu_s.qb.ph %[temp7], %[temp7], $zero \n\t" \
+ "srl %[temp5], %[temp5], 24 \n\t" \
+ "srl %[temp6], %[temp6], 24 \n\t" \
+ "srl %[temp7], %[temp7], 24 \n\t" \
+ "sb %[temp5], " #R "(%[dst]) \n\t" \
+ "sb %[temp6], " #G "(%[dst]) \n\t" \
+ "sb %[temp7], " #B "(%[dst]) \n\t" \
+
+#define ASM_CLOBBER_LIST() \
+ : [temp0]"=&r"(temp0), [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), \
+ [temp3]"=&r"(temp3), [temp4]"=&r"(temp4), [temp5]"=&r"(temp5), \
+ [temp6]"=&r"(temp6), [temp7]"=&r"(temp7) \
+ : [t_con_1]"r"(t_con_1), [t_con_2]"r"(t_con_2), [t_con_3]"r"(t_con_3), \
+ [t_con_4]"r"(t_con_4), [t_con_5]"r"(t_con_5), [t_con_6]"r"(t_con_6), \
+ [u]"r"(u), [v]"r"(v), [y]"r"(y), [dst]"r"(dst), \
+ [t_con_7]"r"(t_con_7), [t_con_8]"r"(t_con_8) \
+ : "memory", "hi", "lo" \
+
+#define ROW_FUNC(FUNC_NAME, XSTEP, R, G, B, A) \
+static void FUNC_NAME(const uint8_t* y, \
+ const uint8_t* u, const uint8_t* v, \
+ uint8_t* dst, int len) { \
+ int i; \
+ uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \
+ const int t_con_1 = 26149; \
+ const int t_con_2 = 13320; \
+ const int t_con_3 = 6419; \
+ const int t_con_4 = 33050; \
+ const int t_con_5 = 19077; \
+ const int t_con_6 = 14234; \
+ const int t_con_7 = 8708; \
+ const int t_con_8 = 17685; \
+ for (i = 0; i < (len >> 1); i++) { \
+ __asm__ volatile ( \
+ ROW_FUNC_PART_1() \
+ ROW_FUNC_PART_2(R, G, B, 1) \
+ ROW_FUNC_PART_2(R + XSTEP, G + XSTEP, B + XSTEP, 0) \
+ ASM_CLOBBER_LIST() \
+ ); \
+ if (A) dst[A] = dst[A + XSTEP] = 0xff; \
+ y += 2; \
+ ++u; \
+ ++v; \
+ dst += 2 * XSTEP; \
+ } \
+ if (len & 1) { \
+ __asm__ volatile ( \
+ ROW_FUNC_PART_1() \
+ ROW_FUNC_PART_2(R, G, B, 0) \
+ ASM_CLOBBER_LIST() \
+ ); \
+ if (A) dst[A] = 0xff; \
+ } \
+}
+
+ROW_FUNC(YuvToRgbRow_MIPSdspR2, 3, 0, 1, 2, 0)
+ROW_FUNC(YuvToRgbaRow_MIPSdspR2, 4, 0, 1, 2, 3)
+ROW_FUNC(YuvToBgrRow_MIPSdspR2, 3, 2, 1, 0, 0)
+ROW_FUNC(YuvToBgraRow_MIPSdspR2, 4, 2, 1, 0, 3)
+
+#undef ROW_FUNC
+#undef ASM_CLOBBER_LIST
+#undef ROW_FUNC_PART_2
+#undef ROW_FUNC_PART_1
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitSamplersMIPSdspR2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersMIPSdspR2(void) {
+ WebPSamplers[MODE_RGB] = YuvToRgbRow_MIPSdspR2;
+ WebPSamplers[MODE_RGBA] = YuvToRgbaRow_MIPSdspR2;
+ WebPSamplers[MODE_BGR] = YuvToBgrRow_MIPSdspR2;
+ WebPSamplers[MODE_BGRA] = YuvToBgraRow_MIPSdspR2;
+}
+
+#else // !WEBP_USE_MIPS_DSP_R2
+
+WEBP_DSP_INIT_STUB(WebPInitSamplersMIPSdspR2)
+
+#endif // WEBP_USE_MIPS_DSP_R2
diff --git a/src/third_party/libwebp/src/dsp/yuv_neon.c b/src/third_party/libwebp/src/dsp/yuv_neon.c
new file mode 100644
index 0000000..a34d602
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/yuv_neon.c
@@ -0,0 +1,288 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// YUV->RGB conversion functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/yuv.h"
+
+#if defined(WEBP_USE_NEON)
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "src/dsp/neon.h"
+
+//-----------------------------------------------------------------------------
+
+static uint8x8_t ConvertRGBToY_NEON(const uint8x8_t R,
+ const uint8x8_t G,
+ const uint8x8_t B) {
+ const uint16x8_t r = vmovl_u8(R);
+ const uint16x8_t g = vmovl_u8(G);
+ const uint16x8_t b = vmovl_u8(B);
+ const uint16x4_t r_lo = vget_low_u16(r);
+ const uint16x4_t r_hi = vget_high_u16(r);
+ const uint16x4_t g_lo = vget_low_u16(g);
+ const uint16x4_t g_hi = vget_high_u16(g);
+ const uint16x4_t b_lo = vget_low_u16(b);
+ const uint16x4_t b_hi = vget_high_u16(b);
+ const uint32x4_t tmp0_lo = vmull_n_u16( r_lo, 16839u);
+ const uint32x4_t tmp0_hi = vmull_n_u16( r_hi, 16839u);
+ const uint32x4_t tmp1_lo = vmlal_n_u16(tmp0_lo, g_lo, 33059u);
+ const uint32x4_t tmp1_hi = vmlal_n_u16(tmp0_hi, g_hi, 33059u);
+ const uint32x4_t tmp2_lo = vmlal_n_u16(tmp1_lo, b_lo, 6420u);
+ const uint32x4_t tmp2_hi = vmlal_n_u16(tmp1_hi, b_hi, 6420u);
+ const uint16x8_t Y1 = vcombine_u16(vrshrn_n_u32(tmp2_lo, 16),
+ vrshrn_n_u32(tmp2_hi, 16));
+ const uint16x8_t Y2 = vaddq_u16(Y1, vdupq_n_u16(16));
+ return vqmovn_u16(Y2);
+}
+
+static void ConvertRGB24ToY_NEON(const uint8_t* rgb, uint8_t* y, int width) {
+ int i;
+ for (i = 0; i + 8 <= width; i += 8, rgb += 3 * 8) {
+ const uint8x8x3_t RGB = vld3_u8(rgb);
+ const uint8x8_t Y = ConvertRGBToY_NEON(RGB.val[0], RGB.val[1], RGB.val[2]);
+ vst1_u8(y + i, Y);
+ }
+ for (; i < width; ++i, rgb += 3) { // left-over
+ y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF);
+ }
+}
+
+static void ConvertBGR24ToY_NEON(const uint8_t* bgr, uint8_t* y, int width) {
+ int i;
+ for (i = 0; i + 8 <= width; i += 8, bgr += 3 * 8) {
+ const uint8x8x3_t BGR = vld3_u8(bgr);
+ const uint8x8_t Y = ConvertRGBToY_NEON(BGR.val[2], BGR.val[1], BGR.val[0]);
+ vst1_u8(y + i, Y);
+ }
+ for (; i < width; ++i, bgr += 3) { // left-over
+ y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF);
+ }
+}
+
+static void ConvertARGBToY_NEON(const uint32_t* argb, uint8_t* y, int width) {
+ int i;
+ for (i = 0; i + 8 <= width; i += 8) {
+ const uint8x8x4_t RGB = vld4_u8((const uint8_t*)&argb[i]);
+ const uint8x8_t Y = ConvertRGBToY_NEON(RGB.val[2], RGB.val[1], RGB.val[0]);
+ vst1_u8(y + i, Y);
+ }
+ for (; i < width; ++i) { // left-over
+ const uint32_t p = argb[i];
+ y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff,
+ YUV_HALF);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+// computes: DST_s16 = [(C0 * r + C1 * g + C2 * b) >> 16] + CST
+#define MULTIPLY_16b_PREAMBLE(r, g, b) \
+ const int16x4_t r_lo = vreinterpret_s16_u16(vget_low_u16(r)); \
+ const int16x4_t r_hi = vreinterpret_s16_u16(vget_high_u16(r)); \
+ const int16x4_t g_lo = vreinterpret_s16_u16(vget_low_u16(g)); \
+ const int16x4_t g_hi = vreinterpret_s16_u16(vget_high_u16(g)); \
+ const int16x4_t b_lo = vreinterpret_s16_u16(vget_low_u16(b)); \
+ const int16x4_t b_hi = vreinterpret_s16_u16(vget_high_u16(b))
+
+#define MULTIPLY_16b(C0, C1, C2, CST, DST_s16) do { \
+ const int32x4_t tmp0_lo = vmull_n_s16( r_lo, C0); \
+ const int32x4_t tmp0_hi = vmull_n_s16( r_hi, C0); \
+ const int32x4_t tmp1_lo = vmlal_n_s16(tmp0_lo, g_lo, C1); \
+ const int32x4_t tmp1_hi = vmlal_n_s16(tmp0_hi, g_hi, C1); \
+ const int32x4_t tmp2_lo = vmlal_n_s16(tmp1_lo, b_lo, C2); \
+ const int32x4_t tmp2_hi = vmlal_n_s16(tmp1_hi, b_hi, C2); \
+ const int16x8_t tmp3 = vcombine_s16(vshrn_n_s32(tmp2_lo, 16), \
+ vshrn_n_s32(tmp2_hi, 16)); \
+ DST_s16 = vaddq_s16(tmp3, vdupq_n_s16(CST)); \
+} while (0)
+
+// This needs to be a macro, since (128 << SHIFT) needs to be an immediate.
+#define CONVERT_RGB_TO_UV(r, g, b, SHIFT, U_DST, V_DST) do { \
+ MULTIPLY_16b_PREAMBLE(r, g, b); \
+ MULTIPLY_16b(-9719, -19081, 28800, 128 << SHIFT, U_DST); \
+ MULTIPLY_16b(28800, -24116, -4684, 128 << SHIFT, V_DST); \
+} while (0)
+
+static void ConvertRGBA32ToUV_NEON(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width) {
+ int i;
+ for (i = 0; i + 8 <= width; i += 8, rgb += 4 * 8) {
+ const uint16x8x4_t RGB = vld4q_u16((const uint16_t*)rgb);
+ int16x8_t U, V;
+ CONVERT_RGB_TO_UV(RGB.val[0], RGB.val[1], RGB.val[2], 2, U, V);
+ vst1_u8(u + i, vqrshrun_n_s16(U, 2));
+ vst1_u8(v + i, vqrshrun_n_s16(V, 2));
+ }
+ for (; i < width; i += 1, rgb += 4) {
+ const int r = rgb[0], g = rgb[1], b = rgb[2];
+ u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2);
+ v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2);
+ }
+}
+
+static void ConvertARGBToUV_NEON(const uint32_t* argb, uint8_t* u, uint8_t* v,
+ int src_width, int do_store) {
+ int i;
+ for (i = 0; i + 16 <= src_width; i += 16, u += 8, v += 8) {
+ const uint8x16x4_t RGB = vld4q_u8((const uint8_t*)&argb[i]);
+ const uint16x8_t R = vpaddlq_u8(RGB.val[2]); // pair-wise adds
+ const uint16x8_t G = vpaddlq_u8(RGB.val[1]);
+ const uint16x8_t B = vpaddlq_u8(RGB.val[0]);
+ int16x8_t U_tmp, V_tmp;
+ CONVERT_RGB_TO_UV(R, G, B, 1, U_tmp, V_tmp);
+ {
+ const uint8x8_t U = vqrshrun_n_s16(U_tmp, 1);
+ const uint8x8_t V = vqrshrun_n_s16(V_tmp, 1);
+ if (do_store) {
+ vst1_u8(u, U);
+ vst1_u8(v, V);
+ } else {
+ const uint8x8_t prev_u = vld1_u8(u);
+ const uint8x8_t prev_v = vld1_u8(v);
+ vst1_u8(u, vrhadd_u8(U, prev_u));
+ vst1_u8(v, vrhadd_u8(V, prev_v));
+ }
+ }
+ }
+ if (i < src_width) { // left-over
+ WebPConvertARGBToUV_C(argb + i, u, v, src_width - i, do_store);
+ }
+}
+
+
+//------------------------------------------------------------------------------
+
+extern void WebPInitConvertARGBToYUVNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVNEON(void) {
+ WebPConvertRGB24ToY = ConvertRGB24ToY_NEON;
+ WebPConvertBGR24ToY = ConvertBGR24ToY_NEON;
+ WebPConvertARGBToY = ConvertARGBToY_NEON;
+ WebPConvertARGBToUV = ConvertARGBToUV_NEON;
+ WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_NEON;
+}
+
+//------------------------------------------------------------------------------
+
+#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic
+static uint16_t clip_y_NEON(int v) {
+ return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
+}
+
+static uint64_t SharpYUVUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
+ uint16_t* dst, int len) {
+ int i;
+ const int16x8_t zero = vdupq_n_s16(0);
+ const int16x8_t max = vdupq_n_s16(MAX_Y);
+ uint64x2_t sum = vdupq_n_u64(0);
+ uint64_t diff;
+
+ for (i = 0; i + 8 <= len; i += 8) {
+ const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i));
+ const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i));
+ const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i));
+ const int16x8_t D = vsubq_s16(A, B); // diff_y
+ const int16x8_t F = vaddq_s16(C, D); // new_y
+ const uint16x8_t H =
+ vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero));
+ const int16x8_t I = vabsq_s16(D); // abs(diff_y)
+ vst1q_u16(dst + i, H);
+ sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I)));
+ }
+ diff = vgetq_lane_u64(sum, 0) + vgetq_lane_u64(sum, 1);
+ for (; i < len; ++i) {
+ const int diff_y = ref[i] - src[i];
+ const int new_y = (int)(dst[i]) + diff_y;
+ dst[i] = clip_y_NEON(new_y);
+ diff += (uint64_t)(abs(diff_y));
+ }
+ return diff;
+}
+
+static void SharpYUVUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
+ int16_t* dst, int len) {
+ int i;
+ for (i = 0; i + 8 <= len; i += 8) {
+ const int16x8_t A = vld1q_s16(ref + i);
+ const int16x8_t B = vld1q_s16(src + i);
+ const int16x8_t C = vld1q_s16(dst + i);
+ const int16x8_t D = vsubq_s16(A, B); // diff_uv
+ const int16x8_t E = vaddq_s16(C, D); // new_uv
+ vst1q_s16(dst + i, E);
+ }
+ for (; i < len; ++i) {
+ const int diff_uv = ref[i] - src[i];
+ dst[i] += diff_uv;
+ }
+}
+
+static void SharpYUVFilterRow_NEON(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out) {
+ int i;
+ const int16x8_t max = vdupq_n_s16(MAX_Y);
+ const int16x8_t zero = vdupq_n_s16(0);
+ for (i = 0; i + 8 <= len; i += 8) {
+ const int16x8_t a0 = vld1q_s16(A + i + 0);
+ const int16x8_t a1 = vld1q_s16(A + i + 1);
+ const int16x8_t b0 = vld1q_s16(B + i + 0);
+ const int16x8_t b1 = vld1q_s16(B + i + 1);
+ const int16x8_t a0b1 = vaddq_s16(a0, b1);
+ const int16x8_t a1b0 = vaddq_s16(a1, b0);
+ const int16x8_t a0a1b0b1 = vaddq_s16(a0b1, a1b0); // A0+A1+B0+B1
+ const int16x8_t a0b1_2 = vaddq_s16(a0b1, a0b1); // 2*(A0+B1)
+ const int16x8_t a1b0_2 = vaddq_s16(a1b0, a1b0); // 2*(A1+B0)
+ const int16x8_t c0 = vshrq_n_s16(vaddq_s16(a0b1_2, a0a1b0b1), 3);
+ const int16x8_t c1 = vshrq_n_s16(vaddq_s16(a1b0_2, a0a1b0b1), 3);
+ const int16x8_t d0 = vaddq_s16(c1, a0);
+ const int16x8_t d1 = vaddq_s16(c0, a1);
+ const int16x8_t e0 = vrshrq_n_s16(d0, 1);
+ const int16x8_t e1 = vrshrq_n_s16(d1, 1);
+ const int16x8x2_t f = vzipq_s16(e0, e1);
+ const int16x8_t g0 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 0));
+ const int16x8_t g1 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 8));
+ const int16x8_t h0 = vaddq_s16(g0, f.val[0]);
+ const int16x8_t h1 = vaddq_s16(g1, f.val[1]);
+ const int16x8_t i0 = vmaxq_s16(vminq_s16(h0, max), zero);
+ const int16x8_t i1 = vmaxq_s16(vminq_s16(h1, max), zero);
+ vst1q_u16(out + 2 * i + 0, vreinterpretq_u16_s16(i0));
+ vst1q_u16(out + 2 * i + 8, vreinterpretq_u16_s16(i1));
+ }
+ for (; i < len; ++i) {
+ const int a0b1 = A[i + 0] + B[i + 1];
+ const int a1b0 = A[i + 1] + B[i + 0];
+ const int a0a1b0b1 = a0b1 + a1b0 + 8;
+ const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+ const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+ out[2 * i + 0] = clip_y_NEON(best_y[2 * i + 0] + v0);
+ out[2 * i + 1] = clip_y_NEON(best_y[2 * i + 1] + v1);
+ }
+}
+#undef MAX_Y
+
+//------------------------------------------------------------------------------
+
+extern void WebPInitSharpYUVNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVNEON(void) {
+ WebPSharpYUVUpdateY = SharpYUVUpdateY_NEON;
+ WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_NEON;
+ WebPSharpYUVFilterRow = SharpYUVFilterRow_NEON;
+}
+
+#else // !WEBP_USE_NEON
+
+WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVNEON)
+WEBP_DSP_INIT_STUB(WebPInitSharpYUVNEON)
+
+#endif // WEBP_USE_NEON
diff --git a/src/third_party/libwebp/src/dsp/yuv_sse2.c b/src/third_party/libwebp/src/dsp/yuv_sse2.c
new file mode 100644
index 0000000..baa48d5
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/yuv_sse2.c
@@ -0,0 +1,874 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// YUV->RGB conversion functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/yuv.h"
+
+#if defined(WEBP_USE_SSE2)
+
+#include "src/dsp/common_sse2.h"
+#include <stdlib.h>
+#include <emmintrin.h>
+
+//-----------------------------------------------------------------------------
+// Convert spans of 32 pixels to various RGB formats for the fancy upsampler.
+
+// These constants are 14b fixed-point version of ITU-R BT.601 constants.
+// R = (19077 * y + 26149 * v - 14234) >> 6
+// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6
+// B = (19077 * y + 33050 * u - 17685) >> 6
+static void ConvertYUV444ToRGB_SSE2(const __m128i* const Y0,
+ const __m128i* const U0,
+ const __m128i* const V0,
+ __m128i* const R,
+ __m128i* const G,
+ __m128i* const B) {
+ const __m128i k19077 = _mm_set1_epi16(19077);
+ const __m128i k26149 = _mm_set1_epi16(26149);
+ const __m128i k14234 = _mm_set1_epi16(14234);
+ // 33050 doesn't fit in a signed short: only use this with unsigned arithmetic
+ const __m128i k33050 = _mm_set1_epi16((short)33050);
+ const __m128i k17685 = _mm_set1_epi16(17685);
+ const __m128i k6419 = _mm_set1_epi16(6419);
+ const __m128i k13320 = _mm_set1_epi16(13320);
+ const __m128i k8708 = _mm_set1_epi16(8708);
+
+ const __m128i Y1 = _mm_mulhi_epu16(*Y0, k19077);
+
+ const __m128i R0 = _mm_mulhi_epu16(*V0, k26149);
+ const __m128i R1 = _mm_sub_epi16(Y1, k14234);
+ const __m128i R2 = _mm_add_epi16(R1, R0);
+
+ const __m128i G0 = _mm_mulhi_epu16(*U0, k6419);
+ const __m128i G1 = _mm_mulhi_epu16(*V0, k13320);
+ const __m128i G2 = _mm_add_epi16(Y1, k8708);
+ const __m128i G3 = _mm_add_epi16(G0, G1);
+ const __m128i G4 = _mm_sub_epi16(G2, G3);
+
+ // be careful with the saturated *unsigned* arithmetic here!
+ const __m128i B0 = _mm_mulhi_epu16(*U0, k33050);
+ const __m128i B1 = _mm_adds_epu16(B0, Y1);
+ const __m128i B2 = _mm_subs_epu16(B1, k17685);
+
+ // use logical shift for B2, which can be larger than 32767
+ *R = _mm_srai_epi16(R2, 6); // range: [-14234, 30815]
+ *G = _mm_srai_epi16(G4, 6); // range: [-10953, 27710]
+ *B = _mm_srli_epi16(B2, 6); // range: [0, 34238]
+}
+
+// Load the bytes into the *upper* part of 16b words. That's "<< 8", basically.
+static WEBP_INLINE __m128i Load_HI_16_SSE2(const uint8_t* src) {
+ const __m128i zero = _mm_setzero_si128();
+ return _mm_unpacklo_epi8(zero, _mm_loadl_epi64((const __m128i*)src));
+}
+
+// Load and replicate the U/V samples
+static WEBP_INLINE __m128i Load_UV_HI_8_SSE2(const uint8_t* src) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src);
+ const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0);
+ return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples
+}
+
+// Convert 32 samples of YUV444 to R/G/B
+static void YUV444ToRGB_SSE2(const uint8_t* const y,
+ const uint8_t* const u,
+ const uint8_t* const v,
+ __m128i* const R, __m128i* const G,
+ __m128i* const B) {
+ const __m128i Y0 = Load_HI_16_SSE2(y), U0 = Load_HI_16_SSE2(u),
+ V0 = Load_HI_16_SSE2(v);
+ ConvertYUV444ToRGB_SSE2(&Y0, &U0, &V0, R, G, B);
+}
+
+// Convert 32 samples of YUV420 to R/G/B
+static void YUV420ToRGB_SSE2(const uint8_t* const y,
+ const uint8_t* const u,
+ const uint8_t* const v,
+ __m128i* const R, __m128i* const G,
+ __m128i* const B) {
+ const __m128i Y0 = Load_HI_16_SSE2(y), U0 = Load_UV_HI_8_SSE2(u),
+ V0 = Load_UV_HI_8_SSE2(v);
+ ConvertYUV444ToRGB_SSE2(&Y0, &U0, &V0, R, G, B);
+}
+
+// Pack R/G/B/A results into 32b output.
+static WEBP_INLINE void PackAndStore4_SSE2(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ const __m128i* const A,
+ uint8_t* const dst) {
+ const __m128i rb = _mm_packus_epi16(*R, *B);
+ const __m128i ga = _mm_packus_epi16(*G, *A);
+ const __m128i rg = _mm_unpacklo_epi8(rb, ga);
+ const __m128i ba = _mm_unpackhi_epi8(rb, ga);
+ const __m128i RGBA_lo = _mm_unpacklo_epi16(rg, ba);
+ const __m128i RGBA_hi = _mm_unpackhi_epi16(rg, ba);
+ _mm_storeu_si128((__m128i*)(dst + 0), RGBA_lo);
+ _mm_storeu_si128((__m128i*)(dst + 16), RGBA_hi);
+}
+
+// Pack R/G/B/A results into 16b output.
+static WEBP_INLINE void PackAndStore4444_SSE2(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ const __m128i* const A,
+ uint8_t* const dst) {
+#if (WEBP_SWAP_16BIT_CSP == 0)
+ const __m128i rg0 = _mm_packus_epi16(*R, *G);
+ const __m128i ba0 = _mm_packus_epi16(*B, *A);
+#else
+ const __m128i rg0 = _mm_packus_epi16(*B, *A);
+ const __m128i ba0 = _mm_packus_epi16(*R, *G);
+#endif
+ const __m128i mask_0xf0 = _mm_set1_epi8(0xf0);
+ const __m128i rb1 = _mm_unpacklo_epi8(rg0, ba0); // rbrbrbrbrb...
+ const __m128i ga1 = _mm_unpackhi_epi8(rg0, ba0); // gagagagaga...
+ const __m128i rb2 = _mm_and_si128(rb1, mask_0xf0);
+ const __m128i ga2 = _mm_srli_epi16(_mm_and_si128(ga1, mask_0xf0), 4);
+ const __m128i rgba4444 = _mm_or_si128(rb2, ga2);
+ _mm_storeu_si128((__m128i*)dst, rgba4444);
+}
+
+// Pack R/G/B results into 16b output.
+static WEBP_INLINE void PackAndStore565_SSE2(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ uint8_t* const dst) {
+ const __m128i r0 = _mm_packus_epi16(*R, *R);
+ const __m128i g0 = _mm_packus_epi16(*G, *G);
+ const __m128i b0 = _mm_packus_epi16(*B, *B);
+ const __m128i r1 = _mm_and_si128(r0, _mm_set1_epi8(0xf8));
+ const __m128i b1 = _mm_and_si128(_mm_srli_epi16(b0, 3), _mm_set1_epi8(0x1f));
+ const __m128i g1 = _mm_srli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0xe0)), 5);
+ const __m128i g2 = _mm_slli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0x1c)), 3);
+ const __m128i rg = _mm_or_si128(r1, g1);
+ const __m128i gb = _mm_or_si128(g2, b1);
+#if (WEBP_SWAP_16BIT_CSP == 0)
+ const __m128i rgb565 = _mm_unpacklo_epi8(rg, gb);
+#else
+ const __m128i rgb565 = _mm_unpacklo_epi8(gb, rg);
+#endif
+ _mm_storeu_si128((__m128i*)dst, rgb565);
+}
+
+// Pack the planar buffers
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ...
+static WEBP_INLINE void PlanarTo24b_SSE2(__m128i* const in0, __m128i* const in1,
+ __m128i* const in2, __m128i* const in3,
+ __m128i* const in4, __m128i* const in5,
+ uint8_t* const rgb) {
+ // The input is 6 registers of sixteen 8b but for the sake of explanation,
+ // let's take 6 registers of four 8b values.
+ // To pack, we will keep taking one every two 8b integer and move it
+ // around as follows:
+ // Input:
+ // r0r1r2r3 | r4r5r6r7 | g0g1g2g3 | g4g5g6g7 | b0b1b2b3 | b4b5b6b7
+ // Split the 6 registers in two sets of 3 registers: the first set as the even
+ // 8b bytes, the second the odd ones:
+ // r0r2r4r6 | g0g2g4g6 | b0b2b4b6 | r1r3r5r7 | g1g3g5g7 | b1b3b5b7
+ // Repeat the same permutations twice more:
+ // r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7
+ // r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7
+ VP8PlanarTo24b_SSE2(in0, in1, in2, in3, in4, in5);
+
+ _mm_storeu_si128((__m128i*)(rgb + 0), *in0);
+ _mm_storeu_si128((__m128i*)(rgb + 16), *in1);
+ _mm_storeu_si128((__m128i*)(rgb + 32), *in2);
+ _mm_storeu_si128((__m128i*)(rgb + 48), *in3);
+ _mm_storeu_si128((__m128i*)(rgb + 64), *in4);
+ _mm_storeu_si128((__m128i*)(rgb + 80), *in5);
+}
+
+void VP8YuvToRgba32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ const __m128i kAlpha = _mm_set1_epi16(255);
+ int n;
+ for (n = 0; n < 32; n += 8, dst += 32) {
+ __m128i R, G, B;
+ YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B);
+ PackAndStore4_SSE2(&R, &G, &B, &kAlpha, dst);
+ }
+}
+
+void VP8YuvToBgra32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ const __m128i kAlpha = _mm_set1_epi16(255);
+ int n;
+ for (n = 0; n < 32; n += 8, dst += 32) {
+ __m128i R, G, B;
+ YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B);
+ PackAndStore4_SSE2(&B, &G, &R, &kAlpha, dst);
+ }
+}
+
+void VP8YuvToArgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ const __m128i kAlpha = _mm_set1_epi16(255);
+ int n;
+ for (n = 0; n < 32; n += 8, dst += 32) {
+ __m128i R, G, B;
+ YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B);
+ PackAndStore4_SSE2(&kAlpha, &R, &G, &B, dst);
+ }
+}
+
+void VP8YuvToRgba444432_SSE2(const uint8_t* y, const uint8_t* u,
+ const uint8_t* v, uint8_t* dst) {
+ const __m128i kAlpha = _mm_set1_epi16(255);
+ int n;
+ for (n = 0; n < 32; n += 8, dst += 16) {
+ __m128i R, G, B;
+ YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B);
+ PackAndStore4444_SSE2(&R, &G, &B, &kAlpha, dst);
+ }
+}
+
+void VP8YuvToRgb56532_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ int n;
+ for (n = 0; n < 32; n += 8, dst += 16) {
+ __m128i R, G, B;
+ YUV444ToRGB_SSE2(y + n, u + n, v + n, &R, &G, &B);
+ PackAndStore565_SSE2(&R, &G, &B, dst);
+ }
+}
+
+void VP8YuvToRgb32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5;
+
+ YUV444ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV444ToRGB_SSE2(y + 8, u + 8, v + 8, &R1, &G1, &B1);
+ YUV444ToRGB_SSE2(y + 16, u + 16, v + 16, &R2, &G2, &B2);
+ YUV444ToRGB_SSE2(y + 24, u + 24, v + 24, &R3, &G3, &B3);
+
+ // Cast to 8b and store as RRRRGGGGBBBB.
+ rgb0 = _mm_packus_epi16(R0, R1);
+ rgb1 = _mm_packus_epi16(R2, R3);
+ rgb2 = _mm_packus_epi16(G0, G1);
+ rgb3 = _mm_packus_epi16(G2, G3);
+ rgb4 = _mm_packus_epi16(B0, B1);
+ rgb5 = _mm_packus_epi16(B2, B3);
+
+ // Pack as RGBRGBRGBRGB.
+ PlanarTo24b_SSE2(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst);
+}
+
+void VP8YuvToBgr32_SSE2(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5;
+
+ YUV444ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV444ToRGB_SSE2(y + 8, u + 8, v + 8, &R1, &G1, &B1);
+ YUV444ToRGB_SSE2(y + 16, u + 16, v + 16, &R2, &G2, &B2);
+ YUV444ToRGB_SSE2(y + 24, u + 24, v + 24, &R3, &G3, &B3);
+
+ // Cast to 8b and store as BBBBGGGGRRRR.
+ bgr0 = _mm_packus_epi16(B0, B1);
+ bgr1 = _mm_packus_epi16(B2, B3);
+ bgr2 = _mm_packus_epi16(G0, G1);
+ bgr3 = _mm_packus_epi16(G2, G3);
+ bgr4 = _mm_packus_epi16(R0, R1);
+ bgr5= _mm_packus_epi16(R2, R3);
+
+ // Pack as BGRBGRBGRBGR.
+ PlanarTo24b_SSE2(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst);
+}
+
+//-----------------------------------------------------------------------------
+// Arbitrary-length row conversion functions
+
+static void YuvToRgbaRow_SSE2(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ const __m128i kAlpha = _mm_set1_epi16(255);
+ int n;
+ for (n = 0; n + 8 <= len; n += 8, dst += 32) {
+ __m128i R, G, B;
+ YUV420ToRGB_SSE2(y, u, v, &R, &G, &B);
+ PackAndStore4_SSE2(&R, &G, &B, &kAlpha, dst);
+ y += 8;
+ u += 4;
+ v += 4;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToRgba(y[0], u[0], v[0], dst);
+ dst += 4;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+static void YuvToBgraRow_SSE2(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ const __m128i kAlpha = _mm_set1_epi16(255);
+ int n;
+ for (n = 0; n + 8 <= len; n += 8, dst += 32) {
+ __m128i R, G, B;
+ YUV420ToRGB_SSE2(y, u, v, &R, &G, &B);
+ PackAndStore4_SSE2(&B, &G, &R, &kAlpha, dst);
+ y += 8;
+ u += 4;
+ v += 4;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToBgra(y[0], u[0], v[0], dst);
+ dst += 4;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+static void YuvToArgbRow_SSE2(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ const __m128i kAlpha = _mm_set1_epi16(255);
+ int n;
+ for (n = 0; n + 8 <= len; n += 8, dst += 32) {
+ __m128i R, G, B;
+ YUV420ToRGB_SSE2(y, u, v, &R, &G, &B);
+ PackAndStore4_SSE2(&kAlpha, &R, &G, &B, dst);
+ y += 8;
+ u += 4;
+ v += 4;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToArgb(y[0], u[0], v[0], dst);
+ dst += 4;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+static void YuvToRgbRow_SSE2(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ int n;
+ for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5;
+
+ YUV420ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV420ToRGB_SSE2(y + 8, u + 4, v + 4, &R1, &G1, &B1);
+ YUV420ToRGB_SSE2(y + 16, u + 8, v + 8, &R2, &G2, &B2);
+ YUV420ToRGB_SSE2(y + 24, u + 12, v + 12, &R3, &G3, &B3);
+
+ // Cast to 8b and store as RRRRGGGGBBBB.
+ rgb0 = _mm_packus_epi16(R0, R1);
+ rgb1 = _mm_packus_epi16(R2, R3);
+ rgb2 = _mm_packus_epi16(G0, G1);
+ rgb3 = _mm_packus_epi16(G2, G3);
+ rgb4 = _mm_packus_epi16(B0, B1);
+ rgb5 = _mm_packus_epi16(B2, B3);
+
+ // Pack as RGBRGBRGBRGB.
+ PlanarTo24b_SSE2(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst);
+
+ y += 32;
+ u += 16;
+ v += 16;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToRgb(y[0], u[0], v[0], dst);
+ dst += 3;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+static void YuvToBgrRow_SSE2(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ int n;
+ for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5;
+
+ YUV420ToRGB_SSE2(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV420ToRGB_SSE2(y + 8, u + 4, v + 4, &R1, &G1, &B1);
+ YUV420ToRGB_SSE2(y + 16, u + 8, v + 8, &R2, &G2, &B2);
+ YUV420ToRGB_SSE2(y + 24, u + 12, v + 12, &R3, &G3, &B3);
+
+ // Cast to 8b and store as BBBBGGGGRRRR.
+ bgr0 = _mm_packus_epi16(B0, B1);
+ bgr1 = _mm_packus_epi16(B2, B3);
+ bgr2 = _mm_packus_epi16(G0, G1);
+ bgr3 = _mm_packus_epi16(G2, G3);
+ bgr4 = _mm_packus_epi16(R0, R1);
+ bgr5 = _mm_packus_epi16(R2, R3);
+
+ // Pack as BGRBGRBGRBGR.
+ PlanarTo24b_SSE2(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst);
+
+ y += 32;
+ u += 16;
+ v += 16;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToBgr(y[0], u[0], v[0], dst);
+ dst += 3;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitSamplersSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersSSE2(void) {
+ WebPSamplers[MODE_RGB] = YuvToRgbRow_SSE2;
+ WebPSamplers[MODE_RGBA] = YuvToRgbaRow_SSE2;
+ WebPSamplers[MODE_BGR] = YuvToBgrRow_SSE2;
+ WebPSamplers[MODE_BGRA] = YuvToBgraRow_SSE2;
+ WebPSamplers[MODE_ARGB] = YuvToArgbRow_SSE2;
+}
+
+//------------------------------------------------------------------------------
+// RGB24/32 -> YUV converters
+
+// Load eight 16b-words from *src.
+#define LOAD_16(src) _mm_loadu_si128((const __m128i*)(src))
+// Store either 16b-words into *dst
+#define STORE_16(V, dst) _mm_storeu_si128((__m128i*)(dst), (V))
+
+// Function that inserts a value of the second half of the in buffer in between
+// every two char of the first half.
+static WEBP_INLINE void RGB24PackedToPlanarHelper_SSE2(
+ const __m128i* const in /*in[6]*/, __m128i* const out /*out[6]*/) {
+ out[0] = _mm_unpacklo_epi8(in[0], in[3]);
+ out[1] = _mm_unpackhi_epi8(in[0], in[3]);
+ out[2] = _mm_unpacklo_epi8(in[1], in[4]);
+ out[3] = _mm_unpackhi_epi8(in[1], in[4]);
+ out[4] = _mm_unpacklo_epi8(in[2], in[5]);
+ out[5] = _mm_unpackhi_epi8(in[2], in[5]);
+}
+
+// Unpack the 8b input rgbrgbrgbrgb ... as contiguous registers:
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// Similar to PlanarTo24bHelper(), but in reverse order.
+static WEBP_INLINE void RGB24PackedToPlanar_SSE2(
+ const uint8_t* const rgb, __m128i* const out /*out[6]*/) {
+ __m128i tmp[6];
+ tmp[0] = _mm_loadu_si128((const __m128i*)(rgb + 0));
+ tmp[1] = _mm_loadu_si128((const __m128i*)(rgb + 16));
+ tmp[2] = _mm_loadu_si128((const __m128i*)(rgb + 32));
+ tmp[3] = _mm_loadu_si128((const __m128i*)(rgb + 48));
+ tmp[4] = _mm_loadu_si128((const __m128i*)(rgb + 64));
+ tmp[5] = _mm_loadu_si128((const __m128i*)(rgb + 80));
+
+ RGB24PackedToPlanarHelper_SSE2(tmp, out);
+ RGB24PackedToPlanarHelper_SSE2(out, tmp);
+ RGB24PackedToPlanarHelper_SSE2(tmp, out);
+ RGB24PackedToPlanarHelper_SSE2(out, tmp);
+ RGB24PackedToPlanarHelper_SSE2(tmp, out);
+}
+
+// Convert 8 packed ARGB to r[], g[], b[]
+static WEBP_INLINE void RGB32PackedToPlanar_SSE2(const uint32_t* const argb,
+ __m128i* const rgb /*in[6]*/) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i a0 = LOAD_16(argb + 0);
+ __m128i a1 = LOAD_16(argb + 4);
+ __m128i a2 = LOAD_16(argb + 8);
+ __m128i a3 = LOAD_16(argb + 12);
+ VP8L32bToPlanar_SSE2(&a0, &a1, &a2, &a3);
+ rgb[0] = _mm_unpacklo_epi8(a1, zero);
+ rgb[1] = _mm_unpackhi_epi8(a1, zero);
+ rgb[2] = _mm_unpacklo_epi8(a2, zero);
+ rgb[3] = _mm_unpackhi_epi8(a2, zero);
+ rgb[4] = _mm_unpacklo_epi8(a3, zero);
+ rgb[5] = _mm_unpackhi_epi8(a3, zero);
+}
+
+// This macro computes (RG * MULT_RG + GB * MULT_GB + ROUNDER) >> DESCALE_FIX
+// It's a macro and not a function because we need to use immediate values with
+// srai_epi32, e.g.
+#define TRANSFORM(RG_LO, RG_HI, GB_LO, GB_HI, MULT_RG, MULT_GB, \
+ ROUNDER, DESCALE_FIX, OUT) do { \
+ const __m128i V0_lo = _mm_madd_epi16(RG_LO, MULT_RG); \
+ const __m128i V0_hi = _mm_madd_epi16(RG_HI, MULT_RG); \
+ const __m128i V1_lo = _mm_madd_epi16(GB_LO, MULT_GB); \
+ const __m128i V1_hi = _mm_madd_epi16(GB_HI, MULT_GB); \
+ const __m128i V2_lo = _mm_add_epi32(V0_lo, V1_lo); \
+ const __m128i V2_hi = _mm_add_epi32(V0_hi, V1_hi); \
+ const __m128i V3_lo = _mm_add_epi32(V2_lo, ROUNDER); \
+ const __m128i V3_hi = _mm_add_epi32(V2_hi, ROUNDER); \
+ const __m128i V5_lo = _mm_srai_epi32(V3_lo, DESCALE_FIX); \
+ const __m128i V5_hi = _mm_srai_epi32(V3_hi, DESCALE_FIX); \
+ (OUT) = _mm_packs_epi32(V5_lo, V5_hi); \
+} while (0)
+
+#define MK_CST_16(A, B) _mm_set_epi16((B), (A), (B), (A), (B), (A), (B), (A))
+static WEBP_INLINE void ConvertRGBToY_SSE2(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ __m128i* const Y) {
+ const __m128i kRG_y = MK_CST_16(16839, 33059 - 16384);
+ const __m128i kGB_y = MK_CST_16(16384, 6420);
+ const __m128i kHALF_Y = _mm_set1_epi32((16 << YUV_FIX) + YUV_HALF);
+
+ const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G);
+ const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G);
+ const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B);
+ const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_y, kGB_y, kHALF_Y, YUV_FIX, *Y);
+}
+
+static WEBP_INLINE void ConvertRGBToUV_SSE2(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ __m128i* const U,
+ __m128i* const V) {
+ const __m128i kRG_u = MK_CST_16(-9719, -19081);
+ const __m128i kGB_u = MK_CST_16(0, 28800);
+ const __m128i kRG_v = MK_CST_16(28800, 0);
+ const __m128i kGB_v = MK_CST_16(-24116, -4684);
+ const __m128i kHALF_UV = _mm_set1_epi32(((128 << YUV_FIX) + YUV_HALF) << 2);
+
+ const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G);
+ const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G);
+ const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B);
+ const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_u, kGB_u,
+ kHALF_UV, YUV_FIX + 2, *U);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_v, kGB_v,
+ kHALF_UV, YUV_FIX + 2, *V);
+}
+
+#undef MK_CST_16
+#undef TRANSFORM
+
+static void ConvertRGB24ToY_SSE2(const uint8_t* rgb, uint8_t* y, int width) {
+ const int max_width = width & ~31;
+ int i;
+ for (i = 0; i < max_width; rgb += 3 * 16 * 2) {
+ __m128i rgb_plane[6];
+ int j;
+
+ RGB24PackedToPlanar_SSE2(rgb, rgb_plane);
+
+ for (j = 0; j < 2; ++j, i += 16) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i r, g, b, Y0, Y1;
+
+ // Convert to 16-bit Y.
+ r = _mm_unpacklo_epi8(rgb_plane[0 + j], zero);
+ g = _mm_unpacklo_epi8(rgb_plane[2 + j], zero);
+ b = _mm_unpacklo_epi8(rgb_plane[4 + j], zero);
+ ConvertRGBToY_SSE2(&r, &g, &b, &Y0);
+
+ // Convert to 16-bit Y.
+ r = _mm_unpackhi_epi8(rgb_plane[0 + j], zero);
+ g = _mm_unpackhi_epi8(rgb_plane[2 + j], zero);
+ b = _mm_unpackhi_epi8(rgb_plane[4 + j], zero);
+ ConvertRGBToY_SSE2(&r, &g, &b, &Y1);
+
+ // Cast to 8-bit and store.
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ }
+ for (; i < width; ++i, rgb += 3) { // left-over
+ y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF);
+ }
+}
+
+static void ConvertBGR24ToY_SSE2(const uint8_t* bgr, uint8_t* y, int width) {
+ const int max_width = width & ~31;
+ int i;
+ for (i = 0; i < max_width; bgr += 3 * 16 * 2) {
+ __m128i bgr_plane[6];
+ int j;
+
+ RGB24PackedToPlanar_SSE2(bgr, bgr_plane);
+
+ for (j = 0; j < 2; ++j, i += 16) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i r, g, b, Y0, Y1;
+
+ // Convert to 16-bit Y.
+ b = _mm_unpacklo_epi8(bgr_plane[0 + j], zero);
+ g = _mm_unpacklo_epi8(bgr_plane[2 + j], zero);
+ r = _mm_unpacklo_epi8(bgr_plane[4 + j], zero);
+ ConvertRGBToY_SSE2(&r, &g, &b, &Y0);
+
+ // Convert to 16-bit Y.
+ b = _mm_unpackhi_epi8(bgr_plane[0 + j], zero);
+ g = _mm_unpackhi_epi8(bgr_plane[2 + j], zero);
+ r = _mm_unpackhi_epi8(bgr_plane[4 + j], zero);
+ ConvertRGBToY_SSE2(&r, &g, &b, &Y1);
+
+ // Cast to 8-bit and store.
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ }
+ for (; i < width; ++i, bgr += 3) { // left-over
+ y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF);
+ }
+}
+
+static void ConvertARGBToY_SSE2(const uint32_t* argb, uint8_t* y, int width) {
+ const int max_width = width & ~15;
+ int i;
+ for (i = 0; i < max_width; i += 16) {
+ __m128i Y0, Y1, rgb[6];
+ RGB32PackedToPlanar_SSE2(&argb[i], rgb);
+ ConvertRGBToY_SSE2(&rgb[0], &rgb[2], &rgb[4], &Y0);
+ ConvertRGBToY_SSE2(&rgb[1], &rgb[3], &rgb[5], &Y1);
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ for (; i < width; ++i) { // left-over
+ const uint32_t p = argb[i];
+ y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff,
+ YUV_HALF);
+ }
+}
+
+// Horizontal add (doubled) of two 16b values, result is 16b.
+// in: A | B | C | D | ... -> out: 2*(A+B) | 2*(C+D) | ...
+static void HorizontalAddPack_SSE2(const __m128i* const A,
+ const __m128i* const B,
+ __m128i* const out) {
+ const __m128i k2 = _mm_set1_epi16(2);
+ const __m128i C = _mm_madd_epi16(*A, k2);
+ const __m128i D = _mm_madd_epi16(*B, k2);
+ *out = _mm_packs_epi32(C, D);
+}
+
+static void ConvertARGBToUV_SSE2(const uint32_t* argb,
+ uint8_t* u, uint8_t* v,
+ int src_width, int do_store) {
+ const int max_width = src_width & ~31;
+ int i;
+ for (i = 0; i < max_width; i += 32, u += 16, v += 16) {
+ __m128i rgb[6], U0, V0, U1, V1;
+ RGB32PackedToPlanar_SSE2(&argb[i], rgb);
+ HorizontalAddPack_SSE2(&rgb[0], &rgb[1], &rgb[0]);
+ HorizontalAddPack_SSE2(&rgb[2], &rgb[3], &rgb[2]);
+ HorizontalAddPack_SSE2(&rgb[4], &rgb[5], &rgb[4]);
+ ConvertRGBToUV_SSE2(&rgb[0], &rgb[2], &rgb[4], &U0, &V0);
+
+ RGB32PackedToPlanar_SSE2(&argb[i + 16], rgb);
+ HorizontalAddPack_SSE2(&rgb[0], &rgb[1], &rgb[0]);
+ HorizontalAddPack_SSE2(&rgb[2], &rgb[3], &rgb[2]);
+ HorizontalAddPack_SSE2(&rgb[4], &rgb[5], &rgb[4]);
+ ConvertRGBToUV_SSE2(&rgb[0], &rgb[2], &rgb[4], &U1, &V1);
+
+ U0 = _mm_packus_epi16(U0, U1);
+ V0 = _mm_packus_epi16(V0, V1);
+ if (!do_store) {
+ const __m128i prev_u = LOAD_16(u);
+ const __m128i prev_v = LOAD_16(v);
+ U0 = _mm_avg_epu8(U0, prev_u);
+ V0 = _mm_avg_epu8(V0, prev_v);
+ }
+ STORE_16(U0, u);
+ STORE_16(V0, v);
+ }
+ if (i < src_width) { // left-over
+ WebPConvertARGBToUV_C(argb + i, u, v, src_width - i, do_store);
+ }
+}
+
+// Convert 16 packed ARGB 16b-values to r[], g[], b[]
+static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE2(
+ const uint16_t* const rgbx,
+ __m128i* const r, __m128i* const g, __m128i* const b) {
+ const __m128i in0 = LOAD_16(rgbx + 0); // r0 | g0 | b0 |x| r1 | g1 | b1 |x
+ const __m128i in1 = LOAD_16(rgbx + 8); // r2 | g2 | b2 |x| r3 | g3 | b3 |x
+ const __m128i in2 = LOAD_16(rgbx + 16); // r4 | ...
+ const __m128i in3 = LOAD_16(rgbx + 24); // r6 | ...
+ // column-wise transpose
+ const __m128i A0 = _mm_unpacklo_epi16(in0, in1);
+ const __m128i A1 = _mm_unpackhi_epi16(in0, in1);
+ const __m128i A2 = _mm_unpacklo_epi16(in2, in3);
+ const __m128i A3 = _mm_unpackhi_epi16(in2, in3);
+ const __m128i B0 = _mm_unpacklo_epi16(A0, A1); // r0 r1 r2 r3 | g0 g1 ..
+ const __m128i B1 = _mm_unpackhi_epi16(A0, A1); // b0 b1 b2 b3 | x x x x
+ const __m128i B2 = _mm_unpacklo_epi16(A2, A3); // r4 r5 r6 r7 | g4 g5 ..
+ const __m128i B3 = _mm_unpackhi_epi16(A2, A3); // b4 b5 b6 b7 | x x x x
+ *r = _mm_unpacklo_epi64(B0, B2);
+ *g = _mm_unpackhi_epi64(B0, B2);
+ *b = _mm_unpacklo_epi64(B1, B3);
+}
+
+static void ConvertRGBA32ToUV_SSE2(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width) {
+ const int max_width = width & ~15;
+ const uint16_t* const last_rgb = rgb + 4 * max_width;
+ while (rgb < last_rgb) {
+ __m128i r, g, b, U0, V0, U1, V1;
+ RGBA32PackedToPlanar_16b_SSE2(rgb + 0, &r, &g, &b);
+ ConvertRGBToUV_SSE2(&r, &g, &b, &U0, &V0);
+ RGBA32PackedToPlanar_16b_SSE2(rgb + 32, &r, &g, &b);
+ ConvertRGBToUV_SSE2(&r, &g, &b, &U1, &V1);
+ STORE_16(_mm_packus_epi16(U0, U1), u);
+ STORE_16(_mm_packus_epi16(V0, V1), v);
+ u += 16;
+ v += 16;
+ rgb += 2 * 32;
+ }
+ if (max_width < width) { // left-over
+ WebPConvertRGBA32ToUV_C(rgb, u, v, width - max_width);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+extern void WebPInitConvertARGBToYUVSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVSSE2(void) {
+ WebPConvertARGBToY = ConvertARGBToY_SSE2;
+ WebPConvertARGBToUV = ConvertARGBToUV_SSE2;
+
+ WebPConvertRGB24ToY = ConvertRGB24ToY_SSE2;
+ WebPConvertBGR24ToY = ConvertBGR24ToY_SSE2;
+
+ WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_SSE2;
+}
+
+//------------------------------------------------------------------------------
+
+#define MAX_Y ((1 << 10) - 1) // 10b precision over 16b-arithmetic
+static uint16_t clip_y(int v) {
+ return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
+}
+
+static uint64_t SharpYUVUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
+ uint16_t* dst, int len) {
+ uint64_t diff = 0;
+ uint32_t tmp[4];
+ int i;
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i max = _mm_set1_epi16(MAX_Y);
+ const __m128i one = _mm_set1_epi16(1);
+ __m128i sum = zero;
+
+ for (i = 0; i + 8 <= len; i += 8) {
+ const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
+ const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
+ const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
+ const __m128i D = _mm_sub_epi16(A, B); // diff_y
+ const __m128i E = _mm_cmpgt_epi16(zero, D); // sign (-1 or 0)
+ const __m128i F = _mm_add_epi16(C, D); // new_y
+ const __m128i G = _mm_or_si128(E, one); // -1 or 1
+ const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero);
+ const __m128i I = _mm_madd_epi16(D, G); // sum(abs(...))
+ _mm_storeu_si128((__m128i*)(dst + i), H);
+ sum = _mm_add_epi32(sum, I);
+ }
+ _mm_storeu_si128((__m128i*)tmp, sum);
+ diff = tmp[3] + tmp[2] + tmp[1] + tmp[0];
+ for (; i < len; ++i) {
+ const int diff_y = ref[i] - src[i];
+ const int new_y = (int)dst[i] + diff_y;
+ dst[i] = clip_y(new_y);
+ diff += (uint64_t)abs(diff_y);
+ }
+ return diff;
+}
+
+static void SharpYUVUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
+ int16_t* dst, int len) {
+ int i = 0;
+ for (i = 0; i + 8 <= len; i += 8) {
+ const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
+ const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
+ const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
+ const __m128i D = _mm_sub_epi16(A, B); // diff_uv
+ const __m128i E = _mm_add_epi16(C, D); // new_uv
+ _mm_storeu_si128((__m128i*)(dst + i), E);
+ }
+ for (; i < len; ++i) {
+ const int diff_uv = ref[i] - src[i];
+ dst[i] += diff_uv;
+ }
+}
+
+static void SharpYUVFilterRow_SSE2(const int16_t* A, const int16_t* B, int len,
+ const uint16_t* best_y, uint16_t* out) {
+ int i;
+ const __m128i kCst8 = _mm_set1_epi16(8);
+ const __m128i max = _mm_set1_epi16(MAX_Y);
+ const __m128i zero = _mm_setzero_si128();
+ for (i = 0; i + 8 <= len; i += 8) {
+ const __m128i a0 = _mm_loadu_si128((const __m128i*)(A + i + 0));
+ const __m128i a1 = _mm_loadu_si128((const __m128i*)(A + i + 1));
+ const __m128i b0 = _mm_loadu_si128((const __m128i*)(B + i + 0));
+ const __m128i b1 = _mm_loadu_si128((const __m128i*)(B + i + 1));
+ const __m128i a0b1 = _mm_add_epi16(a0, b1);
+ const __m128i a1b0 = _mm_add_epi16(a1, b0);
+ const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0); // A0+A1+B0+B1
+ const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8);
+ const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1); // 2*(A0+B1)
+ const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0); // 2*(A1+B0)
+ const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3);
+ const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3);
+ const __m128i d0 = _mm_add_epi16(c1, a0);
+ const __m128i d1 = _mm_add_epi16(c0, a1);
+ const __m128i e0 = _mm_srai_epi16(d0, 1);
+ const __m128i e1 = _mm_srai_epi16(d1, 1);
+ const __m128i f0 = _mm_unpacklo_epi16(e0, e1);
+ const __m128i f1 = _mm_unpackhi_epi16(e0, e1);
+ const __m128i g0 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
+ const __m128i g1 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 8));
+ const __m128i h0 = _mm_add_epi16(g0, f0);
+ const __m128i h1 = _mm_add_epi16(g1, f1);
+ const __m128i i0 = _mm_max_epi16(_mm_min_epi16(h0, max), zero);
+ const __m128i i1 = _mm_max_epi16(_mm_min_epi16(h1, max), zero);
+ _mm_storeu_si128((__m128i*)(out + 2 * i + 0), i0);
+ _mm_storeu_si128((__m128i*)(out + 2 * i + 8), i1);
+ }
+ for (; i < len; ++i) {
+ // (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
+ // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
+ // We reuse the common sub-expressions.
+ const int a0b1 = A[i + 0] + B[i + 1];
+ const int a1b0 = A[i + 1] + B[i + 0];
+ const int a0a1b0b1 = a0b1 + a1b0 + 8;
+ const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+ const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+ out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0);
+ out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1);
+ }
+}
+
+#undef MAX_Y
+
+//------------------------------------------------------------------------------
+
+extern void WebPInitSharpYUVSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVSSE2(void) {
+ WebPSharpYUVUpdateY = SharpYUVUpdateY_SSE2;
+ WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_SSE2;
+ WebPSharpYUVFilterRow = SharpYUVFilterRow_SSE2;
+}
+
+#else // !WEBP_USE_SSE2
+
+WEBP_DSP_INIT_STUB(WebPInitSamplersSSE2)
+WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVSSE2)
+WEBP_DSP_INIT_STUB(WebPInitSharpYUVSSE2)
+
+#endif // WEBP_USE_SSE2
diff --git a/src/third_party/libwebp/src/dsp/yuv_sse41.c b/src/third_party/libwebp/src/dsp/yuv_sse41.c
new file mode 100644
index 0000000..579d1f7
--- /dev/null
+++ b/src/third_party/libwebp/src/dsp/yuv_sse41.c
@@ -0,0 +1,613 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// YUV->RGB conversion functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/dsp/yuv.h"
+
+#if defined(WEBP_USE_SSE41)
+
+#include "src/dsp/common_sse41.h"
+#include <stdlib.h>
+#include <smmintrin.h>
+
+//-----------------------------------------------------------------------------
+// Convert spans of 32 pixels to various RGB formats for the fancy upsampler.
+
+// These constants are 14b fixed-point version of ITU-R BT.601 constants.
+// R = (19077 * y + 26149 * v - 14234) >> 6
+// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6
+// B = (19077 * y + 33050 * u - 17685) >> 6
+static void ConvertYUV444ToRGB_SSE41(const __m128i* const Y0,
+ const __m128i* const U0,
+ const __m128i* const V0,
+ __m128i* const R,
+ __m128i* const G,
+ __m128i* const B) {
+ const __m128i k19077 = _mm_set1_epi16(19077);
+ const __m128i k26149 = _mm_set1_epi16(26149);
+ const __m128i k14234 = _mm_set1_epi16(14234);
+ // 33050 doesn't fit in a signed short: only use this with unsigned arithmetic
+ const __m128i k33050 = _mm_set1_epi16((short)33050);
+ const __m128i k17685 = _mm_set1_epi16(17685);
+ const __m128i k6419 = _mm_set1_epi16(6419);
+ const __m128i k13320 = _mm_set1_epi16(13320);
+ const __m128i k8708 = _mm_set1_epi16(8708);
+
+ const __m128i Y1 = _mm_mulhi_epu16(*Y0, k19077);
+
+ const __m128i R0 = _mm_mulhi_epu16(*V0, k26149);
+ const __m128i R1 = _mm_sub_epi16(Y1, k14234);
+ const __m128i R2 = _mm_add_epi16(R1, R0);
+
+ const __m128i G0 = _mm_mulhi_epu16(*U0, k6419);
+ const __m128i G1 = _mm_mulhi_epu16(*V0, k13320);
+ const __m128i G2 = _mm_add_epi16(Y1, k8708);
+ const __m128i G3 = _mm_add_epi16(G0, G1);
+ const __m128i G4 = _mm_sub_epi16(G2, G3);
+
+ // be careful with the saturated *unsigned* arithmetic here!
+ const __m128i B0 = _mm_mulhi_epu16(*U0, k33050);
+ const __m128i B1 = _mm_adds_epu16(B0, Y1);
+ const __m128i B2 = _mm_subs_epu16(B1, k17685);
+
+ // use logical shift for B2, which can be larger than 32767
+ *R = _mm_srai_epi16(R2, 6); // range: [-14234, 30815]
+ *G = _mm_srai_epi16(G4, 6); // range: [-10953, 27710]
+ *B = _mm_srli_epi16(B2, 6); // range: [0, 34238]
+}
+
+// Load the bytes into the *upper* part of 16b words. That's "<< 8", basically.
+static WEBP_INLINE __m128i Load_HI_16_SSE41(const uint8_t* src) {
+ const __m128i zero = _mm_setzero_si128();
+ return _mm_unpacklo_epi8(zero, _mm_loadl_epi64((const __m128i*)src));
+}
+
+// Load and replicate the U/V samples
+static WEBP_INLINE __m128i Load_UV_HI_8_SSE41(const uint8_t* src) {
+ const __m128i zero = _mm_setzero_si128();
+ const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src);
+ const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0);
+ return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples
+}
+
+// Convert 32 samples of YUV444 to R/G/B
+static void YUV444ToRGB_SSE41(const uint8_t* const y,
+ const uint8_t* const u,
+ const uint8_t* const v,
+ __m128i* const R, __m128i* const G,
+ __m128i* const B) {
+ const __m128i Y0 = Load_HI_16_SSE41(y), U0 = Load_HI_16_SSE41(u),
+ V0 = Load_HI_16_SSE41(v);
+ ConvertYUV444ToRGB_SSE41(&Y0, &U0, &V0, R, G, B);
+}
+
+// Convert 32 samples of YUV420 to R/G/B
+static void YUV420ToRGB_SSE41(const uint8_t* const y,
+ const uint8_t* const u,
+ const uint8_t* const v,
+ __m128i* const R, __m128i* const G,
+ __m128i* const B) {
+ const __m128i Y0 = Load_HI_16_SSE41(y), U0 = Load_UV_HI_8_SSE41(u),
+ V0 = Load_UV_HI_8_SSE41(v);
+ ConvertYUV444ToRGB_SSE41(&Y0, &U0, &V0, R, G, B);
+}
+
+// Pack the planar buffers
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ...
+static WEBP_INLINE void PlanarTo24b_SSE41(
+ __m128i* const in0, __m128i* const in1, __m128i* const in2,
+ __m128i* const in3, __m128i* const in4, __m128i* const in5,
+ uint8_t* const rgb) {
+ // The input is 6 registers of sixteen 8b but for the sake of explanation,
+ // let's take 6 registers of four 8b values.
+ // To pack, we will keep taking one every two 8b integer and move it
+ // around as follows:
+ // Input:
+ // r0r1r2r3 | r4r5r6r7 | g0g1g2g3 | g4g5g6g7 | b0b1b2b3 | b4b5b6b7
+ // Split the 6 registers in two sets of 3 registers: the first set as the even
+ // 8b bytes, the second the odd ones:
+ // r0r2r4r6 | g0g2g4g6 | b0b2b4b6 | r1r3r5r7 | g1g3g5g7 | b1b3b5b7
+ // Repeat the same permutations twice more:
+ // r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7
+ // r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7
+ VP8PlanarTo24b_SSE41(in0, in1, in2, in3, in4, in5);
+
+ _mm_storeu_si128((__m128i*)(rgb + 0), *in0);
+ _mm_storeu_si128((__m128i*)(rgb + 16), *in1);
+ _mm_storeu_si128((__m128i*)(rgb + 32), *in2);
+ _mm_storeu_si128((__m128i*)(rgb + 48), *in3);
+ _mm_storeu_si128((__m128i*)(rgb + 64), *in4);
+ _mm_storeu_si128((__m128i*)(rgb + 80), *in5);
+}
+
+void VP8YuvToRgb32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5;
+
+ YUV444ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV444ToRGB_SSE41(y + 8, u + 8, v + 8, &R1, &G1, &B1);
+ YUV444ToRGB_SSE41(y + 16, u + 16, v + 16, &R2, &G2, &B2);
+ YUV444ToRGB_SSE41(y + 24, u + 24, v + 24, &R3, &G3, &B3);
+
+ // Cast to 8b and store as RRRRGGGGBBBB.
+ rgb0 = _mm_packus_epi16(R0, R1);
+ rgb1 = _mm_packus_epi16(R2, R3);
+ rgb2 = _mm_packus_epi16(G0, G1);
+ rgb3 = _mm_packus_epi16(G2, G3);
+ rgb4 = _mm_packus_epi16(B0, B1);
+ rgb5 = _mm_packus_epi16(B2, B3);
+
+ // Pack as RGBRGBRGBRGB.
+ PlanarTo24b_SSE41(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst);
+}
+
+void VP8YuvToBgr32_SSE41(const uint8_t* y, const uint8_t* u, const uint8_t* v,
+ uint8_t* dst) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5;
+
+ YUV444ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV444ToRGB_SSE41(y + 8, u + 8, v + 8, &R1, &G1, &B1);
+ YUV444ToRGB_SSE41(y + 16, u + 16, v + 16, &R2, &G2, &B2);
+ YUV444ToRGB_SSE41(y + 24, u + 24, v + 24, &R3, &G3, &B3);
+
+ // Cast to 8b and store as BBBBGGGGRRRR.
+ bgr0 = _mm_packus_epi16(B0, B1);
+ bgr1 = _mm_packus_epi16(B2, B3);
+ bgr2 = _mm_packus_epi16(G0, G1);
+ bgr3 = _mm_packus_epi16(G2, G3);
+ bgr4 = _mm_packus_epi16(R0, R1);
+ bgr5= _mm_packus_epi16(R2, R3);
+
+ // Pack as BGRBGRBGRBGR.
+ PlanarTo24b_SSE41(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst);
+}
+
+//-----------------------------------------------------------------------------
+// Arbitrary-length row conversion functions
+
+static void YuvToRgbRow_SSE41(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ int n;
+ for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i rgb0, rgb1, rgb2, rgb3, rgb4, rgb5;
+
+ YUV420ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV420ToRGB_SSE41(y + 8, u + 4, v + 4, &R1, &G1, &B1);
+ YUV420ToRGB_SSE41(y + 16, u + 8, v + 8, &R2, &G2, &B2);
+ YUV420ToRGB_SSE41(y + 24, u + 12, v + 12, &R3, &G3, &B3);
+
+ // Cast to 8b and store as RRRRGGGGBBBB.
+ rgb0 = _mm_packus_epi16(R0, R1);
+ rgb1 = _mm_packus_epi16(R2, R3);
+ rgb2 = _mm_packus_epi16(G0, G1);
+ rgb3 = _mm_packus_epi16(G2, G3);
+ rgb4 = _mm_packus_epi16(B0, B1);
+ rgb5 = _mm_packus_epi16(B2, B3);
+
+ // Pack as RGBRGBRGBRGB.
+ PlanarTo24b_SSE41(&rgb0, &rgb1, &rgb2, &rgb3, &rgb4, &rgb5, dst);
+
+ y += 32;
+ u += 16;
+ v += 16;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToRgb(y[0], u[0], v[0], dst);
+ dst += 3;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+static void YuvToBgrRow_SSE41(const uint8_t* y,
+ const uint8_t* u, const uint8_t* v,
+ uint8_t* dst, int len) {
+ int n;
+ for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) {
+ __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3;
+ __m128i bgr0, bgr1, bgr2, bgr3, bgr4, bgr5;
+
+ YUV420ToRGB_SSE41(y + 0, u + 0, v + 0, &R0, &G0, &B0);
+ YUV420ToRGB_SSE41(y + 8, u + 4, v + 4, &R1, &G1, &B1);
+ YUV420ToRGB_SSE41(y + 16, u + 8, v + 8, &R2, &G2, &B2);
+ YUV420ToRGB_SSE41(y + 24, u + 12, v + 12, &R3, &G3, &B3);
+
+ // Cast to 8b and store as BBBBGGGGRRRR.
+ bgr0 = _mm_packus_epi16(B0, B1);
+ bgr1 = _mm_packus_epi16(B2, B3);
+ bgr2 = _mm_packus_epi16(G0, G1);
+ bgr3 = _mm_packus_epi16(G2, G3);
+ bgr4 = _mm_packus_epi16(R0, R1);
+ bgr5 = _mm_packus_epi16(R2, R3);
+
+ // Pack as BGRBGRBGRBGR.
+ PlanarTo24b_SSE41(&bgr0, &bgr1, &bgr2, &bgr3, &bgr4, &bgr5, dst);
+
+ y += 32;
+ u += 16;
+ v += 16;
+ }
+ for (; n < len; ++n) { // Finish off
+ VP8YuvToBgr(y[0], u[0], v[0], dst);
+ dst += 3;
+ y += 1;
+ u += (n & 1);
+ v += (n & 1);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+extern void WebPInitSamplersSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersSSE41(void) {
+ WebPSamplers[MODE_RGB] = YuvToRgbRow_SSE41;
+ WebPSamplers[MODE_BGR] = YuvToBgrRow_SSE41;
+}
+
+//------------------------------------------------------------------------------
+// RGB24/32 -> YUV converters
+
+// Load eight 16b-words from *src.
+#define LOAD_16(src) _mm_loadu_si128((const __m128i*)(src))
+// Store either 16b-words into *dst
+#define STORE_16(V, dst) _mm_storeu_si128((__m128i*)(dst), (V))
+
+#define WEBP_SSE41_SHUFF(OUT) do { \
+ const __m128i tmp0 = _mm_shuffle_epi8(A0, shuff0); \
+ const __m128i tmp1 = _mm_shuffle_epi8(A1, shuff1); \
+ const __m128i tmp2 = _mm_shuffle_epi8(A2, shuff2); \
+ const __m128i tmp3 = _mm_shuffle_epi8(A3, shuff0); \
+ const __m128i tmp4 = _mm_shuffle_epi8(A4, shuff1); \
+ const __m128i tmp5 = _mm_shuffle_epi8(A5, shuff2); \
+ \
+ /* OR everything to get one channel */ \
+ const __m128i tmp6 = _mm_or_si128(tmp0, tmp1); \
+ const __m128i tmp7 = _mm_or_si128(tmp3, tmp4); \
+ out[OUT + 0] = _mm_or_si128(tmp6, tmp2); \
+ out[OUT + 1] = _mm_or_si128(tmp7, tmp5); \
+} while (0);
+
+// Unpack the 8b input rgbrgbrgbrgb ... as contiguous registers:
+// rrrr... rrrr... gggg... gggg... bbbb... bbbb....
+// Similar to PlanarTo24bHelper(), but in reverse order.
+static WEBP_INLINE void RGB24PackedToPlanar_SSE41(
+ const uint8_t* const rgb, __m128i* const out /*out[6]*/) {
+ const __m128i A0 = _mm_loadu_si128((const __m128i*)(rgb + 0));
+ const __m128i A1 = _mm_loadu_si128((const __m128i*)(rgb + 16));
+ const __m128i A2 = _mm_loadu_si128((const __m128i*)(rgb + 32));
+ const __m128i A3 = _mm_loadu_si128((const __m128i*)(rgb + 48));
+ const __m128i A4 = _mm_loadu_si128((const __m128i*)(rgb + 64));
+ const __m128i A5 = _mm_loadu_si128((const __m128i*)(rgb + 80));
+
+ // Compute RR.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 12, 9, 6, 3, 0);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, 14, 11, 8, 5, 2, -1, -1, -1, -1, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 13, 10, 7, 4, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ WEBP_SSE41_SHUFF(0)
+ }
+ // Compute GG.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 13, 10, 7, 4, 1);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, 15, 12, 9, 6, 3, 0, -1, -1, -1, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 14, 11, 8, 5, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ WEBP_SSE41_SHUFF(2)
+ }
+ // Compute BB.
+ {
+ const __m128i shuff0 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 14, 11, 8, 5, 2);
+ const __m128i shuff1 = _mm_set_epi8(
+ -1, -1, -1, -1, -1, -1, 13, 10, 7, 4, 1, -1, -1, -1, -1, -1);
+ const __m128i shuff2 = _mm_set_epi8(
+ 15, 12, 9, 6, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ WEBP_SSE41_SHUFF(4)
+ }
+}
+
+#undef WEBP_SSE41_SHUFF
+
+// Convert 8 packed ARGB to r[], g[], b[]
+static WEBP_INLINE void RGB32PackedToPlanar_SSE41(
+ const uint32_t* const argb, __m128i* const rgb /*in[6]*/) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i a0 = LOAD_16(argb + 0);
+ __m128i a1 = LOAD_16(argb + 4);
+ __m128i a2 = LOAD_16(argb + 8);
+ __m128i a3 = LOAD_16(argb + 12);
+ VP8L32bToPlanar_SSE41(&a0, &a1, &a2, &a3);
+ rgb[0] = _mm_unpacklo_epi8(a1, zero);
+ rgb[1] = _mm_unpackhi_epi8(a1, zero);
+ rgb[2] = _mm_unpacklo_epi8(a2, zero);
+ rgb[3] = _mm_unpackhi_epi8(a2, zero);
+ rgb[4] = _mm_unpacklo_epi8(a3, zero);
+ rgb[5] = _mm_unpackhi_epi8(a3, zero);
+}
+
+// This macro computes (RG * MULT_RG + GB * MULT_GB + ROUNDER) >> DESCALE_FIX
+// It's a macro and not a function because we need to use immediate values with
+// srai_epi32, e.g.
+#define TRANSFORM(RG_LO, RG_HI, GB_LO, GB_HI, MULT_RG, MULT_GB, \
+ ROUNDER, DESCALE_FIX, OUT) do { \
+ const __m128i V0_lo = _mm_madd_epi16(RG_LO, MULT_RG); \
+ const __m128i V0_hi = _mm_madd_epi16(RG_HI, MULT_RG); \
+ const __m128i V1_lo = _mm_madd_epi16(GB_LO, MULT_GB); \
+ const __m128i V1_hi = _mm_madd_epi16(GB_HI, MULT_GB); \
+ const __m128i V2_lo = _mm_add_epi32(V0_lo, V1_lo); \
+ const __m128i V2_hi = _mm_add_epi32(V0_hi, V1_hi); \
+ const __m128i V3_lo = _mm_add_epi32(V2_lo, ROUNDER); \
+ const __m128i V3_hi = _mm_add_epi32(V2_hi, ROUNDER); \
+ const __m128i V5_lo = _mm_srai_epi32(V3_lo, DESCALE_FIX); \
+ const __m128i V5_hi = _mm_srai_epi32(V3_hi, DESCALE_FIX); \
+ (OUT) = _mm_packs_epi32(V5_lo, V5_hi); \
+} while (0)
+
+#define MK_CST_16(A, B) _mm_set_epi16((B), (A), (B), (A), (B), (A), (B), (A))
+static WEBP_INLINE void ConvertRGBToY_SSE41(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ __m128i* const Y) {
+ const __m128i kRG_y = MK_CST_16(16839, 33059 - 16384);
+ const __m128i kGB_y = MK_CST_16(16384, 6420);
+ const __m128i kHALF_Y = _mm_set1_epi32((16 << YUV_FIX) + YUV_HALF);
+
+ const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G);
+ const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G);
+ const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B);
+ const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_y, kGB_y, kHALF_Y, YUV_FIX, *Y);
+}
+
+static WEBP_INLINE void ConvertRGBToUV_SSE41(const __m128i* const R,
+ const __m128i* const G,
+ const __m128i* const B,
+ __m128i* const U,
+ __m128i* const V) {
+ const __m128i kRG_u = MK_CST_16(-9719, -19081);
+ const __m128i kGB_u = MK_CST_16(0, 28800);
+ const __m128i kRG_v = MK_CST_16(28800, 0);
+ const __m128i kGB_v = MK_CST_16(-24116, -4684);
+ const __m128i kHALF_UV = _mm_set1_epi32(((128 << YUV_FIX) + YUV_HALF) << 2);
+
+ const __m128i RG_lo = _mm_unpacklo_epi16(*R, *G);
+ const __m128i RG_hi = _mm_unpackhi_epi16(*R, *G);
+ const __m128i GB_lo = _mm_unpacklo_epi16(*G, *B);
+ const __m128i GB_hi = _mm_unpackhi_epi16(*G, *B);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_u, kGB_u,
+ kHALF_UV, YUV_FIX + 2, *U);
+ TRANSFORM(RG_lo, RG_hi, GB_lo, GB_hi, kRG_v, kGB_v,
+ kHALF_UV, YUV_FIX + 2, *V);
+}
+
+#undef MK_CST_16
+#undef TRANSFORM
+
+static void ConvertRGB24ToY_SSE41(const uint8_t* rgb, uint8_t* y, int width) {
+ const int max_width = width & ~31;
+ int i;
+ for (i = 0; i < max_width; rgb += 3 * 16 * 2) {
+ __m128i rgb_plane[6];
+ int j;
+
+ RGB24PackedToPlanar_SSE41(rgb, rgb_plane);
+
+ for (j = 0; j < 2; ++j, i += 16) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i r, g, b, Y0, Y1;
+
+ // Convert to 16-bit Y.
+ r = _mm_unpacklo_epi8(rgb_plane[0 + j], zero);
+ g = _mm_unpacklo_epi8(rgb_plane[2 + j], zero);
+ b = _mm_unpacklo_epi8(rgb_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y0);
+
+ // Convert to 16-bit Y.
+ r = _mm_unpackhi_epi8(rgb_plane[0 + j], zero);
+ g = _mm_unpackhi_epi8(rgb_plane[2 + j], zero);
+ b = _mm_unpackhi_epi8(rgb_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y1);
+
+ // Cast to 8-bit and store.
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ }
+ for (; i < width; ++i, rgb += 3) { // left-over
+ y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF);
+ }
+}
+
+static void ConvertBGR24ToY_SSE41(const uint8_t* bgr, uint8_t* y, int width) {
+ const int max_width = width & ~31;
+ int i;
+ for (i = 0; i < max_width; bgr += 3 * 16 * 2) {
+ __m128i bgr_plane[6];
+ int j;
+
+ RGB24PackedToPlanar_SSE41(bgr, bgr_plane);
+
+ for (j = 0; j < 2; ++j, i += 16) {
+ const __m128i zero = _mm_setzero_si128();
+ __m128i r, g, b, Y0, Y1;
+
+ // Convert to 16-bit Y.
+ b = _mm_unpacklo_epi8(bgr_plane[0 + j], zero);
+ g = _mm_unpacklo_epi8(bgr_plane[2 + j], zero);
+ r = _mm_unpacklo_epi8(bgr_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y0);
+
+ // Convert to 16-bit Y.
+ b = _mm_unpackhi_epi8(bgr_plane[0 + j], zero);
+ g = _mm_unpackhi_epi8(bgr_plane[2 + j], zero);
+ r = _mm_unpackhi_epi8(bgr_plane[4 + j], zero);
+ ConvertRGBToY_SSE41(&r, &g, &b, &Y1);
+
+ // Cast to 8-bit and store.
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ }
+ for (; i < width; ++i, bgr += 3) { // left-over
+ y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF);
+ }
+}
+
+static void ConvertARGBToY_SSE41(const uint32_t* argb, uint8_t* y, int width) {
+ const int max_width = width & ~15;
+ int i;
+ for (i = 0; i < max_width; i += 16) {
+ __m128i Y0, Y1, rgb[6];
+ RGB32PackedToPlanar_SSE41(&argb[i], rgb);
+ ConvertRGBToY_SSE41(&rgb[0], &rgb[2], &rgb[4], &Y0);
+ ConvertRGBToY_SSE41(&rgb[1], &rgb[3], &rgb[5], &Y1);
+ STORE_16(_mm_packus_epi16(Y0, Y1), y + i);
+ }
+ for (; i < width; ++i) { // left-over
+ const uint32_t p = argb[i];
+ y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff,
+ YUV_HALF);
+ }
+}
+
+// Horizontal add (doubled) of two 16b values, result is 16b.
+// in: A | B | C | D | ... -> out: 2*(A+B) | 2*(C+D) | ...
+static void HorizontalAddPack_SSE41(const __m128i* const A,
+ const __m128i* const B,
+ __m128i* const out) {
+ const __m128i k2 = _mm_set1_epi16(2);
+ const __m128i C = _mm_madd_epi16(*A, k2);
+ const __m128i D = _mm_madd_epi16(*B, k2);
+ *out = _mm_packs_epi32(C, D);
+}
+
+static void ConvertARGBToUV_SSE41(const uint32_t* argb,
+ uint8_t* u, uint8_t* v,
+ int src_width, int do_store) {
+ const int max_width = src_width & ~31;
+ int i;
+ for (i = 0; i < max_width; i += 32, u += 16, v += 16) {
+ __m128i rgb[6], U0, V0, U1, V1;
+ RGB32PackedToPlanar_SSE41(&argb[i], rgb);
+ HorizontalAddPack_SSE41(&rgb[0], &rgb[1], &rgb[0]);
+ HorizontalAddPack_SSE41(&rgb[2], &rgb[3], &rgb[2]);
+ HorizontalAddPack_SSE41(&rgb[4], &rgb[5], &rgb[4]);
+ ConvertRGBToUV_SSE41(&rgb[0], &rgb[2], &rgb[4], &U0, &V0);
+
+ RGB32PackedToPlanar_SSE41(&argb[i + 16], rgb);
+ HorizontalAddPack_SSE41(&rgb[0], &rgb[1], &rgb[0]);
+ HorizontalAddPack_SSE41(&rgb[2], &rgb[3], &rgb[2]);
+ HorizontalAddPack_SSE41(&rgb[4], &rgb[5], &rgb[4]);
+ ConvertRGBToUV_SSE41(&rgb[0], &rgb[2], &rgb[4], &U1, &V1);
+
+ U0 = _mm_packus_epi16(U0, U1);
+ V0 = _mm_packus_epi16(V0, V1);
+ if (!do_store) {
+ const __m128i prev_u = LOAD_16(u);
+ const __m128i prev_v = LOAD_16(v);
+ U0 = _mm_avg_epu8(U0, prev_u);
+ V0 = _mm_avg_epu8(V0, prev_v);
+ }
+ STORE_16(U0, u);
+ STORE_16(V0, v);
+ }
+ if (i < src_width) { // left-over
+ WebPConvertARGBToUV_C(argb + i, u, v, src_width - i, do_store);
+ }
+}
+
+// Convert 16 packed ARGB 16b-values to r[], g[], b[]
+static WEBP_INLINE void RGBA32PackedToPlanar_16b_SSE41(
+ const uint16_t* const rgbx,
+ __m128i* const r, __m128i* const g, __m128i* const b) {
+ const __m128i in0 = LOAD_16(rgbx + 0); // r0 | g0 | b0 |x| r1 | g1 | b1 |x
+ const __m128i in1 = LOAD_16(rgbx + 8); // r2 | g2 | b2 |x| r3 | g3 | b3 |x
+ const __m128i in2 = LOAD_16(rgbx + 16); // r4 | ...
+ const __m128i in3 = LOAD_16(rgbx + 24); // r6 | ...
+ // aarrggbb as 16-bit.
+ const __m128i shuff0 =
+ _mm_set_epi8(-1, -1, -1, -1, 13, 12, 5, 4, 11, 10, 3, 2, 9, 8, 1, 0);
+ const __m128i shuff1 =
+ _mm_set_epi8(13, 12, 5, 4, -1, -1, -1, -1, 11, 10, 3, 2, 9, 8, 1, 0);
+ const __m128i A0 = _mm_shuffle_epi8(in0, shuff0);
+ const __m128i A1 = _mm_shuffle_epi8(in1, shuff1);
+ const __m128i A2 = _mm_shuffle_epi8(in2, shuff0);
+ const __m128i A3 = _mm_shuffle_epi8(in3, shuff1);
+ // R0R1G0G1
+ // B0B1****
+ // R2R3G2G3
+ // B2B3****
+ // (OR is used to free port 5 for the unpack)
+ const __m128i B0 = _mm_unpacklo_epi32(A0, A1);
+ const __m128i B1 = _mm_or_si128(A0, A1);
+ const __m128i B2 = _mm_unpacklo_epi32(A2, A3);
+ const __m128i B3 = _mm_or_si128(A2, A3);
+ // Gather the channels.
+ *r = _mm_unpacklo_epi64(B0, B2);
+ *g = _mm_unpackhi_epi64(B0, B2);
+ *b = _mm_unpackhi_epi64(B1, B3);
+}
+
+static void ConvertRGBA32ToUV_SSE41(const uint16_t* rgb,
+ uint8_t* u, uint8_t* v, int width) {
+ const int max_width = width & ~15;
+ const uint16_t* const last_rgb = rgb + 4 * max_width;
+ while (rgb < last_rgb) {
+ __m128i r, g, b, U0, V0, U1, V1;
+ RGBA32PackedToPlanar_16b_SSE41(rgb + 0, &r, &g, &b);
+ ConvertRGBToUV_SSE41(&r, &g, &b, &U0, &V0);
+ RGBA32PackedToPlanar_16b_SSE41(rgb + 32, &r, &g, &b);
+ ConvertRGBToUV_SSE41(&r, &g, &b, &U1, &V1);
+ STORE_16(_mm_packus_epi16(U0, U1), u);
+ STORE_16(_mm_packus_epi16(V0, V1), v);
+ u += 16;
+ v += 16;
+ rgb += 2 * 32;
+ }
+ if (max_width < width) { // left-over
+ WebPConvertRGBA32ToUV_C(rgb, u, v, width - max_width);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+extern void WebPInitConvertARGBToYUVSSE41(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUVSSE41(void) {
+ WebPConvertARGBToY = ConvertARGBToY_SSE41;
+ WebPConvertARGBToUV = ConvertARGBToUV_SSE41;
+
+ WebPConvertRGB24ToY = ConvertRGB24ToY_SSE41;
+ WebPConvertBGR24ToY = ConvertBGR24ToY_SSE41;
+
+ WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_SSE41;
+}
+
+//------------------------------------------------------------------------------
+
+#else // !WEBP_USE_SSE41
+
+WEBP_DSP_INIT_STUB(WebPInitSamplersSSE41)
+WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVSSE41)
+
+#endif // WEBP_USE_SSE41
diff --git a/src/third_party/libwebp/src/enc/Makefile.am b/src/third_party/libwebp/src/enc/Makefile.am
new file mode 100644
index 0000000..27d5228
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/Makefile.am
@@ -0,0 +1,42 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES = libwebpencode.la
+
+libwebpencode_la_SOURCES =
+libwebpencode_la_SOURCES += alpha_enc.c
+libwebpencode_la_SOURCES += analysis_enc.c
+libwebpencode_la_SOURCES += backward_references_cost_enc.c
+libwebpencode_la_SOURCES += backward_references_enc.c
+libwebpencode_la_SOURCES += backward_references_enc.h
+libwebpencode_la_SOURCES += config_enc.c
+libwebpencode_la_SOURCES += cost_enc.c
+libwebpencode_la_SOURCES += cost_enc.h
+libwebpencode_la_SOURCES += filter_enc.c
+libwebpencode_la_SOURCES += frame_enc.c
+libwebpencode_la_SOURCES += histogram_enc.c
+libwebpencode_la_SOURCES += histogram_enc.h
+libwebpencode_la_SOURCES += iterator_enc.c
+libwebpencode_la_SOURCES += near_lossless_enc.c
+libwebpencode_la_SOURCES += picture_enc.c
+libwebpencode_la_SOURCES += picture_csp_enc.c
+libwebpencode_la_SOURCES += picture_psnr_enc.c
+libwebpencode_la_SOURCES += picture_rescale_enc.c
+libwebpencode_la_SOURCES += picture_tools_enc.c
+libwebpencode_la_SOURCES += predictor_enc.c
+libwebpencode_la_SOURCES += quant_enc.c
+libwebpencode_la_SOURCES += syntax_enc.c
+libwebpencode_la_SOURCES += token_enc.c
+libwebpencode_la_SOURCES += tree_enc.c
+libwebpencode_la_SOURCES += vp8i_enc.h
+libwebpencode_la_SOURCES += vp8l_enc.c
+libwebpencode_la_SOURCES += vp8li_enc.h
+libwebpencode_la_SOURCES += webp_enc.c
+
+libwebpencodeinclude_HEADERS =
+libwebpencodeinclude_HEADERS += ../webp/encode.h
+libwebpencodeinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpencode_la_LDFLAGS = -lm
+libwebpencode_la_CPPFLAGS = $(AM_CPPFLAGS)
+libwebpencodeincludedir = $(includedir)/webp
diff --git a/src/third_party/libwebp/src/enc/alpha_enc.c b/src/third_party/libwebp/src/enc/alpha_enc.c
new file mode 100644
index 0000000..dce9ca9
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/alpha_enc.c
@@ -0,0 +1,443 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Alpha-plane compression.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "src/enc/vp8i_enc.h"
+#include "src/dsp/dsp.h"
+#include "src/utils/filters_utils.h"
+#include "src/utils/quant_levels_utils.h"
+#include "src/utils/utils.h"
+#include "src/webp/format_constants.h"
+
+// -----------------------------------------------------------------------------
+// Encodes the given alpha data via specified compression method 'method'.
+// The pre-processing (quantization) is performed if 'quality' is less than 100.
+// For such cases, the encoding is lossy. The valid range is [0, 100] for
+// 'quality' and [0, 1] for 'method':
+// 'method = 0' - No compression;
+// 'method = 1' - Use lossless coder on the alpha plane only
+// 'filter' values [0, 4] correspond to prediction modes none, horizontal,
+// vertical & gradient filters. The prediction mode 4 will try all the
+// prediction modes 0 to 3 and pick the best one.
+// 'effort_level': specifies how much effort must be spent to try and reduce
+// the compressed output size. In range 0 (quick) to 6 (slow).
+//
+// 'output' corresponds to the buffer containing compressed alpha data.
+// This buffer is allocated by this method and caller should call
+// WebPSafeFree(*output) when done.
+// 'output_size' corresponds to size of this compressed alpha buffer.
+//
+// Returns 1 on successfully encoding the alpha and
+// 0 if either:
+// invalid quality or method, or
+// memory allocation for the compressed data fails.
+
+#include "src/enc/vp8li_enc.h"
+
+static int EncodeLossless(const uint8_t* const data, int width, int height,
+ int effort_level, // in [0..6] range
+ int use_quality_100, VP8LBitWriter* const bw,
+ WebPAuxStats* const stats) {
+ int ok = 0;
+ WebPConfig config;
+ WebPPicture picture;
+
+ WebPPictureInit(&picture);
+ picture.width = width;
+ picture.height = height;
+ picture.use_argb = 1;
+ picture.stats = stats;
+ if (!WebPPictureAlloc(&picture)) return 0;
+
+ // Transfer the alpha values to the green channel.
+ WebPDispatchAlphaToGreen(data, width, picture.width, picture.height,
+ picture.argb, picture.argb_stride);
+
+ WebPConfigInit(&config);
+ config.lossless = 1;
+ // Enable exact, or it would alter RGB values of transparent alpha, which is
+ // normally OK but not here since we are not encoding the input image but an
+ // internal encoding-related image containing necessary exact information in
+ // RGB channels.
+ config.exact = 1;
+ config.method = effort_level; // impact is very small
+ // Set a low default quality for encoding alpha. Ensure that Alpha quality at
+ // lower methods (3 and below) is less than the threshold for triggering
+ // costly 'BackwardReferencesTraceBackwards'.
+ // If the alpha quality is set to 100 and the method to 6, allow for a high
+ // lossless quality to trigger the cruncher.
+ config.quality =
+ (use_quality_100 && effort_level == 6) ? 100 : 8.f * effort_level;
+ assert(config.quality >= 0 && config.quality <= 100.f);
+
+ // TODO(urvang): Temporary fix to avoid generating images that trigger
+ // a decoder bug related to alpha with color cache.
+ // See: https://code.google.com/p/webp/issues/detail?id=239
+ // Need to re-enable this later.
+ ok = (VP8LEncodeStream(&config, &picture, bw, 0 /*use_cache*/) == VP8_ENC_OK);
+ WebPPictureFree(&picture);
+ ok = ok && !bw->error_;
+ if (!ok) {
+ VP8LBitWriterWipeOut(bw);
+ return 0;
+ }
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+// Small struct to hold the result of a filter mode compression attempt.
+typedef struct {
+ size_t score;
+ VP8BitWriter bw;
+ WebPAuxStats stats;
+} FilterTrial;
+
+// This function always returns an initialized 'bw' object, even upon error.
+static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
+ int method, int filter, int reduce_levels,
+ int effort_level, // in [0..6] range
+ uint8_t* const tmp_alpha,
+ FilterTrial* result) {
+ int ok = 0;
+ const uint8_t* alpha_src;
+ WebPFilterFunc filter_func;
+ uint8_t header;
+ const size_t data_size = width * height;
+ const uint8_t* output = NULL;
+ size_t output_size = 0;
+ VP8LBitWriter tmp_bw;
+
+ assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
+ assert(filter >= 0 && filter < WEBP_FILTER_LAST);
+ assert(method >= ALPHA_NO_COMPRESSION);
+ assert(method <= ALPHA_LOSSLESS_COMPRESSION);
+ assert(sizeof(header) == ALPHA_HEADER_LEN);
+
+ filter_func = WebPFilters[filter];
+ if (filter_func != NULL) {
+ filter_func(data, width, height, width, tmp_alpha);
+ alpha_src = tmp_alpha;
+ } else {
+ alpha_src = data;
+ }
+
+ if (method != ALPHA_NO_COMPRESSION) {
+ ok = VP8LBitWriterInit(&tmp_bw, data_size >> 3);
+ ok = ok && EncodeLossless(alpha_src, width, height, effort_level,
+ !reduce_levels, &tmp_bw, &result->stats);
+ if (ok) {
+ output = VP8LBitWriterFinish(&tmp_bw);
+ output_size = VP8LBitWriterNumBytes(&tmp_bw);
+ if (output_size > data_size) {
+ // compressed size is larger than source! Revert to uncompressed mode.
+ method = ALPHA_NO_COMPRESSION;
+ VP8LBitWriterWipeOut(&tmp_bw);
+ }
+ } else {
+ VP8LBitWriterWipeOut(&tmp_bw);
+ return 0;
+ }
+ }
+
+ if (method == ALPHA_NO_COMPRESSION) {
+ output = alpha_src;
+ output_size = data_size;
+ ok = 1;
+ }
+
+ // Emit final result.
+ header = method | (filter << 2);
+ if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
+
+ VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size);
+ ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
+ ok = ok && VP8BitWriterAppend(&result->bw, output, output_size);
+
+ if (method != ALPHA_NO_COMPRESSION) {
+ VP8LBitWriterWipeOut(&tmp_bw);
+ }
+ ok = ok && !result->bw.error_;
+ result->score = VP8BitWriterSize(&result->bw);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetNumColors(const uint8_t* data, int width, int height,
+ int stride) {
+ int j;
+ int colors = 0;
+ uint8_t color[256] = { 0 };
+
+ for (j = 0; j < height; ++j) {
+ int i;
+ const uint8_t* const p = data + j * stride;
+ for (i = 0; i < width; ++i) {
+ color[p[i]] = 1;
+ }
+ }
+ for (j = 0; j < 256; ++j) {
+ if (color[j] > 0) ++colors;
+ }
+ return colors;
+}
+
+#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE)
+#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1)
+
+// Given the input 'filter' option, return an OR'd bit-set of filters to try.
+static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height,
+ int filter, int effort_level) {
+ uint32_t bit_map = 0U;
+ if (filter == WEBP_FILTER_FAST) {
+ // Quick estimate of the best candidate.
+ int try_filter_none = (effort_level > 3);
+ const int kMinColorsForFilterNone = 16;
+ const int kMaxColorsForFilterNone = 192;
+ const int num_colors = GetNumColors(alpha, width, height, width);
+ // For low number of colors, NONE yields better compression.
+ filter = (num_colors <= kMinColorsForFilterNone)
+ ? WEBP_FILTER_NONE
+ : WebPEstimateBestFilter(alpha, width, height, width);
+ bit_map |= 1 << filter;
+ // For large number of colors, try FILTER_NONE in addition to the best
+ // filter as well.
+ if (try_filter_none || num_colors > kMaxColorsForFilterNone) {
+ bit_map |= FILTER_TRY_NONE;
+ }
+ } else if (filter == WEBP_FILTER_NONE) {
+ bit_map = FILTER_TRY_NONE;
+ } else { // WEBP_FILTER_BEST -> try all
+ bit_map = FILTER_TRY_ALL;
+ }
+ return bit_map;
+}
+
+static void InitFilterTrial(FilterTrial* const score) {
+ score->score = (size_t)~0U;
+ VP8BitWriterInit(&score->bw, 0);
+}
+
+static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height,
+ size_t data_size, int method, int filter,
+ int reduce_levels, int effort_level,
+ uint8_t** const output,
+ size_t* const output_size,
+ WebPAuxStats* const stats) {
+ int ok = 1;
+ FilterTrial best;
+ uint32_t try_map =
+ GetFilterMap(alpha, width, height, filter, effort_level);
+ InitFilterTrial(&best);
+
+ if (try_map != FILTER_TRY_NONE) {
+ uint8_t* filtered_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
+ if (filtered_alpha == NULL) return 0;
+
+ for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) {
+ if (try_map & 1) {
+ FilterTrial trial;
+ ok = EncodeAlphaInternal(alpha, width, height, method, filter,
+ reduce_levels, effort_level, filtered_alpha,
+ &trial);
+ if (ok && trial.score < best.score) {
+ VP8BitWriterWipeOut(&best.bw);
+ best = trial;
+ } else {
+ VP8BitWriterWipeOut(&trial.bw);
+ }
+ }
+ }
+ WebPSafeFree(filtered_alpha);
+ } else {
+ ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE,
+ reduce_levels, effort_level, NULL, &best);
+ }
+ if (ok) {
+#if !defined(WEBP_DISABLE_STATS)
+ if (stats != NULL) {
+ stats->lossless_features = best.stats.lossless_features;
+ stats->histogram_bits = best.stats.histogram_bits;
+ stats->transform_bits = best.stats.transform_bits;
+ stats->cache_bits = best.stats.cache_bits;
+ stats->palette_size = best.stats.palette_size;
+ stats->lossless_size = best.stats.lossless_size;
+ stats->lossless_hdr_size = best.stats.lossless_hdr_size;
+ stats->lossless_data_size = best.stats.lossless_data_size;
+ }
+#else
+ (void)stats;
+#endif
+ *output_size = VP8BitWriterSize(&best.bw);
+ *output = VP8BitWriterBuf(&best.bw);
+ } else {
+ VP8BitWriterWipeOut(&best.bw);
+ }
+ return ok;
+}
+
+static int EncodeAlpha(VP8Encoder* const enc,
+ int quality, int method, int filter,
+ int effort_level,
+ uint8_t** const output, size_t* const output_size) {
+ const WebPPicture* const pic = enc->pic_;
+ const int width = pic->width;
+ const int height = pic->height;
+
+ uint8_t* quant_alpha = NULL;
+ const size_t data_size = width * height;
+ uint64_t sse = 0;
+ int ok = 1;
+ const int reduce_levels = (quality < 100);
+
+ // quick sanity checks
+ assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
+ assert(enc != NULL && pic != NULL && pic->a != NULL);
+ assert(output != NULL && output_size != NULL);
+ assert(width > 0 && height > 0);
+ assert(pic->a_stride >= width);
+ assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST);
+
+ if (quality < 0 || quality > 100) {
+ return 0;
+ }
+
+ if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) {
+ return 0;
+ }
+
+ if (method == ALPHA_NO_COMPRESSION) {
+ // Don't filter, as filtering will make no impact on compressed size.
+ filter = WEBP_FILTER_NONE;
+ }
+
+ quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
+ if (quant_alpha == NULL) {
+ return 0;
+ }
+
+ // Extract alpha data (width x height) from raw_data (stride x height).
+ WebPCopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
+
+ if (reduce_levels) { // No Quantization required for 'quality = 100'.
+ // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
+ // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16]
+ // and Quality:]70, 100] -> Levels:]16, 256].
+ const int alpha_levels = (quality <= 70) ? (2 + quality / 5)
+ : (16 + (quality - 70) * 8);
+ ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse);
+ }
+
+ if (ok) {
+ VP8FiltersInit();
+ ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
+ filter, reduce_levels, effort_level, output,
+ output_size, pic->stats);
+#if !defined(WEBP_DISABLE_STATS)
+ if (pic->stats != NULL) { // need stats?
+ pic->stats->coded_size += (int)(*output_size);
+ enc->sse_[3] = sse;
+ }
+#endif
+ }
+
+ WebPSafeFree(quant_alpha);
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// Main calls
+
+static int CompressAlphaJob(void* arg1, void* dummy) {
+ VP8Encoder* const enc = (VP8Encoder*)arg1;
+ const WebPConfig* config = enc->config_;
+ uint8_t* alpha_data = NULL;
+ size_t alpha_size = 0;
+ const int effort_level = config->method; // maps to [0..6]
+ const WEBP_FILTER_TYPE filter =
+ (config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
+ (config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
+ WEBP_FILTER_BEST;
+ if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
+ filter, effort_level, &alpha_data, &alpha_size)) {
+ return 0;
+ }
+ if (alpha_size != (uint32_t)alpha_size) { // Sanity check.
+ WebPSafeFree(alpha_data);
+ return 0;
+ }
+ enc->alpha_data_size_ = (uint32_t)alpha_size;
+ enc->alpha_data_ = alpha_data;
+ (void)dummy;
+ return 1;
+}
+
+void VP8EncInitAlpha(VP8Encoder* const enc) {
+ WebPInitAlphaProcessing();
+ enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
+ enc->alpha_data_ = NULL;
+ enc->alpha_data_size_ = 0;
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ WebPGetWorkerInterface()->Init(worker);
+ worker->data1 = enc;
+ worker->data2 = NULL;
+ worker->hook = CompressAlphaJob;
+ }
+}
+
+int VP8EncStartAlpha(VP8Encoder* const enc) {
+ if (enc->has_alpha_) {
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ // Makes sure worker is good to go.
+ if (!WebPGetWorkerInterface()->Reset(worker)) {
+ return 0;
+ }
+ WebPGetWorkerInterface()->Launch(worker);
+ return 1;
+ } else {
+ return CompressAlphaJob(enc, NULL); // just do the job right away
+ }
+ }
+ return 1;
+}
+
+int VP8EncFinishAlpha(VP8Encoder* const enc) {
+ if (enc->has_alpha_) {
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ if (!WebPGetWorkerInterface()->Sync(worker)) return 0; // error
+ }
+ }
+ return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
+}
+
+int VP8EncDeleteAlpha(VP8Encoder* const enc) {
+ int ok = 1;
+ if (enc->thread_level_ > 0) {
+ WebPWorker* const worker = &enc->alpha_worker_;
+ // finish anything left in flight
+ ok = WebPGetWorkerInterface()->Sync(worker);
+ // still need to end the worker, even if !ok
+ WebPGetWorkerInterface()->End(worker);
+ }
+ WebPSafeFree(enc->alpha_data_);
+ enc->alpha_data_ = NULL;
+ enc->alpha_data_size_ = 0;
+ enc->has_alpha_ = 0;
+ return ok;
+}
diff --git a/src/third_party/libwebp/src/enc/analysis_enc.c b/src/third_party/libwebp/src/enc/analysis_enc.c
new file mode 100644
index 0000000..a47ff7d
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/analysis_enc.c
@@ -0,0 +1,535 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Macroblock analysis
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "src/enc/vp8i_enc.h"
+#include "src/enc/cost_enc.h"
+#include "src/utils/utils.h"
+
+#define MAX_ITERS_K_MEANS 6
+
+//------------------------------------------------------------------------------
+// Smooth the segment map by replacing isolated block by the majority of its
+// neighbours.
+
+static void SmoothSegmentMap(VP8Encoder* const enc) {
+ int n, x, y;
+ const int w = enc->mb_w_;
+ const int h = enc->mb_h_;
+ const int majority_cnt_3_x_3_grid = 5;
+ uint8_t* const tmp = (uint8_t*)WebPSafeMalloc(w * h, sizeof(*tmp));
+ assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec
+
+ if (tmp == NULL) return;
+ for (y = 1; y < h - 1; ++y) {
+ for (x = 1; x < w - 1; ++x) {
+ int cnt[NUM_MB_SEGMENTS] = { 0 };
+ const VP8MBInfo* const mb = &enc->mb_info_[x + w * y];
+ int majority_seg = mb->segment_;
+ // Check the 8 neighbouring segment values.
+ cnt[mb[-w - 1].segment_]++; // top-left
+ cnt[mb[-w + 0].segment_]++; // top
+ cnt[mb[-w + 1].segment_]++; // top-right
+ cnt[mb[ - 1].segment_]++; // left
+ cnt[mb[ + 1].segment_]++; // right
+ cnt[mb[ w - 1].segment_]++; // bottom-left
+ cnt[mb[ w + 0].segment_]++; // bottom
+ cnt[mb[ w + 1].segment_]++; // bottom-right
+ for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
+ if (cnt[n] >= majority_cnt_3_x_3_grid) {
+ majority_seg = n;
+ break;
+ }
+ }
+ tmp[x + y * w] = majority_seg;
+ }
+ }
+ for (y = 1; y < h - 1; ++y) {
+ for (x = 1; x < w - 1; ++x) {
+ VP8MBInfo* const mb = &enc->mb_info_[x + w * y];
+ mb->segment_ = tmp[x + y * w];
+ }
+ }
+ WebPSafeFree(tmp);
+}
+
+//------------------------------------------------------------------------------
+// set segment susceptibility alpha_ / beta_
+
+static WEBP_INLINE int clip(int v, int m, int M) {
+ return (v < m) ? m : (v > M) ? M : v;
+}
+
+static void SetSegmentAlphas(VP8Encoder* const enc,
+ const int centers[NUM_MB_SEGMENTS],
+ int mid) {
+ const int nb = enc->segment_hdr_.num_segments_;
+ int min = centers[0], max = centers[0];
+ int n;
+
+ if (nb > 1) {
+ for (n = 0; n < nb; ++n) {
+ if (min > centers[n]) min = centers[n];
+ if (max < centers[n]) max = centers[n];
+ }
+ }
+ if (max == min) max = min + 1;
+ assert(mid <= max && mid >= min);
+ for (n = 0; n < nb; ++n) {
+ const int alpha = 255 * (centers[n] - mid) / (max - min);
+ const int beta = 255 * (centers[n] - min) / (max - min);
+ enc->dqm_[n].alpha_ = clip(alpha, -127, 127);
+ enc->dqm_[n].beta_ = clip(beta, 0, 255);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Compute susceptibility based on DCT-coeff histograms:
+// the higher, the "easier" the macroblock is to compress.
+
+#define MAX_ALPHA 255 // 8b of precision for susceptibilities.
+#define ALPHA_SCALE (2 * MAX_ALPHA) // scaling factor for alpha.
+#define DEFAULT_ALPHA (-1)
+#define IS_BETTER_ALPHA(alpha, best_alpha) ((alpha) > (best_alpha))
+
+static int FinalAlphaValue(int alpha) {
+ alpha = MAX_ALPHA - alpha;
+ return clip(alpha, 0, MAX_ALPHA);
+}
+
+static int GetAlpha(const VP8Histogram* const histo) {
+ // 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer
+ // values which happen to be mostly noise. This leaves the maximum precision
+ // for handling the useful small values which contribute most.
+ const int max_value = histo->max_value;
+ const int last_non_zero = histo->last_non_zero;
+ const int alpha =
+ (max_value > 1) ? ALPHA_SCALE * last_non_zero / max_value : 0;
+ return alpha;
+}
+
+static void InitHistogram(VP8Histogram* const histo) {
+ histo->max_value = 0;
+ histo->last_non_zero = 1;
+}
+
+static void MergeHistograms(const VP8Histogram* const in,
+ VP8Histogram* const out) {
+ if (in->max_value > out->max_value) {
+ out->max_value = in->max_value;
+ }
+ if (in->last_non_zero > out->last_non_zero) {
+ out->last_non_zero = in->last_non_zero;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Simplified k-Means, to assign Nb segments based on alpha-histogram
+
+static void AssignSegments(VP8Encoder* const enc,
+ const int alphas[MAX_ALPHA + 1]) {
+ // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an
+ // explicit check is needed to avoid spurious warning about 'n + 1' exceeding
+ // array bounds of 'centers' with some compilers (noticed with gcc-4.9).
+ const int nb = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS) ?
+ enc->segment_hdr_.num_segments_ : NUM_MB_SEGMENTS;
+ int centers[NUM_MB_SEGMENTS];
+ int weighted_average = 0;
+ int map[MAX_ALPHA + 1];
+ int a, n, k;
+ int min_a = 0, max_a = MAX_ALPHA, range_a;
+ // 'int' type is ok for histo, and won't overflow
+ int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS];
+
+ assert(nb >= 1);
+ assert(nb <= NUM_MB_SEGMENTS);
+
+ // bracket the input
+ for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {}
+ min_a = n;
+ for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {}
+ max_a = n;
+ range_a = max_a - min_a;
+
+ // Spread initial centers evenly
+ for (k = 0, n = 1; k < nb; ++k, n += 2) {
+ assert(n < 2 * nb);
+ centers[k] = min_a + (n * range_a) / (2 * nb);
+ }
+
+ for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough
+ int total_weight;
+ int displaced;
+ // Reset stats
+ for (n = 0; n < nb; ++n) {
+ accum[n] = 0;
+ dist_accum[n] = 0;
+ }
+ // Assign nearest center for each 'a'
+ n = 0; // track the nearest center for current 'a'
+ for (a = min_a; a <= max_a; ++a) {
+ if (alphas[a]) {
+ while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) {
+ n++;
+ }
+ map[a] = n;
+ // accumulate contribution into best centroid
+ dist_accum[n] += a * alphas[a];
+ accum[n] += alphas[a];
+ }
+ }
+ // All point are classified. Move the centroids to the
+ // center of their respective cloud.
+ displaced = 0;
+ weighted_average = 0;
+ total_weight = 0;
+ for (n = 0; n < nb; ++n) {
+ if (accum[n]) {
+ const int new_center = (dist_accum[n] + accum[n] / 2) / accum[n];
+ displaced += abs(centers[n] - new_center);
+ centers[n] = new_center;
+ weighted_average += new_center * accum[n];
+ total_weight += accum[n];
+ }
+ }
+ weighted_average = (weighted_average + total_weight / 2) / total_weight;
+ if (displaced < 5) break; // no need to keep on looping...
+ }
+
+ // Map each original value to the closest centroid
+ for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
+ VP8MBInfo* const mb = &enc->mb_info_[n];
+ const int alpha = mb->alpha_;
+ mb->segment_ = map[alpha];
+ mb->alpha_ = centers[map[alpha]]; // for the record.
+ }
+
+ if (nb > 1) {
+ const int smooth = (enc->config_->preprocessing & 1);
+ if (smooth) SmoothSegmentMap(enc);
+ }
+
+ SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas.
+}
+
+//------------------------------------------------------------------------------
+// Macroblock analysis: collect histogram for each mode, deduce the maximal
+// susceptibility and set best modes for this macroblock.
+// Segment assignment is done later.
+
+// Number of modes to inspect for alpha_ evaluation. We don't need to test all
+// the possible modes during the analysis phase: we risk falling into a local
+// optimum, or be subject to boundary effect
+#define MAX_INTRA16_MODE 2
+#define MAX_INTRA4_MODE 2
+#define MAX_UV_MODE 2
+
+static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) {
+ const int max_mode = MAX_INTRA16_MODE;
+ int mode;
+ int best_alpha = DEFAULT_ALPHA;
+ int best_mode = 0;
+
+ VP8MakeLuma16Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ VP8Histogram histo;
+ int alpha;
+
+ InitHistogram(&histo);
+ VP8CollectHistogram(it->yuv_in_ + Y_OFF_ENC,
+ it->yuv_p_ + VP8I16ModeOffsets[mode],
+ 0, 16, &histo);
+ alpha = GetAlpha(&histo);
+ if (IS_BETTER_ALPHA(alpha, best_alpha)) {
+ best_alpha = alpha;
+ best_mode = mode;
+ }
+ }
+ VP8SetIntra16Mode(it, best_mode);
+ return best_alpha;
+}
+
+static int FastMBAnalyze(VP8EncIterator* const it) {
+ // Empirical cut-off value, should be around 16 (~=block size). We use the
+ // [8-17] range and favor intra4 at high quality, intra16 for low quality.
+ const int q = (int)it->enc_->config_->quality;
+ const uint32_t kThreshold = 8 + (17 - 8) * q / 100;
+ int k;
+ uint32_t dc[16], m, m2;
+ for (k = 0; k < 16; k += 4) {
+ VP8Mean16x4(it->yuv_in_ + Y_OFF_ENC + k * BPS, &dc[k]);
+ }
+ for (m = 0, m2 = 0, k = 0; k < 16; ++k) {
+ m += dc[k];
+ m2 += dc[k] * dc[k];
+ }
+ if (kThreshold * m2 < m * m) {
+ VP8SetIntra16Mode(it, 0); // DC16
+ } else {
+ const uint8_t modes[16] = { 0 }; // DC4
+ VP8SetIntra4Mode(it, modes);
+ }
+ return 0;
+}
+
+static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
+ int best_alpha) {
+ uint8_t modes[16];
+ const int max_mode = MAX_INTRA4_MODE;
+ int i4_alpha;
+ VP8Histogram total_histo;
+ int cur_histo = 0;
+ InitHistogram(&total_histo);
+
+ VP8IteratorStartI4(it);
+ do {
+ int mode;
+ int best_mode_alpha = DEFAULT_ALPHA;
+ VP8Histogram histos[2];
+ const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_];
+
+ VP8MakeIntra4Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ int alpha;
+
+ InitHistogram(&histos[cur_histo]);
+ VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode],
+ 0, 1, &histos[cur_histo]);
+ alpha = GetAlpha(&histos[cur_histo]);
+ if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) {
+ best_mode_alpha = alpha;
+ modes[it->i4_] = mode;
+ cur_histo ^= 1; // keep track of best histo so far.
+ }
+ }
+ // accumulate best histogram
+ MergeHistograms(&histos[cur_histo ^ 1], &total_histo);
+ // Note: we reuse the original samples for predictors
+ } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF_ENC));
+
+ i4_alpha = GetAlpha(&total_histo);
+ if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) {
+ VP8SetIntra4Mode(it, modes);
+ best_alpha = i4_alpha;
+ }
+ return best_alpha;
+}
+
+static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
+ int best_alpha = DEFAULT_ALPHA;
+ int smallest_alpha = 0;
+ int best_mode = 0;
+ const int max_mode = MAX_UV_MODE;
+ int mode;
+
+ VP8MakeChroma8Preds(it);
+ for (mode = 0; mode < max_mode; ++mode) {
+ VP8Histogram histo;
+ int alpha;
+ InitHistogram(&histo);
+ VP8CollectHistogram(it->yuv_in_ + U_OFF_ENC,
+ it->yuv_p_ + VP8UVModeOffsets[mode],
+ 16, 16 + 4 + 4, &histo);
+ alpha = GetAlpha(&histo);
+ if (IS_BETTER_ALPHA(alpha, best_alpha)) {
+ best_alpha = alpha;
+ }
+ // The best prediction mode tends to be the one with the smallest alpha.
+ if (mode == 0 || alpha < smallest_alpha) {
+ smallest_alpha = alpha;
+ best_mode = mode;
+ }
+ }
+ VP8SetIntraUVMode(it, best_mode);
+ return best_alpha;
+}
+
+static void MBAnalyze(VP8EncIterator* const it,
+ int alphas[MAX_ALPHA + 1],
+ int* const alpha, int* const uv_alpha) {
+ const VP8Encoder* const enc = it->enc_;
+ int best_alpha, best_uv_alpha;
+
+ VP8SetIntra16Mode(it, 0); // default: Intra16, DC_PRED
+ VP8SetSkip(it, 0); // not skipped
+ VP8SetSegment(it, 0); // default segment, spec-wise.
+
+ if (enc->method_ <= 1) {
+ best_alpha = FastMBAnalyze(it);
+ } else {
+ best_alpha = MBAnalyzeBestIntra16Mode(it);
+ if (enc->method_ >= 5) {
+ // We go and make a fast decision for intra4/intra16.
+ // It's usually not a good and definitive pick, but helps seeding the
+ // stats about level bit-cost.
+ // TODO(skal): improve criterion.
+ best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha);
+ }
+ }
+ best_uv_alpha = MBAnalyzeBestUVMode(it);
+
+ // Final susceptibility mix
+ best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2;
+ best_alpha = FinalAlphaValue(best_alpha);
+ alphas[best_alpha]++;
+ it->mb_->alpha_ = best_alpha; // for later remapping.
+
+ // Accumulate for later complexity analysis.
+ *alpha += best_alpha; // mixed susceptibility (not just luma)
+ *uv_alpha += best_uv_alpha;
+}
+
+static void DefaultMBInfo(VP8MBInfo* const mb) {
+ mb->type_ = 1; // I16x16
+ mb->uv_mode_ = 0;
+ mb->skip_ = 0; // not skipped
+ mb->segment_ = 0; // default segment
+ mb->alpha_ = 0;
+}
+
+//------------------------------------------------------------------------------
+// Main analysis loop:
+// Collect all susceptibilities for each macroblock and record their
+// distribution in alphas[]. Segments is assigned a-posteriori, based on
+// this histogram.
+// We also pick an intra16 prediction mode, which shouldn't be considered
+// final except for fast-encode settings. We can also pick some intra4 modes
+// and decide intra4/intra16, but that's usually almost always a bad choice at
+// this stage.
+
+static void ResetAllMBInfo(VP8Encoder* const enc) {
+ int n;
+ for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
+ DefaultMBInfo(&enc->mb_info_[n]);
+ }
+ // Default susceptibilities.
+ enc->dqm_[0].alpha_ = 0;
+ enc->dqm_[0].beta_ = 0;
+ // Note: we can't compute this alpha_ / uv_alpha_ -> set to default value.
+ enc->alpha_ = 0;
+ enc->uv_alpha_ = 0;
+ WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
+}
+
+// struct used to collect job result
+typedef struct {
+ WebPWorker worker;
+ int alphas[MAX_ALPHA + 1];
+ int alpha, uv_alpha;
+ VP8EncIterator it;
+ int delta_progress;
+} SegmentJob;
+
+// main work call
+static int DoSegmentsJob(void* arg1, void* arg2) {
+ SegmentJob* const job = (SegmentJob*)arg1;
+ VP8EncIterator* const it = (VP8EncIterator*)arg2;
+ int ok = 1;
+ if (!VP8IteratorIsDone(it)) {
+ uint8_t tmp[32 + WEBP_ALIGN_CST];
+ uint8_t* const scratch = (uint8_t*)WEBP_ALIGN(tmp);
+ do {
+ // Let's pretend we have perfect lossless reconstruction.
+ VP8IteratorImport(it, scratch);
+ MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha);
+ ok = VP8IteratorProgress(it, job->delta_progress);
+ } while (ok && VP8IteratorNext(it));
+ }
+ return ok;
+}
+
+static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) {
+ int i;
+ for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i];
+ dst->alpha += src->alpha;
+ dst->uv_alpha += src->uv_alpha;
+}
+
+// initialize the job struct with some TODOs
+static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job,
+ int start_row, int end_row) {
+ WebPGetWorkerInterface()->Init(&job->worker);
+ job->worker.data1 = job;
+ job->worker.data2 = &job->it;
+ job->worker.hook = DoSegmentsJob;
+ VP8IteratorInit(enc, &job->it);
+ VP8IteratorSetRow(&job->it, start_row);
+ VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_);
+ memset(job->alphas, 0, sizeof(job->alphas));
+ job->alpha = 0;
+ job->uv_alpha = 0;
+ // only one of both jobs can record the progress, since we don't
+ // expect the user's hook to be multi-thread safe
+ job->delta_progress = (start_row == 0) ? 20 : 0;
+}
+
+// main entry point
+int VP8EncAnalyze(VP8Encoder* const enc) {
+ int ok = 1;
+ const int do_segments =
+ enc->config_->emulate_jpeg_size || // We need the complexity evaluation.
+ (enc->segment_hdr_.num_segments_ > 1) ||
+ (enc->method_ <= 1); // for method 0 - 1, we need preds_[] to be filled.
+ if (do_segments) {
+ const int last_row = enc->mb_h_;
+ // We give a little more than a half work to the main thread.
+ const int split_row = (9 * last_row + 15) >> 4;
+ const int total_mb = last_row * enc->mb_w_;
+#ifdef WEBP_USE_THREAD
+ const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it
+ const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow);
+#else
+ const int do_mt = 0;
+#endif
+ const WebPWorkerInterface* const worker_interface =
+ WebPGetWorkerInterface();
+ SegmentJob main_job;
+ if (do_mt) {
+ SegmentJob side_job;
+ // Note the use of '&' instead of '&&' because we must call the functions
+ // no matter what.
+ InitSegmentJob(enc, &main_job, 0, split_row);
+ InitSegmentJob(enc, &side_job, split_row, last_row);
+ // we don't need to call Reset() on main_job.worker, since we're calling
+ // WebPWorkerExecute() on it
+ ok &= worker_interface->Reset(&side_job.worker);
+ // launch the two jobs in parallel
+ if (ok) {
+ worker_interface->Launch(&side_job.worker);
+ worker_interface->Execute(&main_job.worker);
+ ok &= worker_interface->Sync(&side_job.worker);
+ ok &= worker_interface->Sync(&main_job.worker);
+ }
+ worker_interface->End(&side_job.worker);
+ if (ok) MergeJobs(&side_job, &main_job); // merge results together
+ } else {
+ // Even for single-thread case, we use the generic Worker tools.
+ InitSegmentJob(enc, &main_job, 0, last_row);
+ worker_interface->Execute(&main_job.worker);
+ ok &= worker_interface->Sync(&main_job.worker);
+ }
+ worker_interface->End(&main_job.worker);
+ if (ok) {
+ enc->alpha_ = main_job.alpha / total_mb;
+ enc->uv_alpha_ = main_job.uv_alpha / total_mb;
+ AssignSegments(enc, main_job.alphas);
+ }
+ } else { // Use only one default segment.
+ ResetAllMBInfo(enc);
+ }
+ return ok;
+}
+
diff --git a/src/third_party/libwebp/src/enc/backward_references_cost_enc.c b/src/third_party/libwebp/src/enc/backward_references_cost_enc.c
new file mode 100644
index 0000000..7175496
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/backward_references_cost_enc.c
@@ -0,0 +1,790 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Improves a given set of backward references by analyzing its bit cost.
+// The algorithm is similar to the Zopfli compression algorithm but tailored to
+// images.
+//
+// Author: Vincent Rabaud (vrabaud@google.com)
+//
+
+#include <assert.h>
+
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
+#include "src/dsp/lossless_common.h"
+#include "src/utils/color_cache_utils.h"
+#include "src/utils/utils.h"
+
+#define VALUES_IN_BYTE 256
+
+extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
+extern int VP8LDistanceToPlaneCode(int xsize, int dist);
+extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
+ const PixOrCopy v);
+
+typedef struct {
+ double alpha_[VALUES_IN_BYTE];
+ double red_[VALUES_IN_BYTE];
+ double blue_[VALUES_IN_BYTE];
+ double distance_[NUM_DISTANCE_CODES];
+ double* literal_;
+} CostModel;
+
+static void ConvertPopulationCountTableToBitEstimates(
+ int num_symbols, const uint32_t population_counts[], double output[]) {
+ uint32_t sum = 0;
+ int nonzeros = 0;
+ int i;
+ for (i = 0; i < num_symbols; ++i) {
+ sum += population_counts[i];
+ if (population_counts[i] > 0) {
+ ++nonzeros;
+ }
+ }
+ if (nonzeros <= 1) {
+ memset(output, 0, num_symbols * sizeof(*output));
+ } else {
+ const double logsum = VP8LFastLog2(sum);
+ for (i = 0; i < num_symbols; ++i) {
+ output[i] = logsum - VP8LFastLog2(population_counts[i]);
+ }
+ }
+}
+
+static int CostModelBuild(CostModel* const m, int xsize, int cache_bits,
+ const VP8LBackwardRefs* const refs) {
+ int ok = 0;
+ VP8LRefsCursor c = VP8LRefsCursorInit(refs);
+ VP8LHistogram* const histo = VP8LAllocateHistogram(cache_bits);
+ if (histo == NULL) goto Error;
+
+ // The following code is similar to VP8LHistogramCreate but converts the
+ // distance to plane code.
+ VP8LHistogramInit(histo, cache_bits);
+ while (VP8LRefsCursorOk(&c)) {
+ VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, VP8LDistanceToPlaneCode,
+ xsize);
+ VP8LRefsCursorNext(&c);
+ }
+
+ ConvertPopulationCountTableToBitEstimates(
+ VP8LHistogramNumCodes(histo->palette_code_bits_),
+ histo->literal_, m->literal_);
+ ConvertPopulationCountTableToBitEstimates(
+ VALUES_IN_BYTE, histo->red_, m->red_);
+ ConvertPopulationCountTableToBitEstimates(
+ VALUES_IN_BYTE, histo->blue_, m->blue_);
+ ConvertPopulationCountTableToBitEstimates(
+ VALUES_IN_BYTE, histo->alpha_, m->alpha_);
+ ConvertPopulationCountTableToBitEstimates(
+ NUM_DISTANCE_CODES, histo->distance_, m->distance_);
+ ok = 1;
+
+ Error:
+ VP8LFreeHistogram(histo);
+ return ok;
+}
+
+static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
+ return m->alpha_[v >> 24] +
+ m->red_[(v >> 16) & 0xff] +
+ m->literal_[(v >> 8) & 0xff] +
+ m->blue_[v & 0xff];
+}
+
+static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
+ const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
+ return m->literal_[literal_idx];
+}
+
+static WEBP_INLINE double GetLengthCost(const CostModel* const m,
+ uint32_t length) {
+ int code, extra_bits;
+ VP8LPrefixEncodeBits(length, &code, &extra_bits);
+ return m->literal_[VALUES_IN_BYTE + code] + extra_bits;
+}
+
+static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
+ uint32_t distance) {
+ int code, extra_bits;
+ VP8LPrefixEncodeBits(distance, &code, &extra_bits);
+ return m->distance_[code] + extra_bits;
+}
+
+static WEBP_INLINE void AddSingleLiteralWithCostModel(
+ const uint32_t* const argb, VP8LColorCache* const hashers,
+ const CostModel* const cost_model, int idx, int use_color_cache,
+ float prev_cost, float* const cost, uint16_t* const dist_array) {
+ double cost_val = prev_cost;
+ const uint32_t color = argb[idx];
+ const int ix = use_color_cache ? VP8LColorCacheContains(hashers, color) : -1;
+ if (ix >= 0) {
+ // use_color_cache is true and hashers contains color
+ const double mul0 = 0.68;
+ cost_val += GetCacheCost(cost_model, ix) * mul0;
+ } else {
+ const double mul1 = 0.82;
+ if (use_color_cache) VP8LColorCacheInsert(hashers, color);
+ cost_val += GetLiteralCost(cost_model, color) * mul1;
+ }
+ if (cost[idx] > cost_val) {
+ cost[idx] = (float)cost_val;
+ dist_array[idx] = 1; // only one is inserted.
+ }
+}
+
+// -----------------------------------------------------------------------------
+// CostManager and interval handling
+
+// Empirical value to avoid high memory consumption but good for performance.
+#define COST_CACHE_INTERVAL_SIZE_MAX 500
+
+// To perform backward reference every pixel at index index_ is considered and
+// the cost for the MAX_LENGTH following pixels computed. Those following pixels
+// at index index_ + k (k from 0 to MAX_LENGTH) have a cost of:
+// cost_ = distance cost at index + GetLengthCost(cost_model, k)
+// and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an
+// array of size MAX_LENGTH.
+// Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the
+// minimal values using intervals of constant cost.
+// An interval is defined by the index_ of the pixel that generated it and
+// is only useful in a range of indices from start_ to end_ (exclusive), i.e.
+// it contains the minimum value for pixels between start_ and end_.
+// Intervals are stored in a linked list and ordered by start_. When a new
+// interval has a better value, old intervals are split or removed. There are
+// therefore no overlapping intervals.
+typedef struct CostInterval CostInterval;
+struct CostInterval {
+ float cost_;
+ int start_;
+ int end_;
+ int index_;
+ CostInterval* previous_;
+ CostInterval* next_;
+};
+
+// The GetLengthCost(cost_model, k) are cached in a CostCacheInterval.
+typedef struct {
+ double cost_;
+ int start_;
+ int end_; // Exclusive.
+} CostCacheInterval;
+
+// This structure is in charge of managing intervals and costs.
+// It caches the different CostCacheInterval, caches the different
+// GetLengthCost(cost_model, k) in cost_cache_ and the CostInterval's (whose
+// count_ is limited by COST_CACHE_INTERVAL_SIZE_MAX).
+#define COST_MANAGER_MAX_FREE_LIST 10
+typedef struct {
+ CostInterval* head_;
+ int count_; // The number of stored intervals.
+ CostCacheInterval* cache_intervals_;
+ size_t cache_intervals_size_;
+ double cost_cache_[MAX_LENGTH]; // Contains the GetLengthCost(cost_model, k).
+ float* costs_;
+ uint16_t* dist_array_;
+ // Most of the time, we only need few intervals -> use a free-list, to avoid
+ // fragmentation with small allocs in most common cases.
+ CostInterval intervals_[COST_MANAGER_MAX_FREE_LIST];
+ CostInterval* free_intervals_;
+ // These are regularly malloc'd remains. This list can't grow larger than than
+ // size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note.
+ CostInterval* recycled_intervals_;
+} CostManager;
+
+static void CostIntervalAddToFreeList(CostManager* const manager,
+ CostInterval* const interval) {
+ interval->next_ = manager->free_intervals_;
+ manager->free_intervals_ = interval;
+}
+
+static int CostIntervalIsInFreeList(const CostManager* const manager,
+ const CostInterval* const interval) {
+ return (interval >= &manager->intervals_[0] &&
+ interval <= &manager->intervals_[COST_MANAGER_MAX_FREE_LIST - 1]);
+}
+
+static void CostManagerInitFreeList(CostManager* const manager) {
+ int i;
+ manager->free_intervals_ = NULL;
+ for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) {
+ CostIntervalAddToFreeList(manager, &manager->intervals_[i]);
+ }
+}
+
+static void DeleteIntervalList(CostManager* const manager,
+ const CostInterval* interval) {
+ while (interval != NULL) {
+ const CostInterval* const next = interval->next_;
+ if (!CostIntervalIsInFreeList(manager, interval)) {
+ WebPSafeFree((void*)interval);
+ } // else: do nothing
+ interval = next;
+ }
+}
+
+static void CostManagerClear(CostManager* const manager) {
+ if (manager == NULL) return;
+
+ WebPSafeFree(manager->costs_);
+ WebPSafeFree(manager->cache_intervals_);
+
+ // Clear the interval lists.
+ DeleteIntervalList(manager, manager->head_);
+ manager->head_ = NULL;
+ DeleteIntervalList(manager, manager->recycled_intervals_);
+ manager->recycled_intervals_ = NULL;
+
+ // Reset pointers, count_ and cache_intervals_size_.
+ memset(manager, 0, sizeof(*manager));
+ CostManagerInitFreeList(manager);
+}
+
+static int CostManagerInit(CostManager* const manager,
+ uint16_t* const dist_array, int pix_count,
+ const CostModel* const cost_model) {
+ int i;
+ const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count;
+
+ manager->costs_ = NULL;
+ manager->cache_intervals_ = NULL;
+ manager->head_ = NULL;
+ manager->recycled_intervals_ = NULL;
+ manager->count_ = 0;
+ manager->dist_array_ = dist_array;
+ CostManagerInitFreeList(manager);
+
+ // Fill in the cost_cache_.
+ manager->cache_intervals_size_ = 1;
+ manager->cost_cache_[0] = GetLengthCost(cost_model, 0);
+ for (i = 1; i < cost_cache_size; ++i) {
+ manager->cost_cache_[i] = GetLengthCost(cost_model, i);
+ // Get the number of bound intervals.
+ if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) {
+ ++manager->cache_intervals_size_;
+ }
+ }
+
+ // With the current cost model, we usually have below 20 intervals.
+ // The worst case scenario with a cost model would be if every length has a
+ // different cost, hence MAX_LENGTH but that is impossible with the current
+ // implementation that spirals around a pixel.
+ assert(manager->cache_intervals_size_ <= MAX_LENGTH);
+ manager->cache_intervals_ = (CostCacheInterval*)WebPSafeMalloc(
+ manager->cache_intervals_size_, sizeof(*manager->cache_intervals_));
+ if (manager->cache_intervals_ == NULL) {
+ CostManagerClear(manager);
+ return 0;
+ }
+
+ // Fill in the cache_intervals_.
+ {
+ CostCacheInterval* cur = manager->cache_intervals_;
+
+ // Consecutive values in cost_cache_ are compared and if a big enough
+ // difference is found, a new interval is created and bounded.
+ cur->start_ = 0;
+ cur->end_ = 1;
+ cur->cost_ = manager->cost_cache_[0];
+ for (i = 1; i < cost_cache_size; ++i) {
+ const double cost_val = manager->cost_cache_[i];
+ if (cost_val != cur->cost_) {
+ ++cur;
+ // Initialize an interval.
+ cur->start_ = i;
+ cur->cost_ = cost_val;
+ }
+ cur->end_ = i + 1;
+ }
+ }
+
+ manager->costs_ = (float*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_));
+ if (manager->costs_ == NULL) {
+ CostManagerClear(manager);
+ return 0;
+ }
+ // Set the initial costs_ high for every pixel as we will keep the minimum.
+ for (i = 0; i < pix_count; ++i) manager->costs_[i] = 1e38f;
+
+ return 1;
+}
+
+// Given the cost and the position that define an interval, update the cost at
+// pixel 'i' if it is smaller than the previously computed value.
+static WEBP_INLINE void UpdateCost(CostManager* const manager, int i,
+ int position, float cost) {
+ const int k = i - position;
+ assert(k >= 0 && k < MAX_LENGTH);
+
+ if (manager->costs_[i] > cost) {
+ manager->costs_[i] = cost;
+ manager->dist_array_[i] = k + 1;
+ }
+}
+
+// Given the cost and the position that define an interval, update the cost for
+// all the pixels between 'start' and 'end' excluded.
+static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager,
+ int start, int end, int position,
+ float cost) {
+ int i;
+ for (i = start; i < end; ++i) UpdateCost(manager, i, position, cost);
+}
+
+// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'.
+static WEBP_INLINE void ConnectIntervals(CostManager* const manager,
+ CostInterval* const prev,
+ CostInterval* const next) {
+ if (prev != NULL) {
+ prev->next_ = next;
+ } else {
+ manager->head_ = next;
+ }
+
+ if (next != NULL) next->previous_ = prev;
+}
+
+// Pop an interval in the manager.
+static WEBP_INLINE void PopInterval(CostManager* const manager,
+ CostInterval* const interval) {
+ if (interval == NULL) return;
+
+ ConnectIntervals(manager, interval->previous_, interval->next_);
+ if (CostIntervalIsInFreeList(manager, interval)) {
+ CostIntervalAddToFreeList(manager, interval);
+ } else { // recycle regularly malloc'd intervals too
+ interval->next_ = manager->recycled_intervals_;
+ manager->recycled_intervals_ = interval;
+ }
+ --manager->count_;
+ assert(manager->count_ >= 0);
+}
+
+// Update the cost at index i by going over all the stored intervals that
+// overlap with i.
+// If 'do_clean_intervals' is set to something different than 0, intervals that
+// end before 'i' will be popped.
+static WEBP_INLINE void UpdateCostAtIndex(CostManager* const manager, int i,
+ int do_clean_intervals) {
+ CostInterval* current = manager->head_;
+
+ while (current != NULL && current->start_ <= i) {
+ CostInterval* const next = current->next_;
+ if (current->end_ <= i) {
+ if (do_clean_intervals) {
+ // We have an outdated interval, remove it.
+ PopInterval(manager, current);
+ }
+ } else {
+ UpdateCost(manager, i, current->index_, current->cost_);
+ }
+ current = next;
+ }
+}
+
+// Given a current orphan interval and its previous interval, before
+// it was orphaned (which can be NULL), set it at the right place in the list
+// of intervals using the start_ ordering and the previous interval as a hint.
+static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager,
+ CostInterval* const current,
+ CostInterval* previous) {
+ assert(current != NULL);
+
+ if (previous == NULL) previous = manager->head_;
+ while (previous != NULL && current->start_ < previous->start_) {
+ previous = previous->previous_;
+ }
+ while (previous != NULL && previous->next_ != NULL &&
+ previous->next_->start_ < current->start_) {
+ previous = previous->next_;
+ }
+
+ if (previous != NULL) {
+ ConnectIntervals(manager, current, previous->next_);
+ } else {
+ ConnectIntervals(manager, current, manager->head_);
+ }
+ ConnectIntervals(manager, previous, current);
+}
+
+// Insert an interval in the list contained in the manager by starting at
+// interval_in as a hint. The intervals are sorted by start_ value.
+static WEBP_INLINE void InsertInterval(CostManager* const manager,
+ CostInterval* const interval_in,
+ float cost, int position, int start,
+ int end) {
+ CostInterval* interval_new;
+
+ if (start >= end) return;
+ if (manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) {
+ // Serialize the interval if we cannot store it.
+ UpdateCostPerInterval(manager, start, end, position, cost);
+ return;
+ }
+ if (manager->free_intervals_ != NULL) {
+ interval_new = manager->free_intervals_;
+ manager->free_intervals_ = interval_new->next_;
+ } else if (manager->recycled_intervals_ != NULL) {
+ interval_new = manager->recycled_intervals_;
+ manager->recycled_intervals_ = interval_new->next_;
+ } else { // malloc for good
+ interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new));
+ if (interval_new == NULL) {
+ // Write down the interval if we cannot create it.
+ UpdateCostPerInterval(manager, start, end, position, cost);
+ return;
+ }
+ }
+
+ interval_new->cost_ = cost;
+ interval_new->index_ = position;
+ interval_new->start_ = start;
+ interval_new->end_ = end;
+ PositionOrphanInterval(manager, interval_new, interval_in);
+
+ ++manager->count_;
+}
+
+// Given a new cost interval defined by its start at position, its length value
+// and distance_cost, add its contributions to the previous intervals and costs.
+// If handling the interval or one of its subintervals becomes to heavy, its
+// contribution is added to the costs right away.
+static WEBP_INLINE void PushInterval(CostManager* const manager,
+ double distance_cost, int position,
+ int len) {
+ size_t i;
+ CostInterval* interval = manager->head_;
+ CostInterval* interval_next;
+ const CostCacheInterval* const cost_cache_intervals =
+ manager->cache_intervals_;
+ // If the interval is small enough, no need to deal with the heavy
+ // interval logic, just serialize it right away. This constant is empirical.
+ const int kSkipDistance = 10;
+
+ if (len < kSkipDistance) {
+ int j;
+ for (j = position; j < position + len; ++j) {
+ const int k = j - position;
+ float cost_tmp;
+ assert(k >= 0 && k < MAX_LENGTH);
+ cost_tmp = (float)(distance_cost + manager->cost_cache_[k]);
+
+ if (manager->costs_[j] > cost_tmp) {
+ manager->costs_[j] = cost_tmp;
+ manager->dist_array_[j] = k + 1;
+ }
+ }
+ return;
+ }
+
+ for (i = 0; i < manager->cache_intervals_size_ &&
+ cost_cache_intervals[i].start_ < len;
+ ++i) {
+ // Define the intersection of the ith interval with the new one.
+ int start = position + cost_cache_intervals[i].start_;
+ const int end = position + (cost_cache_intervals[i].end_ > len
+ ? len
+ : cost_cache_intervals[i].end_);
+ const float cost = (float)(distance_cost + cost_cache_intervals[i].cost_);
+
+ for (; interval != NULL && interval->start_ < end;
+ interval = interval_next) {
+ interval_next = interval->next_;
+
+ // Make sure we have some overlap
+ if (start >= interval->end_) continue;
+
+ if (cost >= interval->cost_) {
+ // When intervals are represented, the lower, the better.
+ // [**********************************************************[
+ // start end
+ // [----------------------------------[
+ // interval->start_ interval->end_
+ // If we are worse than what we already have, add whatever we have so
+ // far up to interval.
+ const int start_new = interval->end_;
+ InsertInterval(manager, interval, cost, position, start,
+ interval->start_);
+ start = start_new;
+ if (start >= end) break;
+ continue;
+ }
+
+ if (start <= interval->start_) {
+ if (interval->end_ <= end) {
+ // [----------------------------------[
+ // interval->start_ interval->end_
+ // [**************************************************************[
+ // start end
+ // We can safely remove the old interval as it is fully included.
+ PopInterval(manager, interval);
+ } else {
+ // [------------------------------------[
+ // interval->start_ interval->end_
+ // [*****************************[
+ // start end
+ interval->start_ = end;
+ break;
+ }
+ } else {
+ if (end < interval->end_) {
+ // [--------------------------------------------------------------[
+ // interval->start_ interval->end_
+ // [*****************************[
+ // start end
+ // We have to split the old interval as it fully contains the new one.
+ const int end_original = interval->end_;
+ interval->end_ = start;
+ InsertInterval(manager, interval, interval->cost_, interval->index_,
+ end, end_original);
+ interval = interval->next_;
+ break;
+ } else {
+ // [------------------------------------[
+ // interval->start_ interval->end_
+ // [*****************************[
+ // start end
+ interval->end_ = start;
+ }
+ }
+ }
+ // Insert the remaining interval from start to end.
+ InsertInterval(manager, interval, cost, position, start, end);
+ }
+}
+
+static int BackwardReferencesHashChainDistanceOnly(
+ int xsize, int ysize, const uint32_t* const argb, int cache_bits,
+ const VP8LHashChain* const hash_chain, const VP8LBackwardRefs* const refs,
+ uint16_t* const dist_array) {
+ int i;
+ int ok = 0;
+ int cc_init = 0;
+ const int pix_count = xsize * ysize;
+ const int use_color_cache = (cache_bits > 0);
+ const size_t literal_array_size =
+ sizeof(double) * (NUM_LITERAL_CODES + NUM_LENGTH_CODES +
+ ((cache_bits > 0) ? (1 << cache_bits) : 0));
+ const size_t cost_model_size = sizeof(CostModel) + literal_array_size;
+ CostModel* const cost_model =
+ (CostModel*)WebPSafeCalloc(1ULL, cost_model_size);
+ VP8LColorCache hashers;
+ CostManager* cost_manager =
+ (CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager));
+ int offset_prev = -1, len_prev = -1;
+ double offset_cost = -1;
+ int first_offset_is_constant = -1; // initialized with 'impossible' value
+ int reach = 0;
+
+ if (cost_model == NULL || cost_manager == NULL) goto Error;
+
+ cost_model->literal_ = (double*)(cost_model + 1);
+ if (use_color_cache) {
+ cc_init = VP8LColorCacheInit(&hashers, cache_bits);
+ if (!cc_init) goto Error;
+ }
+
+ if (!CostModelBuild(cost_model, xsize, cache_bits, refs)) {
+ goto Error;
+ }
+
+ if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) {
+ goto Error;
+ }
+
+ // We loop one pixel at a time, but store all currently best points to
+ // non-processed locations from this point.
+ dist_array[0] = 0;
+ // Add first pixel as literal.
+ AddSingleLiteralWithCostModel(argb, &hashers, cost_model, 0, use_color_cache,
+ 0.f, cost_manager->costs_, dist_array);
+
+ for (i = 1; i < pix_count; ++i) {
+ const float prev_cost = cost_manager->costs_[i - 1];
+ int offset, len;
+ VP8LHashChainFindCopy(hash_chain, i, &offset, &len);
+
+ // Try adding the pixel as a literal.
+ AddSingleLiteralWithCostModel(argb, &hashers, cost_model, i,
+ use_color_cache, prev_cost,
+ cost_manager->costs_, dist_array);
+
+ // If we are dealing with a non-literal.
+ if (len >= 2) {
+ if (offset != offset_prev) {
+ const int code = VP8LDistanceToPlaneCode(xsize, offset);
+ offset_cost = GetDistanceCost(cost_model, code);
+ first_offset_is_constant = 1;
+ PushInterval(cost_manager, prev_cost + offset_cost, i, len);
+ } else {
+ assert(offset_cost >= 0);
+ assert(len_prev >= 0);
+ assert(first_offset_is_constant == 0 || first_offset_is_constant == 1);
+ // Instead of considering all contributions from a pixel i by calling:
+ // PushInterval(cost_manager, prev_cost + offset_cost, i, len);
+ // we optimize these contributions in case offset_cost stays the same
+ // for consecutive pixels. This describes a set of pixels similar to a
+ // previous set (e.g. constant color regions).
+ if (first_offset_is_constant) {
+ reach = i - 1 + len_prev - 1;
+ first_offset_is_constant = 0;
+ }
+
+ if (i + len - 1 > reach) {
+ // We can only be go further with the same offset if the previous
+ // length was maxed, hence len_prev == len == MAX_LENGTH.
+ // TODO(vrabaud), bump i to the end right away (insert cache and
+ // update cost).
+ // TODO(vrabaud), check if one of the points in between does not have
+ // a lower cost.
+ // Already consider the pixel at "reach" to add intervals that are
+ // better than whatever we add.
+ int offset_j, len_j = 0;
+ int j;
+ assert(len == MAX_LENGTH || len == pix_count - i);
+ // Figure out the last consecutive pixel within [i, reach + 1] with
+ // the same offset.
+ for (j = i; j <= reach; ++j) {
+ VP8LHashChainFindCopy(hash_chain, j + 1, &offset_j, &len_j);
+ if (offset_j != offset) {
+ VP8LHashChainFindCopy(hash_chain, j, &offset_j, &len_j);
+ break;
+ }
+ }
+ // Update the cost at j - 1 and j.
+ UpdateCostAtIndex(cost_manager, j - 1, 0);
+ UpdateCostAtIndex(cost_manager, j, 0);
+
+ PushInterval(cost_manager, cost_manager->costs_[j - 1] + offset_cost,
+ j, len_j);
+ reach = j + len_j - 1;
+ }
+ }
+ }
+
+ UpdateCostAtIndex(cost_manager, i, 1);
+ offset_prev = offset;
+ len_prev = len;
+ }
+
+ ok = !refs->error_;
+Error:
+ if (cc_init) VP8LColorCacheClear(&hashers);
+ CostManagerClear(cost_manager);
+ WebPSafeFree(cost_model);
+ WebPSafeFree(cost_manager);
+ return ok;
+}
+
+// We pack the path at the end of *dist_array and return
+// a pointer to this part of the array. Example:
+// dist_array = [1x2xx3x2] => packed [1x2x1232], chosen_path = [1232]
+static void TraceBackwards(uint16_t* const dist_array,
+ int dist_array_size,
+ uint16_t** const chosen_path,
+ int* const chosen_path_size) {
+ uint16_t* path = dist_array + dist_array_size;
+ uint16_t* cur = dist_array + dist_array_size - 1;
+ while (cur >= dist_array) {
+ const int k = *cur;
+ --path;
+ *path = k;
+ cur -= k;
+ }
+ *chosen_path = path;
+ *chosen_path_size = (int)(dist_array + dist_array_size - path);
+}
+
+static int BackwardReferencesHashChainFollowChosenPath(
+ const uint32_t* const argb, int cache_bits,
+ const uint16_t* const chosen_path, int chosen_path_size,
+ const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) {
+ const int use_color_cache = (cache_bits > 0);
+ int ix;
+ int i = 0;
+ int ok = 0;
+ int cc_init = 0;
+ VP8LColorCache hashers;
+
+ if (use_color_cache) {
+ cc_init = VP8LColorCacheInit(&hashers, cache_bits);
+ if (!cc_init) goto Error;
+ }
+
+ VP8LClearBackwardRefs(refs);
+ for (ix = 0; ix < chosen_path_size; ++ix) {
+ const int len = chosen_path[ix];
+ if (len != 1) {
+ int k;
+ const int offset = VP8LHashChainFindOffset(hash_chain, i);
+ VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
+ if (use_color_cache) {
+ for (k = 0; k < len; ++k) {
+ VP8LColorCacheInsert(&hashers, argb[i + k]);
+ }
+ }
+ i += len;
+ } else {
+ PixOrCopy v;
+ const int idx =
+ use_color_cache ? VP8LColorCacheContains(&hashers, argb[i]) : -1;
+ if (idx >= 0) {
+ // use_color_cache is true and hashers contains argb[i]
+ // push pixel as a color cache index
+ v = PixOrCopyCreateCacheIdx(idx);
+ } else {
+ if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]);
+ v = PixOrCopyCreateLiteral(argb[i]);
+ }
+ VP8LBackwardRefsCursorAdd(refs, v);
+ ++i;
+ }
+ }
+ ok = !refs->error_;
+ Error:
+ if (cc_init) VP8LColorCacheClear(&hashers);
+ return ok;
+}
+
+// Returns 1 on success.
+extern int VP8LBackwardReferencesTraceBackwards(
+ int xsize, int ysize, const uint32_t* const argb, int cache_bits,
+ const VP8LHashChain* const hash_chain,
+ const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
+int VP8LBackwardReferencesTraceBackwards(int xsize, int ysize,
+ const uint32_t* const argb,
+ int cache_bits,
+ const VP8LHashChain* const hash_chain,
+ const VP8LBackwardRefs* const refs_src,
+ VP8LBackwardRefs* const refs_dst) {
+ int ok = 0;
+ const int dist_array_size = xsize * ysize;
+ uint16_t* chosen_path = NULL;
+ int chosen_path_size = 0;
+ uint16_t* dist_array =
+ (uint16_t*)WebPSafeMalloc(dist_array_size, sizeof(*dist_array));
+
+ if (dist_array == NULL) goto Error;
+
+ if (!BackwardReferencesHashChainDistanceOnly(
+ xsize, ysize, argb, cache_bits, hash_chain, refs_src, dist_array)) {
+ goto Error;
+ }
+ TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size);
+ if (!BackwardReferencesHashChainFollowChosenPath(
+ argb, cache_bits, chosen_path, chosen_path_size, hash_chain,
+ refs_dst)) {
+ goto Error;
+ }
+ ok = 1;
+ Error:
+ WebPSafeFree(dist_array);
+ return ok;
+}
diff --git a/src/third_party/libwebp/src/enc/backward_references_enc.c b/src/third_party/libwebp/src/enc/backward_references_enc.c
new file mode 100644
index 0000000..3923018
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/backward_references_enc.c
@@ -0,0 +1,943 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+
+#include <assert.h>
+#include <math.h>
+
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+#include "src/dsp/dsp.h"
+#include "src/utils/color_cache_utils.h"
+#include "src/utils/utils.h"
+
+#define MIN_BLOCK_SIZE 256 // minimum block size for backward references
+
+#define MAX_ENTROPY (1e30f)
+
+// 1M window (4M bytes) minus 120 special codes for short distances.
+#define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120)
+
+// Minimum number of pixels for which it is cheaper to encode a
+// distance + length instead of each pixel as a literal.
+#define MIN_LENGTH 4
+
+// -----------------------------------------------------------------------------
+
+static const uint8_t plane_to_code_lut[128] = {
+ 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255,
+ 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79,
+ 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87,
+ 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91,
+ 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100,
+ 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109,
+ 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114,
+ 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117
+};
+
+extern int VP8LDistanceToPlaneCode(int xsize, int dist);
+int VP8LDistanceToPlaneCode(int xsize, int dist) {
+ const int yoffset = dist / xsize;
+ const int xoffset = dist - yoffset * xsize;
+ if (xoffset <= 8 && yoffset < 8) {
+ return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1;
+ } else if (xoffset > xsize - 8 && yoffset < 7) {
+ return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1;
+ }
+ return dist + 120;
+}
+
+// Returns the exact index where array1 and array2 are different. For an index
+// inferior or equal to best_len_match, the return value just has to be strictly
+// inferior to best_len_match. The current behavior is to return 0 if this index
+// is best_len_match, and the index itself otherwise.
+// If no two elements are the same, it returns max_limit.
+static WEBP_INLINE int FindMatchLength(const uint32_t* const array1,
+ const uint32_t* const array2,
+ int best_len_match, int max_limit) {
+ // Before 'expensive' linear match, check if the two arrays match at the
+ // current best length index.
+ if (array1[best_len_match] != array2[best_len_match]) return 0;
+
+ return VP8LVectorMismatch(array1, array2, max_limit);
+}
+
+// -----------------------------------------------------------------------------
+// VP8LBackwardRefs
+
+struct PixOrCopyBlock {
+ PixOrCopyBlock* next_; // next block (or NULL)
+ PixOrCopy* start_; // data start
+ int size_; // currently used size
+};
+
+extern void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
+void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) {
+ assert(refs != NULL);
+ if (refs->tail_ != NULL) {
+ *refs->tail_ = refs->free_blocks_; // recycle all blocks at once
+ }
+ refs->free_blocks_ = refs->refs_;
+ refs->tail_ = &refs->refs_;
+ refs->last_block_ = NULL;
+ refs->refs_ = NULL;
+}
+
+void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) {
+ assert(refs != NULL);
+ VP8LClearBackwardRefs(refs);
+ while (refs->free_blocks_ != NULL) {
+ PixOrCopyBlock* const next = refs->free_blocks_->next_;
+ WebPSafeFree(refs->free_blocks_);
+ refs->free_blocks_ = next;
+ }
+}
+
+void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size) {
+ assert(refs != NULL);
+ memset(refs, 0, sizeof(*refs));
+ refs->tail_ = &refs->refs_;
+ refs->block_size_ =
+ (block_size < MIN_BLOCK_SIZE) ? MIN_BLOCK_SIZE : block_size;
+}
+
+VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs) {
+ VP8LRefsCursor c;
+ c.cur_block_ = refs->refs_;
+ if (refs->refs_ != NULL) {
+ c.cur_pos = c.cur_block_->start_;
+ c.last_pos_ = c.cur_pos + c.cur_block_->size_;
+ } else {
+ c.cur_pos = NULL;
+ c.last_pos_ = NULL;
+ }
+ return c;
+}
+
+void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c) {
+ PixOrCopyBlock* const b = c->cur_block_->next_;
+ c->cur_pos = (b == NULL) ? NULL : b->start_;
+ c->last_pos_ = (b == NULL) ? NULL : b->start_ + b->size_;
+ c->cur_block_ = b;
+}
+
+// Create a new block, either from the free list or allocated
+static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) {
+ PixOrCopyBlock* b = refs->free_blocks_;
+ if (b == NULL) { // allocate new memory chunk
+ const size_t total_size =
+ sizeof(*b) + refs->block_size_ * sizeof(*b->start_);
+ b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size);
+ if (b == NULL) {
+ refs->error_ |= 1;
+ return NULL;
+ }
+ b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned
+ } else { // recycle from free-list
+ refs->free_blocks_ = b->next_;
+ }
+ *refs->tail_ = b;
+ refs->tail_ = &b->next_;
+ refs->last_block_ = b;
+ b->next_ = NULL;
+ b->size_ = 0;
+ return b;
+}
+
+extern void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
+ const PixOrCopy v);
+void VP8LBackwardRefsCursorAdd(VP8LBackwardRefs* const refs,
+ const PixOrCopy v) {
+ PixOrCopyBlock* b = refs->last_block_;
+ if (b == NULL || b->size_ == refs->block_size_) {
+ b = BackwardRefsNewBlock(refs);
+ if (b == NULL) return; // refs->error_ is set
+ }
+ b->start_[b->size_++] = v;
+}
+
+// -----------------------------------------------------------------------------
+// Hash chains
+
+int VP8LHashChainInit(VP8LHashChain* const p, int size) {
+ assert(p->size_ == 0);
+ assert(p->offset_length_ == NULL);
+ assert(size > 0);
+ p->offset_length_ =
+ (uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_));
+ if (p->offset_length_ == NULL) return 0;
+ p->size_ = size;
+
+ return 1;
+}
+
+void VP8LHashChainClear(VP8LHashChain* const p) {
+ assert(p != NULL);
+ WebPSafeFree(p->offset_length_);
+
+ p->size_ = 0;
+ p->offset_length_ = NULL;
+}
+
+// -----------------------------------------------------------------------------
+
+#define HASH_MULTIPLIER_HI (0xc6a4a793ULL)
+#define HASH_MULTIPLIER_LO (0x5bd1e996ULL)
+
+static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) {
+ uint32_t key;
+ key = (argb[1] * HASH_MULTIPLIER_HI) & 0xffffffffu;
+ key += (argb[0] * HASH_MULTIPLIER_LO) & 0xffffffffu;
+ key = key >> (32 - HASH_BITS);
+ return key;
+}
+
+// Returns the maximum number of hash chain lookups to do for a
+// given compression quality. Return value in range [8, 86].
+static int GetMaxItersForQuality(int quality) {
+ return 8 + (quality * quality) / 128;
+}
+
+static int GetWindowSizeForHashChain(int quality, int xsize) {
+ const int max_window_size = (quality > 75) ? WINDOW_SIZE
+ : (quality > 50) ? (xsize << 8)
+ : (quality > 25) ? (xsize << 6)
+ : (xsize << 4);
+ assert(xsize > 0);
+ return (max_window_size > WINDOW_SIZE) ? WINDOW_SIZE : max_window_size;
+}
+
+static WEBP_INLINE int MaxFindCopyLength(int len) {
+ return (len < MAX_LENGTH) ? len : MAX_LENGTH;
+}
+
+int VP8LHashChainFill(VP8LHashChain* const p, int quality,
+ const uint32_t* const argb, int xsize, int ysize,
+ int low_effort) {
+ const int size = xsize * ysize;
+ const int iter_max = GetMaxItersForQuality(quality);
+ const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);
+ int pos;
+ int argb_comp;
+ uint32_t base_position;
+ int32_t* hash_to_first_index;
+ // Temporarily use the p->offset_length_ as a hash chain.
+ int32_t* chain = (int32_t*)p->offset_length_;
+ assert(size > 0);
+ assert(p->size_ != 0);
+ assert(p->offset_length_ != NULL);
+
+ if (size <= 2) {
+ p->offset_length_[0] = p->offset_length_[size - 1] = 0;
+ return 1;
+ }
+
+ hash_to_first_index =
+ (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
+ if (hash_to_first_index == NULL) return 0;
+
+ // Set the int32_t array to -1.
+ memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));
+ // Fill the chain linking pixels with the same hash.
+ argb_comp = (argb[0] == argb[1]);
+ for (pos = 0; pos < size - 2;) {
+ uint32_t hash_code;
+ const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]);
+ if (argb_comp && argb_comp_next) {
+ // Consecutive pixels with the same color will share the same hash.
+ // We therefore use a different hash: the color and its repetition
+ // length.
+ uint32_t tmp[2];
+ uint32_t len = 1;
+ tmp[0] = argb[pos];
+ // Figure out how far the pixels are the same.
+ // The last pixel has a different 64 bit hash, as its next pixel does
+ // not have the same color, so we just need to get to the last pixel equal
+ // to its follower.
+ while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) {
+ ++len;
+ }
+ if (len > MAX_LENGTH) {
+ // Skip the pixels that match for distance=1 and length>MAX_LENGTH
+ // because they are linked to their predecessor and we automatically
+ // check that in the main for loop below. Skipping means setting no
+ // predecessor in the chain, hence -1.
+ memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain));
+ pos += len - MAX_LENGTH;
+ len = MAX_LENGTH;
+ }
+ // Process the rest of the hash chain.
+ while (len) {
+ tmp[1] = len--;
+ hash_code = GetPixPairHash64(tmp);
+ chain[pos] = hash_to_first_index[hash_code];
+ hash_to_first_index[hash_code] = pos++;
+ }
+ argb_comp = 0;
+ } else {
+ // Just move one pixel forward.
+ hash_code = GetPixPairHash64(argb + pos);
+ chain[pos] = hash_to_first_index[hash_code];
+ hash_to_first_index[hash_code] = pos++;
+ argb_comp = argb_comp_next;
+ }
+ }
+ // Process the penultimate pixel.
+ chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];
+
+ WebPSafeFree(hash_to_first_index);
+
+ // Find the best match interval at each pixel, defined by an offset to the
+ // pixel and a length. The right-most pixel cannot match anything to the right
+ // (hence a best length of 0) and the left-most pixel nothing to the left
+ // (hence an offset of 0).
+ assert(size > 2);
+ p->offset_length_[0] = p->offset_length_[size - 1] = 0;
+ for (base_position = size - 2; base_position > 0;) {
+ const int max_len = MaxFindCopyLength(size - 1 - base_position);
+ const uint32_t* const argb_start = argb + base_position;
+ int iter = iter_max;
+ int best_length = 0;
+ uint32_t best_distance = 0;
+ uint32_t best_argb;
+ const int min_pos =
+ (base_position > window_size) ? base_position - window_size : 0;
+ const int length_max = (max_len < 256) ? max_len : 256;
+ uint32_t max_base_position;
+
+ pos = chain[base_position];
+ if (!low_effort) {
+ int curr_length;
+ // Heuristic: use the comparison with the above line as an initialization.
+ if (base_position >= (uint32_t)xsize) {
+ curr_length = FindMatchLength(argb_start - xsize, argb_start,
+ best_length, max_len);
+ if (curr_length > best_length) {
+ best_length = curr_length;
+ best_distance = xsize;
+ }
+ --iter;
+ }
+ // Heuristic: compare to the previous pixel.
+ curr_length =
+ FindMatchLength(argb_start - 1, argb_start, best_length, max_len);
+ if (curr_length > best_length) {
+ best_length = curr_length;
+ best_distance = 1;
+ }
+ --iter;
+ // Skip the for loop if we already have the maximum.
+ if (best_length == MAX_LENGTH) pos = min_pos - 1;
+ }
+ best_argb = argb_start[best_length];
+
+ for (; pos >= min_pos && --iter; pos = chain[pos]) {
+ int curr_length;
+ assert(base_position > (uint32_t)pos);
+
+ if (argb[pos + best_length] != best_argb) continue;
+
+ curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len);
+ if (best_length < curr_length) {
+ best_length = curr_length;
+ best_distance = base_position - pos;
+ best_argb = argb_start[best_length];
+ // Stop if we have reached a good enough length.
+ if (best_length >= length_max) break;
+ }
+ }
+ // We have the best match but in case the two intervals continue matching
+ // to the left, we have the best matches for the left-extended pixels.
+ max_base_position = base_position;
+ while (1) {
+ assert(best_length <= MAX_LENGTH);
+ assert(best_distance <= WINDOW_SIZE);
+ p->offset_length_[base_position] =
+ (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length;
+ --base_position;
+ // Stop if we don't have a match or if we are out of bounds.
+ if (best_distance == 0 || base_position == 0) break;
+ // Stop if we cannot extend the matching intervals to the left.
+ if (base_position < best_distance ||
+ argb[base_position - best_distance] != argb[base_position]) {
+ break;
+ }
+ // Stop if we are matching at its limit because there could be a closer
+ // matching interval with the same maximum length. Then again, if the
+ // matching interval is as close as possible (best_distance == 1), we will
+ // never find anything better so let's continue.
+ if (best_length == MAX_LENGTH && best_distance != 1 &&
+ base_position + MAX_LENGTH < max_base_position) {
+ break;
+ }
+ if (best_length < MAX_LENGTH) {
+ ++best_length;
+ max_base_position = base_position;
+ }
+ }
+ }
+ return 1;
+}
+
+static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,
+ VP8LColorCache* const hashers,
+ VP8LBackwardRefs* const refs) {
+ PixOrCopy v;
+ if (use_color_cache) {
+ const uint32_t key = VP8LColorCacheGetIndex(hashers, pixel);
+ if (VP8LColorCacheLookup(hashers, key) == pixel) {
+ v = PixOrCopyCreateCacheIdx(key);
+ } else {
+ v = PixOrCopyCreateLiteral(pixel);
+ VP8LColorCacheSet(hashers, key, pixel);
+ }
+ } else {
+ v = PixOrCopyCreateLiteral(pixel);
+ }
+ VP8LBackwardRefsCursorAdd(refs, v);
+}
+
+static int BackwardReferencesRle(int xsize, int ysize,
+ const uint32_t* const argb,
+ int cache_bits, VP8LBackwardRefs* const refs) {
+ const int pix_count = xsize * ysize;
+ int i, k;
+ const int use_color_cache = (cache_bits > 0);
+ VP8LColorCache hashers;
+
+ if (use_color_cache && !VP8LColorCacheInit(&hashers, cache_bits)) {
+ return 0;
+ }
+ VP8LClearBackwardRefs(refs);
+ // Add first pixel as literal.
+ AddSingleLiteral(argb[0], use_color_cache, &hashers, refs);
+ i = 1;
+ while (i < pix_count) {
+ const int max_len = MaxFindCopyLength(pix_count - i);
+ const int rle_len = FindMatchLength(argb + i, argb + i - 1, 0, max_len);
+ const int prev_row_len = (i < xsize) ? 0 :
+ FindMatchLength(argb + i, argb + i - xsize, 0, max_len);
+ if (rle_len >= prev_row_len && rle_len >= MIN_LENGTH) {
+ VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(1, rle_len));
+ // We don't need to update the color cache here since it is always the
+ // same pixel being copied, and that does not change the color cache
+ // state.
+ i += rle_len;
+ } else if (prev_row_len >= MIN_LENGTH) {
+ VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(xsize, prev_row_len));
+ if (use_color_cache) {
+ for (k = 0; k < prev_row_len; ++k) {
+ VP8LColorCacheInsert(&hashers, argb[i + k]);
+ }
+ }
+ i += prev_row_len;
+ } else {
+ AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
+ i++;
+ }
+ }
+ if (use_color_cache) VP8LColorCacheClear(&hashers);
+ return !refs->error_;
+}
+
+static int BackwardReferencesLz77(int xsize, int ysize,
+ const uint32_t* const argb, int cache_bits,
+ const VP8LHashChain* const hash_chain,
+ VP8LBackwardRefs* const refs) {
+ int i;
+ int i_last_check = -1;
+ int ok = 0;
+ int cc_init = 0;
+ const int use_color_cache = (cache_bits > 0);
+ const int pix_count = xsize * ysize;
+ VP8LColorCache hashers;
+
+ if (use_color_cache) {
+ cc_init = VP8LColorCacheInit(&hashers, cache_bits);
+ if (!cc_init) goto Error;
+ }
+ VP8LClearBackwardRefs(refs);
+ for (i = 0; i < pix_count;) {
+ // Alternative#1: Code the pixels starting at 'i' using backward reference.
+ int offset = 0;
+ int len = 0;
+ int j;
+ VP8LHashChainFindCopy(hash_chain, i, &offset, &len);
+ if (len >= MIN_LENGTH) {
+ const int len_ini = len;
+ int max_reach = 0;
+ const int j_max =
+ (i + len_ini >= pix_count) ? pix_count - 1 : i + len_ini;
+ // Only start from what we have not checked already.
+ i_last_check = (i > i_last_check) ? i : i_last_check;
+ // We know the best match for the current pixel but we try to find the
+ // best matches for the current pixel AND the next one combined.
+ // The naive method would use the intervals:
+ // [i,i+len) + [i+len, length of best match at i+len)
+ // while we check if we can use:
+ // [i,j) (where j<=i+len) + [j, length of best match at j)
+ for (j = i_last_check + 1; j <= j_max; ++j) {
+ const int len_j = VP8LHashChainFindLength(hash_chain, j);
+ const int reach =
+ j + (len_j >= MIN_LENGTH ? len_j : 1); // 1 for single literal.
+ if (reach > max_reach) {
+ len = j - i;
+ max_reach = reach;
+ if (max_reach >= pix_count) break;
+ }
+ }
+ } else {
+ len = 1;
+ }
+ // Go with literal or backward reference.
+ assert(len > 0);
+ if (len == 1) {
+ AddSingleLiteral(argb[i], use_color_cache, &hashers, refs);
+ } else {
+ VP8LBackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len));
+ if (use_color_cache) {
+ for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]);
+ }
+ }
+ i += len;
+ }
+
+ ok = !refs->error_;
+ Error:
+ if (cc_init) VP8LColorCacheClear(&hashers);
+ return ok;
+}
+
+// Compute an LZ77 by forcing matches to happen within a given distance cost.
+// We therefore limit the algorithm to the lowest 32 values in the PlaneCode
+// definition.
+#define WINDOW_OFFSETS_SIZE_MAX 32
+static int BackwardReferencesLz77Box(int xsize, int ysize,
+ const uint32_t* const argb, int cache_bits,
+ const VP8LHashChain* const hash_chain_best,
+ VP8LHashChain* hash_chain,
+ VP8LBackwardRefs* const refs) {
+ int i;
+ const int pix_count = xsize * ysize;
+ uint16_t* counts;
+ int window_offsets[WINDOW_OFFSETS_SIZE_MAX] = {0};
+ int window_offsets_new[WINDOW_OFFSETS_SIZE_MAX] = {0};
+ int window_offsets_size = 0;
+ int window_offsets_new_size = 0;
+ uint16_t* const counts_ini =
+ (uint16_t*)WebPSafeMalloc(xsize * ysize, sizeof(*counts_ini));
+ int best_offset_prev = -1, best_length_prev = -1;
+ if (counts_ini == NULL) return 0;
+
+ // counts[i] counts how many times a pixel is repeated starting at position i.
+ i = pix_count - 2;
+ counts = counts_ini + i;
+ counts[1] = 1;
+ for (; i >= 0; --i, --counts) {
+ if (argb[i] == argb[i + 1]) {
+ // Max out the counts to MAX_LENGTH.
+ counts[0] = counts[1] + (counts[1] != MAX_LENGTH);
+ } else {
+ counts[0] = 1;
+ }
+ }
+
+ // Figure out the window offsets around a pixel. They are stored in a
+ // spiraling order around the pixel as defined by VP8LDistanceToPlaneCode.
+ {
+ int x, y;
+ for (y = 0; y <= 6; ++y) {
+ for (x = -6; x <= 6; ++x) {
+ const int offset = y * xsize + x;
+ int plane_code;
+ // Ignore offsets that bring us after the pixel.
+ if (offset <= 0) continue;
+ plane_code = VP8LDistanceToPlaneCode(xsize, offset) - 1;
+ if (plane_code >= WINDOW_OFFSETS_SIZE_MAX) continue;
+ window_offsets[plane_code] = offset;
+ }
+ }
+ // For narrow images, not all plane codes are reached, so remove those.
+ for (i = 0; i < WINDOW_OFFSETS_SIZE_MAX; ++i) {
+ if (window_offsets[i] == 0) continue;
+ window_offsets[window_offsets_size++] = window_offsets[i];
+ }
+ // Given a pixel P, find the offsets that reach pixels unreachable from P-1
+ // with any of the offsets in window_offsets[].
+ for (i = 0; i < window_offsets_size; ++i) {
+ int j;
+ int is_reachable = 0;
+ for (j = 0; j < window_offsets_size && !is_reachable; ++j) {
+ is_reachable |= (window_offsets[i] == window_offsets[j] + 1);
+ }
+ if (!is_reachable) {
+ window_offsets_new[window_offsets_new_size] = window_offsets[i];
+ ++window_offsets_new_size;
+ }
+ }
+ }
+
+ hash_chain->offset_length_[0] = 0;
+ for (i = 1; i < pix_count; ++i) {
+ int ind;
+ int best_length = VP8LHashChainFindLength(hash_chain_best, i);
+ int best_offset;
+ int do_compute = 1;
+
+ if (best_length >= MAX_LENGTH) {
+ // Do not recompute the best match if we already have a maximal one in the
+ // window.
+ best_offset = VP8LHashChainFindOffset(hash_chain_best, i);
+ for (ind = 0; ind < window_offsets_size; ++ind) {
+ if (best_offset == window_offsets[ind]) {
+ do_compute = 0;
+ break;
+ }
+ }
+ }
+ if (do_compute) {
+ // Figure out if we should use the offset/length from the previous pixel
+ // as an initial guess and therefore only inspect the offsets in
+ // window_offsets_new[].
+ const int use_prev =
+ (best_length_prev > 1) && (best_length_prev < MAX_LENGTH);
+ const int num_ind =
+ use_prev ? window_offsets_new_size : window_offsets_size;
+ best_length = use_prev ? best_length_prev - 1 : 0;
+ best_offset = use_prev ? best_offset_prev : 0;
+ // Find the longest match in a window around the pixel.
+ for (ind = 0; ind < num_ind; ++ind) {
+ int curr_length = 0;
+ int j = i;
+ int j_offset =
+ use_prev ? i - window_offsets_new[ind] : i - window_offsets[ind];
+ if (j_offset < 0 || argb[j_offset] != argb[i]) continue;
+ // The longest match is the sum of how many times each pixel is
+ // repeated.
+ do {
+ const int counts_j_offset = counts_ini[j_offset];
+ const int counts_j = counts_ini[j];
+ if (counts_j_offset != counts_j) {
+ curr_length +=
+ (counts_j_offset < counts_j) ? counts_j_offset : counts_j;
+ break;
+ }
+ // The same color is repeated counts_pos times at j_offset and j.
+ curr_length += counts_j_offset;
+ j_offset += counts_j_offset;
+ j += counts_j_offset;
+ } while (curr_length <= MAX_LENGTH && j < pix_count &&
+ argb[j_offset] == argb[j]);
+ if (best_length < curr_length) {
+ best_offset =
+ use_prev ? window_offsets_new[ind] : window_offsets[ind];
+ if (curr_length >= MAX_LENGTH) {
+ best_length = MAX_LENGTH;
+ break;
+ } else {
+ best_length = curr_length;
+ }
+ }
+ }
+ }
+
+ assert(i + best_length <= pix_count);
+ assert(best_length <= MAX_LENGTH);
+ if (best_length <= MIN_LENGTH) {
+ hash_chain->offset_length_[i] = 0;
+ best_offset_prev = 0;
+ best_length_prev = 0;
+ } else {
+ hash_chain->offset_length_[i] =
+ (best_offset << MAX_LENGTH_BITS) | (uint32_t)best_length;
+ best_offset_prev = best_offset;
+ best_length_prev = best_length;
+ }
+ }
+ hash_chain->offset_length_[0] = 0;
+ WebPSafeFree(counts_ini);
+
+ return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain,
+ refs);
+}
+
+// -----------------------------------------------------------------------------
+
+static void BackwardReferences2DLocality(int xsize,
+ const VP8LBackwardRefs* const refs) {
+ VP8LRefsCursor c = VP8LRefsCursorInit(refs);
+ while (VP8LRefsCursorOk(&c)) {
+ if (PixOrCopyIsCopy(c.cur_pos)) {
+ const int dist = c.cur_pos->argb_or_distance;
+ const int transformed_dist = VP8LDistanceToPlaneCode(xsize, dist);
+ c.cur_pos->argb_or_distance = transformed_dist;
+ }
+ VP8LRefsCursorNext(&c);
+ }
+}
+
+// Evaluate optimal cache bits for the local color cache.
+// The input *best_cache_bits sets the maximum cache bits to use (passing 0
+// implies disabling the local color cache). The local color cache is also
+// disabled for the lower (<= 25) quality.
+// Returns 0 in case of memory error.
+static int CalculateBestCacheSize(const uint32_t* argb, int quality,
+ const VP8LBackwardRefs* const refs,
+ int* const best_cache_bits) {
+ int i;
+ const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits;
+ double entropy_min = MAX_ENTROPY;
+ int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 };
+ VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1];
+ VP8LRefsCursor c = VP8LRefsCursorInit(refs);
+ VP8LHistogram* histos[MAX_COLOR_CACHE_BITS + 1] = { NULL };
+ int ok = 0;
+
+ assert(cache_bits_max >= 0 && cache_bits_max <= MAX_COLOR_CACHE_BITS);
+
+ if (cache_bits_max == 0) {
+ *best_cache_bits = 0;
+ // Local color cache is disabled.
+ return 1;
+ }
+
+ // Allocate data.
+ for (i = 0; i <= cache_bits_max; ++i) {
+ histos[i] = VP8LAllocateHistogram(i);
+ if (histos[i] == NULL) goto Error;
+ if (i == 0) continue;
+ cc_init[i] = VP8LColorCacheInit(&hashers[i], i);
+ if (!cc_init[i]) goto Error;
+ }
+
+ // Find the cache_bits giving the lowest entropy. The search is done in a
+ // brute-force way as the function (entropy w.r.t cache_bits) can be
+ // anything in practice.
+ while (VP8LRefsCursorOk(&c)) {
+ const PixOrCopy* const v = c.cur_pos;
+ if (PixOrCopyIsLiteral(v)) {
+ const uint32_t pix = *argb++;
+ const uint32_t a = (pix >> 24) & 0xff;
+ const uint32_t r = (pix >> 16) & 0xff;
+ const uint32_t g = (pix >> 8) & 0xff;
+ const uint32_t b = (pix >> 0) & 0xff;
+ // The keys of the caches can be derived from the longest one.
+ int key = VP8LHashPix(pix, 32 - cache_bits_max);
+ // Do not use the color cache for cache_bits = 0.
+ ++histos[0]->blue_[b];
+ ++histos[0]->literal_[g];
+ ++histos[0]->red_[r];
+ ++histos[0]->alpha_[a];
+ // Deal with cache_bits > 0.
+ for (i = cache_bits_max; i >= 1; --i, key >>= 1) {
+ if (VP8LColorCacheLookup(&hashers[i], key) == pix) {
+ ++histos[i]->literal_[NUM_LITERAL_CODES + NUM_LENGTH_CODES + key];
+ } else {
+ VP8LColorCacheSet(&hashers[i], key, pix);
+ ++histos[i]->blue_[b];
+ ++histos[i]->literal_[g];
+ ++histos[i]->red_[r];
+ ++histos[i]->alpha_[a];
+ }
+ }
+ } else {
+ // We should compute the contribution of the (distance,length)
+ // histograms but those are the same independently from the cache size.
+ // As those constant contributions are in the end added to the other
+ // histogram contributions, we can safely ignore them.
+ int len = PixOrCopyLength(v);
+ uint32_t argb_prev = *argb ^ 0xffffffffu;
+ // Update the color caches.
+ do {
+ if (*argb != argb_prev) {
+ // Efficiency: insert only if the color changes.
+ int key = VP8LHashPix(*argb, 32 - cache_bits_max);
+ for (i = cache_bits_max; i >= 1; --i, key >>= 1) {
+ hashers[i].colors_[key] = *argb;
+ }
+ argb_prev = *argb;
+ }
+ argb++;
+ } while (--len != 0);
+ }
+ VP8LRefsCursorNext(&c);
+ }
+
+ for (i = 0; i <= cache_bits_max; ++i) {
+ const double entropy = VP8LHistogramEstimateBits(histos[i]);
+ if (i == 0 || entropy < entropy_min) {
+ entropy_min = entropy;
+ *best_cache_bits = i;
+ }
+ }
+ ok = 1;
+Error:
+ for (i = 0; i <= cache_bits_max; ++i) {
+ if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);
+ VP8LFreeHistogram(histos[i]);
+ }
+ return ok;
+}
+
+// Update (in-place) backward references for specified cache_bits.
+static int BackwardRefsWithLocalCache(const uint32_t* const argb,
+ int cache_bits,
+ VP8LBackwardRefs* const refs) {
+ int pixel_index = 0;
+ VP8LColorCache hashers;
+ VP8LRefsCursor c = VP8LRefsCursorInit(refs);
+ if (!VP8LColorCacheInit(&hashers, cache_bits)) return 0;
+
+ while (VP8LRefsCursorOk(&c)) {
+ PixOrCopy* const v = c.cur_pos;
+ if (PixOrCopyIsLiteral(v)) {
+ const uint32_t argb_literal = v->argb_or_distance;
+ const int ix = VP8LColorCacheContains(&hashers, argb_literal);
+ if (ix >= 0) {
+ // hashers contains argb_literal
+ *v = PixOrCopyCreateCacheIdx(ix);
+ } else {
+ VP8LColorCacheInsert(&hashers, argb_literal);
+ }
+ ++pixel_index;
+ } else {
+ // refs was created without local cache, so it can not have cache indexes.
+ int k;
+ assert(PixOrCopyIsCopy(v));
+ for (k = 0; k < v->len; ++k) {
+ VP8LColorCacheInsert(&hashers, argb[pixel_index++]);
+ }
+ }
+ VP8LRefsCursorNext(&c);
+ }
+ VP8LColorCacheClear(&hashers);
+ return 1;
+}
+
+static VP8LBackwardRefs* GetBackwardReferencesLowEffort(
+ int width, int height, const uint32_t* const argb,
+ int* const cache_bits, const VP8LHashChain* const hash_chain,
+ VP8LBackwardRefs* const refs_lz77) {
+ *cache_bits = 0;
+ if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) {
+ return NULL;
+ }
+ BackwardReferences2DLocality(width, refs_lz77);
+ return refs_lz77;
+}
+
+extern int VP8LBackwardReferencesTraceBackwards(
+ int xsize, int ysize, const uint32_t* const argb, int cache_bits,
+ const VP8LHashChain* const hash_chain,
+ const VP8LBackwardRefs* const refs_src, VP8LBackwardRefs* const refs_dst);
+static VP8LBackwardRefs* GetBackwardReferences(
+ int width, int height, const uint32_t* const argb, int quality,
+ int lz77_types_to_try, int* const cache_bits,
+ const VP8LHashChain* const hash_chain, VP8LBackwardRefs* best,
+ VP8LBackwardRefs* worst) {
+ const int cache_bits_initial = *cache_bits;
+ double bit_cost_best = -1;
+ VP8LHistogram* histo = NULL;
+ int lz77_type, lz77_type_best = 0;
+ VP8LHashChain hash_chain_box;
+ memset(&hash_chain_box, 0, sizeof(hash_chain_box));
+
+ histo = VP8LAllocateHistogram(MAX_COLOR_CACHE_BITS);
+ if (histo == NULL) goto Error;
+
+ for (lz77_type = 1; lz77_types_to_try;
+ lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {
+ int res = 0;
+ double bit_cost;
+ int cache_bits_tmp = cache_bits_initial;
+ if ((lz77_types_to_try & lz77_type) == 0) continue;
+ switch (lz77_type) {
+ case kLZ77RLE:
+ res = BackwardReferencesRle(width, height, argb, 0, worst);
+ break;
+ case kLZ77Standard:
+ // Compute LZ77 with no cache (0 bits), as the ideal LZ77 with a color
+ // cache is not that different in practice.
+ res = BackwardReferencesLz77(width, height, argb, 0, hash_chain, worst);
+ break;
+ case kLZ77Box:
+ if (!VP8LHashChainInit(&hash_chain_box, width * height)) goto Error;
+ res = BackwardReferencesLz77Box(width, height, argb, 0, hash_chain,
+ &hash_chain_box, worst);
+ break;
+ default:
+ assert(0);
+ }
+ if (!res) goto Error;
+
+ // Next, try with a color cache and update the references.
+ if (!CalculateBestCacheSize(argb, quality, worst, &cache_bits_tmp)) {
+ goto Error;
+ }
+ if (cache_bits_tmp > 0) {
+ if (!BackwardRefsWithLocalCache(argb, cache_bits_tmp, worst)) {
+ goto Error;
+ }
+ }
+
+ // Keep the best backward references.
+ VP8LHistogramCreate(histo, worst, cache_bits_tmp);
+ bit_cost = VP8LHistogramEstimateBits(histo);
+ if (lz77_type_best == 0 || bit_cost < bit_cost_best) {
+ VP8LBackwardRefs* const tmp = worst;
+ worst = best;
+ best = tmp;
+ bit_cost_best = bit_cost;
+ *cache_bits = cache_bits_tmp;
+ lz77_type_best = lz77_type;
+ }
+ }
+ assert(lz77_type_best > 0);
+
+ // Improve on simple LZ77 but only for high quality (TraceBackwards is
+ // costly).
+ if ((lz77_type_best == kLZ77Standard || lz77_type_best == kLZ77Box) &&
+ quality >= 25) {
+ const VP8LHashChain* const hash_chain_tmp =
+ (lz77_type_best == kLZ77Standard) ? hash_chain : &hash_chain_box;
+ if (VP8LBackwardReferencesTraceBackwards(width, height, argb, *cache_bits,
+ hash_chain_tmp, best, worst)) {
+ double bit_cost_trace;
+ VP8LHistogramCreate(histo, worst, *cache_bits);
+ bit_cost_trace = VP8LHistogramEstimateBits(histo);
+ if (bit_cost_trace < bit_cost_best) best = worst;
+ }
+ }
+
+ BackwardReferences2DLocality(width, best);
+
+Error:
+ VP8LHashChainClear(&hash_chain_box);
+ VP8LFreeHistogram(histo);
+ return best;
+}
+
+VP8LBackwardRefs* VP8LGetBackwardReferences(
+ int width, int height, const uint32_t* const argb, int quality,
+ int low_effort, int lz77_types_to_try, int* const cache_bits,
+ const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
+ VP8LBackwardRefs* const refs_tmp2) {
+ if (low_effort) {
+ return GetBackwardReferencesLowEffort(width, height, argb, cache_bits,
+ hash_chain, refs_tmp1);
+ } else {
+ return GetBackwardReferences(width, height, argb, quality,
+ lz77_types_to_try, cache_bits, hash_chain,
+ refs_tmp1, refs_tmp2);
+ }
+}
diff --git a/src/third_party/libwebp/src/enc/backward_references_enc.h b/src/third_party/libwebp/src/enc/backward_references_enc.h
new file mode 100644
index 0000000..0e26faa
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/backward_references_enc.h
@@ -0,0 +1,238 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+
+#ifndef WEBP_ENC_BACKWARD_REFERENCES_ENC_H_
+#define WEBP_ENC_BACKWARD_REFERENCES_ENC_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#endif
+#include "src/webp/types.h"
+#include "src/webp/format_constants.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The maximum allowed limit is 11.
+#define MAX_COLOR_CACHE_BITS 10
+
+// -----------------------------------------------------------------------------
+// PixOrCopy
+
+enum Mode {
+ kLiteral,
+ kCacheIdx,
+ kCopy,
+ kNone
+};
+
+typedef struct {
+ // mode as uint8_t to make the memory layout to be exactly 8 bytes.
+ uint8_t mode;
+ uint16_t len;
+ uint32_t argb_or_distance;
+} PixOrCopy;
+
+static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance,
+ uint16_t len) {
+ PixOrCopy retval;
+ retval.mode = kCopy;
+ retval.argb_or_distance = distance;
+ retval.len = len;
+ return retval;
+}
+
+static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) {
+ PixOrCopy retval;
+ SB_DCHECK(idx >= 0);
+ SB_DCHECK(idx < (1 << MAX_COLOR_CACHE_BITS));
+ retval.mode = kCacheIdx;
+ retval.argb_or_distance = idx;
+ retval.len = 1;
+ return retval;
+}
+
+static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) {
+ PixOrCopy retval;
+ retval.mode = kLiteral;
+ retval.argb_or_distance = argb;
+ retval.len = 1;
+ return retval;
+}
+
+static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) {
+ return (p->mode == kLiteral);
+}
+
+static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) {
+ return (p->mode == kCacheIdx);
+}
+
+static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) {
+ return (p->mode == kCopy);
+}
+
+static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p,
+ int component) {
+ SB_DCHECK(p->mode == kLiteral);
+ return (p->argb_or_distance >> (component * 8)) & 0xff;
+}
+
+static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) {
+ return p->len;
+}
+
+static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) {
+ SB_DCHECK(p->mode == kCacheIdx);
+ SB_DCHECK(p->argb_or_distance < (1U << MAX_COLOR_CACHE_BITS));
+ return p->argb_or_distance;
+}
+
+static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) {
+ SB_DCHECK(p->mode == kCopy);
+ return p->argb_or_distance;
+}
+
+// -----------------------------------------------------------------------------
+// VP8LHashChain
+
+#define HASH_BITS 18
+#define HASH_SIZE (1 << HASH_BITS)
+
+// If you change this, you need MAX_LENGTH_BITS + WINDOW_SIZE_BITS <= 32 as it
+// is used in VP8LHashChain.
+#define MAX_LENGTH_BITS 12
+#define WINDOW_SIZE_BITS 20
+// We want the max value to be attainable and stored in MAX_LENGTH_BITS bits.
+#define MAX_LENGTH ((1 << MAX_LENGTH_BITS) - 1)
+#if MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32
+#error "MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32"
+#endif
+
+typedef struct VP8LHashChain VP8LHashChain;
+struct VP8LHashChain {
+ // The 20 most significant bits contain the offset at which the best match
+ // is found. These 20 bits are the limit defined by GetWindowSizeForHashChain
+ // (through WINDOW_SIZE = 1<<20).
+ // The lower 12 bits contain the length of the match. The 12 bit limit is
+ // defined in MaxFindCopyLength with MAX_LENGTH=4096.
+ uint32_t* offset_length_;
+ // This is the maximum size of the hash_chain that can be constructed.
+ // Typically this is the pixel count (width x height) for a given image.
+ int size_;
+};
+
+// Must be called first, to set size.
+int VP8LHashChainInit(VP8LHashChain* const p, int size);
+// Pre-compute the best matches for argb.
+int VP8LHashChainFill(VP8LHashChain* const p, int quality,
+ const uint32_t* const argb, int xsize, int ysize,
+ int low_effort);
+void VP8LHashChainClear(VP8LHashChain* const p); // release memory
+
+static WEBP_INLINE int VP8LHashChainFindOffset(const VP8LHashChain* const p,
+ const int base_position) {
+ return p->offset_length_[base_position] >> MAX_LENGTH_BITS;
+}
+
+static WEBP_INLINE int VP8LHashChainFindLength(const VP8LHashChain* const p,
+ const int base_position) {
+ return p->offset_length_[base_position] & ((1U << MAX_LENGTH_BITS) - 1);
+}
+
+static WEBP_INLINE void VP8LHashChainFindCopy(const VP8LHashChain* const p,
+ int base_position,
+ int* const offset_ptr,
+ int* const length_ptr) {
+ *offset_ptr = VP8LHashChainFindOffset(p, base_position);
+ *length_ptr = VP8LHashChainFindLength(p, base_position);
+}
+
+// -----------------------------------------------------------------------------
+// VP8LBackwardRefs (block-based backward-references storage)
+
+// maximum number of reference blocks the image will be segmented into
+#define MAX_REFS_BLOCK_PER_IMAGE 16
+
+typedef struct PixOrCopyBlock PixOrCopyBlock; // forward declaration
+typedef struct VP8LBackwardRefs VP8LBackwardRefs;
+
+// Container for blocks chain
+struct VP8LBackwardRefs {
+ int block_size_; // common block-size
+ int error_; // set to true if some memory error occurred
+ PixOrCopyBlock* refs_; // list of currently used blocks
+ PixOrCopyBlock** tail_; // for list recycling
+ PixOrCopyBlock* free_blocks_; // free-list
+ PixOrCopyBlock* last_block_; // used for adding new refs (internal)
+};
+
+// Initialize the object. 'block_size' is the common block size to store
+// references (typically, width * height / MAX_REFS_BLOCK_PER_IMAGE).
+void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size);
+// Release memory for backward references.
+void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs);
+
+// Cursor for iterating on references content
+typedef struct {
+ // public:
+ PixOrCopy* cur_pos; // current position
+ // private:
+ PixOrCopyBlock* cur_block_; // current block in the refs list
+ const PixOrCopy* last_pos_; // sentinel for switching to next block
+} VP8LRefsCursor;
+
+// Returns a cursor positioned at the beginning of the references list.
+VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs);
+// Returns true if cursor is pointing at a valid position.
+static WEBP_INLINE int VP8LRefsCursorOk(const VP8LRefsCursor* const c) {
+ return (c->cur_pos != NULL);
+}
+// Move to next block of references. Internal, not to be called directly.
+void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c);
+// Move to next position, or NULL. Should not be called if !VP8LRefsCursorOk().
+static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) {
+ SB_DCHECK(c != NULL);
+ SB_DCHECK(VP8LRefsCursorOk(c));
+ if (++c->cur_pos == c->last_pos_) VP8LRefsCursorNextBlock(c);
+}
+
+// -----------------------------------------------------------------------------
+// Main entry points
+
+enum VP8LLZ77Type {
+ kLZ77Standard = 1,
+ kLZ77RLE = 2,
+ kLZ77Box = 4
+};
+
+// Evaluates best possible backward references for specified quality.
+// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache
+// bits to use (passing 0 implies disabling the local color cache).
+// The optimal cache bits is evaluated and set for the *cache_bits parameter.
+// The return value is the pointer to the best of the two backward refs viz,
+// refs[0] or refs[1].
+VP8LBackwardRefs* VP8LGetBackwardReferences(
+ int width, int height, const uint32_t* const argb, int quality,
+ int low_effort, int lz77_types_to_try, int* const cache_bits,
+ const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_tmp1,
+ VP8LBackwardRefs* const refs_tmp2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WEBP_ENC_BACKWARD_REFERENCES_ENC_H_
diff --git a/src/third_party/libwebp/src/enc/config_enc.c b/src/third_party/libwebp/src/enc/config_enc.c
new file mode 100644
index 0000000..9d48289
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/config_enc.c
@@ -0,0 +1,152 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Coding tools configuration
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#include "src/webp/encode.h"
+
+//------------------------------------------------------------------------------
+// WebPConfig
+//------------------------------------------------------------------------------
+
+int WebPConfigInitInternal(WebPConfig* config,
+ WebPPreset preset, float quality, int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
+ return 0; // caller/system version mismatch!
+ }
+ if (config == NULL) return 0;
+
+ config->quality = quality;
+ config->target_size = 0;
+ config->target_PSNR = 0.;
+ config->method = 4;
+ config->sns_strength = 50;
+ config->filter_strength = 60; // mid-filtering
+ config->filter_sharpness = 0;
+ config->filter_type = 1; // default: strong (so U/V is filtered too)
+ config->partitions = 0;
+ config->segments = 4;
+ config->pass = 1;
+ config->show_compressed = 0;
+ config->preprocessing = 0;
+ config->autofilter = 0;
+ config->partition_limit = 0;
+ config->alpha_compression = 1;
+ config->alpha_filtering = 1;
+ config->alpha_quality = 100;
+ config->lossless = 0;
+ config->exact = 0;
+ config->image_hint = WEBP_HINT_DEFAULT;
+ config->emulate_jpeg_size = 0;
+ config->thread_level = 0;
+ config->low_memory = 0;
+ config->near_lossless = 100;
+ config->use_delta_palette = 0;
+ config->use_sharp_yuv = 0;
+
+ // TODO(skal): tune.
+ switch (preset) {
+ case WEBP_PRESET_PICTURE:
+ config->sns_strength = 80;
+ config->filter_sharpness = 4;
+ config->filter_strength = 35;
+ config->preprocessing &= ~2; // no dithering
+ break;
+ case WEBP_PRESET_PHOTO:
+ config->sns_strength = 80;
+ config->filter_sharpness = 3;
+ config->filter_strength = 30;
+ config->preprocessing |= 2;
+ break;
+ case WEBP_PRESET_DRAWING:
+ config->sns_strength = 25;
+ config->filter_sharpness = 6;
+ config->filter_strength = 10;
+ break;
+ case WEBP_PRESET_ICON:
+ config->sns_strength = 0;
+ config->filter_strength = 0; // disable filtering to retain sharpness
+ config->preprocessing &= ~2; // no dithering
+ break;
+ case WEBP_PRESET_TEXT:
+ config->sns_strength = 0;
+ config->filter_strength = 0; // disable filtering to retain sharpness
+ config->preprocessing &= ~2; // no dithering
+ config->segments = 2;
+ break;
+ case WEBP_PRESET_DEFAULT:
+ default:
+ break;
+ }
+ return WebPValidateConfig(config);
+}
+
+int WebPValidateConfig(const WebPConfig* config) {
+ if (config == NULL) return 0;
+ if (config->quality < 0 || config->quality > 100) return 0;
+ if (config->target_size < 0) return 0;
+ if (config->target_PSNR < 0) return 0;
+ if (config->method < 0 || config->method > 6) return 0;
+ if (config->segments < 1 || config->segments > 4) return 0;
+ if (config->sns_strength < 0 || config->sns_strength > 100) return 0;
+ if (config->filter_strength < 0 || config->filter_strength > 100) return 0;
+ if (config->filter_sharpness < 0 || config->filter_sharpness > 7) return 0;
+ if (config->filter_type < 0 || config->filter_type > 1) return 0;
+ if (config->autofilter < 0 || config->autofilter > 1) return 0;
+ if (config->pass < 1 || config->pass > 10) return 0;
+ if (config->show_compressed < 0 || config->show_compressed > 1) return 0;
+ if (config->preprocessing < 0 || config->preprocessing > 7) return 0;
+ if (config->partitions < 0 || config->partitions > 3) return 0;
+ if (config->partition_limit < 0 || config->partition_limit > 100) return 0;
+ if (config->alpha_compression < 0) return 0;
+ if (config->alpha_filtering < 0) return 0;
+ if (config->alpha_quality < 0 || config->alpha_quality > 100) return 0;
+ if (config->lossless < 0 || config->lossless > 1) return 0;
+ if (config->near_lossless < 0 || config->near_lossless > 100) return 0;
+ if (config->image_hint >= WEBP_HINT_LAST) return 0;
+ if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1) return 0;
+ if (config->thread_level < 0 || config->thread_level > 1) return 0;
+ if (config->low_memory < 0 || config->low_memory > 1) return 0;
+ if (config->exact < 0 || config->exact > 1) return 0;
+ if (config->use_delta_palette < 0 || config->use_delta_palette > 1) {
+ return 0;
+ }
+ if (config->use_sharp_yuv < 0 || config->use_sharp_yuv > 1) return 0;
+
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+#define MAX_LEVEL 9
+
+// Mapping between -z level and -m / -q parameter settings.
+static const struct {
+ uint8_t method_;
+ uint8_t quality_;
+} kLosslessPresets[MAX_LEVEL + 1] = {
+ { 0, 0 }, { 1, 20 }, { 2, 25 }, { 3, 30 }, { 3, 50 },
+ { 4, 50 }, { 4, 75 }, { 4, 90 }, { 5, 90 }, { 6, 100 }
+};
+
+int WebPConfigLosslessPreset(WebPConfig* config, int level) {
+ if (config == NULL || level < 0 || level > MAX_LEVEL) return 0;
+ config->lossless = 1;
+ config->method = kLosslessPresets[level].method_;
+ config->quality = kLosslessPresets[level].quality_;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/cost_enc.c b/src/third_party/libwebp/src/enc/cost_enc.c
new file mode 100644
index 0000000..6577c11
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/cost_enc.c
@@ -0,0 +1,346 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Cost tables for level and modes
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#endif
+
+#include "src/enc/cost_enc.h"
+
+//------------------------------------------------------------------------------
+// Level cost tables
+
+// For each given level, the following table gives the pattern of contexts to
+// use for coding it (in [][0]) as well as the bit value to use for each
+// context (in [][1]).
+const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = {
+ {0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005},
+ {0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023},
+ {0x033, 0x023}, {0x033, 0x023}, {0x033, 0x023}, {0x0d3, 0x013},
+ {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013},
+ {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093},
+ {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053},
+ {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x153}
+};
+
+static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) {
+ int pattern = VP8LevelCodes[level - 1][0];
+ int bits = VP8LevelCodes[level - 1][1];
+ int cost = 0;
+ int i;
+ for (i = 2; pattern; ++i) {
+ if (pattern & 1) {
+ cost += VP8BitCost(bits & 1, probas[i]);
+ }
+ bits >>= 1;
+ pattern >>= 1;
+ }
+ return cost;
+}
+
+//------------------------------------------------------------------------------
+// Pre-calc level costs once for all
+
+void VP8CalculateLevelCosts(VP8EncProba* const proba) {
+ int ctype, band, ctx;
+
+ if (!proba->dirty_) return; // nothing to do.
+
+ for (ctype = 0; ctype < NUM_TYPES; ++ctype) {
+ int n;
+ for (band = 0; band < NUM_BANDS; ++band) {
+ for (ctx = 0; ctx < NUM_CTX; ++ctx) {
+ const uint8_t* const p = proba->coeffs_[ctype][band][ctx];
+ uint16_t* const table = proba->level_cost_[ctype][band][ctx];
+ const int cost0 = (ctx > 0) ? VP8BitCost(1, p[0]) : 0;
+ const int cost_base = VP8BitCost(1, p[1]) + cost0;
+ int v;
+ table[0] = VP8BitCost(0, p[1]) + cost0;
+ for (v = 1; v <= MAX_VARIABLE_LEVEL; ++v) {
+ table[v] = cost_base + VariableLevelCost(v, p);
+ }
+ // Starting at level 67 and up, the variable part of the cost is
+ // actually constant.
+ }
+ }
+ for (n = 0; n < 16; ++n) { // replicate bands. We don't need to sentinel.
+ for (ctx = 0; ctx < NUM_CTX; ++ctx) {
+ proba->remapped_costs_[ctype][n][ctx] =
+ proba->level_cost_[ctype][VP8EncBands[n]][ctx];
+ }
+ }
+ }
+ proba->dirty_ = 0;
+}
+
+//------------------------------------------------------------------------------
+// Mode cost tables.
+
+// These are the fixed probabilities (in the coding trees) turned into bit-cost
+// by calling VP8BitCost().
+const uint16_t VP8FixedCostsUV[4] = { 302, 984, 439, 642 };
+// note: these values include the fixed VP8BitCost(1, 145) mode selection cost.
+const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 };
+const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = {
+ { { 40, 1151, 1723, 1874, 2103, 2019, 1628, 1777, 2226, 2137 },
+ { 192, 469, 1296, 1308, 1849, 1794, 1781, 1703, 1713, 1522 },
+ { 142, 910, 762, 1684, 1849, 1576, 1460, 1305, 1801, 1657 },
+ { 559, 641, 1370, 421, 1182, 1569, 1612, 1725, 863, 1007 },
+ { 299, 1059, 1256, 1108, 636, 1068, 1581, 1883, 869, 1142 },
+ { 277, 1111, 707, 1362, 1089, 672, 1603, 1541, 1545, 1291 },
+ { 214, 781, 1609, 1303, 1632, 2229, 726, 1560, 1713, 918 },
+ { 152, 1037, 1046, 1759, 1983, 2174, 1358, 742, 1740, 1390 },
+ { 512, 1046, 1420, 753, 752, 1297, 1486, 1613, 460, 1207 },
+ { 424, 827, 1362, 719, 1462, 1202, 1199, 1476, 1199, 538 } },
+ { { 240, 402, 1134, 1491, 1659, 1505, 1517, 1555, 1979, 2099 },
+ { 467, 242, 960, 1232, 1714, 1620, 1834, 1570, 1676, 1391 },
+ { 500, 455, 463, 1507, 1699, 1282, 1564, 982, 2114, 2114 },
+ { 672, 643, 1372, 331, 1589, 1667, 1453, 1938, 996, 876 },
+ { 458, 783, 1037, 911, 738, 968, 1165, 1518, 859, 1033 },
+ { 504, 815, 504, 1139, 1219, 719, 1506, 1085, 1268, 1268 },
+ { 333, 630, 1445, 1239, 1883, 3672, 799, 1548, 1865, 598 },
+ { 399, 644, 746, 1342, 1856, 1350, 1493, 613, 1855, 1015 },
+ { 622, 749, 1205, 608, 1066, 1408, 1290, 1406, 546, 971 },
+ { 500, 753, 1041, 668, 1230, 1617, 1297, 1425, 1383, 523 } },
+ { { 394, 553, 523, 1502, 1536, 981, 1608, 1142, 1666, 2181 },
+ { 655, 430, 375, 1411, 1861, 1220, 1677, 1135, 1978, 1553 },
+ { 690, 640, 245, 1954, 2070, 1194, 1528, 982, 1972, 2232 },
+ { 559, 834, 741, 867, 1131, 980, 1225, 852, 1092, 784 },
+ { 690, 875, 516, 959, 673, 894, 1056, 1190, 1528, 1126 },
+ { 740, 951, 384, 1277, 1177, 492, 1579, 1155, 1846, 1513 },
+ { 323, 775, 1062, 1776, 3062, 1274, 813, 1188, 1372, 655 },
+ { 488, 971, 484, 1767, 1515, 1775, 1115, 503, 1539, 1461 },
+ { 740, 1006, 998, 709, 851, 1230, 1337, 788, 741, 721 },
+ { 522, 1073, 573, 1045, 1346, 887, 1046, 1146, 1203, 697 } },
+ { { 105, 864, 1442, 1009, 1934, 1840, 1519, 1920, 1673, 1579 },
+ { 534, 305, 1193, 683, 1388, 2164, 1802, 1894, 1264, 1170 },
+ { 305, 518, 877, 1108, 1426, 3215, 1425, 1064, 1320, 1242 },
+ { 683, 732, 1927, 257, 1493, 2048, 1858, 1552, 1055, 947 },
+ { 394, 814, 1024, 660, 959, 1556, 1282, 1289, 893, 1047 },
+ { 528, 615, 996, 940, 1201, 635, 1094, 2515, 803, 1358 },
+ { 347, 614, 1609, 1187, 3133, 1345, 1007, 1339, 1017, 667 },
+ { 218, 740, 878, 1605, 3650, 3650, 1345, 758, 1357, 1617 },
+ { 672, 750, 1541, 558, 1257, 1599, 1870, 2135, 402, 1087 },
+ { 592, 684, 1161, 430, 1092, 1497, 1475, 1489, 1095, 822 } },
+ { { 228, 1056, 1059, 1368, 752, 982, 1512, 1518, 987, 1782 },
+ { 494, 514, 818, 942, 965, 892, 1610, 1356, 1048, 1363 },
+ { 512, 648, 591, 1042, 761, 991, 1196, 1454, 1309, 1463 },
+ { 683, 749, 1043, 676, 841, 1396, 1133, 1138, 654, 939 },
+ { 622, 1101, 1126, 994, 361, 1077, 1203, 1318, 877, 1219 },
+ { 631, 1068, 857, 1650, 651, 477, 1650, 1419, 828, 1170 },
+ { 555, 727, 1068, 1335, 3127, 1339, 820, 1331, 1077, 429 },
+ { 504, 879, 624, 1398, 889, 889, 1392, 808, 891, 1406 },
+ { 683, 1602, 1289, 977, 578, 983, 1280, 1708, 406, 1122 },
+ { 399, 865, 1433, 1070, 1072, 764, 968, 1477, 1223, 678 } },
+ { { 333, 760, 935, 1638, 1010, 529, 1646, 1410, 1472, 2219 },
+ { 512, 494, 750, 1160, 1215, 610, 1870, 1868, 1628, 1169 },
+ { 572, 646, 492, 1934, 1208, 603, 1580, 1099, 1398, 1995 },
+ { 786, 789, 942, 581, 1018, 951, 1599, 1207, 731, 768 },
+ { 690, 1015, 672, 1078, 582, 504, 1693, 1438, 1108, 2897 },
+ { 768, 1267, 571, 2005, 1243, 244, 2881, 1380, 1786, 1453 },
+ { 452, 899, 1293, 903, 1311, 3100, 465, 1311, 1319, 813 },
+ { 394, 927, 942, 1103, 1358, 1104, 946, 593, 1363, 1109 },
+ { 559, 1005, 1007, 1016, 658, 1173, 1021, 1164, 623, 1028 },
+ { 564, 796, 632, 1005, 1014, 863, 2316, 1268, 938, 764 } },
+ { { 266, 606, 1098, 1228, 1497, 1243, 948, 1030, 1734, 1461 },
+ { 366, 585, 901, 1060, 1407, 1247, 876, 1134, 1620, 1054 },
+ { 452, 565, 542, 1729, 1479, 1479, 1016, 886, 2938, 1150 },
+ { 555, 1088, 1533, 950, 1354, 895, 834, 1019, 1021, 496 },
+ { 704, 815, 1193, 971, 973, 640, 1217, 2214, 832, 578 },
+ { 672, 1245, 579, 871, 875, 774, 872, 1273, 1027, 949 },
+ { 296, 1134, 2050, 1784, 1636, 3425, 442, 1550, 2076, 722 },
+ { 342, 982, 1259, 1846, 1848, 1848, 622, 568, 1847, 1052 },
+ { 555, 1064, 1304, 828, 746, 1343, 1075, 1329, 1078, 494 },
+ { 288, 1167, 1285, 1174, 1639, 1639, 833, 2254, 1304, 509 } },
+ { { 342, 719, 767, 1866, 1757, 1270, 1246, 550, 1746, 2151 },
+ { 483, 653, 694, 1509, 1459, 1410, 1218, 507, 1914, 1266 },
+ { 488, 757, 447, 2979, 1813, 1268, 1654, 539, 1849, 2109 },
+ { 522, 1097, 1085, 851, 1365, 1111, 851, 901, 961, 605 },
+ { 709, 716, 841, 728, 736, 945, 941, 862, 2845, 1057 },
+ { 512, 1323, 500, 1336, 1083, 681, 1342, 717, 1604, 1350 },
+ { 452, 1155, 1372, 1900, 1501, 3290, 311, 944, 1919, 922 },
+ { 403, 1520, 977, 2132, 1733, 3522, 1076, 276, 3335, 1547 },
+ { 559, 1374, 1101, 615, 673, 2462, 974, 795, 984, 984 },
+ { 547, 1122, 1062, 812, 1410, 951, 1140, 622, 1268, 651 } },
+ { { 165, 982, 1235, 938, 1334, 1366, 1659, 1578, 964, 1612 },
+ { 592, 422, 925, 847, 1139, 1112, 1387, 2036, 861, 1041 },
+ { 403, 837, 732, 770, 941, 1658, 1250, 809, 1407, 1407 },
+ { 896, 874, 1071, 381, 1568, 1722, 1437, 2192, 480, 1035 },
+ { 640, 1098, 1012, 1032, 684, 1382, 1581, 2106, 416, 865 },
+ { 559, 1005, 819, 914, 710, 770, 1418, 920, 838, 1435 },
+ { 415, 1258, 1245, 870, 1278, 3067, 770, 1021, 1287, 522 },
+ { 406, 990, 601, 1009, 1265, 1265, 1267, 759, 1017, 1277 },
+ { 968, 1182, 1329, 788, 1032, 1292, 1705, 1714, 203, 1403 },
+ { 732, 877, 1279, 471, 901, 1161, 1545, 1294, 755, 755 } },
+ { { 111, 931, 1378, 1185, 1933, 1648, 1148, 1714, 1873, 1307 },
+ { 406, 414, 1030, 1023, 1910, 1404, 1313, 1647, 1509, 793 },
+ { 342, 640, 575, 1088, 1241, 1349, 1161, 1350, 1756, 1502 },
+ { 559, 766, 1185, 357, 1682, 1428, 1329, 1897, 1219, 802 },
+ { 473, 909, 1164, 771, 719, 2508, 1427, 1432, 722, 782 },
+ { 342, 892, 785, 1145, 1150, 794, 1296, 1550, 973, 1057 },
+ { 208, 1036, 1326, 1343, 1606, 3395, 815, 1455, 1618, 712 },
+ { 228, 928, 890, 1046, 3499, 1711, 994, 829, 1720, 1318 },
+ { 768, 724, 1058, 636, 991, 1075, 1319, 1324, 616, 825 },
+ { 305, 1167, 1358, 899, 1587, 1587, 987, 1988, 1332, 501 } }
+};
+
+//------------------------------------------------------------------------------
+// helper functions for residuals struct VP8Residual.
+
+void VP8InitResidual(int first, int coeff_type,
+ VP8Encoder* const enc, VP8Residual* const res) {
+ res->coeff_type = coeff_type;
+ res->prob = enc->proba_.coeffs_[coeff_type];
+ res->stats = enc->proba_.stats_[coeff_type];
+ res->costs = enc->proba_.remapped_costs_[coeff_type];
+ res->first = first;
+}
+
+//------------------------------------------------------------------------------
+// Mode costs
+
+int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) {
+ const int x = (it->i4_ & 3), y = (it->i4_ >> 2);
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+ int R = 0;
+ int ctx;
+
+ VP8InitResidual(0, 3, enc, &res);
+ ctx = it->top_nz_[x] + it->left_nz_[y];
+ VP8SetResidualCoeffs(levels, &res);
+ R += VP8GetResidualCost(ctx, &res);
+ return R;
+}
+
+int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) {
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+ int x, y;
+ int R = 0;
+
+ VP8IteratorNzToBytes(it); // re-import the non-zero context
+
+ // DC
+ VP8InitResidual(0, 1, enc, &res);
+ VP8SetResidualCoeffs(rd->y_dc_levels, &res);
+ R += VP8GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res);
+
+ // AC
+ VP8InitResidual(1, 0, enc, &res);
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ R += VP8GetResidualCost(ctx, &res);
+ it->top_nz_[x] = it->left_nz_[y] = (res.last >= 0);
+ }
+ }
+ return R;
+}
+
+int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) {
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+ int ch, x, y;
+ int R = 0;
+
+ VP8IteratorNzToBytes(it); // re-import the non-zero context
+
+ VP8InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ R += VP8GetResidualCost(ctx, &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = (res.last >= 0);
+ }
+ }
+ }
+ return R;
+}
+
+
+//------------------------------------------------------------------------------
+// Recording of token probabilities.
+
+// We keep the table-free variant around for reference, in case.
+#define USE_LEVEL_CODE_TABLE
+
+// Simulate block coding, but only record statistics.
+// Note: no need to record the fixed probas.
+int VP8RecordCoeffs(int ctx, const VP8Residual* const res) {
+ int n = res->first;
+ // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ proba_t* s = res->stats[n][ctx];
+ if (res->last < 0) {
+ VP8RecordStats(0, s + 0);
+ return 0;
+ }
+ while (n <= res->last) {
+ int v;
+ VP8RecordStats(1, s + 0); // order of record doesn't matter
+ while ((v = res->coeffs[n++]) == 0) {
+ VP8RecordStats(0, s + 1);
+ s = res->stats[VP8EncBands[n]][0];
+ }
+ VP8RecordStats(1, s + 1);
+ if (!VP8RecordStats(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1
+ s = res->stats[VP8EncBands[n]][1];
+ } else {
+ v = abs(v);
+#if !defined(USE_LEVEL_CODE_TABLE)
+ if (!VP8RecordStats(v > 4, s + 3)) {
+ if (VP8RecordStats(v != 2, s + 4))
+ VP8RecordStats(v == 4, s + 5);
+ } else if (!VP8RecordStats(v > 10, s + 6)) {
+ VP8RecordStats(v > 6, s + 7);
+ } else if (!VP8RecordStats((v >= 3 + (8 << 2)), s + 8)) {
+ VP8RecordStats((v >= 3 + (8 << 1)), s + 9);
+ } else {
+ VP8RecordStats((v >= 3 + (8 << 3)), s + 10);
+ }
+#else
+ if (v > MAX_VARIABLE_LEVEL) {
+ v = MAX_VARIABLE_LEVEL;
+ }
+
+ {
+ const int bits = VP8LevelCodes[v - 1][1];
+ int pattern = VP8LevelCodes[v - 1][0];
+ int i;
+ for (i = 0; (pattern >>= 1) != 0; ++i) {
+ const int mask = 2 << i;
+ if (pattern & 1) VP8RecordStats(!!(bits & mask), s + 3 + i);
+ }
+ }
+#endif
+ s = res->stats[VP8EncBands[n]][2];
+ }
+ }
+ if (n < 16) VP8RecordStats(0, s + 0);
+ return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/cost_enc.h b/src/third_party/libwebp/src/enc/cost_enc.h
new file mode 100644
index 0000000..a5d2d81
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/cost_enc.h
@@ -0,0 +1,87 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Cost tables for level and modes.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_ENC_COST_ENC_H_
+#define WEBP_ENC_COST_ENC_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#endif
+
+#include "src/enc/vp8i_enc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// On-the-fly info about the current set of residuals. Handy to avoid
+// passing zillions of params.
+typedef struct VP8Residual VP8Residual;
+struct VP8Residual {
+ int first;
+ int last;
+ const int16_t* coeffs;
+
+ int coeff_type;
+ ProbaArray* prob;
+ StatsArray* stats;
+ CostArrayPtr costs;
+};
+
+void VP8InitResidual(int first, int coeff_type,
+ VP8Encoder* const enc, VP8Residual* const res);
+
+int VP8RecordCoeffs(int ctx, const VP8Residual* const res);
+
+// Record proba context used.
+static WEBP_INLINE int VP8RecordStats(int bit, proba_t* const stats) {
+ proba_t p = *stats;
+ // An overflow is inbound. Note we handle this at 0xfffe0000u instead of
+ // 0xffff0000u to make sure p + 1u does not overflow.
+ if (p >= 0xfffe0000u) {
+ p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
+ }
+ // record bit count (lower 16 bits) and increment total count (upper 16 bits).
+ p += 0x00010000u + bit;
+ *stats = p;
+ return bit;
+}
+
+// Cost of coding one event with probability 'proba'.
+static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) {
+ return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba];
+}
+
+// Level cost calculations
+extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2];
+void VP8CalculateLevelCosts(VP8EncProba* const proba);
+static WEBP_INLINE int VP8LevelCost(const uint16_t* const table, int level) {
+ return VP8LevelFixedCosts[level]
+ + table[(level > MAX_VARIABLE_LEVEL) ? MAX_VARIABLE_LEVEL : level];
+}
+
+// Mode costs
+extern const uint16_t VP8FixedCostsUV[4];
+extern const uint16_t VP8FixedCostsI16[4];
+extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES];
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_ENC_COST_ENC_H_ */
diff --git a/src/third_party/libwebp/src/enc/filter_enc.c b/src/third_party/libwebp/src/enc/filter_enc.c
new file mode 100644
index 0000000..5a98217
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/filter_enc.c
@@ -0,0 +1,240 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Selecting filter level
+//
+// Author: somnath@google.com (Somnath Banerjee)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#endif
+#include "src/enc/vp8i_enc.h"
+#include "src/dsp/dsp.h"
+
+// This table gives, for a given sharpness, the filtering strength to be
+// used (at least) in order to filter a given edge step delta.
+// This is constructed by brute force inspection: for all delta, we iterate
+// over all possible filtering strength / thresh until needs_filter() returns
+// true.
+#define MAX_DELTA_SIZE 64
+static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18,
+ 20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42,
+ 44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19,
+ 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43,
+ 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19,
+ 21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43,
+ 45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20,
+ 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44,
+ 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20,
+ 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44,
+ 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21,
+ 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45,
+ 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
+ { 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21,
+ 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45,
+ 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }
+};
+
+int VP8FilterStrengthFromDelta(int sharpness, int delta) {
+ const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1;
+ assert(sharpness >= 0 && sharpness <= 7);
+ return kLevelsFromDelta[sharpness][pos];
+}
+
+//------------------------------------------------------------------------------
+// Paragraph 15.4: compute the inner-edge filtering strength
+
+#if !defined(WEBP_REDUCE_SIZE)
+
+static int GetILevel(int sharpness, int level) {
+ if (sharpness > 0) {
+ if (sharpness > 4) {
+ level >>= 2;
+ } else {
+ level >>= 1;
+ }
+ if (level > 9 - sharpness) {
+ level = 9 - sharpness;
+ }
+ }
+ if (level < 1) level = 1;
+ return level;
+}
+
+static void DoFilter(const VP8EncIterator* const it, int level) {
+ const VP8Encoder* const enc = it->enc_;
+ const int ilevel = GetILevel(enc->config_->filter_sharpness, level);
+ const int limit = 2 * level + ilevel;
+
+ uint8_t* const y_dst = it->yuv_out2_ + Y_OFF_ENC;
+ uint8_t* const u_dst = it->yuv_out2_ + U_OFF_ENC;
+ uint8_t* const v_dst = it->yuv_out2_ + V_OFF_ENC;
+
+ // copy current block to yuv_out2_
+ memcpy(y_dst, it->yuv_out_, YUV_SIZE_ENC * sizeof(uint8_t));
+
+ if (enc->filter_hdr_.simple_ == 1) { // simple
+ VP8SimpleHFilter16i(y_dst, BPS, limit);
+ VP8SimpleVFilter16i(y_dst, BPS, limit);
+ } else { // complex
+ const int hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
+ VP8HFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
+ VP8HFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
+ VP8VFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
+ VP8VFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
+ }
+}
+
+//------------------------------------------------------------------------------
+// SSIM metric for one macroblock
+
+static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
+ int x, y;
+ double sum = 0.;
+
+ // compute SSIM in a 10 x 10 window
+ for (y = VP8_SSIM_KERNEL; y < 16 - VP8_SSIM_KERNEL; y++) {
+ for (x = VP8_SSIM_KERNEL; x < 16 - VP8_SSIM_KERNEL; x++) {
+ sum += VP8SSIMGetClipped(yuv1 + Y_OFF_ENC, BPS, yuv2 + Y_OFF_ENC, BPS,
+ x, y, 16, 16);
+ }
+ }
+ for (x = 1; x < 7; x++) {
+ for (y = 1; y < 7; y++) {
+ sum += VP8SSIMGetClipped(yuv1 + U_OFF_ENC, BPS, yuv2 + U_OFF_ENC, BPS,
+ x, y, 8, 8);
+ sum += VP8SSIMGetClipped(yuv1 + V_OFF_ENC, BPS, yuv2 + V_OFF_ENC, BPS,
+ x, y, 8, 8);
+ }
+ }
+ return sum;
+}
+
+#endif // !defined(WEBP_REDUCE_SIZE)
+
+//------------------------------------------------------------------------------
+// Exposed APIs: Encoder should call the following 3 functions to adjust
+// loop filter strength
+
+void VP8InitFilter(VP8EncIterator* const it) {
+#if !defined(WEBP_REDUCE_SIZE)
+ if (it->lf_stats_ != NULL) {
+ int s, i;
+ for (s = 0; s < NUM_MB_SEGMENTS; s++) {
+ for (i = 0; i < MAX_LF_LEVELS; i++) {
+ (*it->lf_stats_)[s][i] = 0;
+ }
+ }
+ VP8SSIMDspInit();
+ }
+#else
+ (void)it;
+#endif
+}
+
+void VP8StoreFilterStats(VP8EncIterator* const it) {
+#if !defined(WEBP_REDUCE_SIZE)
+ int d;
+ VP8Encoder* const enc = it->enc_;
+ const int s = it->mb_->segment_;
+ const int level0 = enc->dqm_[s].fstrength_;
+
+ // explore +/-quant range of values around level0
+ const int delta_min = -enc->dqm_[s].quant_;
+ const int delta_max = enc->dqm_[s].quant_;
+ const int step_size = (delta_max - delta_min >= 4) ? 4 : 1;
+
+ if (it->lf_stats_ == NULL) return;
+
+ // NOTE: Currently we are applying filter only across the sublock edges
+ // There are two reasons for that.
+ // 1. Applying filter on macro block edges will change the pixels in
+ // the left and top macro blocks. That will be hard to restore
+ // 2. Macro Blocks on the bottom and right are not yet compressed. So we
+ // cannot apply filter on the right and bottom macro block edges.
+ if (it->mb_->type_ == 1 && it->mb_->skip_) return;
+
+ // Always try filter level zero
+ (*it->lf_stats_)[s][0] += GetMBSSIM(it->yuv_in_, it->yuv_out_);
+
+ for (d = delta_min; d <= delta_max; d += step_size) {
+ const int level = level0 + d;
+ if (level <= 0 || level >= MAX_LF_LEVELS) {
+ continue;
+ }
+ DoFilter(it, level);
+ (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_);
+ }
+#else // defined(WEBP_REDUCE_SIZE)
+ (void)it;
+#endif // !defined(WEBP_REDUCE_SIZE)
+}
+
+void VP8AdjustFilterStrength(VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+#if !defined(WEBP_REDUCE_SIZE)
+ if (it->lf_stats_ != NULL) {
+ int s;
+ for (s = 0; s < NUM_MB_SEGMENTS; s++) {
+ int i, best_level = 0;
+ // Improvement over filter level 0 should be at least 1e-5 (relatively)
+ double best_v = 1.00001 * (*it->lf_stats_)[s][0];
+ for (i = 1; i < MAX_LF_LEVELS; i++) {
+ const double v = (*it->lf_stats_)[s][i];
+ if (v > best_v) {
+ best_v = v;
+ best_level = i;
+ }
+ }
+ enc->dqm_[s].fstrength_ = best_level;
+ }
+ return;
+ }
+#endif // !defined(WEBP_REDUCE_SIZE)
+ if (enc->config_->filter_strength > 0) {
+ int max_level = 0;
+ int s;
+ for (s = 0; s < NUM_MB_SEGMENTS; s++) {
+ VP8SegmentInfo* const dqm = &enc->dqm_[s];
+ // this '>> 3' accounts for some inverse WHT scaling
+ const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3;
+ const int level =
+ VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta);
+ if (level > dqm->fstrength_) {
+ dqm->fstrength_ = level;
+ }
+ if (max_level < dqm->fstrength_) {
+ max_level = dqm->fstrength_;
+ }
+ }
+ enc->filter_hdr_.level_ = max_level;
+ }
+}
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/frame_enc.c b/src/third_party/libwebp/src/enc/frame_enc.c
new file mode 100644
index 0000000..ac763be
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/frame_enc.c
@@ -0,0 +1,896 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// frame coding and analysis
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/math_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <string.h>
+#include <math.h>
+#endif
+
+#include "src/enc/cost_enc.h"
+#include "src/enc/vp8i_enc.h"
+#include "src/dsp/dsp.h"
+#include "src/webp/format_constants.h" // RIFF constants
+
+#define SEGMENT_VISU 0
+#define DEBUG_SEARCH 0 // useful to track search convergence
+
+//------------------------------------------------------------------------------
+// multi-pass convergence
+
+#define HEADER_SIZE_ESTIMATE (RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + \
+ VP8_FRAME_HEADER_SIZE)
+#define DQ_LIMIT 0.4 // convergence is considered reached if dq < DQ_LIMIT
+// we allow 2k of extra head-room in PARTITION0 limit.
+#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)
+
+typedef struct { // struct for organizing convergence in either size or PSNR
+ int is_first;
+ float dq;
+ float q, last_q;
+ double value, last_value; // PSNR or size
+ double target;
+ int do_size_search;
+} PassStats;
+
+static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
+ const uint64_t target_size = (uint64_t)enc->config_->target_size;
+ const int do_size_search = (target_size != 0);
+ const float target_PSNR = enc->config_->target_PSNR;
+
+ s->is_first = 1;
+ s->dq = 10.f;
+ s->q = s->last_q = enc->config_->quality;
+ s->target = do_size_search ? (double)target_size
+ : (target_PSNR > 0.) ? target_PSNR
+ : 40.; // default, just in case
+ s->value = s->last_value = 0.;
+ s->do_size_search = do_size_search;
+ return do_size_search;
+}
+
+static float Clamp(float v, float min, float max) {
+ return (v < min) ? min : (v > max) ? max : v;
+}
+
+static float ComputeNextQ(PassStats* const s) {
+ float dq;
+ if (s->is_first) {
+ dq = (s->value > s->target) ? -s->dq : s->dq;
+ s->is_first = 0;
+ } else if (s->value != s->last_value) {
+ const double slope = (s->target - s->value) / (s->last_value - s->value);
+ dq = (float)(slope * (s->last_q - s->q));
+ } else {
+ dq = 0.; // we're done?!
+ }
+ // Limit variable to avoid large swings.
+ s->dq = Clamp(dq, -30.f, 30.f);
+ s->last_q = s->q;
+ s->last_value = s->value;
+ s->q = Clamp(s->q + s->dq, 0.f, 100.f);
+ return s->q;
+}
+
+//------------------------------------------------------------------------------
+// Tables for level coding
+
+const uint8_t VP8Cat3[] = { 173, 148, 140 };
+const uint8_t VP8Cat4[] = { 176, 155, 140, 135 };
+const uint8_t VP8Cat5[] = { 180, 157, 141, 134, 130 };
+const uint8_t VP8Cat6[] =
+ { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
+
+//------------------------------------------------------------------------------
+// Reset the statistics about: number of skips, token proba, level cost,...
+
+static void ResetStats(VP8Encoder* const enc) {
+ VP8EncProba* const proba = &enc->proba_;
+ VP8CalculateLevelCosts(proba);
+ proba->nb_skip_ = 0;
+}
+
+//------------------------------------------------------------------------------
+// Skip decision probability
+
+#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK.
+
+static int CalcSkipProba(uint64_t nb, uint64_t total) {
+ return (int)(total ? (total - nb) * 255 / total : 255);
+}
+
+// Returns the bit-cost for coding the skip probability.
+static int FinalizeSkipProba(VP8Encoder* const enc) {
+ VP8EncProba* const proba = &enc->proba_;
+ const int nb_mbs = enc->mb_w_ * enc->mb_h_;
+ const int nb_events = proba->nb_skip_;
+ int size;
+ proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs);
+ proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD);
+ size = 256; // 'use_skip_proba' bit
+ if (proba->use_skip_proba_) {
+ size += nb_events * VP8BitCost(1, proba->skip_proba_)
+ + (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_);
+ size += 8 * 256; // cost of signaling the skip_proba_ itself.
+ }
+ return size;
+}
+
+// Collect statistics and deduce probabilities for next coding pass.
+// Return the total bit-cost for coding the probability updates.
+static int CalcTokenProba(int nb, int total) {
+ assert(nb <= total);
+ return nb ? (255 - nb * 255 / total) : 255;
+}
+
+// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability.
+static int BranchCost(int nb, int total, int proba) {
+ return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba);
+}
+
+static void ResetTokenStats(VP8Encoder* const enc) {
+ VP8EncProba* const proba = &enc->proba_;
+ memset(proba->stats_, 0, sizeof(proba->stats_));
+}
+
+static int FinalizeTokenProbas(VP8EncProba* const proba) {
+ int has_changed = 0;
+ int size = 0;
+ int t, b, c, p;
+ for (t = 0; t < NUM_TYPES; ++t) {
+ for (b = 0; b < NUM_BANDS; ++b) {
+ for (c = 0; c < NUM_CTX; ++c) {
+ for (p = 0; p < NUM_PROBAS; ++p) {
+ const proba_t stats = proba->stats_[t][b][c][p];
+ const int nb = (stats >> 0) & 0xffff;
+ const int total = (stats >> 16) & 0xffff;
+ const int update_proba = VP8CoeffsUpdateProba[t][b][c][p];
+ const int old_p = VP8CoeffsProba0[t][b][c][p];
+ const int new_p = CalcTokenProba(nb, total);
+ const int old_cost = BranchCost(nb, total, old_p)
+ + VP8BitCost(0, update_proba);
+ const int new_cost = BranchCost(nb, total, new_p)
+ + VP8BitCost(1, update_proba)
+ + 8 * 256;
+ const int use_new_p = (old_cost > new_cost);
+ size += VP8BitCost(use_new_p, update_proba);
+ if (use_new_p) { // only use proba that seem meaningful enough.
+ proba->coeffs_[t][b][c][p] = new_p;
+ has_changed |= (new_p != old_p);
+ size += 8 * 256;
+ } else {
+ proba->coeffs_[t][b][c][p] = old_p;
+ }
+ }
+ }
+ }
+ }
+ proba->dirty_ = has_changed;
+ return size;
+}
+
+//------------------------------------------------------------------------------
+// Finalize Segment probability based on the coding tree
+
+static int GetProba(int a, int b) {
+ const int total = a + b;
+ return (total == 0) ? 255 // that's the default probability.
+ : (255 * a + total / 2) / total; // rounded proba
+}
+
+static void ResetSegments(VP8Encoder* const enc) {
+ int n;
+ for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
+ enc->mb_info_[n].segment_ = 0;
+ }
+}
+
+static void SetSegmentProbas(VP8Encoder* const enc) {
+ int p[NUM_MB_SEGMENTS] = { 0 };
+ int n;
+
+ for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
+ const VP8MBInfo* const mb = &enc->mb_info_[n];
+ ++p[mb->segment_];
+ }
+#if !defined(WEBP_DISABLE_STATS)
+ if (enc->pic_->stats != NULL) {
+ for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
+ enc->pic_->stats->segment_size[n] = p[n];
+ }
+ }
+#endif
+ if (enc->segment_hdr_.num_segments_ > 1) {
+ uint8_t* const probas = enc->proba_.segments_;
+ probas[0] = GetProba(p[0] + p[1], p[2] + p[3]);
+ probas[1] = GetProba(p[0], p[1]);
+ probas[2] = GetProba(p[2], p[3]);
+
+ enc->segment_hdr_.update_map_ =
+ (probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255);
+ if (!enc->segment_hdr_.update_map_) ResetSegments(enc);
+ enc->segment_hdr_.size_ =
+ p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) +
+ p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) +
+ p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) +
+ p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2]));
+ } else {
+ enc->segment_hdr_.update_map_ = 0;
+ enc->segment_hdr_.size_ = 0;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Coefficient coding
+
+static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) {
+ int n = res->first;
+ // should be prob[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ const uint8_t* p = res->prob[n][ctx];
+ if (!VP8PutBit(bw, res->last >= 0, p[0])) {
+ return 0;
+ }
+
+ while (n < 16) {
+ const int c = res->coeffs[n++];
+ const int sign = c < 0;
+ int v = sign ? -c : c;
+ if (!VP8PutBit(bw, v != 0, p[1])) {
+ p = res->prob[VP8EncBands[n]][0];
+ continue;
+ }
+ if (!VP8PutBit(bw, v > 1, p[2])) {
+ p = res->prob[VP8EncBands[n]][1];
+ } else {
+ if (!VP8PutBit(bw, v > 4, p[3])) {
+ if (VP8PutBit(bw, v != 2, p[4])) {
+ VP8PutBit(bw, v == 4, p[5]);
+ }
+ } else if (!VP8PutBit(bw, v > 10, p[6])) {
+ if (!VP8PutBit(bw, v > 6, p[7])) {
+ VP8PutBit(bw, v == 6, 159);
+ } else {
+ VP8PutBit(bw, v >= 9, 165);
+ VP8PutBit(bw, !(v & 1), 145);
+ }
+ } else {
+ int mask;
+ const uint8_t* tab;
+ if (v < 3 + (8 << 1)) { // VP8Cat3 (3b)
+ VP8PutBit(bw, 0, p[8]);
+ VP8PutBit(bw, 0, p[9]);
+ v -= 3 + (8 << 0);
+ mask = 1 << 2;
+ tab = VP8Cat3;
+ } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b)
+ VP8PutBit(bw, 0, p[8]);
+ VP8PutBit(bw, 1, p[9]);
+ v -= 3 + (8 << 1);
+ mask = 1 << 3;
+ tab = VP8Cat4;
+ } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b)
+ VP8PutBit(bw, 1, p[8]);
+ VP8PutBit(bw, 0, p[10]);
+ v -= 3 + (8 << 2);
+ mask = 1 << 4;
+ tab = VP8Cat5;
+ } else { // VP8Cat6 (11b)
+ VP8PutBit(bw, 1, p[8]);
+ VP8PutBit(bw, 1, p[10]);
+ v -= 3 + (8 << 3);
+ mask = 1 << 10;
+ tab = VP8Cat6;
+ }
+ while (mask) {
+ VP8PutBit(bw, !!(v & mask), *tab++);
+ mask >>= 1;
+ }
+ }
+ p = res->prob[VP8EncBands[n]][2];
+ }
+ VP8PutBitUniform(bw, sign);
+ if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) {
+ return 1; // EOB
+ }
+ }
+ return 1;
+}
+
+static void CodeResiduals(VP8BitWriter* const bw, VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int x, y, ch;
+ VP8Residual res;
+ uint64_t pos1, pos2, pos3;
+ const int i16 = (it->mb_->type_ == 1);
+ const int segment = it->mb_->segment_;
+ VP8Encoder* const enc = it->enc_;
+
+ VP8IteratorNzToBytes(it);
+
+ pos1 = VP8BitWriterPos(bw);
+ if (i16) {
+ VP8InitResidual(0, 1, enc, &res);
+ VP8SetResidualCoeffs(rd->y_dc_levels, &res);
+ it->top_nz_[8] = it->left_nz_[8] =
+ PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res);
+ VP8InitResidual(1, 0, enc, &res);
+ } else {
+ VP8InitResidual(0, 3, enc, &res);
+ }
+
+ // luma-AC
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ it->top_nz_[x] = it->left_nz_[y] = PutCoeffs(bw, ctx, &res);
+ }
+ }
+ pos2 = VP8BitWriterPos(bw);
+
+ // U/V
+ VP8InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
+ PutCoeffs(bw, ctx, &res);
+ }
+ }
+ }
+ pos3 = VP8BitWriterPos(bw);
+ it->luma_bits_ = pos2 - pos1;
+ it->uv_bits_ = pos3 - pos2;
+ it->bit_count_[segment][i16] += it->luma_bits_;
+ it->bit_count_[segment][2] += it->uv_bits_;
+ VP8IteratorBytesToNz(it);
+}
+
+// Same as CodeResiduals, but doesn't actually write anything.
+// Instead, it just records the event distribution.
+static void RecordResiduals(VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int x, y, ch;
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+
+ VP8IteratorNzToBytes(it);
+
+ if (it->mb_->type_ == 1) { // i16x16
+ VP8InitResidual(0, 1, enc, &res);
+ VP8SetResidualCoeffs(rd->y_dc_levels, &res);
+ it->top_nz_[8] = it->left_nz_[8] =
+ VP8RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res);
+ VP8InitResidual(1, 0, enc, &res);
+ } else {
+ VP8InitResidual(0, 3, enc, &res);
+ }
+
+ // luma-AC
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ it->top_nz_[x] = it->left_nz_[y] = VP8RecordCoeffs(ctx, &res);
+ }
+ }
+
+ // U/V
+ VP8InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
+ VP8RecordCoeffs(ctx, &res);
+ }
+ }
+ }
+
+ VP8IteratorBytesToNz(it);
+}
+
+//------------------------------------------------------------------------------
+// Token buffer
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+static int RecordTokens(VP8EncIterator* const it, const VP8ModeScore* const rd,
+ VP8TBuffer* const tokens) {
+ int x, y, ch;
+ VP8Residual res;
+ VP8Encoder* const enc = it->enc_;
+
+ VP8IteratorNzToBytes(it);
+ if (it->mb_->type_ == 1) { // i16x16
+ const int ctx = it->top_nz_[8] + it->left_nz_[8];
+ VP8InitResidual(0, 1, enc, &res);
+ VP8SetResidualCoeffs(rd->y_dc_levels, &res);
+ it->top_nz_[8] = it->left_nz_[8] =
+ VP8RecordCoeffTokens(ctx, &res, tokens);
+ VP8InitResidual(1, 0, enc, &res);
+ } else {
+ VP8InitResidual(0, 3, enc, &res);
+ }
+
+ // luma-AC
+ for (y = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
+ it->top_nz_[x] = it->left_nz_[y] =
+ VP8RecordCoeffTokens(ctx, &res, tokens);
+ }
+ }
+
+ // U/V
+ VP8InitResidual(0, 2, enc, &res);
+ for (ch = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] =
+ VP8RecordCoeffTokens(ctx, &res, tokens);
+ }
+ }
+ }
+ VP8IteratorBytesToNz(it);
+ return !tokens->error_;
+}
+
+#endif // !DISABLE_TOKEN_BUFFER
+
+//------------------------------------------------------------------------------
+// ExtraInfo map / Debug function
+
+#if !defined(WEBP_DISABLE_STATS)
+
+#if SEGMENT_VISU
+static void SetBlock(uint8_t* p, int value, int size) {
+ int y;
+ for (y = 0; y < size; ++y) {
+ memset(p, value, size);
+ p += BPS;
+ }
+}
+#endif
+
+static void ResetSSE(VP8Encoder* const enc) {
+ enc->sse_[0] = 0;
+ enc->sse_[1] = 0;
+ enc->sse_[2] = 0;
+ // Note: enc->sse_[3] is managed by alpha.c
+ enc->sse_count_ = 0;
+}
+
+static void StoreSSE(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ const uint8_t* const in = it->yuv_in_;
+ const uint8_t* const out = it->yuv_out_;
+ // Note: not totally accurate at boundary. And doesn't include in-loop filter.
+ enc->sse_[0] += VP8SSE16x16(in + Y_OFF_ENC, out + Y_OFF_ENC);
+ enc->sse_[1] += VP8SSE8x8(in + U_OFF_ENC, out + U_OFF_ENC);
+ enc->sse_[2] += VP8SSE8x8(in + V_OFF_ENC, out + V_OFF_ENC);
+ enc->sse_count_ += 16 * 16;
+}
+
+static void StoreSideInfo(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ const VP8MBInfo* const mb = it->mb_;
+ WebPPicture* const pic = enc->pic_;
+
+ if (pic->stats != NULL) {
+ StoreSSE(it);
+ enc->block_count_[0] += (mb->type_ == 0);
+ enc->block_count_[1] += (mb->type_ == 1);
+ enc->block_count_[2] += (mb->skip_ != 0);
+ }
+
+ if (pic->extra_info != NULL) {
+ uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_];
+ switch (pic->extra_info_type) {
+ case 1: *info = mb->type_; break;
+ case 2: *info = mb->segment_; break;
+ case 3: *info = enc->dqm_[mb->segment_].quant_; break;
+ case 4: *info = (mb->type_ == 1) ? it->preds_[0] : 0xff; break;
+ case 5: *info = mb->uv_mode_; break;
+ case 6: {
+ const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3);
+ *info = (b > 255) ? 255 : b; break;
+ }
+ case 7: *info = mb->alpha_; break;
+ default: *info = 0; break;
+ }
+ }
+#if SEGMENT_VISU // visualize segments and prediction modes
+ SetBlock(it->yuv_out_ + Y_OFF_ENC, mb->segment_ * 64, 16);
+ SetBlock(it->yuv_out_ + U_OFF_ENC, it->preds_[0] * 64, 8);
+ SetBlock(it->yuv_out_ + V_OFF_ENC, mb->uv_mode_ * 64, 8);
+#endif
+}
+
+static void ResetSideInfo(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ WebPPicture* const pic = enc->pic_;
+ if (pic->stats != NULL) {
+ memset(enc->block_count_, 0, sizeof(enc->block_count_));
+ }
+ ResetSSE(enc);
+}
+#else // defined(WEBP_DISABLE_STATS)
+static void ResetSSE(VP8Encoder* const enc) {
+ (void)enc;
+}
+static void StoreSideInfo(const VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ WebPPicture* const pic = enc->pic_;
+ if (pic->extra_info != NULL) {
+ if (it->x_ == 0 && it->y_ == 0) { // only do it once, at start
+ memset(pic->extra_info, 0,
+ enc->mb_w_ * enc->mb_h_ * sizeof(*pic->extra_info));
+ }
+ }
+}
+
+static void ResetSideInfo(const VP8EncIterator* const it) {
+ (void)it;
+}
+#endif // !defined(WEBP_DISABLE_STATS)
+
+static double GetPSNR(uint64_t mse, uint64_t size) {
+ return (mse > 0 && size > 0) ? 10. * log10(255. * 255. * size / mse) : 99;
+}
+
+//------------------------------------------------------------------------------
+// StatLoop(): only collect statistics (number of skips, token usage, ...).
+// This is used for deciding optimal probabilities. It also modifies the
+// quantizer value if some target (size, PSNR) was specified.
+
+static void SetLoopParams(VP8Encoder* const enc, float q) {
+ // Make sure the quality parameter is inside valid bounds
+ q = Clamp(q, 0.f, 100.f);
+
+ VP8SetSegmentParams(enc, q); // setup segment quantizations and filters
+ SetSegmentProbas(enc); // compute segment probabilities
+
+ ResetStats(enc);
+ ResetSSE(enc);
+}
+
+static uint64_t OneStatPass(VP8Encoder* const enc, VP8RDLevel rd_opt,
+ int nb_mbs, int percent_delta,
+ PassStats* const s) {
+ VP8EncIterator it;
+ uint64_t size = 0;
+ uint64_t size_p0 = 0;
+ uint64_t distortion = 0;
+ const uint64_t pixel_count = nb_mbs * 384;
+
+ VP8IteratorInit(enc, &it);
+ SetLoopParams(enc, s->q);
+ do {
+ VP8ModeScore info;
+ VP8IteratorImport(&it, NULL);
+ if (VP8Decimate(&it, &info, rd_opt)) {
+ // Just record the number of skips and act like skip_proba is not used.
+ ++enc->proba_.nb_skip_;
+ }
+ RecordResiduals(&it, &info);
+ size += info.R + info.H;
+ size_p0 += info.H;
+ distortion += info.D;
+ if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) {
+ return 0;
+ }
+ VP8IteratorSaveBoundary(&it);
+ } while (VP8IteratorNext(&it) && --nb_mbs > 0);
+
+ size_p0 += enc->segment_hdr_.size_;
+ if (s->do_size_search) {
+ size += FinalizeSkipProba(enc);
+ size += FinalizeTokenProbas(&enc->proba_);
+ size = ((size + size_p0 + 1024) >> 11) + HEADER_SIZE_ESTIMATE;
+ s->value = (double)size;
+ } else {
+ s->value = GetPSNR(distortion, pixel_count);
+ }
+ return size_p0;
+}
+
+static int StatLoop(VP8Encoder* const enc) {
+ const int method = enc->method_;
+ const int do_search = enc->do_search_;
+ const int fast_probe = ((method == 0 || method == 3) && !do_search);
+ int num_pass_left = enc->config_->pass;
+ const int task_percent = 20;
+ const int percent_per_pass =
+ (task_percent + num_pass_left / 2) / num_pass_left;
+ const int final_percent = enc->percent_ + task_percent;
+ const VP8RDLevel rd_opt =
+ (method >= 3 || do_search) ? RD_OPT_BASIC : RD_OPT_NONE;
+ int nb_mbs = enc->mb_w_ * enc->mb_h_;
+ PassStats stats;
+
+ InitPassStats(enc, &stats);
+ ResetTokenStats(enc);
+
+ // Fast mode: quick analysis pass over few mbs. Better than nothing.
+ if (fast_probe) {
+ if (method == 3) { // we need more stats for method 3 to be reliable.
+ nb_mbs = (nb_mbs > 200) ? nb_mbs >> 1 : 100;
+ } else {
+ nb_mbs = (nb_mbs > 200) ? nb_mbs >> 2 : 50;
+ }
+ }
+
+ while (num_pass_left-- > 0) {
+ const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||
+ (num_pass_left == 0) ||
+ (enc->max_i4_header_bits_ == 0);
+ const uint64_t size_p0 =
+ OneStatPass(enc, rd_opt, nb_mbs, percent_per_pass, &stats);
+ if (size_p0 == 0) return 0;
+#if (DEBUG_SEARCH > 0)
+ printf("#%d value:%.1lf -> %.1lf q:%.2f -> %.2f\n",
+ num_pass_left, stats.last_value, stats.value, stats.last_q, stats.q);
+#endif
+ if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
+ ++num_pass_left;
+ enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...
+ continue; // ...and start over
+ }
+ if (is_last_pass) {
+ break;
+ }
+ // If no target size: just do several pass without changing 'q'
+ if (do_search) {
+ ComputeNextQ(&stats);
+ if (fabs(stats.dq) <= DQ_LIMIT) break;
+ }
+ }
+ if (!do_search || !stats.do_size_search) {
+ // Need to finalize probas now, since it wasn't done during the search.
+ FinalizeSkipProba(enc);
+ FinalizeTokenProbas(&enc->proba_);
+ }
+ VP8CalculateLevelCosts(&enc->proba_); // finalize costs
+ return WebPReportProgress(enc->pic_, final_percent, &enc->percent_);
+}
+
+//------------------------------------------------------------------------------
+// Main loops
+//
+
+static const uint8_t kAverageBytesPerMB[8] = { 50, 24, 16, 9, 7, 5, 3, 2 };
+
+static int PreLoopInitialize(VP8Encoder* const enc) {
+ int p;
+ int ok = 1;
+ const int average_bytes_per_MB = kAverageBytesPerMB[enc->base_quant_ >> 4];
+ const int bytes_per_parts =
+ enc->mb_w_ * enc->mb_h_ * average_bytes_per_MB / enc->num_parts_;
+ // Initialize the bit-writers
+ for (p = 0; ok && p < enc->num_parts_; ++p) {
+ ok = VP8BitWriterInit(enc->parts_ + p, bytes_per_parts);
+ }
+ if (!ok) {
+ VP8EncFreeBitWriters(enc); // malloc error occurred
+ WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+ return ok;
+}
+
+static int PostLoopFinalize(VP8EncIterator* const it, int ok) {
+ VP8Encoder* const enc = it->enc_;
+ if (ok) { // Finalize the partitions, check for extra errors.
+ int p;
+ for (p = 0; p < enc->num_parts_; ++p) {
+ VP8BitWriterFinish(enc->parts_ + p);
+ ok &= !enc->parts_[p].error_;
+ }
+ }
+
+ if (ok) { // All good. Finish up.
+#if !defined(WEBP_DISABLE_STATS)
+ if (enc->pic_->stats != NULL) { // finalize byte counters...
+ int i, s;
+ for (i = 0; i <= 2; ++i) {
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ enc->residual_bytes_[i][s] = (int)((it->bit_count_[s][i] + 7) >> 3);
+ }
+ }
+ }
+#endif
+ VP8AdjustFilterStrength(it); // ...and store filter stats.
+ } else {
+ // Something bad happened -> need to do some memory cleanup.
+ VP8EncFreeBitWriters(enc);
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+// VP8EncLoop(): does the final bitstream coding.
+
+static void ResetAfterSkip(VP8EncIterator* const it) {
+ if (it->mb_->type_ == 1) {
+ *it->nz_ = 0; // reset all predictors
+ it->left_nz_[8] = 0;
+ } else {
+ *it->nz_ &= (1 << 24); // preserve the dc_nz bit
+ }
+}
+
+int VP8EncLoop(VP8Encoder* const enc) {
+ VP8EncIterator it;
+ int ok = PreLoopInitialize(enc);
+ if (!ok) return 0;
+
+ StatLoop(enc); // stats-collection loop
+
+ VP8IteratorInit(enc, &it);
+ VP8InitFilter(&it);
+ do {
+ VP8ModeScore info;
+ const int dont_use_skip = !enc->proba_.use_skip_proba_;
+ const VP8RDLevel rd_opt = enc->rd_opt_level_;
+
+ VP8IteratorImport(&it, NULL);
+ // Warning! order is important: first call VP8Decimate() and
+ // *then* decide how to code the skip decision if there's one.
+ if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) {
+ CodeResiduals(it.bw_, &it, &info);
+ } else { // reset predictors after a skip
+ ResetAfterSkip(&it);
+ }
+ StoreSideInfo(&it);
+ VP8StoreFilterStats(&it);
+ VP8IteratorExport(&it);
+ ok = VP8IteratorProgress(&it, 20);
+ VP8IteratorSaveBoundary(&it);
+ } while (ok && VP8IteratorNext(&it));
+
+ return PostLoopFinalize(&it, ok);
+}
+
+//------------------------------------------------------------------------------
+// Single pass using Token Buffer.
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+#define MIN_COUNT 96 // minimum number of macroblocks before updating stats
+
+int VP8EncTokenLoop(VP8Encoder* const enc) {
+ // Roughly refresh the proba eight times per pass
+ int max_count = (enc->mb_w_ * enc->mb_h_) >> 3;
+ int num_pass_left = enc->config_->pass;
+ const int do_search = enc->do_search_;
+ VP8EncIterator it;
+ VP8EncProba* const proba = &enc->proba_;
+ const VP8RDLevel rd_opt = enc->rd_opt_level_;
+ const uint64_t pixel_count = enc->mb_w_ * enc->mb_h_ * 384;
+ PassStats stats;
+ int ok;
+
+ InitPassStats(enc, &stats);
+ ok = PreLoopInitialize(enc);
+ if (!ok) return 0;
+
+ if (max_count < MIN_COUNT) max_count = MIN_COUNT;
+
+ assert(enc->num_parts_ == 1);
+ assert(enc->use_tokens_);
+ assert(proba->use_skip_proba_ == 0);
+ assert(rd_opt >= RD_OPT_BASIC); // otherwise, token-buffer won't be useful
+ assert(num_pass_left > 0);
+
+ while (ok && num_pass_left-- > 0) {
+ const int is_last_pass = (fabs(stats.dq) <= DQ_LIMIT) ||
+ (num_pass_left == 0) ||
+ (enc->max_i4_header_bits_ == 0);
+ uint64_t size_p0 = 0;
+ uint64_t distortion = 0;
+ int cnt = max_count;
+ VP8IteratorInit(enc, &it);
+ SetLoopParams(enc, stats.q);
+ if (is_last_pass) {
+ ResetTokenStats(enc);
+ VP8InitFilter(&it); // don't collect stats until last pass (too costly)
+ }
+ VP8TBufferClear(&enc->tokens_);
+ do {
+ VP8ModeScore info;
+ VP8IteratorImport(&it, NULL);
+ if (--cnt < 0) {
+ FinalizeTokenProbas(proba);
+ VP8CalculateLevelCosts(proba); // refresh cost tables for rd-opt
+ cnt = max_count;
+ }
+ VP8Decimate(&it, &info, rd_opt);
+ ok = RecordTokens(&it, &info, &enc->tokens_);
+ if (!ok) {
+ WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ break;
+ }
+ size_p0 += info.H;
+ distortion += info.D;
+ if (is_last_pass) {
+ StoreSideInfo(&it);
+ VP8StoreFilterStats(&it);
+ VP8IteratorExport(&it);
+ ok = VP8IteratorProgress(&it, 20);
+ }
+ VP8IteratorSaveBoundary(&it);
+ } while (ok && VP8IteratorNext(&it));
+ if (!ok) break;
+
+ size_p0 += enc->segment_hdr_.size_;
+ if (stats.do_size_search) {
+ uint64_t size = FinalizeTokenProbas(&enc->proba_);
+ size += VP8EstimateTokenSize(&enc->tokens_,
+ (const uint8_t*)proba->coeffs_);
+ size = (size + size_p0 + 1024) >> 11; // -> size in bytes
+ size += HEADER_SIZE_ESTIMATE;
+ stats.value = (double)size;
+ } else { // compute and store PSNR
+ stats.value = GetPSNR(distortion, pixel_count);
+ }
+
+#if (DEBUG_SEARCH > 0)
+ printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n",
+ num_pass_left, stats.last_value, stats.value,
+ stats.last_q, stats.q, stats.dq);
+#endif
+ if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
+ ++num_pass_left;
+ enc->max_i4_header_bits_ >>= 1; // strengthen header bit limitation...
+ if (is_last_pass) {
+ ResetSideInfo(&it);
+ }
+ continue; // ...and start over
+ }
+ if (is_last_pass) {
+ break; // done
+ }
+ if (do_search) {
+ ComputeNextQ(&stats); // Adjust q
+ }
+ }
+ if (ok) {
+ if (!stats.do_size_search) {
+ FinalizeTokenProbas(&enc->proba_);
+ }
+ ok = VP8EmitTokens(&enc->tokens_, enc->parts_ + 0,
+ (const uint8_t*)proba->coeffs_, 1);
+ }
+ ok = ok && WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
+ return PostLoopFinalize(&it, ok);
+}
+
+#else
+
+int VP8EncTokenLoop(VP8Encoder* const enc) {
+ (void)enc;
+ return 0; // we shouldn't be here.
+}
+
+#endif // DISABLE_TOKEN_BUFFER
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/histogram_enc.c b/src/third_party/libwebp/src/enc/histogram_enc.c
new file mode 100644
index 0000000..e82ef6e
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/histogram_enc.c
@@ -0,0 +1,1043 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/math_poem.h"
+#else
+#include <math.h>
+#endif
+
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+#include "src/utils/utils.h"
+
+#define MAX_COST 1.e38
+
+// Number of partitions for the three dominant (literal, red and blue) symbol
+// costs.
+#define NUM_PARTITIONS 4
+// The size of the bin-hash corresponding to the three dominant costs.
+#define BIN_SIZE (NUM_PARTITIONS * NUM_PARTITIONS * NUM_PARTITIONS)
+// Maximum number of histograms allowed in greedy combining algorithm.
+#define MAX_HISTO_GREEDY 100
+
+static void HistogramClear(VP8LHistogram* const p) {
+ uint32_t* const literal = p->literal_;
+ const int cache_bits = p->palette_code_bits_;
+ const int histo_size = VP8LGetHistogramSize(cache_bits);
+ memset(p, 0, histo_size);
+ p->palette_code_bits_ = cache_bits;
+ p->literal_ = literal;
+}
+
+// Swap two histogram pointers.
+static void HistogramSwap(VP8LHistogram** const A, VP8LHistogram** const B) {
+ VP8LHistogram* const tmp = *A;
+ *A = *B;
+ *B = tmp;
+}
+
+static void HistogramCopy(const VP8LHistogram* const src,
+ VP8LHistogram* const dst) {
+ uint32_t* const dst_literal = dst->literal_;
+ const int dst_cache_bits = dst->palette_code_bits_;
+ const int histo_size = VP8LGetHistogramSize(dst_cache_bits);
+ assert(src->palette_code_bits_ == dst_cache_bits);
+ memcpy(dst, src, histo_size);
+ dst->literal_ = dst_literal;
+}
+
+int VP8LGetHistogramSize(int cache_bits) {
+ const int literal_size = VP8LHistogramNumCodes(cache_bits);
+ const size_t total_size = sizeof(VP8LHistogram) + sizeof(int) * literal_size;
+ assert(total_size <= (size_t)0x7fffffff);
+ return (int)total_size;
+}
+
+void VP8LFreeHistogram(VP8LHistogram* const histo) {
+ WebPSafeFree(histo);
+}
+
+void VP8LFreeHistogramSet(VP8LHistogramSet* const histo) {
+ WebPSafeFree(histo);
+}
+
+void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
+ VP8LHistogram* const histo) {
+ VP8LRefsCursor c = VP8LRefsCursorInit(refs);
+ while (VP8LRefsCursorOk(&c)) {
+ VP8LHistogramAddSinglePixOrCopy(histo, c.cur_pos, NULL, 0);
+ VP8LRefsCursorNext(&c);
+ }
+}
+
+void VP8LHistogramCreate(VP8LHistogram* const p,
+ const VP8LBackwardRefs* const refs,
+ int palette_code_bits) {
+ if (palette_code_bits >= 0) {
+ p->palette_code_bits_ = palette_code_bits;
+ }
+ HistogramClear(p);
+ VP8LHistogramStoreRefs(refs, p);
+}
+
+void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) {
+ p->palette_code_bits_ = palette_code_bits;
+ HistogramClear(p);
+}
+
+VP8LHistogram* VP8LAllocateHistogram(int cache_bits) {
+ VP8LHistogram* histo = NULL;
+ const int total_size = VP8LGetHistogramSize(cache_bits);
+ uint8_t* const memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
+ if (memory == NULL) return NULL;
+ histo = (VP8LHistogram*)memory;
+ // literal_ won't necessary be aligned.
+ histo->literal_ = (uint32_t*)(memory + sizeof(VP8LHistogram));
+ VP8LHistogramInit(histo, cache_bits);
+ return histo;
+}
+
+VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
+ int i;
+ VP8LHistogramSet* set;
+ const int histo_size = VP8LGetHistogramSize(cache_bits);
+ const size_t total_size =
+ sizeof(*set) + size * (sizeof(*set->histograms) +
+ histo_size + WEBP_ALIGN_CST);
+ uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
+ if (memory == NULL) return NULL;
+
+ set = (VP8LHistogramSet*)memory;
+ memory += sizeof(*set);
+ set->histograms = (VP8LHistogram**)memory;
+ memory += size * sizeof(*set->histograms);
+ set->max_size = size;
+ set->size = size;
+ for (i = 0; i < size; ++i) {
+ memory = (uint8_t*)WEBP_ALIGN(memory);
+ set->histograms[i] = (VP8LHistogram*)memory;
+ // literal_ won't necessary be aligned.
+ set->histograms[i]->literal_ = (uint32_t*)(memory + sizeof(VP8LHistogram));
+ VP8LHistogramInit(set->histograms[i], cache_bits);
+ memory += histo_size;
+ }
+ return set;
+}
+
+// -----------------------------------------------------------------------------
+
+void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
+ const PixOrCopy* const v,
+ int (*const distance_modifier)(int, int),
+ int distance_modifier_arg0) {
+ if (PixOrCopyIsLiteral(v)) {
+ ++histo->alpha_[PixOrCopyLiteral(v, 3)];
+ ++histo->red_[PixOrCopyLiteral(v, 2)];
+ ++histo->literal_[PixOrCopyLiteral(v, 1)];
+ ++histo->blue_[PixOrCopyLiteral(v, 0)];
+ } else if (PixOrCopyIsCacheIdx(v)) {
+ const int literal_ix =
+ NUM_LITERAL_CODES + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
+ ++histo->literal_[literal_ix];
+ } else {
+ int code, extra_bits;
+ VP8LPrefixEncodeBits(PixOrCopyLength(v), &code, &extra_bits);
+ ++histo->literal_[NUM_LITERAL_CODES + code];
+ if (distance_modifier == NULL) {
+ VP8LPrefixEncodeBits(PixOrCopyDistance(v), &code, &extra_bits);
+ } else {
+ VP8LPrefixEncodeBits(
+ distance_modifier(distance_modifier_arg0, PixOrCopyDistance(v)),
+ &code, &extra_bits);
+ }
+ ++histo->distance_[code];
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Entropy-related functions.
+
+static WEBP_INLINE double BitsEntropyRefine(const VP8LBitEntropy* entropy) {
+ double mix;
+ if (entropy->nonzeros < 5) {
+ if (entropy->nonzeros <= 1) {
+ return 0;
+ }
+ // Two symbols, they will be 0 and 1 in a Huffman code.
+ // Let's mix in a bit of entropy to favor good clustering when
+ // distributions of these are combined.
+ if (entropy->nonzeros == 2) {
+ return 0.99 * entropy->sum + 0.01 * entropy->entropy;
+ }
+ // No matter what the entropy says, we cannot be better than min_limit
+ // with Huffman coding. I am mixing a bit of entropy into the
+ // min_limit since it produces much better (~0.5 %) compression results
+ // perhaps because of better entropy clustering.
+ if (entropy->nonzeros == 3) {
+ mix = 0.95;
+ } else {
+ mix = 0.7; // nonzeros == 4.
+ }
+ } else {
+ mix = 0.627;
+ }
+
+ {
+ double min_limit = 2 * entropy->sum - entropy->max_val;
+ min_limit = mix * min_limit + (1.0 - mix) * entropy->entropy;
+ return (entropy->entropy < min_limit) ? min_limit : entropy->entropy;
+ }
+}
+
+double VP8LBitsEntropy(const uint32_t* const array, int n) {
+ VP8LBitEntropy entropy;
+ VP8LBitsEntropyUnrefined(array, n, &entropy);
+
+ return BitsEntropyRefine(&entropy);
+}
+
+static double InitialHuffmanCost(void) {
+ // Small bias because Huffman code length is typically not stored in
+ // full length.
+ static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3;
+ static const double kSmallBias = 9.1;
+ return kHuffmanCodeOfHuffmanCodeSize - kSmallBias;
+}
+
+// Finalize the Huffman cost based on streak numbers and length type (<3 or >=3)
+static double FinalHuffmanCost(const VP8LStreaks* const stats) {
+ // The constants in this function are experimental and got rounded from
+ // their original values in 1/8 when switched to 1/1024.
+ double retval = InitialHuffmanCost();
+ // Second coefficient: Many zeros in the histogram are covered efficiently
+ // by a run-length encode. Originally 2/8.
+ retval += stats->counts[0] * 1.5625 + 0.234375 * stats->streaks[0][1];
+ // Second coefficient: Constant values are encoded less efficiently, but still
+ // RLE'ed. Originally 6/8.
+ retval += stats->counts[1] * 2.578125 + 0.703125 * stats->streaks[1][1];
+ // 0s are usually encoded more efficiently than non-0s.
+ // Originally 15/8.
+ retval += 1.796875 * stats->streaks[0][0];
+ // Originally 26/8.
+ retval += 3.28125 * stats->streaks[1][0];
+ return retval;
+}
+
+// Get the symbol entropy for the distribution 'population'.
+// Set 'trivial_sym', if there's only one symbol present in the distribution.
+static double PopulationCost(const uint32_t* const population, int length,
+ uint32_t* const trivial_sym) {
+ VP8LBitEntropy bit_entropy;
+ VP8LStreaks stats;
+ VP8LGetEntropyUnrefined(population, length, &bit_entropy, &stats);
+ if (trivial_sym != NULL) {
+ *trivial_sym = (bit_entropy.nonzeros == 1) ? bit_entropy.nonzero_code
+ : VP8L_NON_TRIVIAL_SYM;
+ }
+
+ return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
+}
+
+// trivial_at_end is 1 if the two histograms only have one element that is
+// non-zero: both the zero-th one, or both the last one.
+static WEBP_INLINE double GetCombinedEntropy(const uint32_t* const X,
+ const uint32_t* const Y,
+ int length, int trivial_at_end) {
+ VP8LStreaks stats;
+ if (trivial_at_end) {
+ // This configuration is due to palettization that transforms an indexed
+ // pixel into 0xff000000 | (pixel << 8) in VP8LBundleColorMap.
+ // BitsEntropyRefine is 0 for histograms with only one non-zero value.
+ // Only FinalHuffmanCost needs to be evaluated.
+ memset(&stats, 0, sizeof(stats));
+ // Deal with the non-zero value at index 0 or length-1.
+ stats.streaks[1][0] += 1;
+ // Deal with the following/previous zero streak.
+ stats.counts[0] += 1;
+ stats.streaks[0][1] += length - 1;
+ return FinalHuffmanCost(&stats);
+ } else {
+ VP8LBitEntropy bit_entropy;
+ VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats);
+
+ return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
+ }
+}
+
+// Estimates the Entropy + Huffman + other block overhead size cost.
+double VP8LHistogramEstimateBits(const VP8LHistogram* const p) {
+ return
+ PopulationCost(
+ p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), NULL)
+ + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL)
+ + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL)
+ + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL)
+ + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL)
+ + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES)
+ + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES);
+}
+
+// -----------------------------------------------------------------------------
+// Various histogram combine/cost-eval functions
+
+static int GetCombinedHistogramEntropy(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ double cost_threshold,
+ double* cost) {
+ const int palette_code_bits = a->palette_code_bits_;
+ int trivial_at_end = 0;
+ assert(a->palette_code_bits_ == b->palette_code_bits_);
+ *cost += GetCombinedEntropy(a->literal_, b->literal_,
+ VP8LHistogramNumCodes(palette_code_bits), 0);
+ *cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES,
+ b->literal_ + NUM_LITERAL_CODES,
+ NUM_LENGTH_CODES);
+ if (*cost > cost_threshold) return 0;
+
+ if (a->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM &&
+ a->trivial_symbol_ == b->trivial_symbol_) {
+ // A, R and B are all 0 or 0xff.
+ const uint32_t color_a = (a->trivial_symbol_ >> 24) & 0xff;
+ const uint32_t color_r = (a->trivial_symbol_ >> 16) & 0xff;
+ const uint32_t color_b = (a->trivial_symbol_ >> 0) & 0xff;
+ if ((color_a == 0 || color_a == 0xff) &&
+ (color_r == 0 || color_r == 0xff) &&
+ (color_b == 0 || color_b == 0xff)) {
+ trivial_at_end = 1;
+ }
+ }
+
+ *cost +=
+ GetCombinedEntropy(a->red_, b->red_, NUM_LITERAL_CODES, trivial_at_end);
+ if (*cost > cost_threshold) return 0;
+
+ *cost +=
+ GetCombinedEntropy(a->blue_, b->blue_, NUM_LITERAL_CODES, trivial_at_end);
+ if (*cost > cost_threshold) return 0;
+
+ *cost += GetCombinedEntropy(a->alpha_, b->alpha_, NUM_LITERAL_CODES,
+ trivial_at_end);
+ if (*cost > cost_threshold) return 0;
+
+ *cost +=
+ GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES, 0);
+ *cost +=
+ VP8LExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES);
+ if (*cost > cost_threshold) return 0;
+
+ return 1;
+}
+
+static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ VP8LHistogram* const out) {
+ VP8LHistogramAdd(a, b, out);
+ out->trivial_symbol_ = (a->trivial_symbol_ == b->trivial_symbol_)
+ ? a->trivial_symbol_
+ : VP8L_NON_TRIVIAL_SYM;
+}
+
+// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing
+// to the threshold value 'cost_threshold'. The score returned is
+// Score = C(a+b) - C(a) - C(b), where C(a) + C(b) is known and fixed.
+// Since the previous score passed is 'cost_threshold', we only need to compare
+// the partial cost against 'cost_threshold + C(a) + C(b)' to possibly bail-out
+// early.
+static double HistogramAddEval(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ VP8LHistogram* const out,
+ double cost_threshold) {
+ double cost = 0;
+ const double sum_cost = a->bit_cost_ + b->bit_cost_;
+ cost_threshold += sum_cost;
+
+ if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) {
+ HistogramAdd(a, b, out);
+ out->bit_cost_ = cost;
+ out->palette_code_bits_ = a->palette_code_bits_;
+ }
+
+ return cost - sum_cost;
+}
+
+// Same as HistogramAddEval(), except that the resulting histogram
+// is not stored. Only the cost C(a+b) - C(a) is evaluated. We omit
+// the term C(b) which is constant over all the evaluations.
+static double HistogramAddThresh(const VP8LHistogram* const a,
+ const VP8LHistogram* const b,
+ double cost_threshold) {
+ double cost = -a->bit_cost_;
+ GetCombinedHistogramEntropy(a, b, cost_threshold, &cost);
+ return cost;
+}
+
+// -----------------------------------------------------------------------------
+
+// The structure to keep track of cost range for the three dominant entropy
+// symbols.
+// TODO(skal): Evaluate if float can be used here instead of double for
+// representing the entropy costs.
+typedef struct {
+ double literal_max_;
+ double literal_min_;
+ double red_max_;
+ double red_min_;
+ double blue_max_;
+ double blue_min_;
+} DominantCostRange;
+
+static void DominantCostRangeInit(DominantCostRange* const c) {
+ c->literal_max_ = 0.;
+ c->literal_min_ = MAX_COST;
+ c->red_max_ = 0.;
+ c->red_min_ = MAX_COST;
+ c->blue_max_ = 0.;
+ c->blue_min_ = MAX_COST;
+}
+
+static void UpdateDominantCostRange(
+ const VP8LHistogram* const h, DominantCostRange* const c) {
+ if (c->literal_max_ < h->literal_cost_) c->literal_max_ = h->literal_cost_;
+ if (c->literal_min_ > h->literal_cost_) c->literal_min_ = h->literal_cost_;
+ if (c->red_max_ < h->red_cost_) c->red_max_ = h->red_cost_;
+ if (c->red_min_ > h->red_cost_) c->red_min_ = h->red_cost_;
+ if (c->blue_max_ < h->blue_cost_) c->blue_max_ = h->blue_cost_;
+ if (c->blue_min_ > h->blue_cost_) c->blue_min_ = h->blue_cost_;
+}
+
+static void UpdateHistogramCost(VP8LHistogram* const h) {
+ uint32_t alpha_sym, red_sym, blue_sym;
+ const double alpha_cost =
+ PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym);
+ const double distance_cost =
+ PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL) +
+ VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES);
+ const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_);
+ h->literal_cost_ = PopulationCost(h->literal_, num_codes, NULL) +
+ VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES,
+ NUM_LENGTH_CODES);
+ h->red_cost_ = PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym);
+ h->blue_cost_ = PopulationCost(h->blue_, NUM_LITERAL_CODES, &blue_sym);
+ h->bit_cost_ = h->literal_cost_ + h->red_cost_ + h->blue_cost_ +
+ alpha_cost + distance_cost;
+ if ((alpha_sym | red_sym | blue_sym) == VP8L_NON_TRIVIAL_SYM) {
+ h->trivial_symbol_ = VP8L_NON_TRIVIAL_SYM;
+ } else {
+ h->trivial_symbol_ =
+ ((uint32_t)alpha_sym << 24) | (red_sym << 16) | (blue_sym << 0);
+ }
+}
+
+static int GetBinIdForEntropy(double min, double max, double val) {
+ const double range = max - min;
+ if (range > 0.) {
+ const double delta = val - min;
+ return (int)((NUM_PARTITIONS - 1e-6) * delta / range);
+ } else {
+ return 0;
+ }
+}
+
+static int GetHistoBinIndex(const VP8LHistogram* const h,
+ const DominantCostRange* const c, int low_effort) {
+ int bin_id = GetBinIdForEntropy(c->literal_min_, c->literal_max_,
+ h->literal_cost_);
+ assert(bin_id < NUM_PARTITIONS);
+ if (!low_effort) {
+ bin_id = bin_id * NUM_PARTITIONS
+ + GetBinIdForEntropy(c->red_min_, c->red_max_, h->red_cost_);
+ bin_id = bin_id * NUM_PARTITIONS
+ + GetBinIdForEntropy(c->blue_min_, c->blue_max_, h->blue_cost_);
+ assert(bin_id < BIN_SIZE);
+ }
+ return bin_id;
+}
+
+// Construct the histograms from backward references.
+static void HistogramBuild(
+ int xsize, int histo_bits, const VP8LBackwardRefs* const backward_refs,
+ VP8LHistogramSet* const image_histo) {
+ int x = 0, y = 0;
+ const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits);
+ VP8LHistogram** const histograms = image_histo->histograms;
+ VP8LRefsCursor c = VP8LRefsCursorInit(backward_refs);
+ assert(histo_bits > 0);
+ while (VP8LRefsCursorOk(&c)) {
+ const PixOrCopy* const v = c.cur_pos;
+ const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits);
+ VP8LHistogramAddSinglePixOrCopy(histograms[ix], v, NULL, 0);
+ x += PixOrCopyLength(v);
+ while (x >= xsize) {
+ x -= xsize;
+ ++y;
+ }
+ VP8LRefsCursorNext(&c);
+ }
+}
+
+// Copies the histograms and computes its bit_cost.
+static void HistogramCopyAndAnalyze(
+ VP8LHistogramSet* const orig_histo, VP8LHistogramSet* const image_histo) {
+ int i;
+ const int histo_size = orig_histo->size;
+ VP8LHistogram** const orig_histograms = orig_histo->histograms;
+ VP8LHistogram** const histograms = image_histo->histograms;
+ for (i = 0; i < histo_size; ++i) {
+ VP8LHistogram* const histo = orig_histograms[i];
+ UpdateHistogramCost(histo);
+ // Copy histograms from orig_histo[] to image_histo[].
+ HistogramCopy(histo, histograms[i]);
+ }
+}
+
+// Partition histograms to different entropy bins for three dominant (literal,
+// red and blue) symbol costs and compute the histogram aggregate bit_cost.
+static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo,
+ uint16_t* const bin_map,
+ int low_effort) {
+ int i;
+ VP8LHistogram** const histograms = image_histo->histograms;
+ const int histo_size = image_histo->size;
+ DominantCostRange cost_range;
+ DominantCostRangeInit(&cost_range);
+
+ // Analyze the dominant (literal, red and blue) entropy costs.
+ for (i = 0; i < histo_size; ++i) {
+ UpdateDominantCostRange(histograms[i], &cost_range);
+ }
+
+ // bin-hash histograms on three of the dominant (literal, red and blue)
+ // symbol costs and store the resulting bin_id for each histogram.
+ for (i = 0; i < histo_size; ++i) {
+ bin_map[i] = GetHistoBinIndex(histograms[i], &cost_range, low_effort);
+ }
+}
+
+// Compact image_histo[] by merging some histograms with same bin_id together if
+// it's advantageous.
+static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
+ VP8LHistogram* cur_combo,
+ const uint16_t* const bin_map,
+ int bin_map_size, int num_bins,
+ double combine_cost_factor,
+ int low_effort) {
+ VP8LHistogram** const histograms = image_histo->histograms;
+ int idx;
+ // Work in-place: processed histograms are put at the beginning of
+ // image_histo[]. At the end, we just have to truncate the array.
+ int size = 0;
+ struct {
+ int16_t first; // position of the histogram that accumulates all
+ // histograms with the same bin_id
+ uint16_t num_combine_failures; // number of combine failures per bin_id
+ } bin_info[BIN_SIZE];
+
+ assert(num_bins <= BIN_SIZE);
+ for (idx = 0; idx < num_bins; ++idx) {
+ bin_info[idx].first = -1;
+ bin_info[idx].num_combine_failures = 0;
+ }
+
+ for (idx = 0; idx < bin_map_size; ++idx) {
+ const int bin_id = bin_map[idx];
+ const int first = bin_info[bin_id].first;
+ assert(size <= idx);
+ if (first == -1) {
+ // just move histogram #idx to its final position
+ histograms[size] = histograms[idx];
+ bin_info[bin_id].first = size++;
+ } else if (low_effort) {
+ HistogramAdd(histograms[idx], histograms[first], histograms[first]);
+ } else {
+ // try to merge #idx into #first (both share the same bin_id)
+ const double bit_cost = histograms[idx]->bit_cost_;
+ const double bit_cost_thresh = -bit_cost * combine_cost_factor;
+ const double curr_cost_diff =
+ HistogramAddEval(histograms[first], histograms[idx],
+ cur_combo, bit_cost_thresh);
+ if (curr_cost_diff < bit_cost_thresh) {
+ // Try to merge two histograms only if the combo is a trivial one or
+ // the two candidate histograms are already non-trivial.
+ // For some images, 'try_combine' turns out to be false for a lot of
+ // histogram pairs. In that case, we fallback to combining
+ // histograms as usual to avoid increasing the header size.
+ const int try_combine =
+ (cur_combo->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM) ||
+ ((histograms[idx]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM) &&
+ (histograms[first]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM));
+ const int max_combine_failures = 32;
+ if (try_combine ||
+ bin_info[bin_id].num_combine_failures >= max_combine_failures) {
+ // move the (better) merged histogram to its final slot
+ HistogramSwap(&cur_combo, &histograms[first]);
+ } else {
+ histograms[size++] = histograms[idx];
+ ++bin_info[bin_id].num_combine_failures;
+ }
+ } else {
+ histograms[size++] = histograms[idx];
+ }
+ }
+ }
+ image_histo->size = size;
+ if (low_effort) {
+ // for low_effort case, update the final cost when everything is merged
+ for (idx = 0; idx < size; ++idx) {
+ UpdateHistogramCost(histograms[idx]);
+ }
+ }
+}
+
+// Implement a Lehmer random number generator with a multiplicative constant of
+// 48271 and a modulo constant of 2^31 - 1.
+static uint32_t MyRand(uint32_t* const seed) {
+ *seed = (uint32_t)(((uint64_t)(*seed) * 48271u) % 2147483647u);
+ assert(*seed > 0);
+ return *seed;
+}
+
+// -----------------------------------------------------------------------------
+// Histogram pairs priority queue
+
+// Pair of histograms. Negative idx1 value means that pair is out-of-date.
+typedef struct {
+ int idx1;
+ int idx2;
+ double cost_diff;
+ double cost_combo;
+} HistogramPair;
+
+typedef struct {
+ HistogramPair* queue;
+ int size;
+ int max_size;
+} HistoQueue;
+
+static int HistoQueueInit(HistoQueue* const histo_queue, const int max_index) {
+ histo_queue->size = 0;
+ // max_index^2 for the queue size is safe. If you look at
+ // HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes
+ // data to the queue, you insert at most:
+ // - max_index*(max_index-1)/2 (the first two for loops)
+ // - max_index - 1 in the last for loop at the first iteration of the while
+ // loop, max_index - 2 at the second iteration ... therefore
+ // max_index*(max_index-1)/2 overall too
+ histo_queue->max_size = max_index * max_index;
+ // We allocate max_size + 1 because the last element at index "size" is
+ // used as temporary data (and it could be up to max_size).
+ histo_queue->queue = (HistogramPair*)WebPSafeMalloc(
+ histo_queue->max_size + 1, sizeof(*histo_queue->queue));
+ return histo_queue->queue != NULL;
+}
+
+static void HistoQueueClear(HistoQueue* const histo_queue) {
+ assert(histo_queue != NULL);
+ WebPSafeFree(histo_queue->queue);
+ histo_queue->size = 0;
+ histo_queue->max_size = 0;
+}
+
+// Pop a specific pair in the queue by replacing it with the last one
+// and shrinking the queue.
+static void HistoQueuePopPair(HistoQueue* const histo_queue,
+ HistogramPair* const pair) {
+ assert(pair >= histo_queue->queue &&
+ pair < (histo_queue->queue + histo_queue->size));
+ assert(histo_queue->size > 0);
+ *pair = histo_queue->queue[histo_queue->size - 1];
+ --histo_queue->size;
+}
+
+// Check whether a pair in the queue should be updated as head or not.
+static void HistoQueueUpdateHead(HistoQueue* const histo_queue,
+ HistogramPair* const pair) {
+ assert(pair->cost_diff < 0.);
+ assert(pair >= histo_queue->queue &&
+ pair < (histo_queue->queue + histo_queue->size));
+ assert(histo_queue->size > 0);
+ if (pair->cost_diff < histo_queue->queue[0].cost_diff) {
+ // Replace the best pair.
+ const HistogramPair tmp = histo_queue->queue[0];
+ histo_queue->queue[0] = *pair;
+ *pair = tmp;
+ }
+}
+
+// Create a pair from indices "idx1" and "idx2" provided its cost
+// is inferior to "threshold", a negative entropy.
+// It returns the cost of the pair, or 0. if it superior to threshold.
+static double HistoQueuePush(HistoQueue* const histo_queue,
+ VP8LHistogram** const histograms, int idx1,
+ int idx2, double threshold) {
+ const VP8LHistogram* h1;
+ const VP8LHistogram* h2;
+ HistogramPair pair;
+ double sum_cost;
+
+ assert(threshold <= 0.);
+ if (idx1 > idx2) {
+ const int tmp = idx2;
+ idx2 = idx1;
+ idx1 = tmp;
+ }
+ pair.idx1 = idx1;
+ pair.idx2 = idx2;
+ h1 = histograms[idx1];
+ h2 = histograms[idx2];
+ sum_cost = h1->bit_cost_ + h2->bit_cost_;
+ pair.cost_combo = 0.;
+ GetCombinedHistogramEntropy(h1, h2, sum_cost + threshold, &pair.cost_combo);
+ pair.cost_diff = pair.cost_combo - sum_cost;
+
+ // Do not even consider the pair if it does not improve the entropy.
+ if (pair.cost_diff >= threshold) return 0.;
+
+ // We cannot add more elements than the capacity.
+ assert(histo_queue->size < histo_queue->max_size);
+ histo_queue->queue[histo_queue->size++] = pair;
+ HistoQueueUpdateHead(histo_queue, &histo_queue->queue[histo_queue->size - 1]);
+
+ return pair.cost_diff;
+}
+
+// -----------------------------------------------------------------------------
+
+// Combines histograms by continuously choosing the one with the highest cost
+// reduction.
+static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
+ int ok = 0;
+ int image_histo_size = image_histo->size;
+ int i, j;
+ VP8LHistogram** const histograms = image_histo->histograms;
+ // Indexes of remaining histograms.
+ int* const clusters =
+ (int*)WebPSafeMalloc(image_histo_size, sizeof(*clusters));
+ // Priority queue of histogram pairs.
+ HistoQueue histo_queue;
+
+ if (!HistoQueueInit(&histo_queue, image_histo_size) || clusters == NULL) {
+ goto End;
+ }
+
+ for (i = 0; i < image_histo_size; ++i) {
+ // Initialize clusters indexes.
+ clusters[i] = i;
+ for (j = i + 1; j < image_histo_size; ++j) {
+ // Initialize positions array.
+ HistoQueuePush(&histo_queue, histograms, i, j, 0.);
+ }
+ }
+
+ while (image_histo_size > 1 && histo_queue.size > 0) {
+ const int idx1 = histo_queue.queue[0].idx1;
+ const int idx2 = histo_queue.queue[0].idx2;
+ HistogramAdd(histograms[idx2], histograms[idx1], histograms[idx1]);
+ histograms[idx1]->bit_cost_ = histo_queue.queue[0].cost_combo;
+ // Remove merged histogram.
+ for (i = 0; i + 1 < image_histo_size; ++i) {
+ if (clusters[i] >= idx2) {
+ clusters[i] = clusters[i + 1];
+ }
+ }
+ --image_histo_size;
+
+ // Remove pairs intersecting the just combined best pair.
+ for (i = 0; i < histo_queue.size;) {
+ HistogramPair* const p = histo_queue.queue + i;
+ if (p->idx1 == idx1 || p->idx2 == idx1 ||
+ p->idx1 == idx2 || p->idx2 == idx2) {
+ HistoQueuePopPair(&histo_queue, p);
+ } else {
+ HistoQueueUpdateHead(&histo_queue, p);
+ ++i;
+ }
+ }
+
+ // Push new pairs formed with combined histogram to the queue.
+ for (i = 0; i < image_histo_size; ++i) {
+ if (clusters[i] != idx1) {
+ HistoQueuePush(&histo_queue, histograms, idx1, clusters[i], 0.);
+ }
+ }
+ }
+ // Move remaining histograms to the beginning of the array.
+ for (i = 0; i < image_histo_size; ++i) {
+ if (i != clusters[i]) { // swap the two histograms
+ HistogramSwap(&histograms[i], &histograms[clusters[i]]);
+ }
+ }
+
+ image_histo->size = image_histo_size;
+ ok = 1;
+
+ End:
+ WebPSafeFree(clusters);
+ HistoQueueClear(&histo_queue);
+ return ok;
+}
+
+// Perform histogram aggregation using a stochastic approach.
+// 'do_greedy' is set to 1 if a greedy approach needs to be performed
+// afterwards, 0 otherwise.
+static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
+ int min_cluster_size,
+ int* const do_greedy) {
+ int iter;
+ uint32_t seed = 1;
+ int tries_with_no_success = 0;
+ int image_histo_size = image_histo->size;
+ const int outer_iters = image_histo_size;
+ const int num_tries_no_success = outer_iters / 2;
+ VP8LHistogram** const histograms = image_histo->histograms;
+ // Priority queue of histogram pairs. Its size of "kCostHeapSizeSqrt"^2
+ // impacts the quality of the compression and the speed: the smaller the
+ // faster but the worse for the compression.
+ HistoQueue histo_queue;
+ const int kHistoQueueSizeSqrt = 3;
+ int ok = 0;
+
+ if (!HistoQueueInit(&histo_queue, kHistoQueueSizeSqrt)) {
+ goto End;
+ }
+ // Collapse similar histograms in 'image_histo'.
+ ++min_cluster_size;
+ for (iter = 0; iter < outer_iters && image_histo_size >= min_cluster_size &&
+ ++tries_with_no_success < num_tries_no_success;
+ ++iter) {
+ double best_cost =
+ (histo_queue.size == 0) ? 0. : histo_queue.queue[0].cost_diff;
+ int best_idx1 = -1, best_idx2 = 1;
+ int j;
+ const uint32_t rand_range = (image_histo_size - 1) * image_histo_size;
+ // image_histo_size / 2 was chosen empirically. Less means faster but worse
+ // compression.
+ const int num_tries = image_histo_size / 2;
+
+ for (j = 0; j < num_tries; ++j) {
+ double curr_cost;
+ // Choose two different histograms at random and try to combine them.
+ const uint32_t tmp = MyRand(&seed) % rand_range;
+ const uint32_t idx1 = tmp / (image_histo_size - 1);
+ uint32_t idx2 = tmp % (image_histo_size - 1);
+ if (idx2 >= idx1) ++idx2;
+
+ // Calculate cost reduction on combination.
+ curr_cost =
+ HistoQueuePush(&histo_queue, histograms, idx1, idx2, best_cost);
+ if (curr_cost < 0) { // found a better pair?
+ best_cost = curr_cost;
+ // Empty the queue if we reached full capacity.
+ if (histo_queue.size == histo_queue.max_size) break;
+ }
+ }
+ if (histo_queue.size == 0) continue;
+
+ // Merge the two best histograms.
+ best_idx1 = histo_queue.queue[0].idx1;
+ best_idx2 = histo_queue.queue[0].idx2;
+ assert(best_idx1 < best_idx2);
+ HistogramAddEval(histograms[best_idx1], histograms[best_idx2],
+ histograms[best_idx1], 0);
+ // Swap the best_idx2 histogram with the last one (which is now unused).
+ --image_histo_size;
+ if (best_idx2 != image_histo_size) {
+ HistogramSwap(&histograms[image_histo_size], &histograms[best_idx2]);
+ }
+ histograms[image_histo_size] = NULL;
+ // Parse the queue and update each pair that deals with best_idx1,
+ // best_idx2 or image_histo_size.
+ for (j = 0; j < histo_queue.size;) {
+ HistogramPair* const p = histo_queue.queue + j;
+ const int is_idx1_best = p->idx1 == best_idx1 || p->idx1 == best_idx2;
+ const int is_idx2_best = p->idx2 == best_idx1 || p->idx2 == best_idx2;
+ int do_eval = 0;
+ // The front pair could have been duplicated by a random pick so
+ // check for it all the time nevertheless.
+ if (is_idx1_best && is_idx2_best) {
+ HistoQueuePopPair(&histo_queue, p);
+ continue;
+ }
+ // Any pair containing one of the two best indices should only refer to
+ // best_idx1. Its cost should also be updated.
+ if (is_idx1_best) {
+ p->idx1 = best_idx1;
+ do_eval = 1;
+ } else if (is_idx2_best) {
+ p->idx2 = best_idx1;
+ do_eval = 1;
+ }
+ if (p->idx2 == image_histo_size) {
+ // No need to re-evaluate here as it does not involve a pair
+ // containing best_idx1 or best_idx2.
+ p->idx2 = best_idx2;
+ }
+ assert(p->idx2 < image_histo_size);
+ // Make sure the index order is respected.
+ if (p->idx1 > p->idx2) {
+ const int tmp = p->idx2;
+ p->idx2 = p->idx1;
+ p->idx1 = tmp;
+ }
+ if (do_eval) {
+ // Re-evaluate the cost of an updated pair.
+ GetCombinedHistogramEntropy(histograms[p->idx1], histograms[p->idx2], 0,
+ &p->cost_diff);
+ if (p->cost_diff >= 0.) {
+ HistoQueuePopPair(&histo_queue, p);
+ continue;
+ }
+ }
+ HistoQueueUpdateHead(&histo_queue, p);
+ ++j;
+ }
+
+ tries_with_no_success = 0;
+ }
+ image_histo->size = image_histo_size;
+ *do_greedy = (image_histo->size <= min_cluster_size);
+ ok = 1;
+
+End:
+ HistoQueueClear(&histo_queue);
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+// Histogram refinement
+
+// Find the best 'out' histogram for each of the 'in' histograms.
+// Note: we assume that out[]->bit_cost_ is already up-to-date.
+static void HistogramRemap(const VP8LHistogramSet* const in,
+ const VP8LHistogramSet* const out,
+ uint16_t* const symbols) {
+ int i;
+ VP8LHistogram** const in_histo = in->histograms;
+ VP8LHistogram** const out_histo = out->histograms;
+ const int in_size = in->size;
+ const int out_size = out->size;
+ if (out_size > 1) {
+ for (i = 0; i < in_size; ++i) {
+ int best_out = 0;
+ double best_bits = MAX_COST;
+ int k;
+ for (k = 0; k < out_size; ++k) {
+ const double cur_bits =
+ HistogramAddThresh(out_histo[k], in_histo[i], best_bits);
+ if (k == 0 || cur_bits < best_bits) {
+ best_bits = cur_bits;
+ best_out = k;
+ }
+ }
+ symbols[i] = best_out;
+ }
+ } else {
+ assert(out_size == 1);
+ for (i = 0; i < in_size; ++i) {
+ symbols[i] = 0;
+ }
+ }
+
+ // Recompute each out based on raw and symbols.
+ for (i = 0; i < out_size; ++i) {
+ HistogramClear(out_histo[i]);
+ }
+
+ for (i = 0; i < in_size; ++i) {
+ const int idx = symbols[i];
+ HistogramAdd(in_histo[i], out_histo[idx], out_histo[idx]);
+ }
+}
+
+static double GetCombineCostFactor(int histo_size, int quality) {
+ double combine_cost_factor = 0.16;
+ if (quality < 90) {
+ if (histo_size > 256) combine_cost_factor /= 2.;
+ if (histo_size > 512) combine_cost_factor /= 2.;
+ if (histo_size > 1024) combine_cost_factor /= 2.;
+ if (quality <= 50) combine_cost_factor /= 2.;
+ }
+ return combine_cost_factor;
+}
+
+int VP8LGetHistoImageSymbols(int xsize, int ysize,
+ const VP8LBackwardRefs* const refs,
+ int quality, int low_effort,
+ int histo_bits, int cache_bits,
+ VP8LHistogramSet* const image_histo,
+ VP8LHistogram* const tmp_histo,
+ uint16_t* const histogram_symbols) {
+ int ok = 0;
+ const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
+ const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
+ const int image_histo_raw_size = histo_xsize * histo_ysize;
+ VP8LHistogramSet* const orig_histo =
+ VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits);
+ // Don't attempt linear bin-partition heuristic for
+ // histograms of small sizes (as bin_map will be very sparse) and
+ // maximum quality q==100 (to preserve the compression gains at that level).
+ const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE;
+ const int entropy_combine =
+ (orig_histo->size > entropy_combine_num_bins * 2) && (quality < 100);
+
+ if (orig_histo == NULL) goto Error;
+
+ // Construct the histograms from backward references.
+ HistogramBuild(xsize, histo_bits, refs, orig_histo);
+ // Copies the histograms and computes its bit_cost.
+ HistogramCopyAndAnalyze(orig_histo, image_histo);
+
+ if (entropy_combine) {
+ const int bin_map_size = orig_histo->size;
+ // Reuse histogram_symbols storage. By definition, it's guaranteed to be ok.
+ uint16_t* const bin_map = histogram_symbols;
+ const double combine_cost_factor =
+ GetCombineCostFactor(image_histo_raw_size, quality);
+
+ HistogramAnalyzeEntropyBin(orig_histo, bin_map, low_effort);
+ // Collapse histograms with similar entropy.
+ HistogramCombineEntropyBin(image_histo, tmp_histo, bin_map, bin_map_size,
+ entropy_combine_num_bins, combine_cost_factor,
+ low_effort);
+ }
+
+ // Don't combine the histograms using stochastic and greedy heuristics for
+ // low-effort compression mode.
+ if (!low_effort || !entropy_combine) {
+ const float x = quality / 100.f;
+ // cubic ramp between 1 and MAX_HISTO_GREEDY:
+ const int threshold_size = (int)(1 + (x * x * x) * (MAX_HISTO_GREEDY - 1));
+ int do_greedy;
+ if (!HistogramCombineStochastic(image_histo, threshold_size, &do_greedy)) {
+ goto Error;
+ }
+ if (do_greedy && !HistogramCombineGreedy(image_histo)) {
+ goto Error;
+ }
+ }
+
+ // TODO(vrabaud): Optimize HistogramRemap for low-effort compression mode.
+ // Find the optimal map from original histograms to the final ones.
+ HistogramRemap(orig_histo, image_histo, histogram_symbols);
+
+ ok = 1;
+
+ Error:
+ VP8LFreeHistogramSet(orig_histo);
+ return ok;
+}
diff --git a/src/third_party/libwebp/src/enc/histogram_enc.h b/src/third_party/libwebp/src/enc/histogram_enc.h
new file mode 100644
index 0000000..0f9f48d
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/histogram_enc.h
@@ -0,0 +1,126 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+// Models the histograms of literal and distance codes.
+
+#ifndef WEBP_ENC_HISTOGRAM_ENC_H_
+#define WEBP_ENC_HISTOGRAM_ENC_H_
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <string.h>
+#endif
+
+#include "src/enc/backward_references_enc.h"
+#include "src/webp/format_constants.h"
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Not a trivial literal symbol.
+#define VP8L_NON_TRIVIAL_SYM (0xffffffff)
+
+// A simple container for histograms of data.
+typedef struct {
+ // literal_ contains green literal, palette-code and
+ // copy-length-prefix histogram
+ uint32_t* literal_; // Pointer to the allocated buffer for literal.
+ uint32_t red_[NUM_LITERAL_CODES];
+ uint32_t blue_[NUM_LITERAL_CODES];
+ uint32_t alpha_[NUM_LITERAL_CODES];
+ // Backward reference prefix-code histogram.
+ uint32_t distance_[NUM_DISTANCE_CODES];
+ int palette_code_bits_;
+ uint32_t trivial_symbol_; // True, if histograms for Red, Blue & Alpha
+ // literal symbols are single valued.
+ double bit_cost_; // cached value of bit cost.
+ double literal_cost_; // Cached values of dominant entropy costs:
+ double red_cost_; // literal, red & blue.
+ double blue_cost_;
+} VP8LHistogram;
+
+// Collection of histograms with fixed capacity, allocated as one
+// big memory chunk. Can be destroyed by calling WebPSafeFree().
+typedef struct {
+ int size; // number of slots currently in use
+ int max_size; // maximum capacity
+ VP8LHistogram** histograms;
+} VP8LHistogramSet;
+
+// Create the histogram.
+//
+// The input data is the PixOrCopy data, which models the literals, stop
+// codes and backward references (both distances and lengths). Also: if
+// palette_code_bits is >= 0, initialize the histogram with this value.
+void VP8LHistogramCreate(VP8LHistogram* const p,
+ const VP8LBackwardRefs* const refs,
+ int palette_code_bits);
+
+// Return the size of the histogram for a given palette_code_bits.
+int VP8LGetHistogramSize(int palette_code_bits);
+
+// Set the palette_code_bits and reset the stats.
+void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
+
+// Collect all the references into a histogram (without reset)
+void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
+ VP8LHistogram* const histo);
+
+// Free the memory allocated for the histogram.
+void VP8LFreeHistogram(VP8LHistogram* const histo);
+
+// Free the memory allocated for the histogram set.
+void VP8LFreeHistogramSet(VP8LHistogramSet* const histo);
+
+// Allocate an array of pointer to histograms, allocated and initialized
+// using 'cache_bits'. Return NULL in case of memory error.
+VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits);
+
+// Allocate and initialize histogram object with specified 'cache_bits'.
+// Returns NULL in case of memory error.
+// Special case of VP8LAllocateHistogramSet, with size equals 1.
+VP8LHistogram* VP8LAllocateHistogram(int cache_bits);
+
+// Accumulate a token 'v' into a histogram.
+void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
+ const PixOrCopy* const v,
+ int (*const distance_modifier)(int, int),
+ int distance_modifier_arg0);
+
+static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) {
+ return NUM_LITERAL_CODES + NUM_LENGTH_CODES +
+ ((palette_code_bits > 0) ? (1 << palette_code_bits) : 0);
+}
+
+// Builds the histogram image.
+int VP8LGetHistoImageSymbols(int xsize, int ysize,
+ const VP8LBackwardRefs* const refs,
+ int quality, int low_effort,
+ int histogram_bits, int cache_bits,
+ VP8LHistogramSet* const image_in,
+ VP8LHistogram* const tmp_histo,
+ uint16_t* const histogram_symbols);
+
+// Returns the entropy for the symbols in the input array.
+double VP8LBitsEntropy(const uint32_t* const array, int n);
+
+// Estimate how many bits the combined entropy of literals and distance
+// approximately maps to.
+double VP8LHistogramEstimateBits(const VP8LHistogram* const p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WEBP_ENC_HISTOGRAM_ENC_H_
diff --git a/src/third_party/libwebp/src/enc/iterator_enc.c b/src/third_party/libwebp/src/enc/iterator_enc.c
new file mode 100644
index 0000000..1df9cb0
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/iterator_enc.c
@@ -0,0 +1,463 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// VP8Iterator: block iterator
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <string.h>
+#endif
+
+#include "src/enc/vp8i_enc.h"
+
+//------------------------------------------------------------------------------
+// VP8Iterator
+//------------------------------------------------------------------------------
+
+static void InitLeft(VP8EncIterator* const it) {
+ it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] =
+ (it->y_ > 0) ? 129 : 127;
+ memset(it->y_left_, 129, 16);
+ memset(it->u_left_, 129, 8);
+ memset(it->v_left_, 129, 8);
+ it->left_nz_[8] = 0;
+ if (it->top_derr_ != NULL) {
+ memset(&it->left_derr_, 0, sizeof(it->left_derr_));
+ }
+}
+
+static void InitTop(VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ const size_t top_size = enc->mb_w_ * 16;
+ memset(enc->y_top_, 127, 2 * top_size);
+ memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_));
+ if (enc->top_derr_ != NULL) {
+ memset(enc->top_derr_, 0, enc->mb_w_ * sizeof(*enc->top_derr_));
+ }
+}
+
+void VP8IteratorSetRow(VP8EncIterator* const it, int y) {
+ VP8Encoder* const enc = it->enc_;
+ it->x_ = 0;
+ it->y_ = y;
+ it->bw_ = &enc->parts_[y & (enc->num_parts_ - 1)];
+ it->preds_ = enc->preds_ + y * 4 * enc->preds_w_;
+ it->nz_ = enc->nz_;
+ it->mb_ = enc->mb_info_ + y * enc->mb_w_;
+ it->y_top_ = enc->y_top_;
+ it->uv_top_ = enc->uv_top_;
+ InitLeft(it);
+}
+
+void VP8IteratorReset(VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ VP8IteratorSetRow(it, 0);
+ VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default
+ InitTop(it);
+ memset(it->bit_count_, 0, sizeof(it->bit_count_));
+ it->do_trellis_ = 0;
+}
+
+void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down) {
+ it->count_down_ = it->count_down0_ = count_down;
+}
+
+int VP8IteratorIsDone(const VP8EncIterator* const it) {
+ return (it->count_down_ <= 0);
+}
+
+void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
+ it->enc_ = enc;
+ it->yuv_in_ = (uint8_t*)WEBP_ALIGN(it->yuv_mem_);
+ it->yuv_out_ = it->yuv_in_ + YUV_SIZE_ENC;
+ it->yuv_out2_ = it->yuv_out_ + YUV_SIZE_ENC;
+ it->yuv_p_ = it->yuv_out2_ + YUV_SIZE_ENC;
+ it->lf_stats_ = enc->lf_stats_;
+ it->percent0_ = enc->percent_;
+ it->y_left_ = (uint8_t*)WEBP_ALIGN(it->yuv_left_mem_ + 1);
+ it->u_left_ = it->y_left_ + 16 + 16;
+ it->v_left_ = it->u_left_ + 16;
+ it->top_derr_ = enc->top_derr_;
+ VP8IteratorReset(it);
+}
+
+int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
+ VP8Encoder* const enc = it->enc_;
+ if (delta && enc->pic_->progress_hook != NULL) {
+ const int done = it->count_down0_ - it->count_down_;
+ const int percent = (it->count_down0_ <= 0)
+ ? it->percent0_
+ : it->percent0_ + delta * done / it->count_down0_;
+ return WebPReportProgress(enc->pic_, percent, &enc->percent_);
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Import the source samples into the cache. Takes care of replicating
+// boundary pixels if necessary.
+
+static WEBP_INLINE int MinSize(int a, int b) { return (a < b) ? a : b; }
+
+static void ImportBlock(const uint8_t* src, int src_stride,
+ uint8_t* dst, int w, int h, int size) {
+ int i;
+ for (i = 0; i < h; ++i) {
+ memcpy(dst, src, w);
+ if (w < size) {
+ memset(dst + w, dst[w - 1], size - w);
+ }
+ dst += BPS;
+ src += src_stride;
+ }
+ for (i = h; i < size; ++i) {
+ memcpy(dst, dst - BPS, size);
+ dst += BPS;
+ }
+}
+
+static void ImportLine(const uint8_t* src, int src_stride,
+ uint8_t* dst, int len, int total_len) {
+ int i;
+ for (i = 0; i < len; ++i, src += src_stride) dst[i] = *src;
+ for (; i < total_len; ++i) dst[i] = dst[len - 1];
+}
+
+void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32) {
+ const VP8Encoder* const enc = it->enc_;
+ const int x = it->x_, y = it->y_;
+ const WebPPicture* const pic = enc->pic_;
+ const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16;
+ const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8;
+ const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8;
+ const int w = MinSize(pic->width - x * 16, 16);
+ const int h = MinSize(pic->height - y * 16, 16);
+ const int uv_w = (w + 1) >> 1;
+ const int uv_h = (h + 1) >> 1;
+
+ ImportBlock(ysrc, pic->y_stride, it->yuv_in_ + Y_OFF_ENC, w, h, 16);
+ ImportBlock(usrc, pic->uv_stride, it->yuv_in_ + U_OFF_ENC, uv_w, uv_h, 8);
+ ImportBlock(vsrc, pic->uv_stride, it->yuv_in_ + V_OFF_ENC, uv_w, uv_h, 8);
+
+ if (tmp_32 == NULL) return;
+
+ // Import source (uncompressed) samples into boundary.
+ if (x == 0) {
+ InitLeft(it);
+ } else {
+ if (y == 0) {
+ it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127;
+ } else {
+ it->y_left_[-1] = ysrc[- 1 - pic->y_stride];
+ it->u_left_[-1] = usrc[- 1 - pic->uv_stride];
+ it->v_left_[-1] = vsrc[- 1 - pic->uv_stride];
+ }
+ ImportLine(ysrc - 1, pic->y_stride, it->y_left_, h, 16);
+ ImportLine(usrc - 1, pic->uv_stride, it->u_left_, uv_h, 8);
+ ImportLine(vsrc - 1, pic->uv_stride, it->v_left_, uv_h, 8);
+ }
+
+ it->y_top_ = tmp_32 + 0;
+ it->uv_top_ = tmp_32 + 16;
+ if (y == 0) {
+ memset(tmp_32, 127, 32 * sizeof(*tmp_32));
+ } else {
+ ImportLine(ysrc - pic->y_stride, 1, tmp_32, w, 16);
+ ImportLine(usrc - pic->uv_stride, 1, tmp_32 + 16, uv_w, 8);
+ ImportLine(vsrc - pic->uv_stride, 1, tmp_32 + 16 + 8, uv_w, 8);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Copy back the compressed samples into user space if requested.
+
+static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride,
+ int w, int h) {
+ while (h-- > 0) {
+ memcpy(dst, src, w);
+ dst += dst_stride;
+ src += BPS;
+ }
+}
+
+void VP8IteratorExport(const VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ if (enc->config_->show_compressed) {
+ const int x = it->x_, y = it->y_;
+ const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC;
+ const uint8_t* const usrc = it->yuv_out_ + U_OFF_ENC;
+ const uint8_t* const vsrc = it->yuv_out_ + V_OFF_ENC;
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16;
+ uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8;
+ uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8;
+ int w = (pic->width - x * 16);
+ int h = (pic->height - y * 16);
+
+ if (w > 16) w = 16;
+ if (h > 16) h = 16;
+
+ // Luma plane
+ ExportBlock(ysrc, ydst, pic->y_stride, w, h);
+
+ { // U/V planes
+ const int uv_w = (w + 1) >> 1;
+ const int uv_h = (h + 1) >> 1;
+ ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h);
+ ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Non-zero contexts setup/teardown
+
+// Nz bits:
+// 0 1 2 3 Y
+// 4 5 6 7
+// 8 9 10 11
+// 12 13 14 15
+// 16 17 U
+// 18 19
+// 20 21 V
+// 22 23
+// 24 DC-intra16
+
+// Convert packed context to byte array
+#define BIT(nz, n) (!!((nz) & (1 << (n))))
+
+void VP8IteratorNzToBytes(VP8EncIterator* const it) {
+ const int tnz = it->nz_[0], lnz = it->nz_[-1];
+ int* const top_nz = it->top_nz_;
+ int* const left_nz = it->left_nz_;
+
+ // Top-Y
+ top_nz[0] = BIT(tnz, 12);
+ top_nz[1] = BIT(tnz, 13);
+ top_nz[2] = BIT(tnz, 14);
+ top_nz[3] = BIT(tnz, 15);
+ // Top-U
+ top_nz[4] = BIT(tnz, 18);
+ top_nz[5] = BIT(tnz, 19);
+ // Top-V
+ top_nz[6] = BIT(tnz, 22);
+ top_nz[7] = BIT(tnz, 23);
+ // DC
+ top_nz[8] = BIT(tnz, 24);
+
+ // left-Y
+ left_nz[0] = BIT(lnz, 3);
+ left_nz[1] = BIT(lnz, 7);
+ left_nz[2] = BIT(lnz, 11);
+ left_nz[3] = BIT(lnz, 15);
+ // left-U
+ left_nz[4] = BIT(lnz, 17);
+ left_nz[5] = BIT(lnz, 19);
+ // left-V
+ left_nz[6] = BIT(lnz, 21);
+ left_nz[7] = BIT(lnz, 23);
+ // left-DC is special, iterated separately
+}
+
+void VP8IteratorBytesToNz(VP8EncIterator* const it) {
+ uint32_t nz = 0;
+ const int* const top_nz = it->top_nz_;
+ const int* const left_nz = it->left_nz_;
+ // top
+ nz |= (top_nz[0] << 12) | (top_nz[1] << 13);
+ nz |= (top_nz[2] << 14) | (top_nz[3] << 15);
+ nz |= (top_nz[4] << 18) | (top_nz[5] << 19);
+ nz |= (top_nz[6] << 22) | (top_nz[7] << 23);
+ nz |= (top_nz[8] << 24); // we propagate the _top_ bit, esp. for intra4
+ // left
+ nz |= (left_nz[0] << 3) | (left_nz[1] << 7);
+ nz |= (left_nz[2] << 11);
+ nz |= (left_nz[4] << 17) | (left_nz[6] << 21);
+
+ *it->nz_ = nz;
+}
+
+#undef BIT
+
+//------------------------------------------------------------------------------
+// Advance to the next position, doing the bookkeeping.
+
+void VP8IteratorSaveBoundary(VP8EncIterator* const it) {
+ VP8Encoder* const enc = it->enc_;
+ const int x = it->x_, y = it->y_;
+ const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC;
+ const uint8_t* const uvsrc = it->yuv_out_ + U_OFF_ENC;
+ if (x < enc->mb_w_ - 1) { // left
+ int i;
+ for (i = 0; i < 16; ++i) {
+ it->y_left_[i] = ysrc[15 + i * BPS];
+ }
+ for (i = 0; i < 8; ++i) {
+ it->u_left_[i] = uvsrc[7 + i * BPS];
+ it->v_left_[i] = uvsrc[15 + i * BPS];
+ }
+ // top-left (before 'top'!)
+ it->y_left_[-1] = it->y_top_[15];
+ it->u_left_[-1] = it->uv_top_[0 + 7];
+ it->v_left_[-1] = it->uv_top_[8 + 7];
+ }
+ if (y < enc->mb_h_ - 1) { // top
+ memcpy(it->y_top_, ysrc + 15 * BPS, 16);
+ memcpy(it->uv_top_, uvsrc + 7 * BPS, 8 + 8);
+ }
+}
+
+int VP8IteratorNext(VP8EncIterator* const it) {
+ if (++it->x_ == it->enc_->mb_w_) {
+ VP8IteratorSetRow(it, ++it->y_);
+ } else {
+ it->preds_ += 4;
+ it->mb_ += 1;
+ it->nz_ += 1;
+ it->y_top_ += 16;
+ it->uv_top_ += 16;
+ }
+ return (0 < --it->count_down_);
+}
+
+//------------------------------------------------------------------------------
+// Helper function to set mode properties
+
+void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) {
+ uint8_t* preds = it->preds_;
+ int y;
+ for (y = 0; y < 4; ++y) {
+ memset(preds, mode, 4);
+ preds += it->enc_->preds_w_;
+ }
+ it->mb_->type_ = 1;
+}
+
+void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) {
+ uint8_t* preds = it->preds_;
+ int y;
+ for (y = 4; y > 0; --y) {
+ memcpy(preds, modes, 4 * sizeof(*modes));
+ preds += it->enc_->preds_w_;
+ modes += 4;
+ }
+ it->mb_->type_ = 0;
+}
+
+void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) {
+ it->mb_->uv_mode_ = mode;
+}
+
+void VP8SetSkip(const VP8EncIterator* const it, int skip) {
+ it->mb_->skip_ = skip;
+}
+
+void VP8SetSegment(const VP8EncIterator* const it, int segment) {
+ it->mb_->segment_ = segment;
+}
+
+//------------------------------------------------------------------------------
+// Intra4x4 sub-blocks iteration
+//
+// We store and update the boundary samples into an array of 37 pixels. They
+// are updated as we iterate and reconstructs each intra4x4 blocks in turn.
+// The position of the samples has the following snake pattern:
+//
+// 16|17 18 19 20|21 22 23 24|25 26 27 28|29 30 31 32|33 34 35 36 <- Top-right
+// --+-----------+-----------+-----------+-----------+
+// 15| 19| 23| 27| 31|
+// 14| 18| 22| 26| 30|
+// 13| 17| 21| 25| 29|
+// 12|13 14 15 16|17 18 19 20|21 22 23 24|25 26 27 28|
+// --+-----------+-----------+-----------+-----------+
+// 11| 15| 19| 23| 27|
+// 10| 14| 18| 22| 26|
+// 9| 13| 17| 21| 25|
+// 8| 9 10 11 12|13 14 15 16|17 18 19 20|21 22 23 24|
+// --+-----------+-----------+-----------+-----------+
+// 7| 11| 15| 19| 23|
+// 6| 10| 14| 18| 22|
+// 5| 9| 13| 17| 21|
+// 4| 5 6 7 8| 9 10 11 12|13 14 15 16|17 18 19 20|
+// --+-----------+-----------+-----------+-----------+
+// 3| 7| 11| 15| 19|
+// 2| 6| 10| 14| 18|
+// 1| 5| 9| 13| 17|
+// 0| 1 2 3 4| 5 6 7 8| 9 10 11 12|13 14 15 16|
+// --+-----------+-----------+-----------+-----------+
+
+// Array to record the position of the top sample to pass to the prediction
+// functions in dsp.c.
+static const uint8_t VP8TopLeftI4[16] = {
+ 17, 21, 25, 29,
+ 13, 17, 21, 25,
+ 9, 13, 17, 21,
+ 5, 9, 13, 17
+};
+
+void VP8IteratorStartI4(VP8EncIterator* const it) {
+ const VP8Encoder* const enc = it->enc_;
+ int i;
+
+ it->i4_ = 0; // first 4x4 sub-block
+ it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0];
+
+ // Import the boundary samples
+ for (i = 0; i < 17; ++i) { // left
+ it->i4_boundary_[i] = it->y_left_[15 - i];
+ }
+ for (i = 0; i < 16; ++i) { // top
+ it->i4_boundary_[17 + i] = it->y_top_[i];
+ }
+ // top-right samples have a special case on the far right of the picture
+ if (it->x_ < enc->mb_w_ - 1) {
+ for (i = 16; i < 16 + 4; ++i) {
+ it->i4_boundary_[17 + i] = it->y_top_[i];
+ }
+ } else { // else, replicate the last valid pixel four times
+ for (i = 16; i < 16 + 4; ++i) {
+ it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15];
+ }
+ }
+ VP8IteratorNzToBytes(it); // import the non-zero context
+}
+
+int VP8IteratorRotateI4(VP8EncIterator* const it,
+ const uint8_t* const yuv_out) {
+ const uint8_t* const blk = yuv_out + VP8Scan[it->i4_];
+ uint8_t* const top = it->i4_top_;
+ int i;
+
+ // Update the cache with 7 fresh samples
+ for (i = 0; i <= 3; ++i) {
+ top[-4 + i] = blk[i + 3 * BPS]; // store future top samples
+ }
+ if ((it->i4_ & 3) != 3) { // if not on the right sub-blocks #3, #7, #11, #15
+ for (i = 0; i <= 2; ++i) { // store future left samples
+ top[i] = blk[3 + (2 - i) * BPS];
+ }
+ } else { // else replicate top-right samples, as says the specs.
+ for (i = 0; i <= 3; ++i) {
+ top[i] = top[i + 4];
+ }
+ }
+ // move pointers to next sub-block
+ ++it->i4_;
+ if (it->i4_ == 16) { // we're done
+ return 0;
+ }
+
+ it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_];
+ return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/near_lossless_enc.c b/src/third_party/libwebp/src/enc/near_lossless_enc.c
new file mode 100644
index 0000000..1749467
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/near_lossless_enc.c
@@ -0,0 +1,156 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Near-lossless image preprocessing adjusts pixel values to help
+// compressibility with a guarantee of maximum deviation between original and
+// resulting pixel values.
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+// Converted to C by Aleksander Kramarz (akramarz@google.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#endif
+
+#include "src/dsp/lossless_common.h"
+#include "src/utils/utils.h"
+#include "src/enc/vp8li_enc.h"
+
+#if (WEBP_NEAR_LOSSLESS == 1)
+
+#define MIN_DIM_FOR_NEAR_LOSSLESS 64
+#define MAX_LIMIT_BITS 5
+
+// Quantizes the value up or down to a multiple of 1<<bits (or to 255),
+// choosing the closer one, resolving ties using bankers' rounding.
+static uint32_t FindClosestDiscretized(uint32_t a, int bits) {
+ const uint32_t mask = (1u << bits) - 1;
+ const uint32_t biased = a + (mask >> 1) + ((a >> bits) & 1);
+ assert(bits > 0);
+ if (biased > 0xff) return 0xff;
+ return biased & ~mask;
+}
+
+// Applies FindClosestDiscretized to all channels of pixel.
+static uint32_t ClosestDiscretizedArgb(uint32_t a, int bits) {
+ return
+ (FindClosestDiscretized(a >> 24, bits) << 24) |
+ (FindClosestDiscretized((a >> 16) & 0xff, bits) << 16) |
+ (FindClosestDiscretized((a >> 8) & 0xff, bits) << 8) |
+ (FindClosestDiscretized(a & 0xff, bits));
+}
+
+// Checks if distance between corresponding channel values of pixels a and b
+// is within the given limit.
+static int IsNear(uint32_t a, uint32_t b, int limit) {
+ int k;
+ for (k = 0; k < 4; ++k) {
+ const int delta =
+ (int)((a >> (k * 8)) & 0xff) - (int)((b >> (k * 8)) & 0xff);
+ if (delta >= limit || delta <= -limit) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int IsSmooth(const uint32_t* const prev_row,
+ const uint32_t* const curr_row,
+ const uint32_t* const next_row,
+ int ix, int limit) {
+ // Check that all pixels in 4-connected neighborhood are smooth.
+ return (IsNear(curr_row[ix], curr_row[ix - 1], limit) &&
+ IsNear(curr_row[ix], curr_row[ix + 1], limit) &&
+ IsNear(curr_row[ix], prev_row[ix], limit) &&
+ IsNear(curr_row[ix], next_row[ix], limit));
+}
+
+// Adjusts pixel values of image with given maximum error.
+static void NearLossless(int xsize, int ysize, const uint32_t* argb_src,
+ int stride, int limit_bits, uint32_t* copy_buffer,
+ uint32_t* argb_dst) {
+ int x, y;
+ const int limit = 1 << limit_bits;
+ uint32_t* prev_row = copy_buffer;
+ uint32_t* curr_row = prev_row + xsize;
+ uint32_t* next_row = curr_row + xsize;
+ memcpy(curr_row, argb_src, xsize * sizeof(argb_src[0]));
+ memcpy(next_row, argb_src + stride, xsize * sizeof(argb_src[0]));
+
+ for (y = 0; y < ysize; ++y, argb_src += stride, argb_dst += xsize) {
+ if (y == 0 || y == ysize - 1) {
+ memcpy(argb_dst, argb_src, xsize * sizeof(argb_src[0]));
+ } else {
+ memcpy(next_row, argb_src + stride, xsize * sizeof(argb_src[0]));
+ argb_dst[0] = argb_src[0];
+ argb_dst[xsize - 1] = argb_src[xsize - 1];
+ for (x = 1; x < xsize - 1; ++x) {
+ if (IsSmooth(prev_row, curr_row, next_row, x, limit)) {
+ argb_dst[x] = curr_row[x];
+ } else {
+ argb_dst[x] = ClosestDiscretizedArgb(curr_row[x], limit_bits);
+ }
+ }
+ }
+ {
+ // Three-way swap.
+ uint32_t* const temp = prev_row;
+ prev_row = curr_row;
+ curr_row = next_row;
+ next_row = temp;
+ }
+ }
+}
+
+int VP8ApplyNearLossless(const WebPPicture* const picture, int quality,
+ uint32_t* const argb_dst) {
+ int i;
+ const int xsize = picture->width;
+ const int ysize = picture->height;
+ const int stride = picture->argb_stride;
+ uint32_t* const copy_buffer =
+ (uint32_t*)WebPSafeMalloc(xsize * 3, sizeof(*copy_buffer));
+ const int limit_bits = VP8LNearLosslessBits(quality);
+ assert(argb_dst != NULL);
+ assert(limit_bits > 0);
+ assert(limit_bits <= MAX_LIMIT_BITS);
+ if (copy_buffer == NULL) {
+ return 0;
+ }
+ // For small icon images, don't attempt to apply near-lossless compression.
+ if ((xsize < MIN_DIM_FOR_NEAR_LOSSLESS &&
+ ysize < MIN_DIM_FOR_NEAR_LOSSLESS) ||
+ ysize < 3) {
+ for (i = 0; i < ysize; ++i) {
+ memcpy(argb_dst + i * xsize, picture->argb + i * picture->argb_stride,
+ xsize * sizeof(*argb_dst));
+ }
+ WebPSafeFree(copy_buffer);
+ return 1;
+ }
+
+ NearLossless(xsize, ysize, picture->argb, stride, limit_bits, copy_buffer,
+ argb_dst);
+ for (i = limit_bits - 1; i != 0; --i) {
+ NearLossless(xsize, ysize, argb_dst, xsize, i, copy_buffer, argb_dst);
+ }
+ WebPSafeFree(copy_buffer);
+ return 1;
+}
+#else // (WEBP_NEAR_LOSSLESS == 1)
+
+// Define a stub to suppress compiler warnings.
+extern void VP8LNearLosslessStub(void);
+void VP8LNearLosslessStub(void) {}
+
+#endif // (WEBP_NEAR_LOSSLESS == 1)
diff --git a/src/third_party/libwebp/src/enc/picture_csp_enc.c b/src/third_party/libwebp/src/enc/picture_csp_enc.c
new file mode 100644
index 0000000..51a4619
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/picture_csp_enc.c
@@ -0,0 +1,1212 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebPPicture utils for colorspace conversion
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/math_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#include <math.h>
+#endif
+
+#include "src/enc/vp8i_enc.h"
+#include "src/utils/random_utils.h"
+#include "src/utils/utils.h"
+#include "src/dsp/dsp.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/yuv.h"
+
+// Uncomment to disable gamma-compression during RGB->U/V averaging
+#define USE_GAMMA_COMPRESSION
+
+// If defined, use table to compute x / alpha.
+#define USE_INVERSE_ALPHA_TABLE
+
+#ifdef WORDS_BIGENDIAN
+#define ALPHA_OFFSET 0 // uint32_t 0xff000000 is 0xff,00,00,00 in memory
+#else
+#define ALPHA_OFFSET 3 // uint32_t 0xff000000 is 0x00,00,00,ff in memory
+#endif
+
+//------------------------------------------------------------------------------
+// Detection of non-trivial transparency
+
+// Returns true if alpha[] has non-0xff values.
+static int CheckNonOpaque(const uint8_t* alpha, int width, int height,
+ int x_step, int y_step) {
+ if (alpha == NULL) return 0;
+ WebPInitAlphaProcessing();
+ if (x_step == 1) {
+ for (; height-- > 0; alpha += y_step) {
+ if (WebPHasAlpha8b(alpha, width)) return 1;
+ }
+ } else {
+ for (; height-- > 0; alpha += y_step) {
+ if (WebPHasAlpha32b(alpha, width)) return 1;
+ }
+ }
+ return 0;
+}
+
+// Checking for the presence of non-opaque alpha.
+int WebPPictureHasTransparency(const WebPPicture* picture) {
+ if (picture == NULL) return 0;
+ if (!picture->use_argb) {
+ return CheckNonOpaque(picture->a, picture->width, picture->height,
+ 1, picture->a_stride);
+ } else {
+ const int alpha_offset = ALPHA_OFFSET;
+ return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset,
+ picture->width, picture->height,
+ 4, picture->argb_stride * sizeof(*picture->argb));
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// Code for gamma correction
+
+#if defined(USE_GAMMA_COMPRESSION)
+
+// gamma-compensates loss of resolution during chroma subsampling
+#define kGamma 0.80 // for now we use a different gamma value than kGammaF
+#define kGammaFix 12 // fixed-point precision for linear values
+#define kGammaScale ((1 << kGammaFix) - 1)
+#define kGammaTabFix 7 // fixed-point fractional bits precision
+#define kGammaTabScale (1 << kGammaTabFix)
+#define kGammaTabRounder (kGammaTabScale >> 1)
+#define kGammaTabSize (1 << (kGammaFix - kGammaTabFix))
+
+static int kLinearToGammaTab[kGammaTabSize + 1];
+static uint16_t kGammaToLinearTab[256];
+static volatile int kGammaTablesOk = 0;
+
+static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTables(void) {
+ if (!kGammaTablesOk) {
+ int v;
+ const double scale = (double)(1 << kGammaTabFix) / kGammaScale;
+ const double norm = 1. / 255.;
+ for (v = 0; v <= 255; ++v) {
+ kGammaToLinearTab[v] =
+ (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5);
+ }
+ for (v = 0; v <= kGammaTabSize; ++v) {
+ kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5);
+ }
+ kGammaTablesOk = 1;
+ }
+}
+
+static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) {
+ return kGammaToLinearTab[v];
+}
+
+static WEBP_INLINE int Interpolate(int v) {
+ const int tab_pos = v >> (kGammaTabFix + 2); // integer part
+ const int x = v & ((kGammaTabScale << 2) - 1); // fractional part
+ const int v0 = kLinearToGammaTab[tab_pos];
+ const int v1 = kLinearToGammaTab[tab_pos + 1];
+ const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x); // interpolate
+ assert(tab_pos + 1 < kGammaTabSize + 1);
+ return y;
+}
+
+// Convert a linear value 'v' to YUV_FIX+2 fixed-point precision
+// U/V value, suitable for RGBToU/V calls.
+static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
+ const int y = Interpolate(base_value << shift); // final uplifted value
+ return (y + kGammaTabRounder) >> kGammaTabFix; // descale
+}
+
+#else
+
+static void InitGammaTables(void) {}
+static WEBP_INLINE uint32_t GammaToLinear(uint8_t v) { return v; }
+static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
+ return (int)(base_value << shift);
+}
+
+#endif // USE_GAMMA_COMPRESSION
+
+//------------------------------------------------------------------------------
+// RGB -> YUV conversion
+
+static int RGBToY(int r, int g, int b, VP8Random* const rg) {
+ return (rg == NULL) ? VP8RGBToY(r, g, b, YUV_HALF)
+ : VP8RGBToY(r, g, b, VP8RandomBits(rg, YUV_FIX));
+}
+
+static int RGBToU(int r, int g, int b, VP8Random* const rg) {
+ return (rg == NULL) ? VP8RGBToU(r, g, b, YUV_HALF << 2)
+ : VP8RGBToU(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
+}
+
+static int RGBToV(int r, int g, int b, VP8Random* const rg) {
+ return (rg == NULL) ? VP8RGBToV(r, g, b, YUV_HALF << 2)
+ : VP8RGBToV(r, g, b, VP8RandomBits(rg, YUV_FIX + 2));
+}
+
+//------------------------------------------------------------------------------
+// Sharp RGB->YUV conversion
+
+static const int kNumIterations = 4;
+static const int kMinDimensionIterativeConversion = 4;
+
+// We could use SFIX=0 and only uint8_t for fixed_y_t, but it produces some
+// banding sometimes. Better use extra precision.
+#define SFIX 2 // fixed-point precision of RGB and Y/W
+typedef int16_t fixed_t; // signed type with extra SFIX precision for UV
+typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W
+
+#define SHALF (1 << SFIX >> 1)
+#define MAX_Y_T ((256 << SFIX) - 1)
+#define SROUNDER (1 << (YUV_FIX + SFIX - 1))
+
+#if defined(USE_GAMMA_COMPRESSION)
+
+// We use tables of different size and precision for the Rec709 / BT2020
+// transfer function.
+#define kGammaF (1./0.45)
+static uint32_t kLinearToGammaTabS[kGammaTabSize + 2];
+#define GAMMA_TO_LINEAR_BITS 14
+static uint32_t kGammaToLinearTabS[MAX_Y_T + 1]; // size scales with Y_FIX
+static volatile int kGammaTablesSOk = 0;
+
+static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesS(void) {
+ assert(2 * GAMMA_TO_LINEAR_BITS < 32); // we use uint32_t intermediate values
+ if (!kGammaTablesSOk) {
+ int v;
+ const double norm = 1. / MAX_Y_T;
+ const double scale = 1. / kGammaTabSize;
+ const double a = 0.09929682680944;
+ const double thresh = 0.018053968510807;
+ const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
+ for (v = 0; v <= MAX_Y_T; ++v) {
+ const double g = norm * v;
+ double value;
+ if (g <= thresh * 4.5) {
+ value = g / 4.5;
+ } else {
+ const double a_rec = 1. / (1. + a);
+ value = pow(a_rec * (g + a), kGammaF);
+ }
+ kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
+ }
+ for (v = 0; v <= kGammaTabSize; ++v) {
+ const double g = scale * v;
+ double value;
+ if (g <= thresh) {
+ value = 4.5 * g;
+ } else {
+ value = (1. + a) * pow(g, 1. / kGammaF) - a;
+ }
+ // we already incorporate the 1/2 rounding constant here
+ kLinearToGammaTabS[v] =
+ (uint32_t)(MAX_Y_T * value) + (1 << GAMMA_TO_LINEAR_BITS >> 1);
+ }
+ // to prevent small rounding errors to cause read-overflow:
+ kLinearToGammaTabS[kGammaTabSize + 1] = kLinearToGammaTabS[kGammaTabSize];
+ kGammaTablesSOk = 1;
+ }
+}
+
+// return value has a fixed-point precision of GAMMA_TO_LINEAR_BITS
+static WEBP_INLINE uint32_t GammaToLinearS(int v) {
+ return kGammaToLinearTabS[v];
+}
+
+static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
+ // 'value' is in GAMMA_TO_LINEAR_BITS fractional precision
+ const uint32_t v = value * kGammaTabSize;
+ const uint32_t tab_pos = v >> GAMMA_TO_LINEAR_BITS;
+ // fractional part, in GAMMA_TO_LINEAR_BITS fixed-point precision
+ const uint32_t x = v - (tab_pos << GAMMA_TO_LINEAR_BITS); // fractional part
+ // v0 / v1 are in GAMMA_TO_LINEAR_BITS fixed-point precision (range [0..1])
+ const uint32_t v0 = kLinearToGammaTabS[tab_pos + 0];
+ const uint32_t v1 = kLinearToGammaTabS[tab_pos + 1];
+ // Final interpolation. Note that rounding is already included.
+ const uint32_t v2 = (v1 - v0) * x; // note: v1 >= v0.
+ const uint32_t result = v0 + (v2 >> GAMMA_TO_LINEAR_BITS);
+ return result;
+}
+
+#else
+
+static void InitGammaTablesS(void) {}
+static WEBP_INLINE uint32_t GammaToLinearS(int v) {
+ return (v << GAMMA_TO_LINEAR_BITS) / MAX_Y_T;
+}
+static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
+ return (MAX_Y_T * value) >> GAMMA_TO_LINEAR_BITS;
+}
+
+#endif // USE_GAMMA_COMPRESSION
+
+//------------------------------------------------------------------------------
+
+static uint8_t clip_8b(fixed_t v) {
+ return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
+}
+
+static fixed_y_t clip_y(int y) {
+ return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T;
+}
+
+//------------------------------------------------------------------------------
+
+static int RGBToGray(int r, int g, int b) {
+ const int luma = 13933 * r + 46871 * g + 4732 * b + YUV_HALF;
+ return (luma >> YUV_FIX);
+}
+
+static uint32_t ScaleDown(int a, int b, int c, int d) {
+ const uint32_t A = GammaToLinearS(a);
+ const uint32_t B = GammaToLinearS(b);
+ const uint32_t C = GammaToLinearS(c);
+ const uint32_t D = GammaToLinearS(d);
+ return LinearToGammaS((A + B + C + D + 2) >> 2);
+}
+
+static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w) {
+ int i;
+ for (i = 0; i < w; ++i) {
+ const uint32_t R = GammaToLinearS(src[0 * w + i]);
+ const uint32_t G = GammaToLinearS(src[1 * w + i]);
+ const uint32_t B = GammaToLinearS(src[2 * w + i]);
+ const uint32_t Y = RGBToGray(R, G, B);
+ dst[i] = (fixed_y_t)LinearToGammaS(Y);
+ }
+}
+
+static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
+ fixed_t* dst, int uv_w) {
+ int i;
+ for (i = 0; i < uv_w; ++i) {
+ const int r = ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1],
+ src2[0 * uv_w + 0], src2[0 * uv_w + 1]);
+ const int g = ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1],
+ src2[2 * uv_w + 0], src2[2 * uv_w + 1]);
+ const int b = ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1],
+ src2[4 * uv_w + 0], src2[4 * uv_w + 1]);
+ const int W = RGBToGray(r, g, b);
+ dst[0 * uv_w] = (fixed_t)(r - W);
+ dst[1 * uv_w] = (fixed_t)(g - W);
+ dst[2 * uv_w] = (fixed_t)(b - W);
+ dst += 1;
+ src1 += 2;
+ src2 += 2;
+ }
+}
+
+static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
+ int i;
+ for (i = 0; i < w; ++i) {
+ y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0) {
+ const int v0 = (A * 3 + B + 2) >> 2;
+ return clip_y(v0 + W0);
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE fixed_y_t UpLift(uint8_t a) { // 8bit -> SFIX
+ return ((fixed_y_t)a << SFIX) | SHALF;
+}
+
+static void ImportOneRow(const uint8_t* const r_ptr,
+ const uint8_t* const g_ptr,
+ const uint8_t* const b_ptr,
+ int step,
+ int pic_width,
+ fixed_y_t* const dst) {
+ int i;
+ const int w = (pic_width + 1) & ~1;
+ for (i = 0; i < pic_width; ++i) {
+ const int off = i * step;
+ dst[i + 0 * w] = UpLift(r_ptr[off]);
+ dst[i + 1 * w] = UpLift(g_ptr[off]);
+ dst[i + 2 * w] = UpLift(b_ptr[off]);
+ }
+ if (pic_width & 1) { // replicate rightmost pixel
+ dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
+ dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
+ dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
+ }
+}
+
+static void InterpolateTwoRows(const fixed_y_t* const best_y,
+ const fixed_t* prev_uv,
+ const fixed_t* cur_uv,
+ const fixed_t* next_uv,
+ int w,
+ fixed_y_t* out1,
+ fixed_y_t* out2) {
+ const int uv_w = w >> 1;
+ const int len = (w - 1) >> 1; // length to filter
+ int k = 3;
+ while (k-- > 0) { // process each R/G/B segments in turn
+ // special boundary case for i==0
+ out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0]);
+ out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w]);
+
+ WebPSharpYUVFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1);
+ WebPSharpYUVFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1);
+
+ // special boundary case for i == w - 1 when w is even
+ if (!(w & 1)) {
+ out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
+ best_y[w - 1 + 0]);
+ out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
+ best_y[w - 1 + w]);
+ }
+ out1 += w;
+ out2 += w;
+ prev_uv += uv_w;
+ cur_uv += uv_w;
+ next_uv += uv_w;
+ }
+}
+
+static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) {
+ const int luma = 16839 * r + 33059 * g + 6420 * b + SROUNDER;
+ return clip_8b(16 + (luma >> (YUV_FIX + SFIX)));
+}
+
+static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) {
+ const int u = -9719 * r - 19081 * g + 28800 * b + SROUNDER;
+ return clip_8b(128 + (u >> (YUV_FIX + SFIX)));
+}
+
+static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) {
+ const int v = +28800 * r - 24116 * g - 4684 * b + SROUNDER;
+ return clip_8b(128 + (v >> (YUV_FIX + SFIX)));
+}
+
+static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
+ WebPPicture* const picture) {
+ int i, j;
+ uint8_t* dst_y = picture->y;
+ uint8_t* dst_u = picture->u;
+ uint8_t* dst_v = picture->v;
+ const fixed_t* const best_uv_base = best_uv;
+ const int w = (picture->width + 1) & ~1;
+ const int h = (picture->height + 1) & ~1;
+ const int uv_w = w >> 1;
+ const int uv_h = h >> 1;
+ for (best_uv = best_uv_base, j = 0; j < picture->height; ++j) {
+ for (i = 0; i < picture->width; ++i) {
+ const int off = (i >> 1);
+ const int W = best_y[i];
+ const int r = best_uv[off + 0 * uv_w] + W;
+ const int g = best_uv[off + 1 * uv_w] + W;
+ const int b = best_uv[off + 2 * uv_w] + W;
+ dst_y[i] = ConvertRGBToY(r, g, b);
+ }
+ best_y += w;
+ best_uv += (j & 1) * 3 * uv_w;
+ dst_y += picture->y_stride;
+ }
+ for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
+ for (i = 0; i < uv_w; ++i) {
+ const int off = i;
+ const int r = best_uv[off + 0 * uv_w];
+ const int g = best_uv[off + 1 * uv_w];
+ const int b = best_uv[off + 2 * uv_w];
+ dst_u[i] = ConvertRGBToU(r, g, b);
+ dst_v[i] = ConvertRGBToV(r, g, b);
+ }
+ best_uv += 3 * uv_w;
+ dst_u += picture->uv_stride;
+ dst_v += picture->uv_stride;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Main function
+
+#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((W) * (H), sizeof(T)))
+
+static int PreprocessARGB(const uint8_t* r_ptr,
+ const uint8_t* g_ptr,
+ const uint8_t* b_ptr,
+ int step, int rgb_stride,
+ WebPPicture* const picture) {
+ // we expand the right/bottom border if needed
+ const int w = (picture->width + 1) & ~1;
+ const int h = (picture->height + 1) & ~1;
+ const int uv_w = w >> 1;
+ const int uv_h = h >> 1;
+ uint64_t prev_diff_y_sum = ~0;
+ int j, iter;
+
+ // TODO(skal): allocate one big memory chunk. But for now, it's easier
+ // for valgrind debugging to have several chunks.
+ fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t); // scratch
+ fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
+ fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
+ fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
+ fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
+ fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
+ fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
+ fixed_y_t* best_y = best_y_base;
+ fixed_y_t* target_y = target_y_base;
+ fixed_t* best_uv = best_uv_base;
+ fixed_t* target_uv = target_uv_base;
+ const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
+ int ok;
+
+ if (best_y_base == NULL || best_uv_base == NULL ||
+ target_y_base == NULL || target_uv_base == NULL ||
+ best_rgb_y == NULL || best_rgb_uv == NULL ||
+ tmp_buffer == NULL) {
+ ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ goto End;
+ }
+ assert(picture->width >= kMinDimensionIterativeConversion);
+ assert(picture->height >= kMinDimensionIterativeConversion);
+
+ WebPInitConvertARGBToYUV();
+
+ // Import RGB samples to W/RGB representation.
+ for (j = 0; j < picture->height; j += 2) {
+ const int is_last_row = (j == picture->height - 1);
+ fixed_y_t* const src1 = tmp_buffer + 0 * w;
+ fixed_y_t* const src2 = tmp_buffer + 3 * w;
+
+ // prepare two rows of input
+ ImportOneRow(r_ptr, g_ptr, b_ptr, step, picture->width, src1);
+ if (!is_last_row) {
+ ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
+ step, picture->width, src2);
+ } else {
+ memcpy(src2, src1, 3 * w * sizeof(*src2));
+ }
+ StoreGray(src1, best_y + 0, w);
+ StoreGray(src2, best_y + w, w);
+
+ UpdateW(src1, target_y, w);
+ UpdateW(src2, target_y + w, w);
+ UpdateChroma(src1, src2, target_uv, uv_w);
+ memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
+ best_y += 2 * w;
+ best_uv += 3 * uv_w;
+ target_y += 2 * w;
+ target_uv += 3 * uv_w;
+ r_ptr += 2 * rgb_stride;
+ g_ptr += 2 * rgb_stride;
+ b_ptr += 2 * rgb_stride;
+ }
+
+ // Iterate and resolve clipping conflicts.
+ for (iter = 0; iter < kNumIterations; ++iter) {
+ const fixed_t* cur_uv = best_uv_base;
+ const fixed_t* prev_uv = best_uv_base;
+ uint64_t diff_y_sum = 0;
+
+ best_y = best_y_base;
+ best_uv = best_uv_base;
+ target_y = target_y_base;
+ target_uv = target_uv_base;
+ for (j = 0; j < h; j += 2) {
+ fixed_y_t* const src1 = tmp_buffer + 0 * w;
+ fixed_y_t* const src2 = tmp_buffer + 3 * w;
+ {
+ const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
+ InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2);
+ prev_uv = cur_uv;
+ cur_uv = next_uv;
+ }
+
+ UpdateW(src1, best_rgb_y + 0 * w, w);
+ UpdateW(src2, best_rgb_y + 1 * w, w);
+ UpdateChroma(src1, src2, best_rgb_uv, uv_w);
+
+ // update two rows of Y and one row of RGB
+ diff_y_sum += WebPSharpYUVUpdateY(target_y, best_rgb_y, best_y, 2 * w);
+ WebPSharpYUVUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
+
+ best_y += 2 * w;
+ best_uv += 3 * uv_w;
+ target_y += 2 * w;
+ target_uv += 3 * uv_w;
+ }
+ // test exit condition
+ if (iter > 0) {
+ if (diff_y_sum < diff_y_threshold) break;
+ if (diff_y_sum > prev_diff_y_sum) break;
+ }
+ prev_diff_y_sum = diff_y_sum;
+ }
+ // final reconstruction
+ ok = ConvertWRGBToYUV(best_y_base, best_uv_base, picture);
+
+ End:
+ WebPSafeFree(best_y_base);
+ WebPSafeFree(best_uv_base);
+ WebPSafeFree(target_y_base);
+ WebPSafeFree(target_uv_base);
+ WebPSafeFree(best_rgb_y);
+ WebPSafeFree(best_rgb_uv);
+ WebPSafeFree(tmp_buffer);
+ return ok;
+}
+#undef SAFE_ALLOC
+
+//------------------------------------------------------------------------------
+// "Fast" regular RGB->YUV
+
+#define SUM4(ptr, step) LinearToGamma( \
+ GammaToLinear((ptr)[0]) + \
+ GammaToLinear((ptr)[(step)]) + \
+ GammaToLinear((ptr)[rgb_stride]) + \
+ GammaToLinear((ptr)[rgb_stride + (step)]), 0) \
+
+#define SUM2(ptr) \
+ LinearToGamma(GammaToLinear((ptr)[0]) + GammaToLinear((ptr)[rgb_stride]), 1)
+
+#define SUM2ALPHA(ptr) ((ptr)[0] + (ptr)[rgb_stride])
+#define SUM4ALPHA(ptr) (SUM2ALPHA(ptr) + SUM2ALPHA((ptr) + 4))
+
+#if defined(USE_INVERSE_ALPHA_TABLE)
+
+static const int kAlphaFix = 19;
+// Following table is (1 << kAlphaFix) / a. The (v * kInvAlpha[a]) >> kAlphaFix
+// formula is then equal to v / a in most (99.6%) cases. Note that this table
+// and constant are adjusted very tightly to fit 32b arithmetic.
+// In particular, they use the fact that the operands for 'v / a' are actually
+// derived as v = (a0.p0 + a1.p1 + a2.p2 + a3.p3) and a = a0 + a1 + a2 + a3
+// with ai in [0..255] and pi in [0..1<<kGammaFix). The constraint to avoid
+// overflow is: kGammaFix + kAlphaFix <= 31.
+static const uint32_t kInvAlpha[4 * 0xff + 1] = {
+ 0, /* alpha = 0 */
+ 524288, 262144, 174762, 131072, 104857, 87381, 74898, 65536,
+ 58254, 52428, 47662, 43690, 40329, 37449, 34952, 32768,
+ 30840, 29127, 27594, 26214, 24966, 23831, 22795, 21845,
+ 20971, 20164, 19418, 18724, 18078, 17476, 16912, 16384,
+ 15887, 15420, 14979, 14563, 14169, 13797, 13443, 13107,
+ 12787, 12483, 12192, 11915, 11650, 11397, 11155, 10922,
+ 10699, 10485, 10280, 10082, 9892, 9709, 9532, 9362,
+ 9198, 9039, 8886, 8738, 8594, 8456, 8322, 8192,
+ 8065, 7943, 7825, 7710, 7598, 7489, 7384, 7281,
+ 7182, 7084, 6990, 6898, 6808, 6721, 6636, 6553,
+ 6472, 6393, 6316, 6241, 6168, 6096, 6026, 5957,
+ 5890, 5825, 5761, 5698, 5637, 5577, 5518, 5461,
+ 5405, 5349, 5295, 5242, 5190, 5140, 5090, 5041,
+ 4993, 4946, 4899, 4854, 4809, 4766, 4723, 4681,
+ 4639, 4599, 4559, 4519, 4481, 4443, 4405, 4369,
+ 4332, 4297, 4262, 4228, 4194, 4161, 4128, 4096,
+ 4064, 4032, 4002, 3971, 3942, 3912, 3883, 3855,
+ 3826, 3799, 3771, 3744, 3718, 3692, 3666, 3640,
+ 3615, 3591, 3566, 3542, 3518, 3495, 3472, 3449,
+ 3426, 3404, 3382, 3360, 3339, 3318, 3297, 3276,
+ 3256, 3236, 3216, 3196, 3177, 3158, 3139, 3120,
+ 3102, 3084, 3066, 3048, 3030, 3013, 2995, 2978,
+ 2962, 2945, 2928, 2912, 2896, 2880, 2864, 2849,
+ 2833, 2818, 2803, 2788, 2774, 2759, 2744, 2730,
+ 2716, 2702, 2688, 2674, 2661, 2647, 2634, 2621,
+ 2608, 2595, 2582, 2570, 2557, 2545, 2532, 2520,
+ 2508, 2496, 2484, 2473, 2461, 2449, 2438, 2427,
+ 2416, 2404, 2394, 2383, 2372, 2361, 2351, 2340,
+ 2330, 2319, 2309, 2299, 2289, 2279, 2269, 2259,
+ 2250, 2240, 2231, 2221, 2212, 2202, 2193, 2184,
+ 2175, 2166, 2157, 2148, 2139, 2131, 2122, 2114,
+ 2105, 2097, 2088, 2080, 2072, 2064, 2056, 2048,
+ 2040, 2032, 2024, 2016, 2008, 2001, 1993, 1985,
+ 1978, 1971, 1963, 1956, 1949, 1941, 1934, 1927,
+ 1920, 1913, 1906, 1899, 1892, 1885, 1879, 1872,
+ 1865, 1859, 1852, 1846, 1839, 1833, 1826, 1820,
+ 1814, 1807, 1801, 1795, 1789, 1783, 1777, 1771,
+ 1765, 1759, 1753, 1747, 1741, 1736, 1730, 1724,
+ 1718, 1713, 1707, 1702, 1696, 1691, 1685, 1680,
+ 1675, 1669, 1664, 1659, 1653, 1648, 1643, 1638,
+ 1633, 1628, 1623, 1618, 1613, 1608, 1603, 1598,
+ 1593, 1588, 1583, 1579, 1574, 1569, 1565, 1560,
+ 1555, 1551, 1546, 1542, 1537, 1533, 1528, 1524,
+ 1519, 1515, 1510, 1506, 1502, 1497, 1493, 1489,
+ 1485, 1481, 1476, 1472, 1468, 1464, 1460, 1456,
+ 1452, 1448, 1444, 1440, 1436, 1432, 1428, 1424,
+ 1420, 1416, 1413, 1409, 1405, 1401, 1398, 1394,
+ 1390, 1387, 1383, 1379, 1376, 1372, 1368, 1365,
+ 1361, 1358, 1354, 1351, 1347, 1344, 1340, 1337,
+ 1334, 1330, 1327, 1323, 1320, 1317, 1314, 1310,
+ 1307, 1304, 1300, 1297, 1294, 1291, 1288, 1285,
+ 1281, 1278, 1275, 1272, 1269, 1266, 1263, 1260,
+ 1257, 1254, 1251, 1248, 1245, 1242, 1239, 1236,
+ 1233, 1230, 1227, 1224, 1222, 1219, 1216, 1213,
+ 1210, 1208, 1205, 1202, 1199, 1197, 1194, 1191,
+ 1188, 1186, 1183, 1180, 1178, 1175, 1172, 1170,
+ 1167, 1165, 1162, 1159, 1157, 1154, 1152, 1149,
+ 1147, 1144, 1142, 1139, 1137, 1134, 1132, 1129,
+ 1127, 1125, 1122, 1120, 1117, 1115, 1113, 1110,
+ 1108, 1106, 1103, 1101, 1099, 1096, 1094, 1092,
+ 1089, 1087, 1085, 1083, 1081, 1078, 1076, 1074,
+ 1072, 1069, 1067, 1065, 1063, 1061, 1059, 1057,
+ 1054, 1052, 1050, 1048, 1046, 1044, 1042, 1040,
+ 1038, 1036, 1034, 1032, 1030, 1028, 1026, 1024,
+ 1022, 1020, 1018, 1016, 1014, 1012, 1010, 1008,
+ 1006, 1004, 1002, 1000, 998, 996, 994, 992,
+ 991, 989, 987, 985, 983, 981, 979, 978,
+ 976, 974, 972, 970, 969, 967, 965, 963,
+ 961, 960, 958, 956, 954, 953, 951, 949,
+ 948, 946, 944, 942, 941, 939, 937, 936,
+ 934, 932, 931, 929, 927, 926, 924, 923,
+ 921, 919, 918, 916, 914, 913, 911, 910,
+ 908, 907, 905, 903, 902, 900, 899, 897,
+ 896, 894, 893, 891, 890, 888, 887, 885,
+ 884, 882, 881, 879, 878, 876, 875, 873,
+ 872, 870, 869, 868, 866, 865, 863, 862,
+ 860, 859, 858, 856, 855, 853, 852, 851,
+ 849, 848, 846, 845, 844, 842, 841, 840,
+ 838, 837, 836, 834, 833, 832, 830, 829,
+ 828, 826, 825, 824, 823, 821, 820, 819,
+ 817, 816, 815, 814, 812, 811, 810, 809,
+ 807, 806, 805, 804, 802, 801, 800, 799,
+ 798, 796, 795, 794, 793, 791, 790, 789,
+ 788, 787, 786, 784, 783, 782, 781, 780,
+ 779, 777, 776, 775, 774, 773, 772, 771,
+ 769, 768, 767, 766, 765, 764, 763, 762,
+ 760, 759, 758, 757, 756, 755, 754, 753,
+ 752, 751, 750, 748, 747, 746, 745, 744,
+ 743, 742, 741, 740, 739, 738, 737, 736,
+ 735, 734, 733, 732, 731, 730, 729, 728,
+ 727, 726, 725, 724, 723, 722, 721, 720,
+ 719, 718, 717, 716, 715, 714, 713, 712,
+ 711, 710, 709, 708, 707, 706, 705, 704,
+ 703, 702, 701, 700, 699, 699, 698, 697,
+ 696, 695, 694, 693, 692, 691, 690, 689,
+ 688, 688, 687, 686, 685, 684, 683, 682,
+ 681, 680, 680, 679, 678, 677, 676, 675,
+ 674, 673, 673, 672, 671, 670, 669, 668,
+ 667, 667, 666, 665, 664, 663, 662, 661,
+ 661, 660, 659, 658, 657, 657, 656, 655,
+ 654, 653, 652, 652, 651, 650, 649, 648,
+ 648, 647, 646, 645, 644, 644, 643, 642,
+ 641, 640, 640, 639, 638, 637, 637, 636,
+ 635, 634, 633, 633, 632, 631, 630, 630,
+ 629, 628, 627, 627, 626, 625, 624, 624,
+ 623, 622, 621, 621, 620, 619, 618, 618,
+ 617, 616, 616, 615, 614, 613, 613, 612,
+ 611, 611, 610, 609, 608, 608, 607, 606,
+ 606, 605, 604, 604, 603, 602, 601, 601,
+ 600, 599, 599, 598, 597, 597, 596, 595,
+ 595, 594, 593, 593, 592, 591, 591, 590,
+ 589, 589, 588, 587, 587, 586, 585, 585,
+ 584, 583, 583, 582, 581, 581, 580, 579,
+ 579, 578, 578, 577, 576, 576, 575, 574,
+ 574, 573, 572, 572, 571, 571, 570, 569,
+ 569, 568, 568, 567, 566, 566, 565, 564,
+ 564, 563, 563, 562, 561, 561, 560, 560,
+ 559, 558, 558, 557, 557, 556, 555, 555,
+ 554, 554, 553, 553, 552, 551, 551, 550,
+ 550, 549, 548, 548, 547, 547, 546, 546,
+ 545, 544, 544, 543, 543, 542, 542, 541,
+ 541, 540, 539, 539, 538, 538, 537, 537,
+ 536, 536, 535, 534, 534, 533, 533, 532,
+ 532, 531, 531, 530, 530, 529, 529, 528,
+ 527, 527, 526, 526, 525, 525, 524, 524,
+ 523, 523, 522, 522, 521, 521, 520, 520,
+ 519, 519, 518, 518, 517, 517, 516, 516,
+ 515, 515, 514, 514
+};
+
+// Note that LinearToGamma() expects the values to be premultiplied by 4,
+// so we incorporate this factor 4 inside the DIVIDE_BY_ALPHA macro directly.
+#define DIVIDE_BY_ALPHA(sum, a) (((sum) * kInvAlpha[(a)]) >> (kAlphaFix - 2))
+
+#else
+
+#define DIVIDE_BY_ALPHA(sum, a) (4 * (sum) / (a))
+
+#endif // USE_INVERSE_ALPHA_TABLE
+
+static WEBP_INLINE int LinearToGammaWeighted(const uint8_t* src,
+ const uint8_t* a_ptr,
+ uint32_t total_a, int step,
+ int rgb_stride) {
+ const uint32_t sum =
+ a_ptr[0] * GammaToLinear(src[0]) +
+ a_ptr[step] * GammaToLinear(src[step]) +
+ a_ptr[rgb_stride] * GammaToLinear(src[rgb_stride]) +
+ a_ptr[rgb_stride + step] * GammaToLinear(src[rgb_stride + step]);
+ assert(total_a > 0 && total_a <= 4 * 0xff);
+#if defined(USE_INVERSE_ALPHA_TABLE)
+ assert((uint64_t)sum * kInvAlpha[total_a] < ((uint64_t)1 << 32));
+#endif
+ return LinearToGamma(DIVIDE_BY_ALPHA(sum, total_a), 0);
+}
+
+static WEBP_INLINE void ConvertRowToY(const uint8_t* const r_ptr,
+ const uint8_t* const g_ptr,
+ const uint8_t* const b_ptr,
+ int step,
+ uint8_t* const dst_y,
+ int width,
+ VP8Random* const rg) {
+ int i, j;
+ for (i = 0, j = 0; i < width; i += 1, j += step) {
+ dst_y[i] = RGBToY(r_ptr[j], g_ptr[j], b_ptr[j], rg);
+ }
+}
+
+static WEBP_INLINE void AccumulateRGBA(const uint8_t* const r_ptr,
+ const uint8_t* const g_ptr,
+ const uint8_t* const b_ptr,
+ const uint8_t* const a_ptr,
+ int rgb_stride,
+ uint16_t* dst, int width) {
+ int i, j;
+ // we loop over 2x2 blocks and produce one R/G/B/A value for each.
+ for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * 4, dst += 4) {
+ const uint32_t a = SUM4ALPHA(a_ptr + j);
+ int r, g, b;
+ if (a == 4 * 0xff || a == 0) {
+ r = SUM4(r_ptr + j, 4);
+ g = SUM4(g_ptr + j, 4);
+ b = SUM4(b_ptr + j, 4);
+ } else {
+ r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 4, rgb_stride);
+ g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 4, rgb_stride);
+ b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 4, rgb_stride);
+ }
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+ dst[3] = a;
+ }
+ if (width & 1) {
+ const uint32_t a = 2u * SUM2ALPHA(a_ptr + j);
+ int r, g, b;
+ if (a == 4 * 0xff || a == 0) {
+ r = SUM2(r_ptr + j);
+ g = SUM2(g_ptr + j);
+ b = SUM2(b_ptr + j);
+ } else {
+ r = LinearToGammaWeighted(r_ptr + j, a_ptr + j, a, 0, rgb_stride);
+ g = LinearToGammaWeighted(g_ptr + j, a_ptr + j, a, 0, rgb_stride);
+ b = LinearToGammaWeighted(b_ptr + j, a_ptr + j, a, 0, rgb_stride);
+ }
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+ dst[3] = a;
+ }
+}
+
+static WEBP_INLINE void AccumulateRGB(const uint8_t* const r_ptr,
+ const uint8_t* const g_ptr,
+ const uint8_t* const b_ptr,
+ int step, int rgb_stride,
+ uint16_t* dst, int width) {
+ int i, j;
+ for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2 * step, dst += 4) {
+ dst[0] = SUM4(r_ptr + j, step);
+ dst[1] = SUM4(g_ptr + j, step);
+ dst[2] = SUM4(b_ptr + j, step);
+ }
+ if (width & 1) {
+ dst[0] = SUM2(r_ptr + j);
+ dst[1] = SUM2(g_ptr + j);
+ dst[2] = SUM2(b_ptr + j);
+ }
+}
+
+static WEBP_INLINE void ConvertRowsToUV(const uint16_t* rgb,
+ uint8_t* const dst_u,
+ uint8_t* const dst_v,
+ int width,
+ VP8Random* const rg) {
+ int i;
+ for (i = 0; i < width; i += 1, rgb += 4) {
+ const int r = rgb[0], g = rgb[1], b = rgb[2];
+ dst_u[i] = RGBToU(r, g, b, rg);
+ dst_v[i] = RGBToV(r, g, b, rg);
+ }
+}
+
+static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
+ const uint8_t* g_ptr,
+ const uint8_t* b_ptr,
+ const uint8_t* a_ptr,
+ int step, // bytes per pixel
+ int rgb_stride, // bytes per scanline
+ float dithering,
+ int use_iterative_conversion,
+ WebPPicture* const picture) {
+ int y;
+ const int width = picture->width;
+ const int height = picture->height;
+ const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride);
+ const int is_rgb = (r_ptr < b_ptr); // otherwise it's bgr
+
+ picture->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
+ picture->use_argb = 0;
+
+ // disable smart conversion if source is too small (overkill).
+ if (width < kMinDimensionIterativeConversion ||
+ height < kMinDimensionIterativeConversion) {
+ use_iterative_conversion = 0;
+ }
+
+ if (!WebPPictureAllocYUVA(picture, width, height)) {
+ return 0;
+ }
+ if (has_alpha) {
+ assert(step == 4);
+#if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE)
+ assert(kAlphaFix + kGammaFix <= 31);
+#endif
+ }
+
+ if (use_iterative_conversion) {
+ InitGammaTablesS();
+ if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
+ return 0;
+ }
+ if (has_alpha) {
+ WebPExtractAlpha(a_ptr, rgb_stride, width, height,
+ picture->a, picture->a_stride);
+ }
+ } else {
+ const int uv_width = (width + 1) >> 1;
+ int use_dsp = (step == 3); // use special function in this case
+ // temporary storage for accumulated R/G/B values during conversion to U/V
+ uint16_t* const tmp_rgb =
+ (uint16_t*)WebPSafeMalloc(4 * uv_width, sizeof(*tmp_rgb));
+ uint8_t* dst_y = picture->y;
+ uint8_t* dst_u = picture->u;
+ uint8_t* dst_v = picture->v;
+ uint8_t* dst_a = picture->a;
+
+ VP8Random base_rg;
+ VP8Random* rg = NULL;
+ if (dithering > 0.) {
+ VP8InitRandom(&base_rg, dithering);
+ rg = &base_rg;
+ use_dsp = 0; // can't use dsp in this case
+ }
+ WebPInitConvertARGBToYUV();
+ InitGammaTables();
+
+ if (tmp_rgb == NULL) return 0; // malloc error
+
+ // Downsample Y/U/V planes, two rows at a time
+ for (y = 0; y < (height >> 1); ++y) {
+ int rows_have_alpha = has_alpha;
+ if (use_dsp) {
+ if (is_rgb) {
+ WebPConvertRGB24ToY(r_ptr, dst_y, width);
+ WebPConvertRGB24ToY(r_ptr + rgb_stride,
+ dst_y + picture->y_stride, width);
+ } else {
+ WebPConvertBGR24ToY(b_ptr, dst_y, width);
+ WebPConvertBGR24ToY(b_ptr + rgb_stride,
+ dst_y + picture->y_stride, width);
+ }
+ } else {
+ ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
+ ConvertRowToY(r_ptr + rgb_stride,
+ g_ptr + rgb_stride,
+ b_ptr + rgb_stride, step,
+ dst_y + picture->y_stride, width, rg);
+ }
+ dst_y += 2 * picture->y_stride;
+ if (has_alpha) {
+ rows_have_alpha &= !WebPExtractAlpha(a_ptr, rgb_stride, width, 2,
+ dst_a, picture->a_stride);
+ dst_a += 2 * picture->a_stride;
+ }
+ // Collect averaged R/G/B(/A)
+ if (!rows_have_alpha) {
+ AccumulateRGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, tmp_rgb, width);
+ } else {
+ AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, rgb_stride, tmp_rgb, width);
+ }
+ // Convert to U/V
+ if (rg == NULL) {
+ WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width);
+ } else {
+ ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg);
+ }
+ dst_u += picture->uv_stride;
+ dst_v += picture->uv_stride;
+ r_ptr += 2 * rgb_stride;
+ b_ptr += 2 * rgb_stride;
+ g_ptr += 2 * rgb_stride;
+ if (has_alpha) a_ptr += 2 * rgb_stride;
+ }
+ if (height & 1) { // extra last row
+ int row_has_alpha = has_alpha;
+ if (use_dsp) {
+ if (r_ptr < b_ptr) {
+ WebPConvertRGB24ToY(r_ptr, dst_y, width);
+ } else {
+ WebPConvertBGR24ToY(b_ptr, dst_y, width);
+ }
+ } else {
+ ConvertRowToY(r_ptr, g_ptr, b_ptr, step, dst_y, width, rg);
+ }
+ if (row_has_alpha) {
+ row_has_alpha &= !WebPExtractAlpha(a_ptr, 0, width, 1, dst_a, 0);
+ }
+ // Collect averaged R/G/B(/A)
+ if (!row_has_alpha) {
+ // Collect averaged R/G/B
+ AccumulateRGB(r_ptr, g_ptr, b_ptr, step, /* rgb_stride = */ 0,
+ tmp_rgb, width);
+ } else {
+ AccumulateRGBA(r_ptr, g_ptr, b_ptr, a_ptr, /* rgb_stride = */ 0,
+ tmp_rgb, width);
+ }
+ if (rg == NULL) {
+ WebPConvertRGBA32ToUV(tmp_rgb, dst_u, dst_v, uv_width);
+ } else {
+ ConvertRowsToUV(tmp_rgb, dst_u, dst_v, uv_width, rg);
+ }
+ }
+ WebPSafeFree(tmp_rgb);
+ }
+ return 1;
+}
+
+#undef SUM4
+#undef SUM2
+#undef SUM4ALPHA
+#undef SUM2ALPHA
+
+//------------------------------------------------------------------------------
+// call for ARGB->YUVA conversion
+
+static int PictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace,
+ float dithering, int use_iterative_conversion) {
+ if (picture == NULL) return 0;
+ if (picture->argb == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
+ } else if ((colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+ } else {
+ const uint8_t* const argb = (const uint8_t*)picture->argb;
+ const uint8_t* const a = argb + (0 ^ ALPHA_OFFSET);
+ const uint8_t* const r = argb + (1 ^ ALPHA_OFFSET);
+ const uint8_t* const g = argb + (2 ^ ALPHA_OFFSET);
+ const uint8_t* const b = argb + (3 ^ ALPHA_OFFSET);
+
+ picture->colorspace = WEBP_YUV420;
+ return ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride,
+ dithering, use_iterative_conversion, picture);
+ }
+}
+
+int WebPPictureARGBToYUVADithered(WebPPicture* picture, WebPEncCSP colorspace,
+ float dithering) {
+ return PictureARGBToYUVA(picture, colorspace, dithering, 0);
+}
+
+int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) {
+ return PictureARGBToYUVA(picture, colorspace, 0.f, 0);
+}
+
+int WebPPictureSharpARGBToYUVA(WebPPicture* picture) {
+ return PictureARGBToYUVA(picture, WEBP_YUV420, 0.f, 1);
+}
+// for backward compatibility
+int WebPPictureSmartARGBToYUVA(WebPPicture* picture) {
+ return WebPPictureSharpARGBToYUVA(picture);
+}
+
+//------------------------------------------------------------------------------
+// call for YUVA -> ARGB conversion
+
+int WebPPictureYUVAToARGB(WebPPicture* picture) {
+ if (picture == NULL) return 0;
+ if (picture->y == NULL || picture->u == NULL || picture->v == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
+ }
+ if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
+ }
+ if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+ }
+ // Allocate a new argb buffer (discarding the previous one).
+ if (!WebPPictureAllocARGB(picture, picture->width, picture->height)) return 0;
+ picture->use_argb = 1;
+
+ // Convert
+ {
+ int y;
+ const int width = picture->width;
+ const int height = picture->height;
+ const int argb_stride = 4 * picture->argb_stride;
+ uint8_t* dst = (uint8_t*)picture->argb;
+ const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y;
+ WebPUpsampleLinePairFunc upsample =
+ WebPGetLinePairConverter(ALPHA_OFFSET > 0);
+
+ // First row, with replicated top samples.
+ upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
+ cur_y += picture->y_stride;
+ dst += argb_stride;
+ // Center rows.
+ for (y = 1; y + 1 < height; y += 2) {
+ const uint8_t* const top_u = cur_u;
+ const uint8_t* const top_v = cur_v;
+ cur_u += picture->uv_stride;
+ cur_v += picture->uv_stride;
+ upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v,
+ dst, dst + argb_stride, width);
+ cur_y += 2 * picture->y_stride;
+ dst += 2 * argb_stride;
+ }
+ // Last row (if needed), with replicated bottom samples.
+ if (height > 1 && !(height & 1)) {
+ upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width);
+ }
+ // Insert alpha values if needed, in replacement for the default 0xff ones.
+ if (picture->colorspace & WEBP_CSP_ALPHA_BIT) {
+ for (y = 0; y < height; ++y) {
+ uint32_t* const argb_dst = picture->argb + y * picture->argb_stride;
+ const uint8_t* const src = picture->a + y * picture->a_stride;
+ int x;
+ for (x = 0; x < width; ++x) {
+ argb_dst[x] = (argb_dst[x] & 0x00ffffffu) | ((uint32_t)src[x] << 24);
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// automatic import / conversion
+
+static int Import(WebPPicture* const picture,
+ const uint8_t* rgb, int rgb_stride,
+ int step, int swap_rb, int import_alpha) {
+ int y;
+ // swap_rb -> b,g,r,a , !swap_rb -> r,g,b,a
+ const uint8_t* r_ptr = rgb + (swap_rb ? 2 : 0);
+ const uint8_t* g_ptr = rgb + 1;
+ const uint8_t* b_ptr = rgb + (swap_rb ? 0 : 2);
+ const int width = picture->width;
+ const int height = picture->height;
+
+ if (!picture->use_argb) {
+ const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL;
+ return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
+ 0.f /* no dithering */, 0, picture);
+ }
+ if (!WebPPictureAlloc(picture)) return 0;
+
+ VP8LDspInit();
+ WebPInitAlphaProcessing();
+
+ if (import_alpha) {
+ // dst[] byte order is {a,r,g,b} for big-endian, {b,g,r,a} for little endian
+ uint32_t* dst = picture->argb;
+ const int do_copy = (ALPHA_OFFSET == 3) && swap_rb;
+ assert(step == 4);
+ if (do_copy) {
+ for (y = 0; y < height; ++y) {
+ memcpy(dst, rgb, width * 4);
+ rgb += rgb_stride;
+ dst += picture->argb_stride;
+ }
+ } else {
+ for (y = 0; y < height; ++y) {
+#ifdef WORDS_BIGENDIAN
+ // BGRA or RGBA input order.
+ const uint8_t* a_ptr = rgb + 3;
+ WebPPackARGB(a_ptr, r_ptr, g_ptr, b_ptr, width, dst);
+ r_ptr += rgb_stride;
+ g_ptr += rgb_stride;
+ b_ptr += rgb_stride;
+#else
+ // RGBA input order. Need to swap R and B.
+ VP8LConvertBGRAToRGBA((const uint32_t*)rgb, width, (uint8_t*)dst);
+#endif
+ rgb += rgb_stride;
+ dst += picture->argb_stride;
+ }
+ }
+ } else {
+ uint32_t* dst = picture->argb;
+ assert(step >= 3);
+ for (y = 0; y < height; ++y) {
+ WebPPackRGB(r_ptr, g_ptr, b_ptr, width, step, dst);
+ r_ptr += rgb_stride;
+ g_ptr += rgb_stride;
+ b_ptr += rgb_stride;
+ dst += picture->argb_stride;
+ }
+ }
+ return 1;
+}
+
+// Public API
+
+#if !defined(WEBP_REDUCE_CSP)
+
+int WebPPictureImportBGR(WebPPicture* picture,
+ const uint8_t* rgb, int rgb_stride) {
+ return (picture != NULL && rgb != NULL)
+ ? Import(picture, rgb, rgb_stride, 3, 1, 0)
+ : 0;
+}
+
+int WebPPictureImportBGRA(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return (picture != NULL && rgba != NULL)
+ ? Import(picture, rgba, rgba_stride, 4, 1, 1)
+ : 0;
+}
+
+
+int WebPPictureImportBGRX(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return (picture != NULL && rgba != NULL)
+ ? Import(picture, rgba, rgba_stride, 4, 1, 0)
+ : 0;
+}
+
+#endif // WEBP_REDUCE_CSP
+
+int WebPPictureImportRGB(WebPPicture* picture,
+ const uint8_t* rgb, int rgb_stride) {
+ return (picture != NULL && rgb != NULL)
+ ? Import(picture, rgb, rgb_stride, 3, 0, 0)
+ : 0;
+}
+
+int WebPPictureImportRGBA(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return (picture != NULL && rgba != NULL)
+ ? Import(picture, rgba, rgba_stride, 4, 0, 1)
+ : 0;
+}
+
+int WebPPictureImportRGBX(WebPPicture* picture,
+ const uint8_t* rgba, int rgba_stride) {
+ return (picture != NULL && rgba != NULL)
+ ? Import(picture, rgba, rgba_stride, 4, 0, 0)
+ : 0;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/picture_enc.c b/src/third_party/libwebp/src/enc/picture_enc.c
new file mode 100644
index 0000000..fdafbd9
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/picture_enc.c
@@ -0,0 +1,302 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebPPicture class basis
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#endif
+
+#include "src/enc/vp8i_enc.h"
+#include "src/dsp/dsp.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// WebPPicture
+//------------------------------------------------------------------------------
+
+static int DummyWriter(const uint8_t* data, size_t data_size,
+ const WebPPicture* const picture) {
+ // The following are to prevent 'unused variable' error message.
+ (void)data;
+ (void)data_size;
+ (void)picture;
+ return 1;
+}
+
+int WebPPictureInitInternal(WebPPicture* picture, int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
+ return 0; // caller/system version mismatch!
+ }
+ if (picture != NULL) {
+ memset(picture, 0, sizeof(*picture));
+ picture->writer = DummyWriter;
+ WebPEncodingSetError(picture, VP8_ENC_OK);
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+static void WebPPictureResetBufferARGB(WebPPicture* const picture) {
+ picture->memory_argb_ = NULL;
+ picture->argb = NULL;
+ picture->argb_stride = 0;
+}
+
+static void WebPPictureResetBufferYUVA(WebPPicture* const picture) {
+ picture->memory_ = NULL;
+ picture->y = picture->u = picture->v = picture->a = NULL;
+ picture->y_stride = picture->uv_stride = 0;
+ picture->a_stride = 0;
+}
+
+void WebPPictureResetBuffers(WebPPicture* const picture) {
+ WebPPictureResetBufferARGB(picture);
+ WebPPictureResetBufferYUVA(picture);
+}
+
+int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height) {
+ void* memory;
+ const uint64_t argb_size = (uint64_t)width * height;
+
+ assert(picture != NULL);
+
+ WebPSafeFree(picture->memory_argb_);
+ WebPPictureResetBufferARGB(picture);
+
+ if (width <= 0 || height <= 0) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
+ }
+ // allocate a new buffer.
+ memory = WebPSafeMalloc(argb_size + WEBP_ALIGN_CST, sizeof(*picture->argb));
+ if (memory == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+ picture->memory_argb_ = memory;
+ picture->argb = (uint32_t*)WEBP_ALIGN(memory);
+ picture->argb_stride = width;
+ return 1;
+}
+
+int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) {
+ const WebPEncCSP uv_csp =
+ (WebPEncCSP)((int)picture->colorspace & WEBP_CSP_UV_MASK);
+ const int has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT;
+ const int y_stride = width;
+ const int uv_width = (int)(((int64_t)width + 1) >> 1);
+ const int uv_height = (int)(((int64_t)height + 1) >> 1);
+ const int uv_stride = uv_width;
+ int a_width, a_stride;
+ uint64_t y_size, uv_size, a_size, total_size;
+ uint8_t* mem;
+
+ assert(picture != NULL);
+
+ WebPSafeFree(picture->memory_);
+ WebPPictureResetBufferYUVA(picture);
+
+ if (uv_csp != WEBP_YUV420) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+ }
+
+ // alpha
+ a_width = has_alpha ? width : 0;
+ a_stride = a_width;
+ y_size = (uint64_t)y_stride * height;
+ uv_size = (uint64_t)uv_stride * uv_height;
+ a_size = (uint64_t)a_stride * height;
+
+ total_size = y_size + a_size + 2 * uv_size;
+
+ // Security and validation checks
+ if (width <= 0 || height <= 0 || // luma/alpha param error
+ uv_width <= 0 || uv_height <= 0) { // u/v param error
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
+ }
+ // allocate a new buffer.
+ mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem));
+ if (mem == NULL) {
+ return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+
+ // From now on, we're in the clear, we can no longer fail...
+ picture->memory_ = (void*)mem;
+ picture->y_stride = y_stride;
+ picture->uv_stride = uv_stride;
+ picture->a_stride = a_stride;
+
+ // TODO(skal): we could align the y/u/v planes and adjust stride.
+ picture->y = mem;
+ mem += y_size;
+
+ picture->u = mem;
+ mem += uv_size;
+ picture->v = mem;
+ mem += uv_size;
+
+ if (a_size > 0) {
+ picture->a = mem;
+ mem += a_size;
+ }
+ (void)mem; // makes the static analyzer happy
+ return 1;
+}
+
+int WebPPictureAlloc(WebPPicture* picture) {
+ if (picture != NULL) {
+ const int width = picture->width;
+ const int height = picture->height;
+
+ WebPPictureFree(picture); // erase previous buffer
+
+ if (!picture->use_argb) {
+ return WebPPictureAllocYUVA(picture, width, height);
+ } else {
+ return WebPPictureAllocARGB(picture, width, height);
+ }
+ }
+ return 1;
+}
+
+void WebPPictureFree(WebPPicture* picture) {
+ if (picture != NULL) {
+ WebPSafeFree(picture->memory_);
+ WebPSafeFree(picture->memory_argb_);
+ WebPPictureResetBuffers(picture);
+ }
+}
+
+//------------------------------------------------------------------------------
+// WebPMemoryWriter: Write-to-memory
+
+void WebPMemoryWriterInit(WebPMemoryWriter* writer) {
+ writer->mem = NULL;
+ writer->size = 0;
+ writer->max_size = 0;
+}
+
+int WebPMemoryWrite(const uint8_t* data, size_t data_size,
+ const WebPPicture* picture) {
+ WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
+ uint64_t next_size;
+ if (w == NULL) {
+ return 1;
+ }
+ next_size = (uint64_t)w->size + data_size;
+ if (next_size > w->max_size) {
+ uint8_t* new_mem;
+ uint64_t next_max_size = 2ULL * w->max_size;
+ if (next_max_size < next_size) next_max_size = next_size;
+ if (next_max_size < 8192ULL) next_max_size = 8192ULL;
+ new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1);
+ if (new_mem == NULL) {
+ return 0;
+ }
+ if (w->size > 0) {
+ memcpy(new_mem, w->mem, w->size);
+ }
+ WebPSafeFree(w->mem);
+ w->mem = new_mem;
+ // down-cast is ok, thanks to WebPSafeMalloc
+ w->max_size = (size_t)next_max_size;
+ }
+ if (data_size > 0) {
+ memcpy(w->mem + w->size, data, data_size);
+ w->size += data_size;
+ }
+ return 1;
+}
+
+void WebPMemoryWriterClear(WebPMemoryWriter* writer) {
+ if (writer != NULL) {
+ WebPSafeFree(writer->mem);
+ writer->mem = NULL;
+ writer->size = 0;
+ writer->max_size = 0;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Simplest high-level calls:
+
+typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
+
+static size_t Encode(const uint8_t* rgba, int width, int height, int stride,
+ Importer import, float quality_factor, int lossless,
+ uint8_t** output) {
+ WebPPicture pic;
+ WebPConfig config;
+ WebPMemoryWriter wrt;
+ int ok;
+
+ if (output == NULL) return 0;
+
+ if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) ||
+ !WebPPictureInit(&pic)) {
+ return 0; // shouldn't happen, except if system installation is broken
+ }
+
+ config.lossless = !!lossless;
+ pic.use_argb = !!lossless;
+ pic.width = width;
+ pic.height = height;
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &wrt;
+ WebPMemoryWriterInit(&wrt);
+
+ ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic);
+ WebPPictureFree(&pic);
+ if (!ok) {
+ WebPMemoryWriterClear(&wrt);
+ *output = NULL;
+ return 0;
+ }
+ *output = wrt.mem;
+ return wrt.size;
+}
+
+#define ENCODE_FUNC(NAME, IMPORTER) \
+size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
+ uint8_t** out) { \
+ return Encode(in, w, h, bps, IMPORTER, q, 0, out); \
+}
+
+ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB)
+ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA)
+#if !defined(WEBP_REDUCE_CSP)
+ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR)
+ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA)
+#endif // WEBP_REDUCE_CSP
+
+#undef ENCODE_FUNC
+
+#define LOSSLESS_DEFAULT_QUALITY 70.
+#define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \
+size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \
+ return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \
+}
+
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB)
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA)
+#if !defined(WEBP_REDUCE_CSP)
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR)
+LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA)
+#endif // WEBP_REDUCE_CSP
+
+#undef LOSSLESS_ENCODE_FUNC
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/picture_psnr_enc.c b/src/third_party/libwebp/src/enc/picture_psnr_enc.c
new file mode 100644
index 0000000..1a2f0be
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/picture_psnr_enc.c
@@ -0,0 +1,258 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebPPicture tools for measuring distortion
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/webp/encode.h"
+
+#if !(defined(WEBP_DISABLE_STATS) || defined(WEBP_REDUCE_SIZE))
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "src/dsp/dsp.h"
+#include "src/enc/vp8i_enc.h"
+#include "src/utils/utils.h"
+
+typedef double (*AccumulateFunc)(const uint8_t* src, int src_stride,
+ const uint8_t* ref, int ref_stride,
+ int w, int h);
+
+//------------------------------------------------------------------------------
+// local-min distortion
+//
+// For every pixel in the *reference* picture, we search for the local best
+// match in the compressed image. This is not a symmetrical measure.
+
+#define RADIUS 2 // search radius. Shouldn't be too large.
+
+static double AccumulateLSIM(const uint8_t* src, int src_stride,
+ const uint8_t* ref, int ref_stride,
+ int w, int h) {
+ int x, y;
+ double total_sse = 0.;
+ for (y = 0; y < h; ++y) {
+ const int y_0 = (y - RADIUS < 0) ? 0 : y - RADIUS;
+ const int y_1 = (y + RADIUS + 1 >= h) ? h : y + RADIUS + 1;
+ for (x = 0; x < w; ++x) {
+ const int x_0 = (x - RADIUS < 0) ? 0 : x - RADIUS;
+ const int x_1 = (x + RADIUS + 1 >= w) ? w : x + RADIUS + 1;
+ double best_sse = 255. * 255.;
+ const double value = (double)ref[y * ref_stride + x];
+ int i, j;
+ for (j = y_0; j < y_1; ++j) {
+ const uint8_t* const s = src + j * src_stride;
+ for (i = x_0; i < x_1; ++i) {
+ const double diff = s[i] - value;
+ const double sse = diff * diff;
+ if (sse < best_sse) best_sse = sse;
+ }
+ }
+ total_sse += best_sse;
+ }
+ }
+ return total_sse;
+}
+#undef RADIUS
+
+static double AccumulateSSE(const uint8_t* src, int src_stride,
+ const uint8_t* ref, int ref_stride,
+ int w, int h) {
+ int y;
+ double total_sse = 0.;
+ for (y = 0; y < h; ++y) {
+ total_sse += VP8AccumulateSSE(src, ref, w);
+ src += src_stride;
+ ref += ref_stride;
+ }
+ return total_sse;
+}
+
+//------------------------------------------------------------------------------
+
+static double AccumulateSSIM(const uint8_t* src, int src_stride,
+ const uint8_t* ref, int ref_stride,
+ int w, int h) {
+ const int w0 = (w < VP8_SSIM_KERNEL) ? w : VP8_SSIM_KERNEL;
+ const int w1 = w - VP8_SSIM_KERNEL - 1;
+ const int h0 = (h < VP8_SSIM_KERNEL) ? h : VP8_SSIM_KERNEL;
+ const int h1 = h - VP8_SSIM_KERNEL - 1;
+ int x, y;
+ double sum = 0.;
+ for (y = 0; y < h0; ++y) {
+ for (x = 0; x < w; ++x) {
+ sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
+ }
+ }
+ for (; y < h1; ++y) {
+ for (x = 0; x < w0; ++x) {
+ sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
+ }
+ for (; x < w1; ++x) {
+ const int off1 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * src_stride;
+ const int off2 = x - VP8_SSIM_KERNEL + (y - VP8_SSIM_KERNEL) * ref_stride;
+ sum += VP8SSIMGet(src + off1, src_stride, ref + off2, ref_stride);
+ }
+ for (; x < w; ++x) {
+ sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
+ }
+ }
+ for (; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ sum += VP8SSIMGetClipped(src, src_stride, ref, ref_stride, x, y, w, h);
+ }
+ }
+ return sum;
+}
+
+//------------------------------------------------------------------------------
+// Distortion
+
+// Max value returned in case of exact similarity.
+static const double kMinDistortion_dB = 99.;
+
+static double GetPSNR(double v, double size) {
+ return (v > 0. && size > 0.) ? -4.3429448 * log(v / (size * 255 * 255.))
+ : kMinDistortion_dB;
+}
+
+static double GetLogSSIM(double v, double size) {
+ v = (size > 0.) ? v / size : 1.;
+ return (v < 1.) ? -10.0 * log10(1. - v) : kMinDistortion_dB;
+}
+
+int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
+ const uint8_t* ref, size_t ref_stride,
+ int width, int height, size_t x_step,
+ int type, float* distortion, float* result) {
+ uint8_t* allocated = NULL;
+ const AccumulateFunc metric = (type == 0) ? AccumulateSSE :
+ (type == 1) ? AccumulateSSIM :
+ AccumulateLSIM;
+ if (src == NULL || ref == NULL ||
+ src_stride < x_step * width || ref_stride < x_step * width ||
+ result == NULL || distortion == NULL) {
+ return 0;
+ }
+
+ VP8SSIMDspInit();
+ if (x_step != 1) { // extract a packed plane if needed
+ int x, y;
+ uint8_t* tmp1;
+ uint8_t* tmp2;
+ allocated =
+ (uint8_t*)WebPSafeMalloc(2ULL * width * height, sizeof(*allocated));
+ if (allocated == NULL) return 0;
+ tmp1 = allocated;
+ tmp2 = tmp1 + (size_t)width * height;
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ tmp1[x + y * width] = src[x * x_step + y * src_stride];
+ tmp2[x + y * width] = ref[x * x_step + y * ref_stride];
+ }
+ }
+ src = tmp1;
+ ref = tmp2;
+ }
+ *distortion = (float)metric(src, width, ref, width, width, height);
+ WebPSafeFree(allocated);
+
+ *result = (type == 1) ? (float)GetLogSSIM(*distortion, (double)width * height)
+ : (float)GetPSNR(*distortion, (double)width * height);
+ return 1;
+}
+
+#ifdef WORDS_BIGENDIAN
+#define BLUE_OFFSET 3 // uint32_t 0x000000ff is 0x00,00,00,ff in memory
+#else
+#define BLUE_OFFSET 0 // uint32_t 0x000000ff is 0xff,00,00,00 in memory
+#endif
+
+int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
+ int type, float results[5]) {
+ int w, h, c;
+ int ok = 0;
+ WebPPicture p0, p1;
+ double total_size = 0., total_distortion = 0.;
+ if (src == NULL || ref == NULL ||
+ src->width != ref->width || src->height != ref->height ||
+ results == NULL) {
+ return 0;
+ }
+
+ VP8SSIMDspInit();
+ if (!WebPPictureInit(&p0) || !WebPPictureInit(&p1)) return 0;
+ w = src->width;
+ h = src->height;
+ if (!WebPPictureView(src, 0, 0, w, h, &p0)) goto Error;
+ if (!WebPPictureView(ref, 0, 0, w, h, &p1)) goto Error;
+
+ // We always measure distortion in ARGB space.
+ if (p0.use_argb == 0 && !WebPPictureYUVAToARGB(&p0)) goto Error;
+ if (p1.use_argb == 0 && !WebPPictureYUVAToARGB(&p1)) goto Error;
+ for (c = 0; c < 4; ++c) {
+ float distortion;
+ const size_t stride0 = 4 * (size_t)p0.argb_stride;
+ const size_t stride1 = 4 * (size_t)p1.argb_stride;
+ // results are reported as BGRA
+ const int offset = c ^ BLUE_OFFSET;
+ if (!WebPPlaneDistortion((const uint8_t*)p0.argb + offset, stride0,
+ (const uint8_t*)p1.argb + offset, stride1,
+ w, h, 4, type, &distortion, results + c)) {
+ goto Error;
+ }
+ total_distortion += distortion;
+ total_size += w * h;
+ }
+
+ results[4] = (type == 1) ? (float)GetLogSSIM(total_distortion, total_size)
+ : (float)GetPSNR(total_distortion, total_size);
+ ok = 1;
+
+ Error:
+ WebPPictureFree(&p0);
+ WebPPictureFree(&p1);
+ return ok;
+}
+
+#undef BLUE_OFFSET
+
+#else // defined(WEBP_DISABLE_STATS)
+int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
+ const uint8_t* ref, size_t ref_stride,
+ int width, int height, size_t x_step,
+ int type, float* distortion, float* result) {
+ (void)src;
+ (void)src_stride;
+ (void)ref;
+ (void)ref_stride;
+ (void)width;
+ (void)height;
+ (void)x_step;
+ (void)type;
+ if (distortion == NULL || result == NULL) return 0;
+ *distortion = 0.f;
+ *result = 0.f;
+ return 1;
+}
+
+int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
+ int type, float results[5]) {
+ int i;
+ (void)src;
+ (void)ref;
+ (void)type;
+ if (results == NULL) return 0;
+ for (i = 0; i < 5; ++i) results[i] = 0.f;
+ return 1;
+}
+
+#endif // !defined(WEBP_DISABLE_STATS)
diff --git a/src/third_party/libwebp/src/enc/picture_rescale_enc.c b/src/third_party/libwebp/src/enc/picture_rescale_enc.c
new file mode 100644
index 0000000..58a6ae7
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/picture_rescale_enc.c
@@ -0,0 +1,309 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebPPicture tools: copy, crop, rescaling and view.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/webp/encode.h"
+
+#if !defined(WEBP_REDUCE_SIZE)
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "src/enc/vp8i_enc.h"
+#include "src/utils/rescaler_utils.h"
+#include "src/utils/utils.h"
+
+#define HALVE(x) (((x) + 1) >> 1)
+
+// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
+// into 'dst'. Mark 'dst' as not owning any memory.
+static void PictureGrabSpecs(const WebPPicture* const src,
+ WebPPicture* const dst) {
+ assert(src != NULL && dst != NULL);
+ *dst = *src;
+ WebPPictureResetBuffers(dst);
+}
+
+//------------------------------------------------------------------------------
+
+// Adjust top-left corner to chroma sample position.
+static void SnapTopLeftPosition(const WebPPicture* const pic,
+ int* const left, int* const top) {
+ if (!pic->use_argb) {
+ *left &= ~1;
+ *top &= ~1;
+ }
+}
+
+// Adjust top-left corner and verify that the sub-rectangle is valid.
+static int AdjustAndCheckRectangle(const WebPPicture* const pic,
+ int* const left, int* const top,
+ int width, int height) {
+ SnapTopLeftPosition(pic, left, top);
+ if ((*left) < 0 || (*top) < 0) return 0;
+ if (width <= 0 || height <= 0) return 0;
+ if ((*left) + width > pic->width) return 0;
+ if ((*top) + height > pic->height) return 0;
+ return 1;
+}
+
+int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
+ if (src == NULL || dst == NULL) return 0;
+ if (src == dst) return 1;
+
+ PictureGrabSpecs(src, dst);
+ if (!WebPPictureAlloc(dst)) return 0;
+
+ if (!src->use_argb) {
+ WebPCopyPlane(src->y, src->y_stride,
+ dst->y, dst->y_stride, dst->width, dst->height);
+ WebPCopyPlane(src->u, src->uv_stride, dst->u, dst->uv_stride,
+ HALVE(dst->width), HALVE(dst->height));
+ WebPCopyPlane(src->v, src->uv_stride, dst->v, dst->uv_stride,
+ HALVE(dst->width), HALVE(dst->height));
+ if (dst->a != NULL) {
+ WebPCopyPlane(src->a, src->a_stride,
+ dst->a, dst->a_stride, dst->width, dst->height);
+ }
+ } else {
+ WebPCopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
+ (uint8_t*)dst->argb, 4 * dst->argb_stride,
+ 4 * dst->width, dst->height);
+ }
+ return 1;
+}
+
+int WebPPictureIsView(const WebPPicture* picture) {
+ if (picture == NULL) return 0;
+ if (picture->use_argb) {
+ return (picture->memory_argb_ == NULL);
+ }
+ return (picture->memory_ == NULL);
+}
+
+int WebPPictureView(const WebPPicture* src,
+ int left, int top, int width, int height,
+ WebPPicture* dst) {
+ if (src == NULL || dst == NULL) return 0;
+
+ // verify rectangle position.
+ if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
+
+ if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'.
+ PictureGrabSpecs(src, dst);
+ }
+ dst->width = width;
+ dst->height = height;
+ if (!src->use_argb) {
+ dst->y = src->y + top * src->y_stride + left;
+ dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
+ dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
+ dst->y_stride = src->y_stride;
+ dst->uv_stride = src->uv_stride;
+ if (src->a != NULL) {
+ dst->a = src->a + top * src->a_stride + left;
+ dst->a_stride = src->a_stride;
+ }
+ } else {
+ dst->argb = src->argb + top * src->argb_stride + left;
+ dst->argb_stride = src->argb_stride;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Picture cropping
+
+int WebPPictureCrop(WebPPicture* pic,
+ int left, int top, int width, int height) {
+ WebPPicture tmp;
+
+ if (pic == NULL) return 0;
+ if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
+
+ PictureGrabSpecs(pic, &tmp);
+ tmp.width = width;
+ tmp.height = height;
+ if (!WebPPictureAlloc(&tmp)) return 0;
+
+ if (!pic->use_argb) {
+ const int y_offset = top * pic->y_stride + left;
+ const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
+ WebPCopyPlane(pic->y + y_offset, pic->y_stride,
+ tmp.y, tmp.y_stride, width, height);
+ WebPCopyPlane(pic->u + uv_offset, pic->uv_stride,
+ tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
+ WebPCopyPlane(pic->v + uv_offset, pic->uv_stride,
+ tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
+
+ if (tmp.a != NULL) {
+ const int a_offset = top * pic->a_stride + left;
+ WebPCopyPlane(pic->a + a_offset, pic->a_stride,
+ tmp.a, tmp.a_stride, width, height);
+ }
+ } else {
+ const uint8_t* const src =
+ (const uint8_t*)(pic->argb + top * pic->argb_stride + left);
+ WebPCopyPlane(src, pic->argb_stride * 4, (uint8_t*)tmp.argb,
+ tmp.argb_stride * 4, width * 4, height);
+ }
+ WebPPictureFree(pic);
+ *pic = tmp;
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+// Simple picture rescaler
+
+static void RescalePlane(const uint8_t* src,
+ int src_width, int src_height, int src_stride,
+ uint8_t* dst,
+ int dst_width, int dst_height, int dst_stride,
+ rescaler_t* const work,
+ int num_channels) {
+ WebPRescaler rescaler;
+ int y = 0;
+ WebPRescalerInit(&rescaler, src_width, src_height,
+ dst, dst_width, dst_height, dst_stride,
+ num_channels, work);
+ while (y < src_height) {
+ y += WebPRescalerImport(&rescaler, src_height - y,
+ src + y * src_stride, src_stride);
+ WebPRescalerExport(&rescaler);
+ }
+}
+
+static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) {
+ assert(pic->argb != NULL);
+ WebPMultARGBRows((uint8_t*)pic->argb, pic->argb_stride * sizeof(*pic->argb),
+ pic->width, pic->height, inverse);
+}
+
+static void AlphaMultiplyY(WebPPicture* const pic, int inverse) {
+ if (pic->a != NULL) {
+ WebPMultRows(pic->y, pic->y_stride, pic->a, pic->a_stride,
+ pic->width, pic->height, inverse);
+ }
+}
+
+int WebPPictureRescale(WebPPicture* pic, int width, int height) {
+ WebPPicture tmp;
+ int prev_width, prev_height;
+ rescaler_t* work;
+
+ if (pic == NULL) return 0;
+ prev_width = pic->width;
+ prev_height = pic->height;
+ if (!WebPRescalerGetScaledDimensions(
+ prev_width, prev_height, &width, &height)) {
+ return 0;
+ }
+
+ PictureGrabSpecs(pic, &tmp);
+ tmp.width = width;
+ tmp.height = height;
+ if (!WebPPictureAlloc(&tmp)) return 0;
+
+ if (!pic->use_argb) {
+ work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
+ if (work == NULL) {
+ WebPPictureFree(&tmp);
+ return 0;
+ }
+ // If present, we need to rescale alpha first (for AlphaMultiplyY).
+ if (pic->a != NULL) {
+ WebPInitAlphaProcessing();
+ RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
+ tmp.a, width, height, tmp.a_stride, work, 1);
+ }
+
+ // We take transparency into account on the luma plane only. That's not
+ // totally exact blending, but still is a good approximation.
+ AlphaMultiplyY(pic, 0);
+ RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
+ tmp.y, width, height, tmp.y_stride, work, 1);
+ AlphaMultiplyY(&tmp, 1);
+
+ RescalePlane(pic->u,
+ HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
+ tmp.u,
+ HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
+ RescalePlane(pic->v,
+ HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
+ tmp.v,
+ HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
+ } else {
+ work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
+ if (work == NULL) {
+ WebPPictureFree(&tmp);
+ return 0;
+ }
+ // In order to correctly interpolate colors, we need to apply the alpha
+ // weighting first (black-matting), scale the RGB values, and remove
+ // the premultiplication afterward (while preserving the alpha channel).
+ WebPInitAlphaProcessing();
+ AlphaMultiplyARGB(pic, 0);
+ RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
+ pic->argb_stride * 4,
+ (uint8_t*)tmp.argb, width, height,
+ tmp.argb_stride * 4,
+ work, 4);
+ AlphaMultiplyARGB(&tmp, 1);
+ }
+ WebPPictureFree(pic);
+ WebPSafeFree(work);
+ *pic = tmp;
+ return 1;
+}
+
+#else // defined(WEBP_REDUCE_SIZE)
+
+int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
+ (void)src;
+ (void)dst;
+ return 0;
+}
+
+int WebPPictureIsView(const WebPPicture* picture) {
+ (void)picture;
+ return 0;
+}
+
+int WebPPictureView(const WebPPicture* src,
+ int left, int top, int width, int height,
+ WebPPicture* dst) {
+ (void)src;
+ (void)left;
+ (void)top;
+ (void)width;
+ (void)height;
+ (void)dst;
+ return 0;
+}
+
+int WebPPictureCrop(WebPPicture* pic,
+ int left, int top, int width, int height) {
+ (void)pic;
+ (void)left;
+ (void)top;
+ (void)width;
+ (void)height;
+ return 0;
+}
+
+int WebPPictureRescale(WebPPicture* pic, int width, int height) {
+ (void)pic;
+ (void)width;
+ (void)height;
+ return 0;
+}
+#endif // !defined(WEBP_REDUCE_SIZE)
diff --git a/src/third_party/libwebp/src/enc/picture_tools_enc.c b/src/third_party/libwebp/src/enc/picture_tools_enc.c
new file mode 100644
index 0000000..01dc6ca
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/picture_tools_enc.c
@@ -0,0 +1,278 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebPPicture tools: alpha handling, etc.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#endif
+
+#include "src/enc/vp8i_enc.h"
+#include "src/dsp/yuv.h"
+
+static WEBP_INLINE uint32_t MakeARGB32(int r, int g, int b) {
+ return (0xff000000u | (r << 16) | (g << 8) | b);
+}
+
+//------------------------------------------------------------------------------
+// Helper: clean up fully transparent area to help compressibility.
+
+#define SIZE 8
+#define SIZE2 (SIZE / 2)
+static int IsTransparentARGBArea(const uint32_t* ptr, int stride, int size) {
+ int y, x;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) {
+ if (ptr[x] & 0xff000000u) {
+ return 0;
+ }
+ }
+ ptr += stride;
+ }
+ return 1;
+}
+
+static void Flatten(uint8_t* ptr, int v, int stride, int size) {
+ int y;
+ for (y = 0; y < size; ++y) {
+ memset(ptr, v, size);
+ ptr += stride;
+ }
+}
+
+static void FlattenARGB(uint32_t* ptr, uint32_t v, int stride, int size) {
+ int x, y;
+ for (y = 0; y < size; ++y) {
+ for (x = 0; x < size; ++x) ptr[x] = v;
+ ptr += stride;
+ }
+}
+
+// Smoothen the luma components of transparent pixels. Return true if the whole
+// block is transparent.
+static int SmoothenBlock(const uint8_t* a_ptr, int a_stride, uint8_t* y_ptr,
+ int y_stride, int width, int height) {
+ int sum = 0, count = 0;
+ int x, y;
+ const uint8_t* alpha_ptr = a_ptr;
+ uint8_t* luma_ptr = y_ptr;
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ if (alpha_ptr[x] != 0) {
+ ++count;
+ sum += luma_ptr[x];
+ }
+ }
+ alpha_ptr += a_stride;
+ luma_ptr += y_stride;
+ }
+ if (count > 0 && count < width * height) {
+ const uint8_t avg_u8 = (uint8_t)(sum / count);
+ alpha_ptr = a_ptr;
+ luma_ptr = y_ptr;
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ if (alpha_ptr[x] == 0) luma_ptr[x] = avg_u8;
+ }
+ alpha_ptr += a_stride;
+ luma_ptr += y_stride;
+ }
+ }
+ return (count == 0);
+}
+
+void WebPCleanupTransparentArea(WebPPicture* pic) {
+ int x, y, w, h;
+ if (pic == NULL) return;
+ w = pic->width / SIZE;
+ h = pic->height / SIZE;
+
+ // note: we ignore the left-overs on right/bottom, except for SmoothenBlock().
+ if (pic->use_argb) {
+ uint32_t argb_value = 0;
+ for (y = 0; y < h; ++y) {
+ int need_reset = 1;
+ for (x = 0; x < w; ++x) {
+ const int off = (y * pic->argb_stride + x) * SIZE;
+ if (IsTransparentARGBArea(pic->argb + off, pic->argb_stride, SIZE)) {
+ if (need_reset) {
+ argb_value = pic->argb[off];
+ need_reset = 0;
+ }
+ FlattenARGB(pic->argb + off, argb_value, pic->argb_stride, SIZE);
+ } else {
+ need_reset = 1;
+ }
+ }
+ }
+ } else {
+ const int width = pic->width;
+ const int height = pic->height;
+ const int y_stride = pic->y_stride;
+ const int uv_stride = pic->uv_stride;
+ const int a_stride = pic->a_stride;
+ uint8_t* y_ptr = pic->y;
+ uint8_t* u_ptr = pic->u;
+ uint8_t* v_ptr = pic->v;
+ const uint8_t* a_ptr = pic->a;
+ int values[3] = { 0 };
+ if (a_ptr == NULL || y_ptr == NULL || u_ptr == NULL || v_ptr == NULL) {
+ return;
+ }
+ for (y = 0; y + SIZE <= height; y += SIZE) {
+ int need_reset = 1;
+ for (x = 0; x + SIZE <= width; x += SIZE) {
+ if (SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
+ SIZE, SIZE)) {
+ if (need_reset) {
+ values[0] = y_ptr[x];
+ values[1] = u_ptr[x >> 1];
+ values[2] = v_ptr[x >> 1];
+ need_reset = 0;
+ }
+ Flatten(y_ptr + x, values[0], y_stride, SIZE);
+ Flatten(u_ptr + (x >> 1), values[1], uv_stride, SIZE2);
+ Flatten(v_ptr + (x >> 1), values[2], uv_stride, SIZE2);
+ } else {
+ need_reset = 1;
+ }
+ }
+ if (x < width) {
+ SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
+ width - x, SIZE);
+ }
+ a_ptr += SIZE * a_stride;
+ y_ptr += SIZE * y_stride;
+ u_ptr += SIZE2 * uv_stride;
+ v_ptr += SIZE2 * uv_stride;
+ }
+ if (y < height) {
+ const int sub_height = height - y;
+ for (x = 0; x + SIZE <= width; x += SIZE) {
+ SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
+ SIZE, sub_height);
+ }
+ if (x < width) {
+ SmoothenBlock(a_ptr + x, a_stride, y_ptr + x, y_stride,
+ width - x, sub_height);
+ }
+ }
+ }
+}
+
+#undef SIZE
+#undef SIZE2
+
+void WebPCleanupTransparentAreaLossless(WebPPicture* const pic) {
+ int x, y, w, h;
+ uint32_t* argb;
+ assert(pic != NULL && pic->use_argb);
+ w = pic->width;
+ h = pic->height;
+ argb = pic->argb;
+
+ for (y = 0; y < h; ++y) {
+ for (x = 0; x < w; ++x) {
+ if ((argb[x] & 0xff000000) == 0) {
+ argb[x] = 0x00000000;
+ }
+ }
+ argb += pic->argb_stride;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Blend color and remove transparency info
+
+#define BLEND(V0, V1, ALPHA) \
+ ((((V0) * (255 - (ALPHA)) + (V1) * (ALPHA)) * 0x101 + 256) >> 16)
+#define BLEND_10BIT(V0, V1, ALPHA) \
+ ((((V0) * (1020 - (ALPHA)) + (V1) * (ALPHA)) * 0x101 + 1024) >> 18)
+
+void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
+ const int red = (background_rgb >> 16) & 0xff;
+ const int green = (background_rgb >> 8) & 0xff;
+ const int blue = (background_rgb >> 0) & 0xff;
+ int x, y;
+ if (pic == NULL) return;
+ if (!pic->use_argb) {
+ const int uv_width = (pic->width >> 1); // omit last pixel during u/v loop
+ const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF);
+ // VP8RGBToU/V expects the u/v values summed over four pixels
+ const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
+ const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
+ const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
+ if (!has_alpha || pic->a == NULL) return; // nothing to do
+ for (y = 0; y < pic->height; ++y) {
+ // Luma blending
+ uint8_t* const y_ptr = pic->y + y * pic->y_stride;
+ uint8_t* const a_ptr = pic->a + y * pic->a_stride;
+ for (x = 0; x < pic->width; ++x) {
+ const int alpha = a_ptr[x];
+ if (alpha < 0xff) {
+ y_ptr[x] = BLEND(Y0, y_ptr[x], a_ptr[x]);
+ }
+ }
+ // Chroma blending every even line
+ if ((y & 1) == 0) {
+ uint8_t* const u = pic->u + (y >> 1) * pic->uv_stride;
+ uint8_t* const v = pic->v + (y >> 1) * pic->uv_stride;
+ uint8_t* const a_ptr2 =
+ (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
+ for (x = 0; x < uv_width; ++x) {
+ // Average four alpha values into a single blending weight.
+ // TODO(skal): might lead to visible contouring. Can we do better?
+ const int alpha =
+ a_ptr[2 * x + 0] + a_ptr[2 * x + 1] +
+ a_ptr2[2 * x + 0] + a_ptr2[2 * x + 1];
+ u[x] = BLEND_10BIT(U0, u[x], alpha);
+ v[x] = BLEND_10BIT(V0, v[x], alpha);
+ }
+ if (pic->width & 1) { // rightmost pixel
+ const int alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
+ u[x] = BLEND_10BIT(U0, u[x], alpha);
+ v[x] = BLEND_10BIT(V0, v[x], alpha);
+ }
+ }
+ memset(a_ptr, 0xff, pic->width);
+ }
+ } else {
+ uint32_t* argb = pic->argb;
+ const uint32_t background = MakeARGB32(red, green, blue);
+ for (y = 0; y < pic->height; ++y) {
+ for (x = 0; x < pic->width; ++x) {
+ const int alpha = (argb[x] >> 24) & 0xff;
+ if (alpha != 0xff) {
+ if (alpha > 0) {
+ int r = (argb[x] >> 16) & 0xff;
+ int g = (argb[x] >> 8) & 0xff;
+ int b = (argb[x] >> 0) & 0xff;
+ r = BLEND(red, r, alpha);
+ g = BLEND(green, g, alpha);
+ b = BLEND(blue, b, alpha);
+ argb[x] = MakeARGB32(r, g, b);
+ } else {
+ argb[x] = background;
+ }
+ }
+ }
+ argb += pic->argb_stride;
+ }
+ }
+}
+
+#undef BLEND
+#undef BLEND_10BIT
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/predictor_enc.c b/src/third_party/libwebp/src/enc/predictor_enc.c
new file mode 100644
index 0000000..469cdea
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/predictor_enc.c
@@ -0,0 +1,776 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Image transform methods for lossless encoder.
+//
+// Authors: Vikas Arora (vikaas.arora@gmail.com)
+// Jyrki Alakuijala (jyrki@google.com)
+// Urvang Joshi (urvang@google.com)
+// Vincent Rabaud (vrabaud@google.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#endif
+
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+#include "src/enc/vp8li_enc.h"
+
+#define MAX_DIFF_COST (1e30f)
+
+static const float kSpatialPredictorBias = 15.f;
+static const int kPredLowEffort = 11;
+static const uint32_t kMaskAlpha = 0xff000000;
+
+// Mostly used to reduce code size + readability
+static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; }
+
+//------------------------------------------------------------------------------
+// Methods to calculate Entropy (Shannon).
+
+static float PredictionCostSpatial(const int counts[256], int weight_0,
+ double exp_val) {
+ const int significant_symbols = 256 >> 4;
+ const double exp_decay_factor = 0.6;
+ double bits = weight_0 * counts[0];
+ int i;
+ for (i = 1; i < significant_symbols; ++i) {
+ bits += exp_val * (counts[i] + counts[256 - i]);
+ exp_val *= exp_decay_factor;
+ }
+ return (float)(-0.1 * bits);
+}
+
+static float PredictionCostSpatialHistogram(const int accumulated[4][256],
+ const int tile[4][256]) {
+ int i;
+ double retval = 0;
+ for (i = 0; i < 4; ++i) {
+ const double kExpValue = 0.94;
+ retval += PredictionCostSpatial(tile[i], 1, kExpValue);
+ retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]);
+ }
+ return (float)retval;
+}
+
+static WEBP_INLINE void UpdateHisto(int histo_argb[4][256], uint32_t argb) {
+ ++histo_argb[0][argb >> 24];
+ ++histo_argb[1][(argb >> 16) & 0xff];
+ ++histo_argb[2][(argb >> 8) & 0xff];
+ ++histo_argb[3][argb & 0xff];
+}
+
+//------------------------------------------------------------------------------
+// Spatial transform functions.
+
+static WEBP_INLINE void PredictBatch(int mode, int x_start, int y,
+ int num_pixels, const uint32_t* current,
+ const uint32_t* upper, uint32_t* out) {
+ if (x_start == 0) {
+ if (y == 0) {
+ // ARGB_BLACK.
+ VP8LPredictorsSub[0](current, NULL, 1, out);
+ } else {
+ // Top one.
+ VP8LPredictorsSub[2](current, upper, 1, out);
+ }
+ ++x_start;
+ ++out;
+ --num_pixels;
+ }
+ if (y == 0) {
+ // Left one.
+ VP8LPredictorsSub[1](current + x_start, NULL, num_pixels, out);
+ } else {
+ VP8LPredictorsSub[mode](current + x_start, upper + x_start, num_pixels,
+ out);
+ }
+}
+
+#if (WEBP_NEAR_LOSSLESS == 1)
+static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; }
+
+static int MaxDiffBetweenPixels(uint32_t p1, uint32_t p2) {
+ const int diff_a = abs((int)(p1 >> 24) - (int)(p2 >> 24));
+ const int diff_r = abs((int)((p1 >> 16) & 0xff) - (int)((p2 >> 16) & 0xff));
+ const int diff_g = abs((int)((p1 >> 8) & 0xff) - (int)((p2 >> 8) & 0xff));
+ const int diff_b = abs((int)(p1 & 0xff) - (int)(p2 & 0xff));
+ return GetMax(GetMax(diff_a, diff_r), GetMax(diff_g, diff_b));
+}
+
+static int MaxDiffAroundPixel(uint32_t current, uint32_t up, uint32_t down,
+ uint32_t left, uint32_t right) {
+ const int diff_up = MaxDiffBetweenPixels(current, up);
+ const int diff_down = MaxDiffBetweenPixels(current, down);
+ const int diff_left = MaxDiffBetweenPixels(current, left);
+ const int diff_right = MaxDiffBetweenPixels(current, right);
+ return GetMax(GetMax(diff_up, diff_down), GetMax(diff_left, diff_right));
+}
+
+static uint32_t AddGreenToBlueAndRed(uint32_t argb) {
+ const uint32_t green = (argb >> 8) & 0xff;
+ uint32_t red_blue = argb & 0x00ff00ffu;
+ red_blue += (green << 16) | green;
+ red_blue &= 0x00ff00ffu;
+ return (argb & 0xff00ff00u) | red_blue;
+}
+
+static void MaxDiffsForRow(int width, int stride, const uint32_t* const argb,
+ uint8_t* const max_diffs, int used_subtract_green) {
+ uint32_t current, up, down, left, right;
+ int x;
+ if (width <= 2) return;
+ current = argb[0];
+ right = argb[1];
+ if (used_subtract_green) {
+ current = AddGreenToBlueAndRed(current);
+ right = AddGreenToBlueAndRed(right);
+ }
+ // max_diffs[0] and max_diffs[width - 1] are never used.
+ for (x = 1; x < width - 1; ++x) {
+ up = argb[-stride + x];
+ down = argb[stride + x];
+ left = current;
+ current = right;
+ right = argb[x + 1];
+ if (used_subtract_green) {
+ up = AddGreenToBlueAndRed(up);
+ down = AddGreenToBlueAndRed(down);
+ right = AddGreenToBlueAndRed(right);
+ }
+ max_diffs[x] = MaxDiffAroundPixel(current, up, down, left, right);
+ }
+}
+
+// Quantize the difference between the actual component value and its prediction
+// to a multiple of quantization, working modulo 256, taking care not to cross
+// a boundary (inclusive upper limit).
+static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict,
+ uint8_t boundary, int quantization) {
+ const int residual = (value - predict) & 0xff;
+ const int boundary_residual = (boundary - predict) & 0xff;
+ const int lower = residual & ~(quantization - 1);
+ const int upper = lower + quantization;
+ // Resolve ties towards a value closer to the prediction (i.e. towards lower
+ // if value comes after prediction and towards upper otherwise).
+ const int bias = ((boundary - value) & 0xff) < boundary_residual;
+ if (residual - lower < upper - residual + bias) {
+ // lower is closer to residual than upper.
+ if (residual > boundary_residual && lower <= boundary_residual) {
+ // Halve quantization step to avoid crossing boundary. This midpoint is
+ // on the same side of boundary as residual because midpoint >= residual
+ // (since lower is closer than upper) and residual is above the boundary.
+ return lower + (quantization >> 1);
+ }
+ return lower;
+ } else {
+ // upper is closer to residual than lower.
+ if (residual <= boundary_residual && upper > boundary_residual) {
+ // Halve quantization step to avoid crossing boundary. This midpoint is
+ // on the same side of boundary as residual because midpoint <= residual
+ // (since upper is closer than lower) and residual is below the boundary.
+ return lower + (quantization >> 1);
+ }
+ return upper & 0xff;
+ }
+}
+
+// Quantize every component of the difference between the actual pixel value and
+// its prediction to a multiple of a quantization (a power of 2, not larger than
+// max_quantization which is a power of 2, smaller than max_diff). Take care if
+// value and predict have undergone subtract green, which means that red and
+// blue are represented as offsets from green.
+#define NEAR_LOSSLESS_DIFF(a, b) (uint8_t)((((int)(a) - (int)(b))) & 0xff)
+static uint32_t NearLossless(uint32_t value, uint32_t predict,
+ int max_quantization, int max_diff,
+ int used_subtract_green) {
+ int quantization;
+ uint8_t new_green = 0;
+ uint8_t green_diff = 0;
+ uint8_t a, r, g, b;
+ if (max_diff <= 2) {
+ return VP8LSubPixels(value, predict);
+ }
+ quantization = max_quantization;
+ while (quantization >= max_diff) {
+ quantization >>= 1;
+ }
+ if ((value >> 24) == 0 || (value >> 24) == 0xff) {
+ // Preserve transparency of fully transparent or fully opaque pixels.
+ a = NEAR_LOSSLESS_DIFF(value >> 24, predict >> 24);
+ } else {
+ a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization);
+ }
+ g = NearLosslessComponent((value >> 8) & 0xff, (predict >> 8) & 0xff, 0xff,
+ quantization);
+ if (used_subtract_green) {
+ // The green offset will be added to red and blue components during decoding
+ // to obtain the actual red and blue values.
+ new_green = ((predict >> 8) + g) & 0xff;
+ // The amount by which green has been adjusted during quantization. It is
+ // subtracted from red and blue for compensation, to avoid accumulating two
+ // quantization errors in them.
+ green_diff = NEAR_LOSSLESS_DIFF(new_green, value >> 8);
+ }
+ r = NearLosslessComponent(NEAR_LOSSLESS_DIFF(value >> 16, green_diff),
+ (predict >> 16) & 0xff, 0xff - new_green,
+ quantization);
+ b = NearLosslessComponent(NEAR_LOSSLESS_DIFF(value, green_diff),
+ predict & 0xff, 0xff - new_green, quantization);
+ return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+}
+#undef NEAR_LOSSLESS_DIFF
+#endif // (WEBP_NEAR_LOSSLESS == 1)
+
+// Stores the difference between the pixel and its prediction in "out".
+// In case of a lossy encoding, updates the source image to avoid propagating
+// the deviation further to pixels which depend on the current pixel for their
+// predictions.
+static WEBP_INLINE void GetResidual(
+ int width, int height, uint32_t* const upper_row,
+ uint32_t* const current_row, const uint8_t* const max_diffs, int mode,
+ int x_start, int x_end, int y, int max_quantization, int exact,
+ int used_subtract_green, uint32_t* const out) {
+ if (exact) {
+ PredictBatch(mode, x_start, y, x_end - x_start, current_row, upper_row,
+ out);
+ } else {
+ const VP8LPredictorFunc pred_func = VP8LPredictors[mode];
+ int x;
+ for (x = x_start; x < x_end; ++x) {
+ uint32_t predict;
+ uint32_t residual;
+ if (y == 0) {
+ predict = (x == 0) ? ARGB_BLACK : current_row[x - 1]; // Left.
+ } else if (x == 0) {
+ predict = upper_row[x]; // Top.
+ } else {
+ predict = pred_func(current_row[x - 1], upper_row + x);
+ }
+#if (WEBP_NEAR_LOSSLESS == 1)
+ if (max_quantization == 1 || mode == 0 || y == 0 || y == height - 1 ||
+ x == 0 || x == width - 1) {
+ residual = VP8LSubPixels(current_row[x], predict);
+ } else {
+ residual = NearLossless(current_row[x], predict, max_quantization,
+ max_diffs[x], used_subtract_green);
+ // Update the source image.
+ current_row[x] = VP8LAddPixels(predict, residual);
+ // x is never 0 here so we do not need to update upper_row like below.
+ }
+#else
+ (void)max_diffs;
+ (void)height;
+ (void)max_quantization;
+ (void)used_subtract_green;
+ residual = VP8LSubPixels(current_row[x], predict);
+#endif
+ if ((current_row[x] & kMaskAlpha) == 0) {
+ // If alpha is 0, cleanup RGB. We can choose the RGB values of the
+ // residual for best compression. The prediction of alpha itself can be
+ // non-zero and must be kept though. We choose RGB of the residual to be
+ // 0.
+ residual &= kMaskAlpha;
+ // Update the source image.
+ current_row[x] = predict & ~kMaskAlpha;
+ // The prediction for the rightmost pixel in a row uses the leftmost
+ // pixel
+ // in that row as its top-right context pixel. Hence if we change the
+ // leftmost pixel of current_row, the corresponding change must be
+ // applied
+ // to upper_row as well where top-right context is being read from.
+ if (x == 0 && y != 0) upper_row[width] = current_row[0];
+ }
+ out[x - x_start] = residual;
+ }
+ }
+}
+
+// Returns best predictor and updates the accumulated histogram.
+// If max_quantization > 1, assumes that near lossless processing will be
+// applied, quantizing residuals to multiples of quantization levels up to
+// max_quantization (the actual quantization level depends on smoothness near
+// the given pixel).
+static int GetBestPredictorForTile(int width, int height,
+ int tile_x, int tile_y, int bits,
+ int accumulated[4][256],
+ uint32_t* const argb_scratch,
+ const uint32_t* const argb,
+ int max_quantization,
+ int exact, int used_subtract_green,
+ const uint32_t* const modes) {
+ const int kNumPredModes = 14;
+ const int start_x = tile_x << bits;
+ const int start_y = tile_y << bits;
+ const int tile_size = 1 << bits;
+ const int max_y = GetMin(tile_size, height - start_y);
+ const int max_x = GetMin(tile_size, width - start_x);
+ // Whether there exist columns just outside the tile.
+ const int have_left = (start_x > 0);
+ // Position and size of the strip covering the tile and adjacent columns if
+ // they exist.
+ const int context_start_x = start_x - have_left;
+#if (WEBP_NEAR_LOSSLESS == 1)
+ const int context_width = max_x + have_left + (max_x < width - start_x);
+#endif
+ const int tiles_per_row = VP8LSubSampleSize(width, bits);
+ // Prediction modes of the left and above neighbor tiles.
+ const int left_mode = (tile_x > 0) ?
+ (modes[tile_y * tiles_per_row + tile_x - 1] >> 8) & 0xff : 0xff;
+ const int above_mode = (tile_y > 0) ?
+ (modes[(tile_y - 1) * tiles_per_row + tile_x] >> 8) & 0xff : 0xff;
+ // The width of upper_row and current_row is one pixel larger than image width
+ // to allow the top right pixel to point to the leftmost pixel of the next row
+ // when at the right edge.
+ uint32_t* upper_row = argb_scratch;
+ uint32_t* current_row = upper_row + width + 1;
+ uint8_t* const max_diffs = (uint8_t*)(current_row + width + 1);
+ float best_diff = MAX_DIFF_COST;
+ int best_mode = 0;
+ int mode;
+ int histo_stack_1[4][256];
+ int histo_stack_2[4][256];
+ // Need pointers to be able to swap arrays.
+ int (*histo_argb)[256] = histo_stack_1;
+ int (*best_histo)[256] = histo_stack_2;
+ int i, j;
+ uint32_t residuals[1 << MAX_TRANSFORM_BITS];
+ assert(bits <= MAX_TRANSFORM_BITS);
+ assert(max_x <= (1 << MAX_TRANSFORM_BITS));
+
+ for (mode = 0; mode < kNumPredModes; ++mode) {
+ float cur_diff;
+ int relative_y;
+ memset(histo_argb, 0, sizeof(histo_stack_1));
+ if (start_y > 0) {
+ // Read the row above the tile which will become the first upper_row.
+ // Include a pixel to the left if it exists; include a pixel to the right
+ // in all cases (wrapping to the leftmost pixel of the next row if it does
+ // not exist).
+ memcpy(current_row + context_start_x,
+ argb + (start_y - 1) * width + context_start_x,
+ sizeof(*argb) * (max_x + have_left + 1));
+ }
+ for (relative_y = 0; relative_y < max_y; ++relative_y) {
+ const int y = start_y + relative_y;
+ int relative_x;
+ uint32_t* tmp = upper_row;
+ upper_row = current_row;
+ current_row = tmp;
+ // Read current_row. Include a pixel to the left if it exists; include a
+ // pixel to the right in all cases except at the bottom right corner of
+ // the image (wrapping to the leftmost pixel of the next row if it does
+ // not exist in the current row).
+ memcpy(current_row + context_start_x,
+ argb + y * width + context_start_x,
+ sizeof(*argb) * (max_x + have_left + (y + 1 < height)));
+#if (WEBP_NEAR_LOSSLESS == 1)
+ if (max_quantization > 1 && y >= 1 && y + 1 < height) {
+ MaxDiffsForRow(context_width, width, argb + y * width + context_start_x,
+ max_diffs + context_start_x, used_subtract_green);
+ }
+#endif
+
+ GetResidual(width, height, upper_row, current_row, max_diffs, mode,
+ start_x, start_x + max_x, y, max_quantization, exact,
+ used_subtract_green, residuals);
+ for (relative_x = 0; relative_x < max_x; ++relative_x) {
+ UpdateHisto(histo_argb, residuals[relative_x]);
+ }
+ }
+ cur_diff = PredictionCostSpatialHistogram(
+ (const int (*)[256])accumulated, (const int (*)[256])histo_argb);
+ // Favor keeping the areas locally similar.
+ if (mode == left_mode) cur_diff -= kSpatialPredictorBias;
+ if (mode == above_mode) cur_diff -= kSpatialPredictorBias;
+
+ if (cur_diff < best_diff) {
+ int (*tmp)[256] = histo_argb;
+ histo_argb = best_histo;
+ best_histo = tmp;
+ best_diff = cur_diff;
+ best_mode = mode;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 256; j++) {
+ accumulated[i][j] += best_histo[i][j];
+ }
+ }
+
+ return best_mode;
+}
+
+// Converts pixels of the image to residuals with respect to predictions.
+// If max_quantization > 1, applies near lossless processing, quantizing
+// residuals to multiples of quantization levels up to max_quantization
+// (the actual quantization level depends on smoothness near the given pixel).
+static void CopyImageWithPrediction(int width, int height,
+ int bits, uint32_t* const modes,
+ uint32_t* const argb_scratch,
+ uint32_t* const argb,
+ int low_effort, int max_quantization,
+ int exact, int used_subtract_green) {
+ const int tiles_per_row = VP8LSubSampleSize(width, bits);
+ // The width of upper_row and current_row is one pixel larger than image width
+ // to allow the top right pixel to point to the leftmost pixel of the next row
+ // when at the right edge.
+ uint32_t* upper_row = argb_scratch;
+ uint32_t* current_row = upper_row + width + 1;
+ uint8_t* current_max_diffs = (uint8_t*)(current_row + width + 1);
+#if (WEBP_NEAR_LOSSLESS == 1)
+ uint8_t* lower_max_diffs = current_max_diffs + width;
+#endif
+ int y;
+
+ for (y = 0; y < height; ++y) {
+ int x;
+ uint32_t* const tmp32 = upper_row;
+ upper_row = current_row;
+ current_row = tmp32;
+ memcpy(current_row, argb + y * width,
+ sizeof(*argb) * (width + (y + 1 < height)));
+
+ if (low_effort) {
+ PredictBatch(kPredLowEffort, 0, y, width, current_row, upper_row,
+ argb + y * width);
+ } else {
+#if (WEBP_NEAR_LOSSLESS == 1)
+ if (max_quantization > 1) {
+ // Compute max_diffs for the lower row now, because that needs the
+ // contents of argb for the current row, which we will overwrite with
+ // residuals before proceeding with the next row.
+ uint8_t* const tmp8 = current_max_diffs;
+ current_max_diffs = lower_max_diffs;
+ lower_max_diffs = tmp8;
+ if (y + 2 < height) {
+ MaxDiffsForRow(width, width, argb + (y + 1) * width, lower_max_diffs,
+ used_subtract_green);
+ }
+ }
+#endif
+ for (x = 0; x < width;) {
+ const int mode =
+ (modes[(y >> bits) * tiles_per_row + (x >> bits)] >> 8) & 0xff;
+ int x_end = x + (1 << bits);
+ if (x_end > width) x_end = width;
+ GetResidual(width, height, upper_row, current_row, current_max_diffs,
+ mode, x, x_end, y, max_quantization, exact,
+ used_subtract_green, argb + y * width + x);
+ x = x_end;
+ }
+ }
+ }
+}
+
+// Finds the best predictor for each tile, and converts the image to residuals
+// with respect to predictions. If near_lossless_quality < 100, applies
+// near lossless processing, shaving off more bits of residuals for lower
+// qualities.
+void VP8LResidualImage(int width, int height, int bits, int low_effort,
+ uint32_t* const argb, uint32_t* const argb_scratch,
+ uint32_t* const image, int near_lossless_quality,
+ int exact, int used_subtract_green) {
+ const int tiles_per_row = VP8LSubSampleSize(width, bits);
+ const int tiles_per_col = VP8LSubSampleSize(height, bits);
+ int tile_y;
+ int histo[4][256];
+ const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality);
+ if (low_effort) {
+ int i;
+ for (i = 0; i < tiles_per_row * tiles_per_col; ++i) {
+ image[i] = ARGB_BLACK | (kPredLowEffort << 8);
+ }
+ } else {
+ memset(histo, 0, sizeof(histo));
+ for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) {
+ int tile_x;
+ for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) {
+ const int pred = GetBestPredictorForTile(width, height, tile_x, tile_y,
+ bits, histo, argb_scratch, argb, max_quantization, exact,
+ used_subtract_green, image);
+ image[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (pred << 8);
+ }
+ }
+ }
+
+ CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb,
+ low_effort, max_quantization, exact,
+ used_subtract_green);
+}
+
+//------------------------------------------------------------------------------
+// Color transform functions.
+
+static WEBP_INLINE void MultipliersClear(VP8LMultipliers* const m) {
+ m->green_to_red_ = 0;
+ m->green_to_blue_ = 0;
+ m->red_to_blue_ = 0;
+}
+
+static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code,
+ VP8LMultipliers* const m) {
+ m->green_to_red_ = (color_code >> 0) & 0xff;
+ m->green_to_blue_ = (color_code >> 8) & 0xff;
+ m->red_to_blue_ = (color_code >> 16) & 0xff;
+}
+
+static WEBP_INLINE uint32_t MultipliersToColorCode(
+ const VP8LMultipliers* const m) {
+ return 0xff000000u |
+ ((uint32_t)(m->red_to_blue_) << 16) |
+ ((uint32_t)(m->green_to_blue_) << 8) |
+ m->green_to_red_;
+}
+
+static float PredictionCostCrossColor(const int accumulated[256],
+ const int counts[256]) {
+ // Favor low entropy, locally and globally.
+ // Favor small absolute values for PredictionCostSpatial
+ static const double kExpValue = 2.4;
+ return VP8LCombinedShannonEntropy(counts, accumulated) +
+ PredictionCostSpatial(counts, 3, kExpValue);
+}
+
+static float GetPredictionCostCrossColorRed(
+ const uint32_t* argb, int stride, int tile_width, int tile_height,
+ VP8LMultipliers prev_x, VP8LMultipliers prev_y, int green_to_red,
+ const int accumulated_red_histo[256]) {
+ int histo[256] = { 0 };
+ float cur_diff;
+
+ VP8LCollectColorRedTransforms(argb, stride, tile_width, tile_height,
+ green_to_red, histo);
+
+ cur_diff = PredictionCostCrossColor(accumulated_red_histo, histo);
+ if ((uint8_t)green_to_red == prev_x.green_to_red_) {
+ cur_diff -= 3; // favor keeping the areas locally similar
+ }
+ if ((uint8_t)green_to_red == prev_y.green_to_red_) {
+ cur_diff -= 3; // favor keeping the areas locally similar
+ }
+ if (green_to_red == 0) {
+ cur_diff -= 3;
+ }
+ return cur_diff;
+}
+
+static void GetBestGreenToRed(
+ const uint32_t* argb, int stride, int tile_width, int tile_height,
+ VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality,
+ const int accumulated_red_histo[256], VP8LMultipliers* const best_tx) {
+ const int kMaxIters = 4 + ((7 * quality) >> 8); // in range [4..6]
+ int green_to_red_best = 0;
+ int iter, offset;
+ float best_diff = GetPredictionCostCrossColorRed(
+ argb, stride, tile_width, tile_height, prev_x, prev_y,
+ green_to_red_best, accumulated_red_histo);
+ for (iter = 0; iter < kMaxIters; ++iter) {
+ // ColorTransformDelta is a 3.5 bit fixed point, so 32 is equal to
+ // one in color computation. Having initial delta here as 1 is sufficient
+ // to explore the range of (-2, 2).
+ const int delta = 32 >> iter;
+ // Try a negative and a positive delta from the best known value.
+ for (offset = -delta; offset <= delta; offset += 2 * delta) {
+ const int green_to_red_cur = offset + green_to_red_best;
+ const float cur_diff = GetPredictionCostCrossColorRed(
+ argb, stride, tile_width, tile_height, prev_x, prev_y,
+ green_to_red_cur, accumulated_red_histo);
+ if (cur_diff < best_diff) {
+ best_diff = cur_diff;
+ green_to_red_best = green_to_red_cur;
+ }
+ }
+ }
+ best_tx->green_to_red_ = green_to_red_best;
+}
+
+static float GetPredictionCostCrossColorBlue(
+ const uint32_t* argb, int stride, int tile_width, int tile_height,
+ VP8LMultipliers prev_x, VP8LMultipliers prev_y,
+ int green_to_blue, int red_to_blue, const int accumulated_blue_histo[256]) {
+ int histo[256] = { 0 };
+ float cur_diff;
+
+ VP8LCollectColorBlueTransforms(argb, stride, tile_width, tile_height,
+ green_to_blue, red_to_blue, histo);
+
+ cur_diff = PredictionCostCrossColor(accumulated_blue_histo, histo);
+ if ((uint8_t)green_to_blue == prev_x.green_to_blue_) {
+ cur_diff -= 3; // favor keeping the areas locally similar
+ }
+ if ((uint8_t)green_to_blue == prev_y.green_to_blue_) {
+ cur_diff -= 3; // favor keeping the areas locally similar
+ }
+ if ((uint8_t)red_to_blue == prev_x.red_to_blue_) {
+ cur_diff -= 3; // favor keeping the areas locally similar
+ }
+ if ((uint8_t)red_to_blue == prev_y.red_to_blue_) {
+ cur_diff -= 3; // favor keeping the areas locally similar
+ }
+ if (green_to_blue == 0) {
+ cur_diff -= 3;
+ }
+ if (red_to_blue == 0) {
+ cur_diff -= 3;
+ }
+ return cur_diff;
+}
+
+#define kGreenRedToBlueNumAxis 8
+#define kGreenRedToBlueMaxIters 7
+static void GetBestGreenRedToBlue(
+ const uint32_t* argb, int stride, int tile_width, int tile_height,
+ VP8LMultipliers prev_x, VP8LMultipliers prev_y, int quality,
+ const int accumulated_blue_histo[256],
+ VP8LMultipliers* const best_tx) {
+ const int8_t offset[kGreenRedToBlueNumAxis][2] =
+ {{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}};
+ const int8_t delta_lut[kGreenRedToBlueMaxIters] = { 16, 16, 8, 4, 2, 2, 2 };
+ const int iters =
+ (quality < 25) ? 1 : (quality > 50) ? kGreenRedToBlueMaxIters : 4;
+ int green_to_blue_best = 0;
+ int red_to_blue_best = 0;
+ int iter;
+ // Initial value at origin:
+ float best_diff = GetPredictionCostCrossColorBlue(
+ argb, stride, tile_width, tile_height, prev_x, prev_y,
+ green_to_blue_best, red_to_blue_best, accumulated_blue_histo);
+ for (iter = 0; iter < iters; ++iter) {
+ const int delta = delta_lut[iter];
+ int axis;
+ for (axis = 0; axis < kGreenRedToBlueNumAxis; ++axis) {
+ const int green_to_blue_cur =
+ offset[axis][0] * delta + green_to_blue_best;
+ const int red_to_blue_cur = offset[axis][1] * delta + red_to_blue_best;
+ const float cur_diff = GetPredictionCostCrossColorBlue(
+ argb, stride, tile_width, tile_height, prev_x, prev_y,
+ green_to_blue_cur, red_to_blue_cur, accumulated_blue_histo);
+ if (cur_diff < best_diff) {
+ best_diff = cur_diff;
+ green_to_blue_best = green_to_blue_cur;
+ red_to_blue_best = red_to_blue_cur;
+ }
+ if (quality < 25 && iter == 4) {
+ // Only axis aligned diffs for lower quality.
+ break; // next iter.
+ }
+ }
+ if (delta == 2 && green_to_blue_best == 0 && red_to_blue_best == 0) {
+ // Further iterations would not help.
+ break; // out of iter-loop.
+ }
+ }
+ best_tx->green_to_blue_ = green_to_blue_best;
+ best_tx->red_to_blue_ = red_to_blue_best;
+}
+#undef kGreenRedToBlueMaxIters
+#undef kGreenRedToBlueNumAxis
+
+static VP8LMultipliers GetBestColorTransformForTile(
+ int tile_x, int tile_y, int bits,
+ VP8LMultipliers prev_x,
+ VP8LMultipliers prev_y,
+ int quality, int xsize, int ysize,
+ const int accumulated_red_histo[256],
+ const int accumulated_blue_histo[256],
+ const uint32_t* const argb) {
+ const int max_tile_size = 1 << bits;
+ const int tile_y_offset = tile_y * max_tile_size;
+ const int tile_x_offset = tile_x * max_tile_size;
+ const int all_x_max = GetMin(tile_x_offset + max_tile_size, xsize);
+ const int all_y_max = GetMin(tile_y_offset + max_tile_size, ysize);
+ const int tile_width = all_x_max - tile_x_offset;
+ const int tile_height = all_y_max - tile_y_offset;
+ const uint32_t* const tile_argb = argb + tile_y_offset * xsize
+ + tile_x_offset;
+ VP8LMultipliers best_tx;
+ MultipliersClear(&best_tx);
+
+ GetBestGreenToRed(tile_argb, xsize, tile_width, tile_height,
+ prev_x, prev_y, quality, accumulated_red_histo, &best_tx);
+ GetBestGreenRedToBlue(tile_argb, xsize, tile_width, tile_height,
+ prev_x, prev_y, quality, accumulated_blue_histo,
+ &best_tx);
+ return best_tx;
+}
+
+static void CopyTileWithColorTransform(int xsize, int ysize,
+ int tile_x, int tile_y,
+ int max_tile_size,
+ VP8LMultipliers color_transform,
+ uint32_t* argb) {
+ const int xscan = GetMin(max_tile_size, xsize - tile_x);
+ int yscan = GetMin(max_tile_size, ysize - tile_y);
+ argb += tile_y * xsize + tile_x;
+ while (yscan-- > 0) {
+ VP8LTransformColor(&color_transform, argb, xscan);
+ argb += xsize;
+ }
+}
+
+void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
+ uint32_t* const argb, uint32_t* image) {
+ const int max_tile_size = 1 << bits;
+ const int tile_xsize = VP8LSubSampleSize(width, bits);
+ const int tile_ysize = VP8LSubSampleSize(height, bits);
+ int accumulated_red_histo[256] = { 0 };
+ int accumulated_blue_histo[256] = { 0 };
+ int tile_x, tile_y;
+ VP8LMultipliers prev_x, prev_y;
+ MultipliersClear(&prev_y);
+ MultipliersClear(&prev_x);
+ for (tile_y = 0; tile_y < tile_ysize; ++tile_y) {
+ for (tile_x = 0; tile_x < tile_xsize; ++tile_x) {
+ int y;
+ const int tile_x_offset = tile_x * max_tile_size;
+ const int tile_y_offset = tile_y * max_tile_size;
+ const int all_x_max = GetMin(tile_x_offset + max_tile_size, width);
+ const int all_y_max = GetMin(tile_y_offset + max_tile_size, height);
+ const int offset = tile_y * tile_xsize + tile_x;
+ if (tile_y != 0) {
+ ColorCodeToMultipliers(image[offset - tile_xsize], &prev_y);
+ }
+ prev_x = GetBestColorTransformForTile(tile_x, tile_y, bits,
+ prev_x, prev_y,
+ quality, width, height,
+ accumulated_red_histo,
+ accumulated_blue_histo,
+ argb);
+ image[offset] = MultipliersToColorCode(&prev_x);
+ CopyTileWithColorTransform(width, height, tile_x_offset, tile_y_offset,
+ max_tile_size, prev_x, argb);
+
+ // Gather accumulated histogram data.
+ for (y = tile_y_offset; y < all_y_max; ++y) {
+ int ix = y * width + tile_x_offset;
+ const int ix_end = ix + all_x_max - tile_x_offset;
+ for (; ix < ix_end; ++ix) {
+ const uint32_t pix = argb[ix];
+ if (ix >= 2 &&
+ pix == argb[ix - 2] &&
+ pix == argb[ix - 1]) {
+ continue; // repeated pixels are handled by backward references
+ }
+ if (ix >= width + 2 &&
+ argb[ix - 2] == argb[ix - width - 2] &&
+ argb[ix - 1] == argb[ix - width - 1] &&
+ pix == argb[ix - width]) {
+ continue; // repeated pixels are handled by backward references
+ }
+ ++accumulated_red_histo[(pix >> 16) & 0xff];
+ ++accumulated_blue_histo[(pix >> 0) & 0xff];
+ }
+ }
+ }
+ }
+}
diff --git a/src/third_party/libwebp/src/enc/quant_enc.c b/src/third_party/libwebp/src/enc/quant_enc.c
new file mode 100644
index 0000000..e21fd4e
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/quant_enc.c
@@ -0,0 +1,1377 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Quantization
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/math_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h> // for abs()
+#endif
+
+#include "src/enc/vp8i_enc.h"
+#include "src/enc/cost_enc.h"
+
+#define DO_TRELLIS_I4 1
+#define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate.
+#define DO_TRELLIS_UV 0 // disable trellis for UV. Risky. Not worth.
+#define USE_TDISTO 1
+
+#define MID_ALPHA 64 // neutral value for susceptibility
+#define MIN_ALPHA 30 // lowest usable value for susceptibility
+#define MAX_ALPHA 100 // higher meaningful value for susceptibility
+
+#define SNS_TO_DQ 0.9 // Scaling constant between the sns value and the QP
+ // power-law modulation. Must be strictly less than 1.
+
+// number of non-zero coeffs below which we consider the block very flat
+// (and apply a penalty to complex predictions)
+#define FLATNESS_LIMIT_I16 10 // I16 mode
+#define FLATNESS_LIMIT_I4 3 // I4 mode
+#define FLATNESS_LIMIT_UV 2 // UV mode
+#define FLATNESS_PENALTY 140 // roughly ~1bit per block
+
+#define MULT_8B(a, b) (((a) * (b) + 128) >> 8)
+
+#define RD_DISTO_MULT 256 // distortion multiplier (equivalent of lambda)
+
+// #define DEBUG_BLOCK
+
+//------------------------------------------------------------------------------
+
+#if defined(DEBUG_BLOCK)
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void PrintBlockInfo(const VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int i, j;
+ const int is_i16 = (it->mb_->type_ == 1);
+ const uint8_t* const y_in = it->yuv_in_ + Y_OFF_ENC;
+ const uint8_t* const y_out = it->yuv_out_ + Y_OFF_ENC;
+ const uint8_t* const uv_in = it->yuv_in_ + U_OFF_ENC;
+ const uint8_t* const uv_out = it->yuv_out_ + U_OFF_ENC;
+ printf("SOURCE / OUTPUT / ABS DELTA\n");
+ for (j = 0; j < 16; ++j) {
+ for (i = 0; i < 16; ++i) printf("%3d ", y_in[i + j * BPS]);
+ printf(" ");
+ for (i = 0; i < 16; ++i) printf("%3d ", y_out[i + j * BPS]);
+ printf(" ");
+ for (i = 0; i < 16; ++i) {
+ printf("%1d ", abs(y_in[i + j * BPS] - y_out[i + j * BPS]));
+ }
+ printf("\n");
+ }
+ printf("\n"); // newline before the U/V block
+ for (j = 0; j < 8; ++j) {
+ for (i = 0; i < 8; ++i) printf("%3d ", uv_in[i + j * BPS]);
+ printf(" ");
+ for (i = 8; i < 16; ++i) printf("%3d ", uv_in[i + j * BPS]);
+ printf(" ");
+ for (i = 0; i < 8; ++i) printf("%3d ", uv_out[i + j * BPS]);
+ printf(" ");
+ for (i = 8; i < 16; ++i) printf("%3d ", uv_out[i + j * BPS]);
+ printf(" ");
+ for (i = 0; i < 8; ++i) {
+ printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS]));
+ }
+ printf(" ");
+ for (i = 8; i < 16; ++i) {
+ printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS]));
+ }
+ printf("\n");
+ }
+ printf("\nD:%d SD:%d R:%d H:%d nz:0x%x score:%d\n",
+ (int)rd->D, (int)rd->SD, (int)rd->R, (int)rd->H, (int)rd->nz,
+ (int)rd->score);
+ if (is_i16) {
+ printf("Mode: %d\n", rd->mode_i16);
+ printf("y_dc_levels:");
+ for (i = 0; i < 16; ++i) printf("%3d ", rd->y_dc_levels[i]);
+ printf("\n");
+ } else {
+ printf("Modes[16]: ");
+ for (i = 0; i < 16; ++i) printf("%d ", rd->modes_i4[i]);
+ printf("\n");
+ }
+ printf("y_ac_levels:\n");
+ for (j = 0; j < 16; ++j) {
+ for (i = is_i16 ? 1 : 0; i < 16; ++i) {
+ printf("%4d ", rd->y_ac_levels[j][i]);
+ }
+ printf("\n");
+ }
+ printf("\n");
+ printf("uv_levels (mode=%d):\n", rd->mode_uv);
+ for (j = 0; j < 8; ++j) {
+ for (i = 0; i < 16; ++i) {
+ printf("%4d ", rd->uv_levels[j][i]);
+ }
+ printf("\n");
+ }
+}
+
+#endif // DEBUG_BLOCK
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE int clip(int v, int m, int M) {
+ return v < m ? m : v > M ? M : v;
+}
+
+static const uint8_t kZigzag[16] = {
+ 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
+};
+
+static const uint8_t kDcTable[128] = {
+ 4, 5, 6, 7, 8, 9, 10, 10,
+ 11, 12, 13, 14, 15, 16, 17, 17,
+ 18, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 25, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89,
+ 91, 93, 95, 96, 98, 100, 101, 102,
+ 104, 106, 108, 110, 112, 114, 116, 118,
+ 122, 124, 126, 128, 130, 132, 134, 136,
+ 138, 140, 143, 145, 148, 151, 154, 157
+};
+
+static const uint16_t kAcTable[128] = {
+ 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 60,
+ 62, 64, 66, 68, 70, 72, 74, 76,
+ 78, 80, 82, 84, 86, 88, 90, 92,
+ 94, 96, 98, 100, 102, 104, 106, 108,
+ 110, 112, 114, 116, 119, 122, 125, 128,
+ 131, 134, 137, 140, 143, 146, 149, 152,
+ 155, 158, 161, 164, 167, 170, 173, 177,
+ 181, 185, 189, 193, 197, 201, 205, 209,
+ 213, 217, 221, 225, 229, 234, 239, 245,
+ 249, 254, 259, 264, 269, 274, 279, 284
+};
+
+static const uint16_t kAcTable2[128] = {
+ 8, 8, 9, 10, 12, 13, 15, 17,
+ 18, 20, 21, 23, 24, 26, 27, 29,
+ 31, 32, 34, 35, 37, 38, 40, 41,
+ 43, 44, 46, 48, 49, 51, 52, 54,
+ 55, 57, 58, 60, 62, 63, 65, 66,
+ 68, 69, 71, 72, 74, 75, 77, 79,
+ 80, 82, 83, 85, 86, 88, 89, 93,
+ 96, 99, 102, 105, 108, 111, 114, 117,
+ 120, 124, 127, 130, 133, 136, 139, 142,
+ 145, 148, 151, 155, 158, 161, 164, 167,
+ 170, 173, 176, 179, 184, 189, 193, 198,
+ 203, 207, 212, 217, 221, 226, 230, 235,
+ 240, 244, 249, 254, 258, 263, 268, 274,
+ 280, 286, 292, 299, 305, 311, 317, 323,
+ 330, 336, 342, 348, 354, 362, 370, 379,
+ 385, 393, 401, 409, 416, 424, 432, 440
+};
+
+static const uint8_t kBiasMatrices[3][2] = { // [luma-ac,luma-dc,chroma][dc,ac]
+ { 96, 110 }, { 96, 108 }, { 110, 115 }
+};
+
+// Sharpening by (slightly) raising the hi-frequency coeffs.
+// Hack-ish but helpful for mid-bitrate range. Use with care.
+#define SHARPEN_BITS 11 // number of descaling bits for sharpening bias
+static const uint8_t kFreqSharpening[16] = {
+ 0, 30, 60, 90,
+ 30, 60, 90, 90,
+ 60, 90, 90, 90,
+ 90, 90, 90, 90
+};
+
+//------------------------------------------------------------------------------
+// Initialize quantization parameters in VP8Matrix
+
+// Returns the average quantizer
+static int ExpandMatrix(VP8Matrix* const m, int type) {
+ int i, sum;
+ for (i = 0; i < 2; ++i) {
+ const int is_ac_coeff = (i > 0);
+ const int bias = kBiasMatrices[type][is_ac_coeff];
+ m->iq_[i] = (1 << QFIX) / m->q_[i];
+ m->bias_[i] = BIAS(bias);
+ // zthresh_ is the exact value such that QUANTDIV(coeff, iQ, B) is:
+ // * zero if coeff <= zthresh
+ // * non-zero if coeff > zthresh
+ m->zthresh_[i] = ((1 << QFIX) - 1 - m->bias_[i]) / m->iq_[i];
+ }
+ for (i = 2; i < 16; ++i) {
+ m->q_[i] = m->q_[1];
+ m->iq_[i] = m->iq_[1];
+ m->bias_[i] = m->bias_[1];
+ m->zthresh_[i] = m->zthresh_[1];
+ }
+ for (sum = 0, i = 0; i < 16; ++i) {
+ if (type == 0) { // we only use sharpening for AC luma coeffs
+ m->sharpen_[i] = (kFreqSharpening[i] * m->q_[i]) >> SHARPEN_BITS;
+ } else {
+ m->sharpen_[i] = 0;
+ }
+ sum += m->q_[i];
+ }
+ return (sum + 8) >> 4;
+}
+
+static void CheckLambdaValue(int* const v) { if (*v < 1) *v = 1; }
+
+static void SetupMatrices(VP8Encoder* enc) {
+ int i;
+ const int tlambda_scale =
+ (enc->method_ >= 4) ? enc->config_->sns_strength
+ : 0;
+ const int num_segments = enc->segment_hdr_.num_segments_;
+ for (i = 0; i < num_segments; ++i) {
+ VP8SegmentInfo* const m = &enc->dqm_[i];
+ const int q = m->quant_;
+ int q_i4, q_i16, q_uv;
+ m->y1_.q_[0] = kDcTable[clip(q + enc->dq_y1_dc_, 0, 127)];
+ m->y1_.q_[1] = kAcTable[clip(q, 0, 127)];
+
+ m->y2_.q_[0] = kDcTable[ clip(q + enc->dq_y2_dc_, 0, 127)] * 2;
+ m->y2_.q_[1] = kAcTable2[clip(q + enc->dq_y2_ac_, 0, 127)];
+
+ m->uv_.q_[0] = kDcTable[clip(q + enc->dq_uv_dc_, 0, 117)];
+ m->uv_.q_[1] = kAcTable[clip(q + enc->dq_uv_ac_, 0, 127)];
+
+ q_i4 = ExpandMatrix(&m->y1_, 0);
+ q_i16 = ExpandMatrix(&m->y2_, 1);
+ q_uv = ExpandMatrix(&m->uv_, 2);
+
+ m->lambda_i4_ = (3 * q_i4 * q_i4) >> 7;
+ m->lambda_i16_ = (3 * q_i16 * q_i16);
+ m->lambda_uv_ = (3 * q_uv * q_uv) >> 6;
+ m->lambda_mode_ = (1 * q_i4 * q_i4) >> 7;
+ m->lambda_trellis_i4_ = (7 * q_i4 * q_i4) >> 3;
+ m->lambda_trellis_i16_ = (q_i16 * q_i16) >> 2;
+ m->lambda_trellis_uv_ = (q_uv * q_uv) << 1;
+ m->tlambda_ = (tlambda_scale * q_i4) >> 5;
+
+ // none of these constants should be < 1
+ CheckLambdaValue(&m->lambda_i4_);
+ CheckLambdaValue(&m->lambda_i16_);
+ CheckLambdaValue(&m->lambda_uv_);
+ CheckLambdaValue(&m->lambda_mode_);
+ CheckLambdaValue(&m->lambda_trellis_i4_);
+ CheckLambdaValue(&m->lambda_trellis_i16_);
+ CheckLambdaValue(&m->lambda_trellis_uv_);
+ CheckLambdaValue(&m->tlambda_);
+
+ m->min_disto_ = 20 * m->y1_.q_[0]; // quantization-aware min disto
+ m->max_edge_ = 0;
+
+ m->i4_penalty_ = 1000 * q_i4 * q_i4;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Initialize filtering parameters
+
+// Very small filter-strength values have close to no visual effect. So we can
+// save a little decoding-CPU by turning filtering off for these.
+#define FSTRENGTH_CUTOFF 2
+
+static void SetupFilterStrength(VP8Encoder* const enc) {
+ int i;
+ // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
+ const int level0 = 5 * enc->config_->filter_strength;
+ for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ VP8SegmentInfo* const m = &enc->dqm_[i];
+ // We focus on the quantization of AC coeffs.
+ const int qstep = kAcTable[clip(m->quant_, 0, 127)] >> 2;
+ const int base_strength =
+ VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, qstep);
+ // Segments with lower complexity ('beta') will be less filtered.
+ const int f = base_strength * level0 / (256 + m->beta_);
+ m->fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f;
+ }
+ // We record the initial strength (mainly for the case of 1-segment only).
+ enc->filter_hdr_.level_ = enc->dqm_[0].fstrength_;
+ enc->filter_hdr_.simple_ = (enc->config_->filter_type == 0);
+ enc->filter_hdr_.sharpness_ = enc->config_->filter_sharpness;
+}
+
+//------------------------------------------------------------------------------
+
+// Note: if you change the values below, remember that the max range
+// allowed by the syntax for DQ_UV is [-16,16].
+#define MAX_DQ_UV (6)
+#define MIN_DQ_UV (-4)
+
+// We want to emulate jpeg-like behaviour where the expected "good" quality
+// is around q=75. Internally, our "good" middle is around c=50. So we
+// map accordingly using linear piece-wise function
+static double QualityToCompression(double c) {
+ const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.;
+ // The file size roughly scales as pow(quantizer, 3.). Actually, the
+ // exponent is somewhere between 2.8 and 3.2, but we're mostly interested
+ // in the mid-quant range. So we scale the compressibility inversely to
+ // this power-law: quant ~= compression ^ 1/3. This law holds well for
+ // low quant. Finer modeling for high-quant would make use of kAcTable[]
+ // more explicitly.
+ const double v = pow(linear_c, 1 / 3.);
+ return v;
+}
+
+static double QualityToJPEGCompression(double c, double alpha) {
+ // We map the complexity 'alpha' and quality setting 'c' to a compression
+ // exponent empirically matched to the compression curve of libjpeg6b.
+ // On average, the WebP output size will be roughly similar to that of a
+ // JPEG file compressed with same quality factor.
+ const double amin = 0.30;
+ const double amax = 0.85;
+ const double exp_min = 0.4;
+ const double exp_max = 0.9;
+ const double slope = (exp_min - exp_max) / (amax - amin);
+ // Linearly interpolate 'expn' from exp_min to exp_max
+ // in the [amin, amax] range.
+ const double expn = (alpha > amax) ? exp_min
+ : (alpha < amin) ? exp_max
+ : exp_max + slope * (alpha - amin);
+ const double v = pow(c, expn);
+ return v;
+}
+
+static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1,
+ const VP8SegmentInfo* const S2) {
+ return (S1->quant_ == S2->quant_) && (S1->fstrength_ == S2->fstrength_);
+}
+
+static void SimplifySegments(VP8Encoder* const enc) {
+ int map[NUM_MB_SEGMENTS] = { 0, 1, 2, 3 };
+ // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an
+ // explicit check is needed to avoid a spurious warning about 'i' exceeding
+ // array bounds of 'dqm_' with some compilers (noticed with gcc-4.9).
+ const int num_segments = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS)
+ ? enc->segment_hdr_.num_segments_
+ : NUM_MB_SEGMENTS;
+ int num_final_segments = 1;
+ int s1, s2;
+ for (s1 = 1; s1 < num_segments; ++s1) { // find similar segments
+ const VP8SegmentInfo* const S1 = &enc->dqm_[s1];
+ int found = 0;
+ // check if we already have similar segment
+ for (s2 = 0; s2 < num_final_segments; ++s2) {
+ const VP8SegmentInfo* const S2 = &enc->dqm_[s2];
+ if (SegmentsAreEquivalent(S1, S2)) {
+ found = 1;
+ break;
+ }
+ }
+ map[s1] = s2;
+ if (!found) {
+ if (num_final_segments != s1) {
+ enc->dqm_[num_final_segments] = enc->dqm_[s1];
+ }
+ ++num_final_segments;
+ }
+ }
+ if (num_final_segments < num_segments) { // Remap
+ int i = enc->mb_w_ * enc->mb_h_;
+ while (i-- > 0) enc->mb_info_[i].segment_ = map[enc->mb_info_[i].segment_];
+ enc->segment_hdr_.num_segments_ = num_final_segments;
+ // Replicate the trailing segment infos (it's mostly cosmetics)
+ for (i = num_final_segments; i < num_segments; ++i) {
+ enc->dqm_[i] = enc->dqm_[num_final_segments - 1];
+ }
+ }
+}
+
+void VP8SetSegmentParams(VP8Encoder* const enc, float quality) {
+ int i;
+ int dq_uv_ac, dq_uv_dc;
+ const int num_segments = enc->segment_hdr_.num_segments_;
+ const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.;
+ const double Q = quality / 100.;
+ const double c_base = enc->config_->emulate_jpeg_size ?
+ QualityToJPEGCompression(Q, enc->alpha_ / 255.) :
+ QualityToCompression(Q);
+ for (i = 0; i < num_segments; ++i) {
+ // We modulate the base coefficient to accommodate for the quantization
+ // susceptibility and allow denser segments to be quantized more.
+ const double expn = 1. - amp * enc->dqm_[i].alpha_;
+ const double c = pow(c_base, expn);
+ const int q = (int)(127. * (1. - c));
+ assert(expn > 0.);
+ enc->dqm_[i].quant_ = clip(q, 0, 127);
+ }
+
+ // purely indicative in the bitstream (except for the 1-segment case)
+ enc->base_quant_ = enc->dqm_[0].quant_;
+
+ // fill-in values for the unused segments (required by the syntax)
+ for (i = num_segments; i < NUM_MB_SEGMENTS; ++i) {
+ enc->dqm_[i].quant_ = enc->base_quant_;
+ }
+
+ // uv_alpha_ is normally spread around ~60. The useful range is
+ // typically ~30 (quite bad) to ~100 (ok to decimate UV more).
+ // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv.
+ dq_uv_ac = (enc->uv_alpha_ - MID_ALPHA) * (MAX_DQ_UV - MIN_DQ_UV)
+ / (MAX_ALPHA - MIN_ALPHA);
+ // we rescale by the user-defined strength of adaptation
+ dq_uv_ac = dq_uv_ac * enc->config_->sns_strength / 100;
+ // and make it safe.
+ dq_uv_ac = clip(dq_uv_ac, MIN_DQ_UV, MAX_DQ_UV);
+ // We also boost the dc-uv-quant a little, based on sns-strength, since
+ // U/V channels are quite more reactive to high quants (flat DC-blocks
+ // tend to appear, and are unpleasant).
+ dq_uv_dc = -4 * enc->config_->sns_strength / 100;
+ dq_uv_dc = clip(dq_uv_dc, -15, 15); // 4bit-signed max allowed
+
+ enc->dq_y1_dc_ = 0; // TODO(skal): dq-lum
+ enc->dq_y2_dc_ = 0;
+ enc->dq_y2_ac_ = 0;
+ enc->dq_uv_dc_ = dq_uv_dc;
+ enc->dq_uv_ac_ = dq_uv_ac;
+
+ SetupFilterStrength(enc); // initialize segments' filtering, eventually
+
+ if (num_segments > 1) SimplifySegments(enc);
+
+ SetupMatrices(enc); // finalize quantization matrices
+}
+
+//------------------------------------------------------------------------------
+// Form the predictions in cache
+
+// Must be ordered using {DC_PRED, TM_PRED, V_PRED, H_PRED} as index
+const uint16_t VP8I16ModeOffsets[4] = { I16DC16, I16TM16, I16VE16, I16HE16 };
+const uint16_t VP8UVModeOffsets[4] = { C8DC8, C8TM8, C8VE8, C8HE8 };
+
+// Must be indexed using {B_DC_PRED -> B_HU_PRED} as index
+const uint16_t VP8I4ModeOffsets[NUM_BMODES] = {
+ I4DC4, I4TM4, I4VE4, I4HE4, I4RD4, I4VR4, I4LD4, I4VL4, I4HD4, I4HU4
+};
+
+void VP8MakeLuma16Preds(const VP8EncIterator* const it) {
+ const uint8_t* const left = it->x_ ? it->y_left_ : NULL;
+ const uint8_t* const top = it->y_ ? it->y_top_ : NULL;
+ VP8EncPredLuma16(it->yuv_p_, left, top);
+}
+
+void VP8MakeChroma8Preds(const VP8EncIterator* const it) {
+ const uint8_t* const left = it->x_ ? it->u_left_ : NULL;
+ const uint8_t* const top = it->y_ ? it->uv_top_ : NULL;
+ VP8EncPredChroma8(it->yuv_p_, left, top);
+}
+
+void VP8MakeIntra4Preds(const VP8EncIterator* const it) {
+ VP8EncPredLuma4(it->yuv_p_, it->i4_top_);
+}
+
+//------------------------------------------------------------------------------
+// Quantize
+
+// Layout:
+// +----+----+
+// |YYYY|UUVV| 0
+// |YYYY|UUVV| 4
+// |YYYY|....| 8
+// |YYYY|....| 12
+// +----+----+
+
+const uint16_t VP8Scan[16] = { // Luma
+ 0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
+ 0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
+ 0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
+ 0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS,
+};
+
+static const uint16_t VP8ScanUV[4 + 4] = {
+ 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U
+ 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V
+};
+
+//------------------------------------------------------------------------------
+// Distortion measurement
+
+static const uint16_t kWeightY[16] = {
+ 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2
+};
+
+static const uint16_t kWeightTrellis[16] = {
+#if USE_TDISTO == 0
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
+#else
+ 30, 27, 19, 11,
+ 27, 24, 17, 10,
+ 19, 17, 12, 8,
+ 11, 10, 8, 6
+#endif
+};
+
+// Init/Copy the common fields in score.
+static void InitScore(VP8ModeScore* const rd) {
+ rd->D = 0;
+ rd->SD = 0;
+ rd->R = 0;
+ rd->H = 0;
+ rd->nz = 0;
+ rd->score = MAX_COST;
+}
+
+static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+ dst->D = src->D;
+ dst->SD = src->SD;
+ dst->R = src->R;
+ dst->H = src->H;
+ dst->nz = src->nz; // note that nz is not accumulated, but just copied.
+ dst->score = src->score;
+}
+
+static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+ dst->D += src->D;
+ dst->SD += src->SD;
+ dst->R += src->R;
+ dst->H += src->H;
+ dst->nz |= src->nz; // here, new nz bits are accumulated.
+ dst->score += src->score;
+}
+
+//------------------------------------------------------------------------------
+// Performs trellis-optimized quantization.
+
+// Trellis node
+typedef struct {
+ int8_t prev; // best previous node
+ int8_t sign; // sign of coeff_i
+ int16_t level; // level
+} Node;
+
+// Score state
+typedef struct {
+ score_t score; // partial RD score
+ const uint16_t* costs; // shortcut to cost tables
+} ScoreState;
+
+// If a coefficient was quantized to a value Q (using a neutral bias),
+// we test all alternate possibilities between [Q-MIN_DELTA, Q+MAX_DELTA]
+// We don't test negative values though.
+#define MIN_DELTA 0 // how much lower level to try
+#define MAX_DELTA 1 // how much higher
+#define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA)
+#define NODE(n, l) (nodes[(n)][(l) + MIN_DELTA])
+#define SCORE_STATE(n, l) (score_states[n][(l) + MIN_DELTA])
+
+static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) {
+ rd->score = (rd->R + rd->H) * lambda + RD_DISTO_MULT * (rd->D + rd->SD);
+}
+
+static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate,
+ score_t distortion) {
+ return rate * lambda + RD_DISTO_MULT * distortion;
+}
+
+static int TrellisQuantizeBlock(const VP8Encoder* const enc,
+ int16_t in[16], int16_t out[16],
+ int ctx0, int coeff_type,
+ const VP8Matrix* const mtx,
+ int lambda) {
+ const ProbaArray* const probas = enc->proba_.coeffs_[coeff_type];
+ CostArrayPtr const costs =
+ (CostArrayPtr)enc->proba_.remapped_costs_[coeff_type];
+ const int first = (coeff_type == 0) ? 1 : 0;
+ Node nodes[16][NUM_NODES];
+ ScoreState score_states[2][NUM_NODES];
+ ScoreState* ss_cur = &SCORE_STATE(0, MIN_DELTA);
+ ScoreState* ss_prev = &SCORE_STATE(1, MIN_DELTA);
+ int best_path[3] = {-1, -1, -1}; // store best-last/best-level/best-previous
+ score_t best_score;
+ int n, m, p, last;
+
+ {
+ score_t cost;
+ const int thresh = mtx->q_[1] * mtx->q_[1] / 4;
+ const int last_proba = probas[VP8EncBands[first]][ctx0][0];
+
+ // compute the position of the last interesting coefficient
+ last = first - 1;
+ for (n = 15; n >= first; --n) {
+ const int j = kZigzag[n];
+ const int err = in[j] * in[j];
+ if (err > thresh) {
+ last = n;
+ break;
+ }
+ }
+ // we don't need to go inspect up to n = 16 coeffs. We can just go up
+ // to last + 1 (inclusive) without losing much.
+ if (last < 15) ++last;
+
+ // compute 'skip' score. This is the max score one can do.
+ cost = VP8BitCost(0, last_proba);
+ best_score = RDScoreTrellis(lambda, cost, 0);
+
+ // initialize source node.
+ for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
+ const score_t rate = (ctx0 == 0) ? VP8BitCost(1, last_proba) : 0;
+ ss_cur[m].score = RDScoreTrellis(lambda, rate, 0);
+ ss_cur[m].costs = costs[first][ctx0];
+ }
+ }
+
+ // traverse trellis.
+ for (n = first; n <= last; ++n) {
+ const int j = kZigzag[n];
+ const uint32_t Q = mtx->q_[j];
+ const uint32_t iQ = mtx->iq_[j];
+ const uint32_t B = BIAS(0x00); // neutral bias
+ // note: it's important to take sign of the _original_ coeff,
+ // so we don't have to consider level < 0 afterward.
+ const int sign = (in[j] < 0);
+ const uint32_t coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
+ int level0 = QUANTDIV(coeff0, iQ, B);
+ int thresh_level = QUANTDIV(coeff0, iQ, BIAS(0x80));
+ if (thresh_level > MAX_LEVEL) thresh_level = MAX_LEVEL;
+ if (level0 > MAX_LEVEL) level0 = MAX_LEVEL;
+
+ { // Swap current and previous score states
+ ScoreState* const tmp = ss_cur;
+ ss_cur = ss_prev;
+ ss_prev = tmp;
+ }
+
+ // test all alternate level values around level0.
+ for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) {
+ Node* const cur = &NODE(n, m);
+ int level = level0 + m;
+ const int ctx = (level > 2) ? 2 : level;
+ const int band = VP8EncBands[n + 1];
+ score_t base_score;
+ score_t best_cur_score = MAX_COST;
+ int best_prev = 0; // default, in case
+
+ ss_cur[m].score = MAX_COST;
+ ss_cur[m].costs = costs[n + 1][ctx];
+ if (level < 0 || level > thresh_level) {
+ // Node is dead.
+ continue;
+ }
+
+ {
+ // Compute delta_error = how much coding this level will
+ // subtract to max_error as distortion.
+ // Here, distortion = sum of (|coeff_i| - level_i * Q_i)^2
+ const int new_error = coeff0 - level * Q;
+ const int delta_error =
+ kWeightTrellis[j] * (new_error * new_error - coeff0 * coeff0);
+ base_score = RDScoreTrellis(lambda, 0, delta_error);
+ }
+
+ // Inspect all possible non-dead predecessors. Retain only the best one.
+ for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) {
+ // Dead nodes (with ss_prev[p].score >= MAX_COST) are automatically
+ // eliminated since their score can't be better than the current best.
+ const score_t cost = VP8LevelCost(ss_prev[p].costs, level);
+ // Examine node assuming it's a non-terminal one.
+ const score_t score =
+ base_score + ss_prev[p].score + RDScoreTrellis(lambda, cost, 0);
+ if (score < best_cur_score) {
+ best_cur_score = score;
+ best_prev = p;
+ }
+ }
+ // Store best finding in current node.
+ cur->sign = sign;
+ cur->level = level;
+ cur->prev = best_prev;
+ ss_cur[m].score = best_cur_score;
+
+ // Now, record best terminal node (and thus best entry in the graph).
+ if (level != 0) {
+ const score_t last_pos_cost =
+ (n < 15) ? VP8BitCost(0, probas[band][ctx][0]) : 0;
+ const score_t last_pos_score = RDScoreTrellis(lambda, last_pos_cost, 0);
+ const score_t score = best_cur_score + last_pos_score;
+ if (score < best_score) {
+ best_score = score;
+ best_path[0] = n; // best eob position
+ best_path[1] = m; // best node index
+ best_path[2] = best_prev; // best predecessor
+ }
+ }
+ }
+ }
+
+ // Fresh start
+ memset(in + first, 0, (16 - first) * sizeof(*in));
+ memset(out + first, 0, (16 - first) * sizeof(*out));
+ if (best_path[0] == -1) {
+ return 0; // skip!
+ }
+
+ {
+ // Unwind the best path.
+ // Note: best-prev on terminal node is not necessarily equal to the
+ // best_prev for non-terminal. So we patch best_path[2] in.
+ int nz = 0;
+ int best_node = best_path[1];
+ n = best_path[0];
+ NODE(n, best_node).prev = best_path[2]; // force best-prev for terminal
+
+ for (; n >= first; --n) {
+ const Node* const node = &NODE(n, best_node);
+ const int j = kZigzag[n];
+ out[n] = node->sign ? -node->level : node->level;
+ nz |= node->level;
+ in[j] = out[n] * mtx->q_[j];
+ best_node = node->prev;
+ }
+ return (nz != 0);
+ }
+}
+
+#undef NODE
+
+//------------------------------------------------------------------------------
+// Performs: difference, transform, quantize, back-transform, add
+// all at once. Output is the reconstructed block in *yuv_out, and the
+// quantized levels in *levels.
+
+static int ReconstructIntra16(VP8EncIterator* const it,
+ VP8ModeScore* const rd,
+ uint8_t* const yuv_out,
+ int mode) {
+ const VP8Encoder* const enc = it->enc_;
+ const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
+ const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC;
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ int nz = 0;
+ int n;
+ int16_t tmp[16][16], dc_tmp[16];
+
+ for (n = 0; n < 16; n += 2) {
+ VP8FTransform2(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]);
+ }
+ VP8FTransformWHT(tmp[0], dc_tmp);
+ nz |= VP8EncQuantizeBlockWHT(dc_tmp, rd->y_dc_levels, &dqm->y2_) << 24;
+
+ if (DO_TRELLIS_I16 && it->do_trellis_) {
+ int x, y;
+ VP8IteratorNzToBytes(it);
+ for (y = 0, n = 0; y < 4; ++y) {
+ for (x = 0; x < 4; ++x, ++n) {
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ const int non_zero =
+ TrellisQuantizeBlock(enc, tmp[n], rd->y_ac_levels[n], ctx, 0,
+ &dqm->y1_, dqm->lambda_trellis_i16_);
+ it->top_nz_[x] = it->left_nz_[y] = non_zero;
+ rd->y_ac_levels[n][0] = 0;
+ nz |= non_zero << n;
+ }
+ }
+ } else {
+ for (n = 0; n < 16; n += 2) {
+ // Zero-out the first coeff, so that: a) nz is correct below, and
+ // b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified.
+ tmp[n][0] = tmp[n + 1][0] = 0;
+ nz |= VP8EncQuantize2Blocks(tmp[n], rd->y_ac_levels[n], &dqm->y1_) << n;
+ assert(rd->y_ac_levels[n + 0][0] == 0);
+ assert(rd->y_ac_levels[n + 1][0] == 0);
+ }
+ }
+
+ // Transform back
+ VP8TransformWHT(dc_tmp, tmp[0]);
+ for (n = 0; n < 16; n += 2) {
+ VP8ITransform(ref + VP8Scan[n], tmp[n], yuv_out + VP8Scan[n], 1);
+ }
+
+ return nz;
+}
+
+static int ReconstructIntra4(VP8EncIterator* const it,
+ int16_t levels[16],
+ const uint8_t* const src,
+ uint8_t* const yuv_out,
+ int mode) {
+ const VP8Encoder* const enc = it->enc_;
+ const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode];
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ int nz = 0;
+ int16_t tmp[16];
+
+ VP8FTransform(src, ref, tmp);
+ if (DO_TRELLIS_I4 && it->do_trellis_) {
+ const int x = it->i4_ & 3, y = it->i4_ >> 2;
+ const int ctx = it->top_nz_[x] + it->left_nz_[y];
+ nz = TrellisQuantizeBlock(enc, tmp, levels, ctx, 3, &dqm->y1_,
+ dqm->lambda_trellis_i4_);
+ } else {
+ nz = VP8EncQuantizeBlock(tmp, levels, &dqm->y1_);
+ }
+ VP8ITransform(ref, tmp, yuv_out, 0);
+ return nz;
+}
+
+//------------------------------------------------------------------------------
+// DC-error diffusion
+
+// Diffusion weights. We under-correct a bit (15/16th of the error is actually
+// diffused) to avoid 'rainbow' chessboard pattern of blocks at q~=0.
+#define C1 7 // fraction of error sent to the 4x4 block below
+#define C2 8 // fraction of error sent to the 4x4 block on the right
+#define DSHIFT 4
+#define DSCALE 1 // storage descaling, needed to make the error fit int8_t
+
+// Quantize as usual, but also compute and return the quantization error.
+// Error is already divided by DSHIFT.
+static int QuantizeSingle(int16_t* const v, const VP8Matrix* const mtx) {
+ int V = *v;
+ const int sign = (V < 0);
+ if (sign) V = -V;
+ if (V > (int)mtx->zthresh_[0]) {
+ const int qV = QUANTDIV(V, mtx->iq_[0], mtx->bias_[0]) * mtx->q_[0];
+ const int err = (V - qV);
+ *v = sign ? -qV : qV;
+ return (sign ? -err : err) >> DSCALE;
+ }
+ *v = 0;
+ return (sign ? -V : V) >> DSCALE;
+}
+
+static void CorrectDCValues(const VP8EncIterator* const it,
+ const VP8Matrix* const mtx,
+ int16_t tmp[][16], VP8ModeScore* const rd) {
+ // | top[0] | top[1]
+ // --------+--------+---------
+ // left[0] | tmp[0] tmp[1] <-> err0 err1
+ // left[1] | tmp[2] tmp[3] err2 err3
+ //
+ // Final errors {err1,err2,err3} are preserved and later restored
+ // as top[]/left[] on the next block.
+ int ch;
+ for (ch = 0; ch <= 1; ++ch) {
+ const int8_t* const top = it->top_derr_[it->x_][ch];
+ const int8_t* const left = it->left_derr_[ch];
+ int16_t (* const c)[16] = &tmp[ch * 4];
+ int err0, err1, err2, err3;
+ c[0][0] += (C1 * top[0] + C2 * left[0]) >> (DSHIFT - DSCALE);
+ err0 = QuantizeSingle(&c[0][0], mtx);
+ c[1][0] += (C1 * top[1] + C2 * err0) >> (DSHIFT - DSCALE);
+ err1 = QuantizeSingle(&c[1][0], mtx);
+ c[2][0] += (C1 * err0 + C2 * left[1]) >> (DSHIFT - DSCALE);
+ err2 = QuantizeSingle(&c[2][0], mtx);
+ c[3][0] += (C1 * err1 + C2 * err2) >> (DSHIFT - DSCALE);
+ err3 = QuantizeSingle(&c[3][0], mtx);
+ // error 'err' is bounded by mtx->q_[0] which is 132 at max. Hence
+ // err >> DSCALE will fit in an int8_t type if DSCALE>=1.
+ assert(abs(err1) <= 127 && abs(err2) <= 127 && abs(err3) <= 127);
+ rd->derr[ch][0] = (int8_t)err1;
+ rd->derr[ch][1] = (int8_t)err2;
+ rd->derr[ch][2] = (int8_t)err3;
+ }
+}
+
+static void StoreDiffusionErrors(VP8EncIterator* const it,
+ const VP8ModeScore* const rd) {
+ int ch;
+ for (ch = 0; ch <= 1; ++ch) {
+ int8_t* const top = it->top_derr_[it->x_][ch];
+ int8_t* const left = it->left_derr_[ch];
+ left[0] = rd->derr[ch][0]; // restore err1
+ left[1] = 3 * rd->derr[ch][2] >> 2; // ... 3/4th of err3
+ top[0] = rd->derr[ch][1]; // ... err2
+ top[1] = rd->derr[ch][2] - left[1]; // ... 1/4th of err3.
+ }
+}
+
+#undef C1
+#undef C2
+#undef DSHIFT
+#undef DSCALE
+
+//------------------------------------------------------------------------------
+
+static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
+ uint8_t* const yuv_out, int mode) {
+ const VP8Encoder* const enc = it->enc_;
+ const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode];
+ const uint8_t* const src = it->yuv_in_ + U_OFF_ENC;
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ int nz = 0;
+ int n;
+ int16_t tmp[8][16];
+
+ for (n = 0; n < 8; n += 2) {
+ VP8FTransform2(src + VP8ScanUV[n], ref + VP8ScanUV[n], tmp[n]);
+ }
+ if (it->top_derr_ != NULL) CorrectDCValues(it, &dqm->uv_, tmp, rd);
+
+ if (DO_TRELLIS_UV && it->do_trellis_) {
+ int ch, x, y;
+ for (ch = 0, n = 0; ch <= 2; ch += 2) {
+ for (y = 0; y < 2; ++y) {
+ for (x = 0; x < 2; ++x, ++n) {
+ const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
+ const int non_zero =
+ TrellisQuantizeBlock(enc, tmp[n], rd->uv_levels[n], ctx, 2,
+ &dqm->uv_, dqm->lambda_trellis_uv_);
+ it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = non_zero;
+ nz |= non_zero << n;
+ }
+ }
+ }
+ } else {
+ for (n = 0; n < 8; n += 2) {
+ nz |= VP8EncQuantize2Blocks(tmp[n], rd->uv_levels[n], &dqm->uv_) << n;
+ }
+ }
+
+ for (n = 0; n < 8; n += 2) {
+ VP8ITransform(ref + VP8ScanUV[n], tmp[n], yuv_out + VP8ScanUV[n], 1);
+ }
+ return (nz << 16);
+}
+
+//------------------------------------------------------------------------------
+// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost.
+// Pick the mode is lower RD-cost = Rate + lambda * Distortion.
+
+static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) {
+ // We look at the first three AC coefficients to determine what is the average
+ // delta between each sub-4x4 block.
+ const int v0 = abs(DCs[1]);
+ const int v1 = abs(DCs[2]);
+ const int v2 = abs(DCs[4]);
+ int max_v = (v1 > v0) ? v1 : v0;
+ max_v = (v2 > max_v) ? v2 : max_v;
+ if (max_v > dqm->max_edge_) dqm->max_edge_ = max_v;
+}
+
+static void SwapModeScore(VP8ModeScore** a, VP8ModeScore** b) {
+ VP8ModeScore* const tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+static void SwapPtr(uint8_t** a, uint8_t** b) {
+ uint8_t* const tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+static void SwapOut(VP8EncIterator* const it) {
+ SwapPtr(&it->yuv_out_, &it->yuv_out2_);
+}
+
+static score_t IsFlat(const int16_t* levels, int num_blocks, score_t thresh) {
+ score_t score = 0;
+ while (num_blocks-- > 0) { // TODO(skal): refine positional scoring?
+ int i;
+ for (i = 1; i < 16; ++i) { // omit DC, we're only interested in AC
+ score += (levels[i] != 0);
+ if (score > thresh) return 0;
+ }
+ levels += 16;
+ }
+ return 1;
+}
+
+static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
+ const int kNumBlocks = 16;
+ VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_];
+ const int lambda = dqm->lambda_i16_;
+ const int tlambda = dqm->tlambda_;
+ const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC;
+ VP8ModeScore rd_tmp;
+ VP8ModeScore* rd_cur = &rd_tmp;
+ VP8ModeScore* rd_best = rd;
+ int mode;
+
+ rd->mode_i16 = -1;
+ for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
+ uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF_ENC; // scratch buffer
+ rd_cur->mode_i16 = mode;
+
+ // Reconstruct
+ rd_cur->nz = ReconstructIntra16(it, rd_cur, tmp_dst, mode);
+
+ // Measure RD-score
+ rd_cur->D = VP8SSE16x16(src, tmp_dst);
+ rd_cur->SD =
+ tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) : 0;
+ rd_cur->H = VP8FixedCostsI16[mode];
+ rd_cur->R = VP8GetCostLuma16(it, rd_cur);
+ if (mode > 0 &&
+ IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16)) {
+ // penalty to avoid flat area to be mispredicted by complex mode
+ rd_cur->R += FLATNESS_PENALTY * kNumBlocks;
+ }
+
+ // Since we always examine Intra16 first, we can overwrite *rd directly.
+ SetRDScore(lambda, rd_cur);
+ if (mode == 0 || rd_cur->score < rd_best->score) {
+ SwapModeScore(&rd_cur, &rd_best);
+ SwapOut(it);
+ }
+ }
+ if (rd_best != rd) {
+ memcpy(rd, rd_best, sizeof(*rd));
+ }
+ SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision.
+ VP8SetIntra16Mode(it, rd->mode_i16);
+
+ // we have a blocky macroblock (only DCs are non-zero) with fairly high
+ // distortion, record max delta so we can later adjust the minimal filtering
+ // strength needed to smooth these blocks out.
+ if ((rd->nz & 0x100ffff) == 0x1000000 && rd->D > dqm->min_disto_) {
+ StoreMaxDelta(dqm, rd->y_dc_levels);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+// return the cost array corresponding to the surrounding prediction modes.
+static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
+ const uint8_t modes[16]) {
+ const int preds_w = it->enc_->preds_w_;
+ const int x = (it->i4_ & 3), y = it->i4_ >> 2;
+ const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1];
+ const int top = (y == 0) ? it->preds_[-preds_w + x] : modes[it->i4_ - 4];
+ return VP8FixedCostsI4[top][left];
+}
+
+static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ const VP8Encoder* const enc = it->enc_;
+ const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
+ const int lambda = dqm->lambda_i4_;
+ const int tlambda = dqm->tlambda_;
+ const uint8_t* const src0 = it->yuv_in_ + Y_OFF_ENC;
+ uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF_ENC;
+ int total_header_bits = 0;
+ VP8ModeScore rd_best;
+
+ if (enc->max_i4_header_bits_ == 0) {
+ return 0;
+ }
+
+ InitScore(&rd_best);
+ rd_best.H = 211; // '211' is the value of VP8BitCost(0, 145)
+ SetRDScore(dqm->lambda_mode_, &rd_best);
+ VP8IteratorStartI4(it);
+ do {
+ const int kNumBlocks = 1;
+ VP8ModeScore rd_i4;
+ int mode;
+ int best_mode = -1;
+ const uint8_t* const src = src0 + VP8Scan[it->i4_];
+ const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4);
+ uint8_t* best_block = best_blocks + VP8Scan[it->i4_];
+ uint8_t* tmp_dst = it->yuv_p_ + I4TMP; // scratch buffer.
+
+ InitScore(&rd_i4);
+ VP8MakeIntra4Preds(it);
+ for (mode = 0; mode < NUM_BMODES; ++mode) {
+ VP8ModeScore rd_tmp;
+ int16_t tmp_levels[16];
+
+ // Reconstruct
+ rd_tmp.nz =
+ ReconstructIntra4(it, tmp_levels, src, tmp_dst, mode) << it->i4_;
+
+ // Compute RD-score
+ rd_tmp.D = VP8SSE4x4(src, tmp_dst);
+ rd_tmp.SD =
+ tlambda ? MULT_8B(tlambda, VP8TDisto4x4(src, tmp_dst, kWeightY))
+ : 0;
+ rd_tmp.H = mode_costs[mode];
+
+ // Add flatness penalty
+ if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) {
+ rd_tmp.R = FLATNESS_PENALTY * kNumBlocks;
+ } else {
+ rd_tmp.R = 0;
+ }
+
+ // early-out check
+ SetRDScore(lambda, &rd_tmp);
+ if (best_mode >= 0 && rd_tmp.score >= rd_i4.score) continue;
+
+ // finish computing score
+ rd_tmp.R += VP8GetCostLuma4(it, tmp_levels);
+ SetRDScore(lambda, &rd_tmp);
+
+ if (best_mode < 0 || rd_tmp.score < rd_i4.score) {
+ CopyScore(&rd_i4, &rd_tmp);
+ best_mode = mode;
+ SwapPtr(&tmp_dst, &best_block);
+ memcpy(rd_best.y_ac_levels[it->i4_], tmp_levels,
+ sizeof(rd_best.y_ac_levels[it->i4_]));
+ }
+ }
+ SetRDScore(dqm->lambda_mode_, &rd_i4);
+ AddScore(&rd_best, &rd_i4);
+ if (rd_best.score >= rd->score) {
+ return 0;
+ }
+ total_header_bits += (int)rd_i4.H; // <- equal to mode_costs[best_mode];
+ if (total_header_bits > enc->max_i4_header_bits_) {
+ return 0;
+ }
+ // Copy selected samples if not in the right place already.
+ if (best_block != best_blocks + VP8Scan[it->i4_]) {
+ VP8Copy4x4(best_block, best_blocks + VP8Scan[it->i4_]);
+ }
+ rd->modes_i4[it->i4_] = best_mode;
+ it->top_nz_[it->i4_ & 3] = it->left_nz_[it->i4_ >> 2] = (rd_i4.nz ? 1 : 0);
+ } while (VP8IteratorRotateI4(it, best_blocks));
+
+ // finalize state
+ CopyScore(rd, &rd_best);
+ VP8SetIntra4Mode(it, rd->modes_i4);
+ SwapOut(it);
+ memcpy(rd->y_ac_levels, rd_best.y_ac_levels, sizeof(rd->y_ac_levels));
+ return 1; // select intra4x4 over intra16x16
+}
+
+//------------------------------------------------------------------------------
+
+static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ const int kNumBlocks = 8;
+ const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_];
+ const int lambda = dqm->lambda_uv_;
+ const uint8_t* const src = it->yuv_in_ + U_OFF_ENC;
+ uint8_t* tmp_dst = it->yuv_out2_ + U_OFF_ENC; // scratch buffer
+ uint8_t* dst0 = it->yuv_out_ + U_OFF_ENC;
+ uint8_t* dst = dst0;
+ VP8ModeScore rd_best;
+ int mode;
+
+ rd->mode_uv = -1;
+ InitScore(&rd_best);
+ for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
+ VP8ModeScore rd_uv;
+
+ // Reconstruct
+ rd_uv.nz = ReconstructUV(it, &rd_uv, tmp_dst, mode);
+
+ // Compute RD-score
+ rd_uv.D = VP8SSE16x8(src, tmp_dst);
+ rd_uv.SD = 0; // not calling TDisto here: it tends to flatten areas.
+ rd_uv.H = VP8FixedCostsUV[mode];
+ rd_uv.R = VP8GetCostUV(it, &rd_uv);
+ if (mode > 0 && IsFlat(rd_uv.uv_levels[0], kNumBlocks, FLATNESS_LIMIT_UV)) {
+ rd_uv.R += FLATNESS_PENALTY * kNumBlocks;
+ }
+
+ SetRDScore(lambda, &rd_uv);
+ if (mode == 0 || rd_uv.score < rd_best.score) {
+ CopyScore(&rd_best, &rd_uv);
+ rd->mode_uv = mode;
+ memcpy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels));
+ if (it->top_derr_ != NULL) {
+ memcpy(rd->derr, rd_uv.derr, sizeof(rd_uv.derr));
+ }
+ SwapPtr(&dst, &tmp_dst);
+ }
+ }
+ VP8SetIntraUVMode(it, rd->mode_uv);
+ AddScore(rd, &rd_best);
+ if (dst != dst0) { // copy 16x8 block if needed
+ VP8Copy16x8(dst, dst0);
+ }
+ if (it->top_derr_ != NULL) { // store diffusion errors for next block
+ StoreDiffusionErrors(it, rd);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Final reconstruction and quantization.
+
+static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) {
+ const VP8Encoder* const enc = it->enc_;
+ const int is_i16 = (it->mb_->type_ == 1);
+ int nz = 0;
+
+ if (is_i16) {
+ nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]);
+ } else {
+ VP8IteratorStartI4(it);
+ do {
+ const int mode =
+ it->preds_[(it->i4_ & 3) + (it->i4_ >> 2) * enc->preds_w_];
+ const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_];
+ uint8_t* const dst = it->yuv_out_ + Y_OFF_ENC + VP8Scan[it->i4_];
+ VP8MakeIntra4Preds(it);
+ nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_],
+ src, dst, mode) << it->i4_;
+ } while (VP8IteratorRotateI4(it, it->yuv_out_ + Y_OFF_ENC));
+ }
+
+ nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_);
+ rd->nz = nz;
+}
+
+// Refine intra16/intra4 sub-modes based on distortion only (not rate).
+static void RefineUsingDistortion(VP8EncIterator* const it,
+ int try_both_modes, int refine_uv_mode,
+ VP8ModeScore* const rd) {
+ score_t best_score = MAX_COST;
+ int nz = 0;
+ int mode;
+ int is_i16 = try_both_modes || (it->mb_->type_ == 1);
+
+ const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_];
+ // Some empiric constants, of approximate order of magnitude.
+ const int lambda_d_i16 = 106;
+ const int lambda_d_i4 = 11;
+ const int lambda_d_uv = 120;
+ score_t score_i4 = dqm->i4_penalty_;
+ score_t i4_bit_sum = 0;
+ const score_t bit_limit = try_both_modes ? it->enc_->mb_header_limit_
+ : MAX_COST; // no early-out allowed
+
+ if (is_i16) { // First, evaluate Intra16 distortion
+ int best_mode = -1;
+ const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC;
+ for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
+ const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
+ const score_t score = (score_t)VP8SSE16x16(src, ref) * RD_DISTO_MULT
+ + VP8FixedCostsI16[mode] * lambda_d_i16;
+ if (mode > 0 && VP8FixedCostsI16[mode] > bit_limit) {
+ continue;
+ }
+ if (score < best_score) {
+ best_mode = mode;
+ best_score = score;
+ }
+ }
+ VP8SetIntra16Mode(it, best_mode);
+ // we'll reconstruct later, if i16 mode actually gets selected
+ }
+
+ // Next, evaluate Intra4
+ if (try_both_modes || !is_i16) {
+ // We don't evaluate the rate here, but just account for it through a
+ // constant penalty (i4 mode usually needs more bits compared to i16).
+ is_i16 = 0;
+ VP8IteratorStartI4(it);
+ do {
+ int best_i4_mode = -1;
+ score_t best_i4_score = MAX_COST;
+ const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_];
+ const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4);
+
+ VP8MakeIntra4Preds(it);
+ for (mode = 0; mode < NUM_BMODES; ++mode) {
+ const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode];
+ const score_t score = VP8SSE4x4(src, ref) * RD_DISTO_MULT
+ + mode_costs[mode] * lambda_d_i4;
+ if (score < best_i4_score) {
+ best_i4_mode = mode;
+ best_i4_score = score;
+ }
+ }
+ i4_bit_sum += mode_costs[best_i4_mode];
+ rd->modes_i4[it->i4_] = best_i4_mode;
+ score_i4 += best_i4_score;
+ if (score_i4 >= best_score || i4_bit_sum > bit_limit) {
+ // Intra4 won't be better than Intra16. Bail out and pick Intra16.
+ is_i16 = 1;
+ break;
+ } else { // reconstruct partial block inside yuv_out2_ buffer
+ uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF_ENC + VP8Scan[it->i4_];
+ nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_],
+ src, tmp_dst, best_i4_mode) << it->i4_;
+ }
+ } while (VP8IteratorRotateI4(it, it->yuv_out2_ + Y_OFF_ENC));
+ }
+
+ // Final reconstruction, depending on which mode is selected.
+ if (!is_i16) {
+ VP8SetIntra4Mode(it, rd->modes_i4);
+ SwapOut(it);
+ best_score = score_i4;
+ } else {
+ nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]);
+ }
+
+ // ... and UV!
+ if (refine_uv_mode) {
+ int best_mode = -1;
+ score_t best_uv_score = MAX_COST;
+ const uint8_t* const src = it->yuv_in_ + U_OFF_ENC;
+ for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
+ const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode];
+ const score_t score = VP8SSE16x8(src, ref) * RD_DISTO_MULT
+ + VP8FixedCostsUV[mode] * lambda_d_uv;
+ if (score < best_uv_score) {
+ best_mode = mode;
+ best_uv_score = score;
+ }
+ }
+ VP8SetIntraUVMode(it, best_mode);
+ }
+ nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_);
+
+ rd->nz = nz;
+ rd->score = best_score;
+}
+
+//------------------------------------------------------------------------------
+// Entry point
+
+int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
+ VP8RDLevel rd_opt) {
+ int is_skipped;
+ const int method = it->enc_->method_;
+
+ InitScore(rd);
+
+ // We can perform predictions for Luma16x16 and Chroma8x8 already.
+ // Luma4x4 predictions needs to be done as-we-go.
+ VP8MakeLuma16Preds(it);
+ VP8MakeChroma8Preds(it);
+
+ if (rd_opt > RD_OPT_NONE) {
+ it->do_trellis_ = (rd_opt >= RD_OPT_TRELLIS_ALL);
+ PickBestIntra16(it, rd);
+ if (method >= 2) {
+ PickBestIntra4(it, rd);
+ }
+ PickBestUV(it, rd);
+ if (rd_opt == RD_OPT_TRELLIS) { // finish off with trellis-optim now
+ it->do_trellis_ = 1;
+ SimpleQuantize(it, rd);
+ }
+ } else {
+ // At this point we have heuristically decided intra16 / intra4.
+ // For method >= 2, pick the best intra4/intra16 based on SSE (~tad slower).
+ // For method <= 1, we don't re-examine the decision but just go ahead with
+ // quantization/reconstruction.
+ RefineUsingDistortion(it, (method >= 2), (method >= 1), rd);
+ }
+ is_skipped = (rd->nz == 0);
+ VP8SetSkip(it, is_skipped);
+ return is_skipped;
+}
diff --git a/src/third_party/libwebp/src/enc/syntax_enc.c b/src/third_party/libwebp/src/enc/syntax_enc.c
new file mode 100644
index 0000000..a9e5a6c
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/syntax_enc.c
@@ -0,0 +1,388 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Header syntax writing
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+
+#include "src/utils/utils.h"
+#include "src/webp/format_constants.h" // RIFF constants
+#include "src/webp/mux_types.h" // ALPHA_FLAG
+#include "src/enc/vp8i_enc.h"
+
+//------------------------------------------------------------------------------
+// Helper functions
+
+static int IsVP8XNeeded(const VP8Encoder* const enc) {
+ return !!enc->has_alpha_; // Currently the only case when VP8X is needed.
+ // This could change in the future.
+}
+
+static int PutPaddingByte(const WebPPicture* const pic) {
+ const uint8_t pad_byte[1] = { 0 };
+ return !!pic->writer(pad_byte, 1, pic);
+}
+
+//------------------------------------------------------------------------------
+// Writers for header's various pieces (in order of appearance)
+
+static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc,
+ size_t riff_size) {
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t riff[RIFF_HEADER_SIZE] = {
+ 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'
+ };
+ assert(riff_size == (uint32_t)riff_size);
+ PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
+ if (!pic->writer(riff, sizeof(riff), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) {
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = {
+ 'V', 'P', '8', 'X'
+ };
+ uint32_t flags = 0;
+
+ assert(IsVP8XNeeded(enc));
+ assert(pic->width >= 1 && pic->height >= 1);
+ assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE);
+
+ if (enc->has_alpha_) {
+ flags |= ALPHA_FLAG;
+ }
+
+ PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE);
+ PutLE32(vp8x + CHUNK_HEADER_SIZE, flags);
+ PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1);
+ PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1);
+ if (!pic->writer(vp8x, sizeof(vp8x), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) {
+ const WebPPicture* const pic = enc->pic_;
+ uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = {
+ 'A', 'L', 'P', 'H'
+ };
+
+ assert(enc->has_alpha_);
+
+ // Alpha chunk header.
+ PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_);
+ if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+
+ // Alpha chunk data.
+ if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+
+ // Padding.
+ if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutVP8Header(const WebPPicture* const pic,
+ size_t vp8_size) {
+ uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = {
+ 'V', 'P', '8', ' '
+ };
+ assert(vp8_size == (uint32_t)vp8_size);
+ PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size);
+ if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic,
+ int profile, size_t size0) {
+ uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE];
+ uint32_t bits;
+
+ if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit
+ return VP8_ENC_ERROR_PARTITION0_OVERFLOW;
+ }
+
+ // Paragraph 9.1.
+ bits = 0 // keyframe (1b)
+ | (profile << 1) // profile (3b)
+ | (1 << 4) // visible (1b)
+ | ((uint32_t)size0 << 5); // partition length (19b)
+ vp8_frm_hdr[0] = (bits >> 0) & 0xff;
+ vp8_frm_hdr[1] = (bits >> 8) & 0xff;
+ vp8_frm_hdr[2] = (bits >> 16) & 0xff;
+ // signature
+ vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff;
+ vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff;
+ vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff;
+ // dimensions
+ vp8_frm_hdr[6] = pic->width & 0xff;
+ vp8_frm_hdr[7] = pic->width >> 8;
+ vp8_frm_hdr[8] = pic->height & 0xff;
+ vp8_frm_hdr[9] = pic->height >> 8;
+
+ if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+// WebP Headers.
+static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0,
+ size_t vp8_size, size_t riff_size) {
+ WebPPicture* const pic = enc->pic_;
+ WebPEncodingError err = VP8_ENC_OK;
+
+ // RIFF header.
+ err = PutRIFFHeader(enc, riff_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // VP8X.
+ if (IsVP8XNeeded(enc)) {
+ err = PutVP8XHeader(enc);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+
+ // Alpha.
+ if (enc->has_alpha_) {
+ err = PutAlphaChunk(enc);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+
+ // VP8 header.
+ err = PutVP8Header(pic, vp8_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // VP8 frame header.
+ err = PutVP8FrameHeader(pic, enc->profile_, size0);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // All OK.
+ return 1;
+
+ // Error.
+ Error:
+ return WebPEncodingSetError(pic, err);
+}
+
+// Segmentation header
+static void PutSegmentHeader(VP8BitWriter* const bw,
+ const VP8Encoder* const enc) {
+ const VP8EncSegmentHeader* const hdr = &enc->segment_hdr_;
+ const VP8EncProba* const proba = &enc->proba_;
+ if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) {
+ // We always 'update' the quant and filter strength values
+ const int update_data = 1;
+ int s;
+ VP8PutBitUniform(bw, hdr->update_map_);
+ if (VP8PutBitUniform(bw, update_data)) {
+ // we always use absolute values, not relative ones
+ VP8PutBitUniform(bw, 1); // (segment_feature_mode = 1. Paragraph 9.3.)
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ VP8PutSignedBits(bw, enc->dqm_[s].quant_, 7);
+ }
+ for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
+ VP8PutSignedBits(bw, enc->dqm_[s].fstrength_, 6);
+ }
+ }
+ if (hdr->update_map_) {
+ for (s = 0; s < 3; ++s) {
+ if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) {
+ VP8PutBits(bw, proba->segments_[s], 8);
+ }
+ }
+ }
+ }
+}
+
+// Filtering parameters header
+static void PutFilterHeader(VP8BitWriter* const bw,
+ const VP8EncFilterHeader* const hdr) {
+ const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0);
+ VP8PutBitUniform(bw, hdr->simple_);
+ VP8PutBits(bw, hdr->level_, 6);
+ VP8PutBits(bw, hdr->sharpness_, 3);
+ if (VP8PutBitUniform(bw, use_lf_delta)) {
+ // '0' is the default value for i4x4_lf_delta_ at frame #0.
+ const int need_update = (hdr->i4x4_lf_delta_ != 0);
+ if (VP8PutBitUniform(bw, need_update)) {
+ // we don't use ref_lf_delta => emit four 0 bits
+ VP8PutBits(bw, 0, 4);
+ // we use mode_lf_delta for i4x4
+ VP8PutSignedBits(bw, hdr->i4x4_lf_delta_, 6);
+ VP8PutBits(bw, 0, 3); // all others unused
+ }
+ }
+}
+
+// Nominal quantization parameters
+static void PutQuant(VP8BitWriter* const bw,
+ const VP8Encoder* const enc) {
+ VP8PutBits(bw, enc->base_quant_, 7);
+ VP8PutSignedBits(bw, enc->dq_y1_dc_, 4);
+ VP8PutSignedBits(bw, enc->dq_y2_dc_, 4);
+ VP8PutSignedBits(bw, enc->dq_y2_ac_, 4);
+ VP8PutSignedBits(bw, enc->dq_uv_dc_, 4);
+ VP8PutSignedBits(bw, enc->dq_uv_ac_, 4);
+}
+
+// Partition sizes
+static int EmitPartitionsSize(const VP8Encoder* const enc,
+ WebPPicture* const pic) {
+ uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)];
+ int p;
+ for (p = 0; p < enc->num_parts_ - 1; ++p) {
+ const size_t part_size = VP8BitWriterSize(enc->parts_ + p);
+ if (part_size >= VP8_MAX_PARTITION_SIZE) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW);
+ }
+ buf[3 * p + 0] = (part_size >> 0) & 0xff;
+ buf[3 * p + 1] = (part_size >> 8) & 0xff;
+ buf[3 * p + 2] = (part_size >> 16) & 0xff;
+ }
+ return p ? pic->writer(buf, 3 * p, pic) : 1;
+}
+
+//------------------------------------------------------------------------------
+
+static int GeneratePartition0(VP8Encoder* const enc) {
+ VP8BitWriter* const bw = &enc->bw_;
+ const int mb_size = enc->mb_w_ * enc->mb_h_;
+ uint64_t pos1, pos2, pos3;
+
+ pos1 = VP8BitWriterPos(bw);
+ if (!VP8BitWriterInit(bw, mb_size * 7 / 8)) { // ~7 bits per macroblock
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+ VP8PutBitUniform(bw, 0); // colorspace
+ VP8PutBitUniform(bw, 0); // clamp type
+
+ PutSegmentHeader(bw, enc);
+ PutFilterHeader(bw, &enc->filter_hdr_);
+ VP8PutBits(bw, enc->num_parts_ == 8 ? 3 :
+ enc->num_parts_ == 4 ? 2 :
+ enc->num_parts_ == 2 ? 1 : 0, 2);
+ PutQuant(bw, enc);
+ VP8PutBitUniform(bw, 0); // no proba update
+ VP8WriteProbas(bw, &enc->proba_);
+ pos2 = VP8BitWriterPos(bw);
+ VP8CodeIntraModes(enc);
+ VP8BitWriterFinish(bw);
+
+ pos3 = VP8BitWriterPos(bw);
+
+#if !defined(WEBP_DISABLE_STATS)
+ if (enc->pic_->stats) {
+ enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3);
+ enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3);
+ enc->pic_->stats->alpha_data_size = (int)enc->alpha_data_size_;
+ }
+#else
+ (void)pos1;
+ (void)pos2;
+ (void)pos3;
+#endif
+ if (bw->error_) {
+ return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ }
+ return 1;
+}
+
+void VP8EncFreeBitWriters(VP8Encoder* const enc) {
+ int p;
+ VP8BitWriterWipeOut(&enc->bw_);
+ for (p = 0; p < enc->num_parts_; ++p) {
+ VP8BitWriterWipeOut(enc->parts_ + p);
+ }
+}
+
+int VP8EncWrite(VP8Encoder* const enc) {
+ WebPPicture* const pic = enc->pic_;
+ VP8BitWriter* const bw = &enc->bw_;
+ const int task_percent = 19;
+ const int percent_per_part = task_percent / enc->num_parts_;
+ const int final_percent = enc->percent_ + task_percent;
+ int ok = 0;
+ size_t vp8_size, pad, riff_size;
+ int p;
+
+ // Partition #0 with header and partition sizes
+ ok = GeneratePartition0(enc);
+ if (!ok) return 0;
+
+ // Compute VP8 size
+ vp8_size = VP8_FRAME_HEADER_SIZE +
+ VP8BitWriterSize(bw) +
+ 3 * (enc->num_parts_ - 1);
+ for (p = 0; p < enc->num_parts_; ++p) {
+ vp8_size += VP8BitWriterSize(enc->parts_ + p);
+ }
+ pad = vp8_size & 1;
+ vp8_size += pad;
+
+ // Compute RIFF size
+ // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
+ riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size;
+ if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data.
+ riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
+ }
+ if (enc->has_alpha_) { // Add size for: ALPH header + data.
+ const uint32_t padded_alpha_size = enc->alpha_data_size_ +
+ (enc->alpha_data_size_ & 1);
+ riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
+ }
+ // Sanity check.
+ if (riff_size > 0xfffffffeU) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
+ }
+
+ // Emit headers and partition #0
+ {
+ const uint8_t* const part0 = VP8BitWriterBuf(bw);
+ const size_t size0 = VP8BitWriterSize(bw);
+ ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size)
+ && pic->writer(part0, size0, pic)
+ && EmitPartitionsSize(enc, pic);
+ VP8BitWriterWipeOut(bw); // will free the internal buffer.
+ }
+
+ // Token partitions
+ for (p = 0; p < enc->num_parts_; ++p) {
+ const uint8_t* const buf = VP8BitWriterBuf(enc->parts_ + p);
+ const size_t size = VP8BitWriterSize(enc->parts_ + p);
+ if (size) ok = ok && pic->writer(buf, size, pic);
+ VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer.
+ ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part,
+ &enc->percent_);
+ }
+
+ // Padding byte
+ if (ok && pad) {
+ ok = PutPaddingByte(pic);
+ }
+
+ enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size);
+ ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_);
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/src/third_party/libwebp/src/enc/token_enc.c b/src/third_party/libwebp/src/enc/token_enc.c
new file mode 100644
index 0000000..5226245
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/token_enc.c
@@ -0,0 +1,268 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Paginated token buffer
+//
+// A 'token' is a bit value associated with a probability, either fixed
+// or a later-to-be-determined after statistics have been collected.
+// For dynamic probability, we just record the slot id (idx) for the probability
+// value in the final probability array (uint8_t* probas in VP8EmitTokens).
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#include "src/enc/cost_enc.h"
+#include "src/enc/vp8i_enc.h"
+#include "src/utils/utils.h"
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+// we use pages to reduce the number of memcpy()
+#define MIN_PAGE_SIZE 8192 // minimum number of token per page
+#define FIXED_PROBA_BIT (1u << 14)
+
+typedef uint16_t token_t; // bit #15: bit value
+ // bit #14: flags for constant proba or idx
+ // bits #0..13: slot or constant proba
+struct VP8Tokens {
+ VP8Tokens* next_; // pointer to next page
+};
+// Token data is located in memory just after the next_ field.
+// This macro is used to return their address and hide the trick.
+#define TOKEN_DATA(p) ((const token_t*)&(p)[1])
+
+//------------------------------------------------------------------------------
+
+void VP8TBufferInit(VP8TBuffer* const b, int page_size) {
+ b->tokens_ = NULL;
+ b->pages_ = NULL;
+ b->last_page_ = &b->pages_;
+ b->left_ = 0;
+ b->page_size_ = (page_size < MIN_PAGE_SIZE) ? MIN_PAGE_SIZE : page_size;
+ b->error_ = 0;
+}
+
+void VP8TBufferClear(VP8TBuffer* const b) {
+ if (b != NULL) {
+ VP8Tokens* p = b->pages_;
+ while (p != NULL) {
+ VP8Tokens* const next = p->next_;
+ WebPSafeFree(p);
+ p = next;
+ }
+ VP8TBufferInit(b, b->page_size_);
+ }
+}
+
+static int TBufferNewPage(VP8TBuffer* const b) {
+ VP8Tokens* page = NULL;
+ if (!b->error_) {
+ const size_t size = sizeof(*page) + b->page_size_ * sizeof(token_t);
+ page = (VP8Tokens*)WebPSafeMalloc(1ULL, size);
+ }
+ if (page == NULL) {
+ b->error_ = 1;
+ return 0;
+ }
+ page->next_ = NULL;
+
+ *b->last_page_ = page;
+ b->last_page_ = &page->next_;
+ b->left_ = b->page_size_;
+ b->tokens_ = (token_t*)TOKEN_DATA(page);
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+
+#define TOKEN_ID(t, b, ctx) \
+ (NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t))))
+
+static WEBP_INLINE uint32_t AddToken(VP8TBuffer* const b, uint32_t bit,
+ uint32_t proba_idx,
+ proba_t* const stats) {
+ assert(proba_idx < FIXED_PROBA_BIT);
+ assert(bit <= 1);
+ if (b->left_ > 0 || TBufferNewPage(b)) {
+ const int slot = --b->left_;
+ b->tokens_[slot] = (bit << 15) | proba_idx;
+ }
+ VP8RecordStats(bit, stats);
+ return bit;
+}
+
+static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b,
+ uint32_t bit, uint32_t proba) {
+ assert(proba < 256);
+ assert(bit <= 1);
+ if (b->left_ > 0 || TBufferNewPage(b)) {
+ const int slot = --b->left_;
+ b->tokens_[slot] = (bit << 15) | FIXED_PROBA_BIT | proba;
+ }
+}
+
+int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res,
+ VP8TBuffer* const tokens) {
+ const int16_t* const coeffs = res->coeffs;
+ const int coeff_type = res->coeff_type;
+ const int last = res->last;
+ int n = res->first;
+ uint32_t base_id = TOKEN_ID(coeff_type, n, ctx);
+ // should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1
+ proba_t* s = res->stats[n][ctx];
+ if (!AddToken(tokens, last >= 0, base_id + 0, s + 0)) {
+ return 0;
+ }
+
+ while (n < 16) {
+ const int c = coeffs[n++];
+ const int sign = c < 0;
+ const uint32_t v = sign ? -c : c;
+ if (!AddToken(tokens, v != 0, base_id + 1, s + 1)) {
+ base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 0); // ctx=0
+ s = res->stats[VP8EncBands[n]][0];
+ continue;
+ }
+ if (!AddToken(tokens, v > 1, base_id + 2, s + 2)) {
+ base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 1); // ctx=1
+ s = res->stats[VP8EncBands[n]][1];
+ } else {
+ if (!AddToken(tokens, v > 4, base_id + 3, s + 3)) {
+ if (AddToken(tokens, v != 2, base_id + 4, s + 4)) {
+ AddToken(tokens, v == 4, base_id + 5, s + 5);
+ }
+ } else if (!AddToken(tokens, v > 10, base_id + 6, s + 6)) {
+ if (!AddToken(tokens, v > 6, base_id + 7, s + 7)) {
+ AddConstantToken(tokens, v == 6, 159);
+ } else {
+ AddConstantToken(tokens, v >= 9, 165);
+ AddConstantToken(tokens, !(v & 1), 145);
+ }
+ } else {
+ int mask;
+ const uint8_t* tab;
+ uint32_t residue = v - 3;
+ if (residue < (8 << 1)) { // VP8Cat3 (3b)
+ AddToken(tokens, 0, base_id + 8, s + 8);
+ AddToken(tokens, 0, base_id + 9, s + 9);
+ residue -= (8 << 0);
+ mask = 1 << 2;
+ tab = VP8Cat3;
+ } else if (residue < (8 << 2)) { // VP8Cat4 (4b)
+ AddToken(tokens, 0, base_id + 8, s + 8);
+ AddToken(tokens, 1, base_id + 9, s + 9);
+ residue -= (8 << 1);
+ mask = 1 << 3;
+ tab = VP8Cat4;
+ } else if (residue < (8 << 3)) { // VP8Cat5 (5b)
+ AddToken(tokens, 1, base_id + 8, s + 8);
+ AddToken(tokens, 0, base_id + 10, s + 9);
+ residue -= (8 << 2);
+ mask = 1 << 4;
+ tab = VP8Cat5;
+ } else { // VP8Cat6 (11b)
+ AddToken(tokens, 1, base_id + 8, s + 8);
+ AddToken(tokens, 1, base_id + 10, s + 9);
+ residue -= (8 << 3);
+ mask = 1 << 10;
+ tab = VP8Cat6;
+ }
+ while (mask) {
+ AddConstantToken(tokens, !!(residue & mask), *tab++);
+ mask >>= 1;
+ }
+ }
+ base_id = TOKEN_ID(coeff_type, VP8EncBands[n], 2); // ctx=2
+ s = res->stats[VP8EncBands[n]][2];
+ }
+ AddConstantToken(tokens, sign, 128);
+ if (n == 16 || !AddToken(tokens, n <= last, base_id + 0, s + 0)) {
+ return 1; // EOB
+ }
+ }
+ return 1;
+}
+
+#undef TOKEN_ID
+
+//------------------------------------------------------------------------------
+// Final coding pass, with known probabilities
+
+int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
+ const uint8_t* const probas, int final_pass) {
+ const VP8Tokens* p = b->pages_;
+ assert(!b->error_);
+ while (p != NULL) {
+ const VP8Tokens* const next = p->next_;
+ const int N = (next == NULL) ? b->left_ : 0;
+ int n = b->page_size_;
+ const token_t* const tokens = TOKEN_DATA(p);
+ while (n-- > N) {
+ const token_t token = tokens[n];
+ const int bit = (token >> 15) & 1;
+ if (token & FIXED_PROBA_BIT) {
+ VP8PutBit(bw, bit, token & 0xffu); // constant proba
+ } else {
+ VP8PutBit(bw, bit, probas[token & 0x3fffu]);
+ }
+ }
+ if (final_pass) WebPSafeFree((void*)p);
+ p = next;
+ }
+ if (final_pass) b->pages_ = NULL;
+ return 1;
+}
+
+// Size estimation
+size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas) {
+ size_t size = 0;
+ const VP8Tokens* p = b->pages_;
+ assert(!b->error_);
+ while (p != NULL) {
+ const VP8Tokens* const next = p->next_;
+ const int N = (next == NULL) ? b->left_ : 0;
+ int n = b->page_size_;
+ const token_t* const tokens = TOKEN_DATA(p);
+ while (n-- > N) {
+ const token_t token = tokens[n];
+ const int bit = token & (1 << 15);
+ if (token & FIXED_PROBA_BIT) {
+ size += VP8BitCost(bit, token & 0xffu);
+ } else {
+ size += VP8BitCost(bit, probas[token & 0x3fffu]);
+ }
+ }
+ p = next;
+ }
+ return size;
+}
+
+//------------------------------------------------------------------------------
+
+#else // DISABLE_TOKEN_BUFFER
+
+void VP8TBufferInit(VP8TBuffer* const b, int page_size) {
+ (void)b;
+ (void)page_size;
+}
+void VP8TBufferClear(VP8TBuffer* const b) {
+ (void)b;
+}
+
+#endif // !DISABLE_TOKEN_BUFFER
+
diff --git a/src/third_party/libwebp/src/enc/tree_enc.c b/src/third_party/libwebp/src/enc/tree_enc.c
new file mode 100644
index 0000000..afb0543
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/tree_enc.c
@@ -0,0 +1,508 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Coding of token probabilities, intra modes and segments.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/string_poem.h"
+#endif
+
+#include "src/enc/vp8i_enc.h"
+
+//------------------------------------------------------------------------------
+// Default probabilities
+
+// Paragraph 13.5
+const uint8_t
+ VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
+ { { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
+ { 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
+ { 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
+ { 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
+ { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
+ },
+ { { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
+ { 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
+ { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
+ },
+ { { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
+ { 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
+ { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
+ { 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
+ { 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
+ { 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
+ { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
+ { 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
+ { 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
+ },
+ { { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
+ { 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
+ { 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
+ },
+ { { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
+ { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
+ { 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
+ },
+ { { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
+ { 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
+ { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
+ { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
+ { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
+ },
+ { { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
+ { 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
+ { 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
+ },
+ { { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
+ { 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
+ { 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
+ },
+ { { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
+ { 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
+ { 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
+ { 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
+ },
+ { { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
+ { 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
+ { 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
+ { 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
+ { 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
+ },
+ { { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
+ { 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
+ { 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
+ { 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ },
+ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ },
+ { { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
+ { 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
+ { 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
+ },
+ { { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
+ { 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
+ { 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
+ },
+ { { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
+ { 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
+ { 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
+ },
+ { { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
+ { 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
+ { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
+ },
+ { { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
+ { 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
+ { 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
+ },
+ { { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
+ { 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
+ { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
+ },
+ { { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
+ { 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
+ { 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
+ },
+ { { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
+ { 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
+ }
+ }
+};
+
+void VP8DefaultProbas(VP8Encoder* const enc) {
+ VP8EncProba* const probas = &enc->proba_;
+ probas->use_skip_proba_ = 0;
+ memset(probas->segments_, 255u, sizeof(probas->segments_));
+ memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0));
+ // Note: we could hard-code the level_costs_ corresponding to VP8CoeffsProba0,
+ // but that's ~11k of static data. Better call VP8CalculateLevelCosts() later.
+ probas->dirty_ = 1;
+}
+
+// Paragraph 11.5. 900bytes.
+static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
+ { { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
+ { 152, 179, 64, 126, 170, 118, 46, 70, 95 },
+ { 175, 69, 143, 80, 85, 82, 72, 155, 103 },
+ { 56, 58, 10, 171, 218, 189, 17, 13, 152 },
+ { 114, 26, 17, 163, 44, 195, 21, 10, 173 },
+ { 121, 24, 80, 195, 26, 62, 44, 64, 85 },
+ { 144, 71, 10, 38, 171, 213, 144, 34, 26 },
+ { 170, 46, 55, 19, 136, 160, 33, 206, 71 },
+ { 63, 20, 8, 114, 114, 208, 12, 9, 226 },
+ { 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
+ { { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
+ { 72, 187, 100, 130, 157, 111, 32, 75, 80 },
+ { 66, 102, 167, 99, 74, 62, 40, 234, 128 },
+ { 41, 53, 9, 178, 241, 141, 26, 8, 107 },
+ { 74, 43, 26, 146, 73, 166, 49, 23, 157 },
+ { 65, 38, 105, 160, 51, 52, 31, 115, 128 },
+ { 104, 79, 12, 27, 217, 255, 87, 17, 7 },
+ { 87, 68, 71, 44, 114, 51, 15, 186, 23 },
+ { 47, 41, 14, 110, 182, 183, 21, 17, 194 },
+ { 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
+ { { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
+ { 43, 97, 183, 117, 85, 38, 35, 179, 61 },
+ { 39, 53, 200, 87, 26, 21, 43, 232, 171 },
+ { 56, 34, 51, 104, 114, 102, 29, 93, 77 },
+ { 39, 28, 85, 171, 58, 165, 90, 98, 64 },
+ { 34, 22, 116, 206, 23, 34, 43, 166, 73 },
+ { 107, 54, 32, 26, 51, 1, 81, 43, 31 },
+ { 68, 25, 106, 22, 64, 171, 36, 225, 114 },
+ { 34, 19, 21, 102, 132, 188, 16, 76, 124 },
+ { 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
+ { { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
+ { 60, 148, 31, 172, 219, 228, 21, 18, 111 },
+ { 112, 113, 77, 85, 179, 255, 38, 120, 114 },
+ { 40, 42, 1, 196, 245, 209, 10, 25, 109 },
+ { 88, 43, 29, 140, 166, 213, 37, 43, 154 },
+ { 61, 63, 30, 155, 67, 45, 68, 1, 209 },
+ { 100, 80, 8, 43, 154, 1, 51, 26, 71 },
+ { 142, 78, 78, 16, 255, 128, 34, 197, 171 },
+ { 41, 40, 5, 102, 211, 183, 4, 1, 221 },
+ { 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
+ { { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
+ { 67, 87, 58, 169, 82, 115, 26, 59, 179 },
+ { 63, 59, 90, 180, 59, 166, 93, 73, 154 },
+ { 40, 40, 21, 116, 143, 209, 34, 39, 175 },
+ { 47, 15, 16, 183, 34, 223, 49, 45, 183 },
+ { 46, 17, 33, 183, 6, 98, 15, 32, 183 },
+ { 57, 46, 22, 24, 128, 1, 54, 17, 37 },
+ { 65, 32, 73, 115, 28, 128, 23, 128, 205 },
+ { 40, 3, 9, 115, 51, 192, 18, 6, 223 },
+ { 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
+ { { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
+ { 64, 90, 70, 205, 40, 41, 23, 26, 57 },
+ { 54, 57, 112, 184, 5, 41, 38, 166, 213 },
+ { 30, 34, 26, 133, 152, 116, 10, 32, 134 },
+ { 39, 19, 53, 221, 26, 114, 32, 73, 255 },
+ { 31, 9, 65, 234, 2, 15, 1, 118, 73 },
+ { 75, 32, 12, 51, 192, 255, 160, 43, 51 },
+ { 88, 31, 35, 67, 102, 85, 55, 186, 85 },
+ { 56, 21, 23, 111, 59, 205, 45, 37, 192 },
+ { 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
+ { { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
+ { 95, 84, 53, 89, 128, 100, 113, 101, 45 },
+ { 75, 79, 123, 47, 51, 128, 81, 171, 1 },
+ { 57, 17, 5, 71, 102, 57, 53, 41, 49 },
+ { 38, 33, 13, 121, 57, 73, 26, 1, 85 },
+ { 41, 10, 67, 138, 77, 110, 90, 47, 114 },
+ { 115, 21, 2, 10, 102, 255, 166, 23, 6 },
+ { 101, 29, 16, 10, 85, 128, 101, 196, 26 },
+ { 57, 18, 10, 102, 102, 213, 34, 20, 43 },
+ { 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
+ { { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
+ { 69, 60, 71, 38, 73, 119, 28, 222, 37 },
+ { 68, 45, 128, 34, 1, 47, 11, 245, 171 },
+ { 62, 17, 19, 70, 146, 85, 55, 62, 70 },
+ { 37, 43, 37, 154, 100, 163, 85, 160, 1 },
+ { 63, 9, 92, 136, 28, 64, 32, 201, 85 },
+ { 75, 15, 9, 9, 64, 255, 184, 119, 16 },
+ { 86, 6, 28, 5, 64, 255, 25, 248, 1 },
+ { 56, 8, 17, 132, 137, 255, 55, 116, 128 },
+ { 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
+ { { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
+ { 51, 103, 44, 131, 131, 123, 31, 6, 158 },
+ { 86, 40, 64, 135, 148, 224, 45, 183, 128 },
+ { 22, 26, 17, 131, 240, 154, 14, 1, 209 },
+ { 45, 16, 21, 91, 64, 222, 7, 1, 197 },
+ { 56, 21, 39, 155, 60, 138, 23, 102, 213 },
+ { 83, 12, 13, 54, 192, 255, 68, 47, 28 },
+ { 85, 26, 85, 85, 128, 128, 32, 146, 171 },
+ { 18, 11, 7, 63, 144, 171, 4, 4, 246 },
+ { 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
+ { { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
+ { 85, 126, 47, 87, 176, 51, 41, 20, 32 },
+ { 101, 75, 128, 139, 118, 146, 116, 128, 85 },
+ { 56, 41, 15, 176, 236, 85, 37, 9, 62 },
+ { 71, 30, 17, 119, 118, 255, 17, 18, 138 },
+ { 101, 38, 60, 138, 55, 70, 43, 26, 142 },
+ { 146, 36, 19, 30, 171, 255, 97, 27, 20 },
+ { 138, 45, 61, 62, 219, 1, 81, 188, 64 },
+ { 32, 41, 20, 117, 151, 142, 20, 21, 163 },
+ { 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
+};
+
+static int PutI4Mode(VP8BitWriter* const bw, int mode,
+ const uint8_t* const prob) {
+ if (VP8PutBit(bw, mode != B_DC_PRED, prob[0])) {
+ if (VP8PutBit(bw, mode != B_TM_PRED, prob[1])) {
+ if (VP8PutBit(bw, mode != B_VE_PRED, prob[2])) {
+ if (!VP8PutBit(bw, mode >= B_LD_PRED, prob[3])) {
+ if (VP8PutBit(bw, mode != B_HE_PRED, prob[4])) {
+ VP8PutBit(bw, mode != B_RD_PRED, prob[5]);
+ }
+ } else {
+ if (VP8PutBit(bw, mode != B_LD_PRED, prob[6])) {
+ if (VP8PutBit(bw, mode != B_VL_PRED, prob[7])) {
+ VP8PutBit(bw, mode != B_HD_PRED, prob[8]);
+ }
+ }
+ }
+ }
+ }
+ }
+ return mode;
+}
+
+static void PutI16Mode(VP8BitWriter* const bw, int mode) {
+ if (VP8PutBit(bw, (mode == TM_PRED || mode == H_PRED), 156)) {
+ VP8PutBit(bw, mode == TM_PRED, 128); // TM or HE
+ } else {
+ VP8PutBit(bw, mode == V_PRED, 163); // VE or DC
+ }
+}
+
+static void PutUVMode(VP8BitWriter* const bw, int uv_mode) {
+ if (VP8PutBit(bw, uv_mode != DC_PRED, 142)) {
+ if (VP8PutBit(bw, uv_mode != V_PRED, 114)) {
+ VP8PutBit(bw, uv_mode != H_PRED, 183); // else: TM_PRED
+ }
+ }
+}
+
+static void PutSegment(VP8BitWriter* const bw, int s, const uint8_t* p) {
+ if (VP8PutBit(bw, s >= 2, p[0])) p += 1;
+ VP8PutBit(bw, s & 1, p[1]);
+}
+
+void VP8CodeIntraModes(VP8Encoder* const enc) {
+ VP8BitWriter* const bw = &enc->bw_;
+ VP8EncIterator it;
+ VP8IteratorInit(enc, &it);
+ do {
+ const VP8MBInfo* const mb = it.mb_;
+ const uint8_t* preds = it.preds_;
+ if (enc->segment_hdr_.update_map_) {
+ PutSegment(bw, mb->segment_, enc->proba_.segments_);
+ }
+ if (enc->proba_.use_skip_proba_) {
+ VP8PutBit(bw, mb->skip_, enc->proba_.skip_proba_);
+ }
+ if (VP8PutBit(bw, (mb->type_ != 0), 145)) { // i16x16
+ PutI16Mode(bw, preds[0]);
+ } else {
+ const int preds_w = enc->preds_w_;
+ const uint8_t* top_pred = preds - preds_w;
+ int x, y;
+ for (y = 0; y < 4; ++y) {
+ int left = preds[-1];
+ for (x = 0; x < 4; ++x) {
+ const uint8_t* const probas = kBModesProba[top_pred[x]][left];
+ left = PutI4Mode(bw, preds[x], probas);
+ }
+ top_pred = preds;
+ preds += preds_w;
+ }
+ }
+ PutUVMode(bw, mb->uv_mode_);
+ } while (VP8IteratorNext(&it));
+}
+
+//------------------------------------------------------------------------------
+// Paragraph 13
+
+const uint8_t
+ VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
+ { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
+ { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
+ },
+ { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ },
+ { { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
+ { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ },
+ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
+ }
+ }
+};
+
+void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas) {
+ int t, b, c, p;
+ for (t = 0; t < NUM_TYPES; ++t) {
+ for (b = 0; b < NUM_BANDS; ++b) {
+ for (c = 0; c < NUM_CTX; ++c) {
+ for (p = 0; p < NUM_PROBAS; ++p) {
+ const uint8_t p0 = probas->coeffs_[t][b][c][p];
+ const int update = (p0 != VP8CoeffsProba0[t][b][c][p]);
+ if (VP8PutBit(bw, update, VP8CoeffsUpdateProba[t][b][c][p])) {
+ VP8PutBits(bw, p0, 8);
+ }
+ }
+ }
+ }
+ }
+ if (VP8PutBitUniform(bw, probas->use_skip_proba_)) {
+ VP8PutBits(bw, probas->skip_proba_, 8);
+ }
+}
+
diff --git a/src/third_party/libwebp/src/enc/vp8i_enc.h b/src/third_party/libwebp/src/enc/vp8i_enc.h
new file mode 100644
index 0000000..64dbfa4
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/vp8i_enc.h
@@ -0,0 +1,520 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP encoder: internal header.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_ENC_VP8I_ENC_H_
+#define WEBP_ENC_VP8I_ENC_H_
+
+#if !defined(STARBOARD)
+#include <string.h> // for memcpy()
+#endif
+#include "src/dec/common_dec.h"
+#include "src/dsp/dsp.h"
+#include "src/utils/bit_writer_utils.h"
+#include "src/utils/thread_utils.h"
+#include "src/utils/utils.h"
+#include "src/webp/encode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Various defines and enums
+
+// version numbers
+#define ENC_MAJ_VERSION 1
+#define ENC_MIN_VERSION 0
+#define ENC_REV_VERSION 0
+
+enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
+ MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
+ MAX_LEVEL = 2047 // max level (note: max codable is 2047 + 67)
+ };
+
+typedef enum { // Rate-distortion optimization levels
+ RD_OPT_NONE = 0, // no rd-opt
+ RD_OPT_BASIC = 1, // basic scoring (no trellis)
+ RD_OPT_TRELLIS = 2, // perform trellis-quant on the final decision only
+ RD_OPT_TRELLIS_ALL = 3 // trellis-quant for every scoring (much slower)
+} VP8RDLevel;
+
+// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
+// The original or reconstructed samples can be accessed using VP8Scan[].
+// The predicted blocks can be accessed using offsets to yuv_p_ and
+// the arrays VP8*ModeOffsets[].
+// * YUV Samples area (yuv_in_/yuv_out_/yuv_out2_)
+// (see VP8Scan[] for accessing the blocks, along with
+// Y_OFF_ENC/U_OFF_ENC/V_OFF_ENC):
+// +----+----+
+// Y_OFF_ENC |YYYY|UUVV|
+// U_OFF_ENC |YYYY|UUVV|
+// V_OFF_ENC |YYYY|....| <- 25% wasted U/V area
+// |YYYY|....|
+// +----+----+
+// * Prediction area ('yuv_p_', size = PRED_SIZE_ENC)
+// Intra16 predictions (16x16 block each, two per row):
+// |I16DC16|I16TM16|
+// |I16VE16|I16HE16|
+// Chroma U/V predictions (16x8 block each, two per row):
+// |C8DC8|C8TM8|
+// |C8VE8|C8HE8|
+// Intra 4x4 predictions (4x4 block each)
+// |I4DC4 I4TM4 I4VE4 I4HE4|I4RD4 I4VR4 I4LD4 I4VL4|
+// |I4HD4 I4HU4 I4TMP .....|.......................| <- ~31% wasted
+#define YUV_SIZE_ENC (BPS * 16)
+#define PRED_SIZE_ENC (32 * BPS + 16 * BPS + 8 * BPS) // I16+Chroma+I4 preds
+#define Y_OFF_ENC (0)
+#define U_OFF_ENC (16)
+#define V_OFF_ENC (16 + 8)
+
+extern const uint16_t VP8Scan[16];
+extern const uint16_t VP8UVModeOffsets[4];
+extern const uint16_t VP8I16ModeOffsets[4];
+extern const uint16_t VP8I4ModeOffsets[NUM_BMODES];
+
+// Layout of prediction blocks
+// intra 16x16
+#define I16DC16 (0 * 16 * BPS)
+#define I16TM16 (I16DC16 + 16)
+#define I16VE16 (1 * 16 * BPS)
+#define I16HE16 (I16VE16 + 16)
+// chroma 8x8, two U/V blocks side by side (hence: 16x8 each)
+#define C8DC8 (2 * 16 * BPS)
+#define C8TM8 (C8DC8 + 1 * 16)
+#define C8VE8 (2 * 16 * BPS + 8 * BPS)
+#define C8HE8 (C8VE8 + 1 * 16)
+// intra 4x4
+#define I4DC4 (3 * 16 * BPS + 0)
+#define I4TM4 (I4DC4 + 4)
+#define I4VE4 (I4DC4 + 8)
+#define I4HE4 (I4DC4 + 12)
+#define I4RD4 (I4DC4 + 16)
+#define I4VR4 (I4DC4 + 20)
+#define I4LD4 (I4DC4 + 24)
+#define I4VL4 (I4DC4 + 28)
+#define I4HD4 (3 * 16 * BPS + 4 * BPS)
+#define I4HU4 (I4HD4 + 4)
+#define I4TMP (I4HD4 + 8)
+
+typedef int64_t score_t; // type used for scores, rate, distortion
+// Note that MAX_COST is not the maximum allowed by sizeof(score_t),
+// in order to allow overflowing computations.
+#define MAX_COST ((score_t)0x7fffffffffffffLL)
+
+#define QFIX 17
+#define BIAS(b) ((b) << (QFIX - 8))
+// Fun fact: this is the _only_ line where we're actually being lossy and
+// discarding bits.
+static WEBP_INLINE int QUANTDIV(uint32_t n, uint32_t iQ, uint32_t B) {
+ return (int)((n * iQ + B) >> QFIX);
+}
+
+// Uncomment the following to remove token-buffer code:
+// #define DISABLE_TOKEN_BUFFER
+
+// quality below which error-diffusion is enabled
+#define ERROR_DIFFUSION_QUALITY 98
+
+//------------------------------------------------------------------------------
+// Headers
+
+typedef uint32_t proba_t; // 16b + 16b
+typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS];
+typedef proba_t StatsArray[NUM_CTX][NUM_PROBAS];
+typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1];
+typedef const uint16_t* (*CostArrayPtr)[NUM_CTX]; // for easy casting
+typedef const uint16_t* CostArrayMap[16][NUM_CTX];
+typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats
+
+typedef struct VP8Encoder VP8Encoder;
+
+// segment features
+typedef struct {
+ int num_segments_; // Actual number of segments. 1 segment only = unused.
+ int update_map_; // whether to update the segment map or not.
+ // must be 0 if there's only 1 segment.
+ int size_; // bit-cost for transmitting the segment map
+} VP8EncSegmentHeader;
+
+// Struct collecting all frame-persistent probabilities.
+typedef struct {
+ uint8_t segments_[3]; // probabilities for segment tree
+ uint8_t skip_proba_; // final probability of being skipped.
+ ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 1056 bytes
+ StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes
+ CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 13056 bytes
+ CostArrayMap remapped_costs_[NUM_TYPES]; // 1536 bytes
+ int dirty_; // if true, need to call VP8CalculateLevelCosts()
+ int use_skip_proba_; // Note: we always use skip_proba for now.
+ int nb_skip_; // number of skipped blocks
+} VP8EncProba;
+
+// Filter parameters. Not actually used in the code (we don't perform
+// the in-loop filtering), but filled from user's config
+typedef struct {
+ int simple_; // filtering type: 0=complex, 1=simple
+ int level_; // base filter level [0..63]
+ int sharpness_; // [0..7]
+ int i4x4_lf_delta_; // delta filter level for i4x4 relative to i16x16
+} VP8EncFilterHeader;
+
+//------------------------------------------------------------------------------
+// Informations about the macroblocks.
+
+typedef struct {
+ // block type
+ unsigned int type_:2; // 0=i4x4, 1=i16x16
+ unsigned int uv_mode_:2;
+ unsigned int skip_:1;
+ unsigned int segment_:2;
+ uint8_t alpha_; // quantization-susceptibility
+} VP8MBInfo;
+
+typedef struct VP8Matrix {
+ uint16_t q_[16]; // quantizer steps
+ uint16_t iq_[16]; // reciprocals, fixed point.
+ uint32_t bias_[16]; // rounding bias
+ uint32_t zthresh_[16]; // value below which a coefficient is zeroed
+ uint16_t sharpen_[16]; // frequency boosters for slight sharpening
+} VP8Matrix;
+
+typedef struct {
+ VP8Matrix y1_, y2_, uv_; // quantization matrices
+ int alpha_; // quant-susceptibility, range [-127,127]. Zero is neutral.
+ // Lower values indicate a lower risk of blurriness.
+ int beta_; // filter-susceptibility, range [0,255].
+ int quant_; // final segment quantizer.
+ int fstrength_; // final in-loop filtering strength
+ int max_edge_; // max edge delta (for filtering strength)
+ int min_disto_; // minimum distortion required to trigger filtering record
+ // reactivities
+ int lambda_i16_, lambda_i4_, lambda_uv_;
+ int lambda_mode_, lambda_trellis_, tlambda_;
+ int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_;
+
+ // lambda values for distortion-based evaluation
+ score_t i4_penalty_; // penalty for using Intra4
+} VP8SegmentInfo;
+
+typedef int8_t DError[2 /* u/v */][2 /* top or left */];
+
+// Handy transient struct to accumulate score and info during RD-optimization
+// and mode evaluation.
+typedef struct {
+ score_t D, SD; // Distortion, spectral distortion
+ score_t H, R, score; // header bits, rate, score.
+ int16_t y_dc_levels[16]; // Quantized levels for luma-DC, luma-AC, chroma.
+ int16_t y_ac_levels[16][16];
+ int16_t uv_levels[4 + 4][16];
+ int mode_i16; // mode number for intra16 prediction
+ uint8_t modes_i4[16]; // mode numbers for intra4 predictions
+ int mode_uv; // mode number of chroma prediction
+ uint32_t nz; // non-zero blocks
+ int8_t derr[2][3]; // DC diffusion errors for U/V for blocks #1/2/3
+} VP8ModeScore;
+
+// Iterator structure to iterate through macroblocks, pointing to the
+// right neighbouring data (samples, predictions, contexts, ...)
+typedef struct {
+ int x_, y_; // current macroblock
+ uint8_t* yuv_in_; // input samples
+ uint8_t* yuv_out_; // output samples
+ uint8_t* yuv_out2_; // secondary buffer swapped with yuv_out_.
+ uint8_t* yuv_p_; // scratch buffer for prediction
+ VP8Encoder* enc_; // back-pointer
+ VP8MBInfo* mb_; // current macroblock
+ VP8BitWriter* bw_; // current bit-writer
+ uint8_t* preds_; // intra mode predictors (4x4 blocks)
+ uint32_t* nz_; // non-zero pattern
+ uint8_t i4_boundary_[37]; // 32+5 boundary samples needed by intra4x4
+ uint8_t* i4_top_; // pointer to the current top boundary sample
+ int i4_; // current intra4x4 mode being tested
+ int top_nz_[9]; // top-non-zero context.
+ int left_nz_[9]; // left-non-zero. left_nz[8] is independent.
+ uint64_t bit_count_[4][3]; // bit counters for coded levels.
+ uint64_t luma_bits_; // macroblock bit-cost for luma
+ uint64_t uv_bits_; // macroblock bit-cost for chroma
+ LFStats* lf_stats_; // filter stats (borrowed from enc_)
+ int do_trellis_; // if true, perform extra level optimisation
+ int count_down_; // number of mb still to be processed
+ int count_down0_; // starting counter value (for progress)
+ int percent0_; // saved initial progress percent
+
+ DError left_derr_; // left error diffusion (u/v)
+ DError *top_derr_; // top diffusion error - NULL if disabled
+
+ uint8_t* y_left_; // left luma samples (addressable from index -1 to 15).
+ uint8_t* u_left_; // left u samples (addressable from index -1 to 7)
+ uint8_t* v_left_; // left v samples (addressable from index -1 to 7)
+
+ uint8_t* y_top_; // top luma samples at position 'x_'
+ uint8_t* uv_top_; // top u/v samples at position 'x_', packed as 16 bytes
+
+ // memory for storing y/u/v_left_
+ uint8_t yuv_left_mem_[17 + 16 + 16 + 8 + WEBP_ALIGN_CST];
+ // memory for yuv_*
+ uint8_t yuv_mem_[3 * YUV_SIZE_ENC + PRED_SIZE_ENC + WEBP_ALIGN_CST];
+} VP8EncIterator;
+
+ // in iterator.c
+// must be called first
+void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it);
+// restart a scan
+void VP8IteratorReset(VP8EncIterator* const it);
+// reset iterator position to row 'y'
+void VP8IteratorSetRow(VP8EncIterator* const it, int y);
+// set count down (=number of iterations to go)
+void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down);
+// return true if iteration is finished
+int VP8IteratorIsDone(const VP8EncIterator* const it);
+// Import uncompressed samples from source.
+// If tmp_32 is not NULL, import boundary samples too.
+// tmp_32 is a 32-bytes scratch buffer that must be aligned in memory.
+void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32);
+// export decimated samples
+void VP8IteratorExport(const VP8EncIterator* const it);
+// go to next macroblock. Returns false if not finished.
+int VP8IteratorNext(VP8EncIterator* const it);
+// save the yuv_out_ boundary values to top_/left_ arrays for next iterations.
+void VP8IteratorSaveBoundary(VP8EncIterator* const it);
+// Report progression based on macroblock rows. Return 0 for user-abort request.
+int VP8IteratorProgress(const VP8EncIterator* const it,
+ int final_delta_percent);
+// Intra4x4 iterations
+void VP8IteratorStartI4(VP8EncIterator* const it);
+// returns true if not done.
+int VP8IteratorRotateI4(VP8EncIterator* const it,
+ const uint8_t* const yuv_out);
+
+// Non-zero context setup/teardown
+void VP8IteratorNzToBytes(VP8EncIterator* const it);
+void VP8IteratorBytesToNz(VP8EncIterator* const it);
+
+// Helper functions to set mode properties
+void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode);
+void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes);
+void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode);
+void VP8SetSkip(const VP8EncIterator* const it, int skip);
+void VP8SetSegment(const VP8EncIterator* const it, int segment);
+
+//------------------------------------------------------------------------------
+// Paginated token buffer
+
+typedef struct VP8Tokens VP8Tokens; // struct details in token.c
+
+typedef struct {
+#if !defined(DISABLE_TOKEN_BUFFER)
+ VP8Tokens* pages_; // first page
+ VP8Tokens** last_page_; // last page
+ uint16_t* tokens_; // set to (*last_page_)->tokens_
+ int left_; // how many free tokens left before the page is full
+ int page_size_; // number of tokens per page
+#endif
+ int error_; // true in case of malloc error
+} VP8TBuffer;
+
+// initialize an empty buffer
+void VP8TBufferInit(VP8TBuffer* const b, int page_size);
+void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory
+
+#if !defined(DISABLE_TOKEN_BUFFER)
+
+// Finalizes bitstream when probabilities are known.
+// Deletes the allocated token memory if final_pass is true.
+int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
+ const uint8_t* const probas, int final_pass);
+
+// record the coding of coefficients without knowing the probabilities yet
+int VP8RecordCoeffTokens(int ctx, const struct VP8Residual* const res,
+ VP8TBuffer* const tokens);
+
+// Estimate the final coded size given a set of 'probas'.
+size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas);
+
+#endif // !DISABLE_TOKEN_BUFFER
+
+//------------------------------------------------------------------------------
+// VP8Encoder
+
+struct VP8Encoder {
+ const WebPConfig* config_; // user configuration and parameters
+ WebPPicture* pic_; // input / output picture
+
+ // headers
+ VP8EncFilterHeader filter_hdr_; // filtering information
+ VP8EncSegmentHeader segment_hdr_; // segment information
+
+ int profile_; // VP8's profile, deduced from Config.
+
+ // dimension, in macroblock units.
+ int mb_w_, mb_h_;
+ int preds_w_; // stride of the *preds_ prediction plane (=4*mb_w + 1)
+
+ // number of partitions (1, 2, 4 or 8 = MAX_NUM_PARTITIONS)
+ int num_parts_;
+
+ // per-partition boolean decoders.
+ VP8BitWriter bw_; // part0
+ VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions
+ VP8TBuffer tokens_; // token buffer
+
+ int percent_; // for progress
+
+ // transparency blob
+ int has_alpha_;
+ uint8_t* alpha_data_; // non-NULL if transparency is present
+ uint32_t alpha_data_size_;
+ WebPWorker alpha_worker_;
+
+ // quantization info (one set of DC/AC dequant factor per segment)
+ VP8SegmentInfo dqm_[NUM_MB_SEGMENTS];
+ int base_quant_; // nominal quantizer value. Only used
+ // for relative coding of segments' quant.
+ int alpha_; // global susceptibility (<=> complexity)
+ int uv_alpha_; // U/V quantization susceptibility
+ // global offset of quantizers, shared by all segments
+ int dq_y1_dc_;
+ int dq_y2_dc_, dq_y2_ac_;
+ int dq_uv_dc_, dq_uv_ac_;
+
+ // probabilities and statistics
+ VP8EncProba proba_;
+ uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
+ uint64_t sse_count_; // pixel count for the sse_[] stats
+ int coded_size_;
+ int residual_bytes_[3][4];
+ int block_count_[3];
+
+ // quality/speed settings
+ int method_; // 0=fastest, 6=best/slowest.
+ VP8RDLevel rd_opt_level_; // Deduced from method_.
+ int max_i4_header_bits_; // partition #0 safeness factor
+ int mb_header_limit_; // rough limit for header bits per MB
+ int thread_level_; // derived from config->thread_level
+ int do_search_; // derived from config->target_XXX
+ int use_tokens_; // if true, use token buffer
+
+ // Memory
+ VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1)
+ uint8_t* preds_; // predictions modes: (4*mb_w+1) * (4*mb_h+1)
+ uint32_t* nz_; // non-zero bit context: mb_w+1
+ uint8_t* y_top_; // top luma samples.
+ uint8_t* uv_top_; // top u/v samples.
+ // U and V are packed into 16 bytes (8 U + 8 V)
+ LFStats* lf_stats_; // autofilter stats (if NULL, autofilter is off)
+ DError* top_derr_; // diffusion error (NULL if disabled)
+};
+
+//------------------------------------------------------------------------------
+// internal functions. Not public.
+
+ // in tree.c
+extern const uint8_t VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
+extern const uint8_t
+ VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
+// Reset the token probabilities to their initial (default) values
+void VP8DefaultProbas(VP8Encoder* const enc);
+// Write the token probabilities
+void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas);
+// Writes the partition #0 modes (that is: all intra modes)
+void VP8CodeIntraModes(VP8Encoder* const enc);
+
+ // in syntax.c
+// Generates the final bitstream by coding the partition0 and headers,
+// and appending an assembly of all the pre-coded token partitions.
+// Return true if everything is ok.
+int VP8EncWrite(VP8Encoder* const enc);
+// Release memory allocated for bit-writing in VP8EncLoop & seq.
+void VP8EncFreeBitWriters(VP8Encoder* const enc);
+
+ // in frame.c
+extern const uint8_t VP8Cat3[];
+extern const uint8_t VP8Cat4[];
+extern const uint8_t VP8Cat5[];
+extern const uint8_t VP8Cat6[];
+
+// Form all the four Intra16x16 predictions in the yuv_p_ cache
+void VP8MakeLuma16Preds(const VP8EncIterator* const it);
+// Form all the four Chroma8x8 predictions in the yuv_p_ cache
+void VP8MakeChroma8Preds(const VP8EncIterator* const it);
+// Form all the ten Intra4x4 predictions in the yuv_p_ cache
+// for the 4x4 block it->i4_
+void VP8MakeIntra4Preds(const VP8EncIterator* const it);
+// Rate calculation
+int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd);
+int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]);
+int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd);
+// Main coding calls
+int VP8EncLoop(VP8Encoder* const enc);
+int VP8EncTokenLoop(VP8Encoder* const enc);
+
+ // in webpenc.c
+// Assign an error code to a picture. Return false for convenience.
+int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error);
+int WebPReportProgress(const WebPPicture* const pic,
+ int percent, int* const percent_store);
+
+ // in analysis.c
+// Main analysis loop. Decides the segmentations and complexity.
+// Assigns a first guess for Intra16 and uvmode_ prediction modes.
+int VP8EncAnalyze(VP8Encoder* const enc);
+
+ // in quant.c
+// Sets up segment's quantization values, base_quant_ and filter strengths.
+void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
+// Pick best modes and fills the levels. Returns true if skipped.
+int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
+ VP8RDLevel rd_opt);
+
+ // in alpha.c
+void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
+int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process
+int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
+int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
+
+// autofilter
+void VP8InitFilter(VP8EncIterator* const it);
+void VP8StoreFilterStats(VP8EncIterator* const it);
+void VP8AdjustFilterStrength(VP8EncIterator* const it);
+
+// returns the approximate filtering strength needed to smooth a edge
+// step of 'delta', given a sharpness parameter 'sharpness'.
+int VP8FilterStrengthFromDelta(int sharpness, int delta);
+
+ // misc utils for picture_*.c:
+
+// Remove reference to the ARGB/YUVA buffer (doesn't free anything).
+void WebPPictureResetBuffers(WebPPicture* const picture);
+
+// Allocates ARGB buffer of given dimension (previous one is always free'd).
+// Preserves the YUV(A) buffer. Returns false in case of error (invalid param,
+// out-of-memory).
+int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height);
+
+// Allocates YUVA buffer of given dimension (previous one is always free'd).
+// Uses picture->csp to determine whether an alpha buffer is needed.
+// Preserves the ARGB buffer.
+// Returns false in case of error (invalid param, out-of-memory).
+int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height);
+
+// Clean-up the RGB samples under fully transparent area, to help lossless
+// compressibility (no guarantee, though). Assumes that pic->use_argb is true.
+void WebPCleanupTransparentAreaLossless(WebPPicture* const pic);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_ENC_VP8I_ENC_H_ */
diff --git a/src/third_party/libwebp/src/enc/vp8l_enc.c b/src/third_party/libwebp/src/enc/vp8l_enc.c
new file mode 100644
index 0000000..09fa731
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/vp8l_enc.c
@@ -0,0 +1,1915 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// main entry for the lossless encoder.
+//
+// Author: Vikas Arora (vikaas.arora@gmail.com)
+//
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#endif
+
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
+#include "src/enc/vp8i_enc.h"
+#include "src/enc/vp8li_enc.h"
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
+#include "src/utils/bit_writer_utils.h"
+#include "src/utils/huffman_encode_utils.h"
+#include "src/utils/utils.h"
+#include "src/webp/format_constants.h"
+
+// Maximum number of histogram images (sub-blocks).
+#define MAX_HUFF_IMAGE_SIZE 2600
+
+// Palette reordering for smaller sum of deltas (and for smaller storage).
+
+static int PaletteCompareColorsForQsort(const void* p1, const void* p2) {
+ const uint32_t a = WebPMemToUint32((uint8_t*)p1);
+ const uint32_t b = WebPMemToUint32((uint8_t*)p2);
+ assert(a != b);
+ return (a < b) ? -1 : 1;
+}
+
+static WEBP_INLINE uint32_t PaletteComponentDistance(uint32_t v) {
+ return (v <= 128) ? v : (256 - v);
+}
+
+// Computes a value that is related to the entropy created by the
+// palette entry diff.
+//
+// Note that the last & 0xff is a no-operation in the next statement, but
+// removed by most compilers and is here only for regularity of the code.
+static WEBP_INLINE uint32_t PaletteColorDistance(uint32_t col1, uint32_t col2) {
+ const uint32_t diff = VP8LSubPixels(col1, col2);
+ const int kMoreWeightForRGBThanForAlpha = 9;
+ uint32_t score;
+ score = PaletteComponentDistance((diff >> 0) & 0xff);
+ score += PaletteComponentDistance((diff >> 8) & 0xff);
+ score += PaletteComponentDistance((diff >> 16) & 0xff);
+ score *= kMoreWeightForRGBThanForAlpha;
+ score += PaletteComponentDistance((diff >> 24) & 0xff);
+ return score;
+}
+
+static WEBP_INLINE void SwapColor(uint32_t* const col1, uint32_t* const col2) {
+ const uint32_t tmp = *col1;
+ *col1 = *col2;
+ *col2 = tmp;
+}
+
+static void GreedyMinimizeDeltas(uint32_t palette[], int num_colors) {
+ // Find greedily always the closest color of the predicted color to minimize
+ // deltas in the palette. This reduces storage needs since the
+ // palette is stored with delta encoding.
+ uint32_t predict = 0x00000000;
+ int i, k;
+ for (i = 0; i < num_colors; ++i) {
+ int best_ix = i;
+ uint32_t best_score = ~0U;
+ for (k = i; k < num_colors; ++k) {
+ const uint32_t cur_score = PaletteColorDistance(palette[k], predict);
+ if (best_score > cur_score) {
+ best_score = cur_score;
+ best_ix = k;
+ }
+ }
+ SwapColor(&palette[best_ix], &palette[i]);
+ predict = palette[i];
+ }
+}
+
+// The palette has been sorted by alpha. This function checks if the other
+// components of the palette have a monotonic development with regards to
+// position in the palette. If all have monotonic development, there is
+// no benefit to re-organize them greedily. A monotonic development
+// would be spotted in green-only situations (like lossy alpha) or gray-scale
+// images.
+static int PaletteHasNonMonotonousDeltas(uint32_t palette[], int num_colors) {
+ uint32_t predict = 0x000000;
+ int i;
+ uint8_t sign_found = 0x00;
+ for (i = 0; i < num_colors; ++i) {
+ const uint32_t diff = VP8LSubPixels(palette[i], predict);
+ const uint8_t rd = (diff >> 16) & 0xff;
+ const uint8_t gd = (diff >> 8) & 0xff;
+ const uint8_t bd = (diff >> 0) & 0xff;
+ if (rd != 0x00) {
+ sign_found |= (rd < 0x80) ? 1 : 2;
+ }
+ if (gd != 0x00) {
+ sign_found |= (gd < 0x80) ? 8 : 16;
+ }
+ if (bd != 0x00) {
+ sign_found |= (bd < 0x80) ? 64 : 128;
+ }
+ predict = palette[i];
+ }
+ return (sign_found & (sign_found << 1)) != 0; // two consequent signs.
+}
+
+// -----------------------------------------------------------------------------
+// Palette
+
+// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
+// creates a palette and returns true, else returns false.
+static int AnalyzeAndCreatePalette(const WebPPicture* const pic,
+ int low_effort,
+ uint32_t palette[MAX_PALETTE_SIZE],
+ int* const palette_size) {
+ const int num_colors = WebPGetColorPalette(pic, palette);
+ if (num_colors > MAX_PALETTE_SIZE) {
+ *palette_size = 0;
+ return 0;
+ }
+ *palette_size = num_colors;
+ qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort);
+ if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) {
+ GreedyMinimizeDeltas(palette, num_colors);
+ }
+ return 1;
+}
+
+// These five modes are evaluated and their respective entropy is computed.
+typedef enum {
+ kDirect = 0,
+ kSpatial = 1,
+ kSubGreen = 2,
+ kSpatialSubGreen = 3,
+ kPalette = 4,
+ kNumEntropyIx = 5
+} EntropyIx;
+
+typedef enum {
+ kHistoAlpha = 0,
+ kHistoAlphaPred,
+ kHistoGreen,
+ kHistoGreenPred,
+ kHistoRed,
+ kHistoRedPred,
+ kHistoBlue,
+ kHistoBluePred,
+ kHistoRedSubGreen,
+ kHistoRedPredSubGreen,
+ kHistoBlueSubGreen,
+ kHistoBluePredSubGreen,
+ kHistoPalette,
+ kHistoTotal // Must be last.
+} HistoIx;
+
+static void AddSingleSubGreen(int p, uint32_t* const r, uint32_t* const b) {
+ const int green = p >> 8; // The upper bits are masked away later.
+ ++r[((p >> 16) - green) & 0xff];
+ ++b[((p >> 0) - green) & 0xff];
+}
+
+static void AddSingle(uint32_t p,
+ uint32_t* const a, uint32_t* const r,
+ uint32_t* const g, uint32_t* const b) {
+ ++a[(p >> 24) & 0xff];
+ ++r[(p >> 16) & 0xff];
+ ++g[(p >> 8) & 0xff];
+ ++b[(p >> 0) & 0xff];
+}
+
+static WEBP_INLINE uint32_t HashPix(uint32_t pix) {
+ // Note that masking with 0xffffffffu is for preventing an
+ // 'unsigned int overflow' warning. Doesn't impact the compiled code.
+ return ((((uint64_t)pix + (pix >> 19)) * 0x39c5fba7ull) & 0xffffffffu) >> 24;
+}
+
+static int AnalyzeEntropy(const uint32_t* argb,
+ int width, int height, int argb_stride,
+ int use_palette,
+ int palette_size, int transform_bits,
+ EntropyIx* const min_entropy_ix,
+ int* const red_and_blue_always_zero) {
+ // Allocate histogram set with cache_bits = 0.
+ uint32_t* histo;
+
+ if (use_palette && palette_size <= 16) {
+ // In the case of small palettes, we pack 2, 4 or 8 pixels together. In
+ // practice, small palettes are better than any other transform.
+ *min_entropy_ix = kPalette;
+ *red_and_blue_always_zero = 1;
+ return 1;
+ }
+ histo = (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256);
+ if (histo != NULL) {
+ int i, x, y;
+ const uint32_t* prev_row = NULL;
+ const uint32_t* curr_row = argb;
+ uint32_t pix_prev = argb[0]; // Skip the first pixel.
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ const uint32_t pix = curr_row[x];
+ const uint32_t pix_diff = VP8LSubPixels(pix, pix_prev);
+ pix_prev = pix;
+ if ((pix_diff == 0) || (prev_row != NULL && pix == prev_row[x])) {
+ continue;
+ }
+ AddSingle(pix,
+ &histo[kHistoAlpha * 256],
+ &histo[kHistoRed * 256],
+ &histo[kHistoGreen * 256],
+ &histo[kHistoBlue * 256]);
+ AddSingle(pix_diff,
+ &histo[kHistoAlphaPred * 256],
+ &histo[kHistoRedPred * 256],
+ &histo[kHistoGreenPred * 256],
+ &histo[kHistoBluePred * 256]);
+ AddSingleSubGreen(pix,
+ &histo[kHistoRedSubGreen * 256],
+ &histo[kHistoBlueSubGreen * 256]);
+ AddSingleSubGreen(pix_diff,
+ &histo[kHistoRedPredSubGreen * 256],
+ &histo[kHistoBluePredSubGreen * 256]);
+ {
+ // Approximate the palette by the entropy of the multiplicative hash.
+ const uint32_t hash = HashPix(pix);
+ ++histo[kHistoPalette * 256 + hash];
+ }
+ }
+ prev_row = curr_row;
+ curr_row += argb_stride;
+ }
+ {
+ double entropy_comp[kHistoTotal];
+ double entropy[kNumEntropyIx];
+ int k;
+ int last_mode_to_analyze = use_palette ? kPalette : kSpatialSubGreen;
+ int j;
+ // Let's add one zero to the predicted histograms. The zeros are removed
+ // too efficiently by the pix_diff == 0 comparison, at least one of the
+ // zeros is likely to exist.
+ ++histo[kHistoRedPredSubGreen * 256];
+ ++histo[kHistoBluePredSubGreen * 256];
+ ++histo[kHistoRedPred * 256];
+ ++histo[kHistoGreenPred * 256];
+ ++histo[kHistoBluePred * 256];
+ ++histo[kHistoAlphaPred * 256];
+
+ for (j = 0; j < kHistoTotal; ++j) {
+ entropy_comp[j] = VP8LBitsEntropy(&histo[j * 256], 256);
+ }
+ entropy[kDirect] = entropy_comp[kHistoAlpha] +
+ entropy_comp[kHistoRed] +
+ entropy_comp[kHistoGreen] +
+ entropy_comp[kHistoBlue];
+ entropy[kSpatial] = entropy_comp[kHistoAlphaPred] +
+ entropy_comp[kHistoRedPred] +
+ entropy_comp[kHistoGreenPred] +
+ entropy_comp[kHistoBluePred];
+ entropy[kSubGreen] = entropy_comp[kHistoAlpha] +
+ entropy_comp[kHistoRedSubGreen] +
+ entropy_comp[kHistoGreen] +
+ entropy_comp[kHistoBlueSubGreen];
+ entropy[kSpatialSubGreen] = entropy_comp[kHistoAlphaPred] +
+ entropy_comp[kHistoRedPredSubGreen] +
+ entropy_comp[kHistoGreenPred] +
+ entropy_comp[kHistoBluePredSubGreen];
+ entropy[kPalette] = entropy_comp[kHistoPalette];
+
+ // When including transforms, there is an overhead in bits from
+ // storing them. This overhead is small but matters for small images.
+ // For spatial, there are 14 transformations.
+ entropy[kSpatial] += VP8LSubSampleSize(width, transform_bits) *
+ VP8LSubSampleSize(height, transform_bits) *
+ VP8LFastLog2(14);
+ // For color transforms: 24 as only 3 channels are considered in a
+ // ColorTransformElement.
+ entropy[kSpatialSubGreen] += VP8LSubSampleSize(width, transform_bits) *
+ VP8LSubSampleSize(height, transform_bits) *
+ VP8LFastLog2(24);
+ // For palettes, add the cost of storing the palette.
+ // We empirically estimate the cost of a compressed entry as 8 bits.
+ // The palette is differential-coded when compressed hence a much
+ // lower cost than sizeof(uint32_t)*8.
+ entropy[kPalette] += palette_size * 8;
+
+ *min_entropy_ix = kDirect;
+ for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) {
+ if (entropy[*min_entropy_ix] > entropy[k]) {
+ *min_entropy_ix = (EntropyIx)k;
+ }
+ }
+ assert((int)*min_entropy_ix <= last_mode_to_analyze);
+ *red_and_blue_always_zero = 1;
+ // Let's check if the histogram of the chosen entropy mode has
+ // non-zero red and blue values. If all are zero, we can later skip
+ // the cross color optimization.
+ {
+ static const uint8_t kHistoPairs[5][2] = {
+ { kHistoRed, kHistoBlue },
+ { kHistoRedPred, kHistoBluePred },
+ { kHistoRedSubGreen, kHistoBlueSubGreen },
+ { kHistoRedPredSubGreen, kHistoBluePredSubGreen },
+ { kHistoRed, kHistoBlue }
+ };
+ const uint32_t* const red_histo =
+ &histo[256 * kHistoPairs[*min_entropy_ix][0]];
+ const uint32_t* const blue_histo =
+ &histo[256 * kHistoPairs[*min_entropy_ix][1]];
+ for (i = 1; i < 256; ++i) {
+ if ((red_histo[i] | blue_histo[i]) != 0) {
+ *red_and_blue_always_zero = 0;
+ break;
+ }
+ }
+ }
+ }
+ WebPSafeFree(histo);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int GetHistoBits(int method, int use_palette, int width, int height) {
+ // Make tile size a function of encoding method (Range: 0 to 6).
+ int histo_bits = (use_palette ? 9 : 7) - method;
+ while (1) {
+ const int huff_image_size = VP8LSubSampleSize(width, histo_bits) *
+ VP8LSubSampleSize(height, histo_bits);
+ if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break;
+ ++histo_bits;
+ }
+ return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS :
+ (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits;
+}
+
+static int GetTransformBits(int method, int histo_bits) {
+ const int max_transform_bits = (method < 4) ? 6 : (method > 4) ? 4 : 5;
+ const int res =
+ (histo_bits > max_transform_bits) ? max_transform_bits : histo_bits;
+ assert(res <= MAX_TRANSFORM_BITS);
+ return res;
+}
+
+// Set of parameters to be used in each iteration of the cruncher.
+#define CRUNCH_CONFIGS_LZ77_MAX 2
+typedef struct {
+ int entropy_idx_;
+ int lz77s_types_to_try_[CRUNCH_CONFIGS_LZ77_MAX];
+ int lz77s_types_to_try_size_;
+} CrunchConfig;
+
+#define CRUNCH_CONFIGS_MAX kNumEntropyIx
+
+static int EncoderAnalyze(VP8LEncoder* const enc,
+ CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX],
+ int* const crunch_configs_size,
+ int* const red_and_blue_always_zero) {
+ const WebPPicture* const pic = enc->pic_;
+ const int width = pic->width;
+ const int height = pic->height;
+ const WebPConfig* const config = enc->config_;
+ const int method = config->method;
+ const int low_effort = (config->method == 0);
+ int i;
+ int use_palette;
+ int n_lz77s;
+ assert(pic != NULL && pic->argb != NULL);
+
+ use_palette =
+ AnalyzeAndCreatePalette(pic, low_effort,
+ enc->palette_, &enc->palette_size_);
+
+ // Empirical bit sizes.
+ enc->histo_bits_ = GetHistoBits(method, use_palette,
+ pic->width, pic->height);
+ enc->transform_bits_ = GetTransformBits(method, enc->histo_bits_);
+
+ if (low_effort) {
+ // AnalyzeEntropy is somewhat slow.
+ crunch_configs[0].entropy_idx_ = use_palette ? kPalette : kSpatialSubGreen;
+ n_lz77s = 1;
+ *crunch_configs_size = 1;
+ } else {
+ EntropyIx min_entropy_ix;
+ // Try out multiple LZ77 on images with few colors.
+ n_lz77s = (enc->palette_size_ > 0 && enc->palette_size_ <= 16) ? 2 : 1;
+ if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, use_palette,
+ enc->palette_size_, enc->transform_bits_,
+ &min_entropy_ix, red_and_blue_always_zero)) {
+ return 0;
+ }
+ if (method == 6 && config->quality == 100) {
+ // Go brute force on all transforms.
+ *crunch_configs_size = 0;
+ for (i = 0; i < kNumEntropyIx; ++i) {
+ if (i != kPalette || use_palette) {
+ assert(*crunch_configs_size < CRUNCH_CONFIGS_MAX);
+ crunch_configs[(*crunch_configs_size)++].entropy_idx_ = i;
+ }
+ }
+ } else {
+ // Only choose the guessed best transform.
+ *crunch_configs_size = 1;
+ crunch_configs[0].entropy_idx_ = min_entropy_ix;
+ }
+ }
+ // Fill in the different LZ77s.
+ assert(n_lz77s <= CRUNCH_CONFIGS_LZ77_MAX);
+ for (i = 0; i < *crunch_configs_size; ++i) {
+ int j;
+ for (j = 0; j < n_lz77s; ++j) {
+ crunch_configs[i].lz77s_types_to_try_[j] =
+ (j == 0) ? kLZ77Standard | kLZ77RLE : kLZ77Box;
+ }
+ crunch_configs[i].lz77s_types_to_try_size_ = n_lz77s;
+ }
+ return 1;
+}
+
+static int EncoderInit(VP8LEncoder* const enc) {
+ const WebPPicture* const pic = enc->pic_;
+ const int width = pic->width;
+ const int height = pic->height;
+ const int pix_cnt = width * height;
+ // we round the block size up, so we're guaranteed to have
+ // at most MAX_REFS_BLOCK_PER_IMAGE blocks used:
+ const int refs_block_size = (pix_cnt - 1) / MAX_REFS_BLOCK_PER_IMAGE + 1;
+ int i;
+ if (!VP8LHashChainInit(&enc->hash_chain_, pix_cnt)) return 0;
+
+ for (i = 0; i < 3; ++i) VP8LBackwardRefsInit(&enc->refs_[i], refs_block_size);
+
+ return 1;
+}
+
+// Returns false in case of memory error.
+static int GetHuffBitLengthsAndCodes(
+ const VP8LHistogramSet* const histogram_image,
+ HuffmanTreeCode* const huffman_codes) {
+ int i, k;
+ int ok = 0;
+ uint64_t total_length_size = 0;
+ uint8_t* mem_buf = NULL;
+ const int histogram_image_size = histogram_image->size;
+ int max_num_symbols = 0;
+ uint8_t* buf_rle = NULL;
+ HuffmanTree* huff_tree = NULL;
+
+ // Iterate over all histograms and get the aggregate number of codes used.
+ for (i = 0; i < histogram_image_size; ++i) {
+ const VP8LHistogram* const histo = histogram_image->histograms[i];
+ HuffmanTreeCode* const codes = &huffman_codes[5 * i];
+ for (k = 0; k < 5; ++k) {
+ const int num_symbols =
+ (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) :
+ (k == 4) ? NUM_DISTANCE_CODES : 256;
+ codes[k].num_symbols = num_symbols;
+ total_length_size += num_symbols;
+ }
+ }
+
+ // Allocate and Set Huffman codes.
+ {
+ uint16_t* codes;
+ uint8_t* lengths;
+ mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size,
+ sizeof(*lengths) + sizeof(*codes));
+ if (mem_buf == NULL) goto End;
+
+ codes = (uint16_t*)mem_buf;
+ lengths = (uint8_t*)&codes[total_length_size];
+ for (i = 0; i < 5 * histogram_image_size; ++i) {
+ const int bit_length = huffman_codes[i].num_symbols;
+ huffman_codes[i].codes = codes;
+ huffman_codes[i].code_lengths = lengths;
+ codes += bit_length;
+ lengths += bit_length;
+ if (max_num_symbols < bit_length) {
+ max_num_symbols = bit_length;
+ }
+ }
+ }
+
+ buf_rle = (uint8_t*)WebPSafeMalloc(1ULL, max_num_symbols);
+ huff_tree = (HuffmanTree*)WebPSafeMalloc(3ULL * max_num_symbols,
+ sizeof(*huff_tree));
+ if (buf_rle == NULL || huff_tree == NULL) goto End;
+
+ // Create Huffman trees.
+ for (i = 0; i < histogram_image_size; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[5 * i];
+ VP8LHistogram* const histo = histogram_image->histograms[i];
+ VP8LCreateHuffmanTree(histo->literal_, 15, buf_rle, huff_tree, codes + 0);
+ VP8LCreateHuffmanTree(histo->red_, 15, buf_rle, huff_tree, codes + 1);
+ VP8LCreateHuffmanTree(histo->blue_, 15, buf_rle, huff_tree, codes + 2);
+ VP8LCreateHuffmanTree(histo->alpha_, 15, buf_rle, huff_tree, codes + 3);
+ VP8LCreateHuffmanTree(histo->distance_, 15, buf_rle, huff_tree, codes + 4);
+ }
+ ok = 1;
+ End:
+ WebPSafeFree(huff_tree);
+ WebPSafeFree(buf_rle);
+ if (!ok) {
+ WebPSafeFree(mem_buf);
+ memset(huffman_codes, 0, 5 * histogram_image_size * sizeof(*huffman_codes));
+ }
+ return ok;
+}
+
+static void StoreHuffmanTreeOfHuffmanTreeToBitMask(
+ VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) {
+ // RFC 1951 will calm you down if you are worried about this funny sequence.
+ // This sequence is tuned from that, but more weighted for lower symbol count,
+ // and more spiking histograms.
+ static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = {
+ 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ };
+ int i;
+ // Throw away trailing zeros:
+ int codes_to_store = CODE_LENGTH_CODES;
+ for (; codes_to_store > 4; --codes_to_store) {
+ if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
+ break;
+ }
+ }
+ VP8LPutBits(bw, codes_to_store - 4, 4);
+ for (i = 0; i < codes_to_store; ++i) {
+ VP8LPutBits(bw, code_length_bitdepth[kStorageOrder[i]], 3);
+ }
+}
+
+static void ClearHuffmanTreeIfOnlyOneSymbol(
+ HuffmanTreeCode* const huffman_code) {
+ int k;
+ int count = 0;
+ for (k = 0; k < huffman_code->num_symbols; ++k) {
+ if (huffman_code->code_lengths[k] != 0) {
+ ++count;
+ if (count > 1) return;
+ }
+ }
+ for (k = 0; k < huffman_code->num_symbols; ++k) {
+ huffman_code->code_lengths[k] = 0;
+ huffman_code->codes[k] = 0;
+ }
+}
+
+static void StoreHuffmanTreeToBitMask(
+ VP8LBitWriter* const bw,
+ const HuffmanTreeToken* const tokens, const int num_tokens,
+ const HuffmanTreeCode* const huffman_code) {
+ int i;
+ for (i = 0; i < num_tokens; ++i) {
+ const int ix = tokens[i].code;
+ const int extra_bits = tokens[i].extra_bits;
+ VP8LPutBits(bw, huffman_code->codes[ix], huffman_code->code_lengths[ix]);
+ switch (ix) {
+ case 16:
+ VP8LPutBits(bw, extra_bits, 2);
+ break;
+ case 17:
+ VP8LPutBits(bw, extra_bits, 3);
+ break;
+ case 18:
+ VP8LPutBits(bw, extra_bits, 7);
+ break;
+ }
+ }
+}
+
+// 'huff_tree' and 'tokens' are pre-alloacted buffers.
+static void StoreFullHuffmanCode(VP8LBitWriter* const bw,
+ HuffmanTree* const huff_tree,
+ HuffmanTreeToken* const tokens,
+ const HuffmanTreeCode* const tree) {
+ uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 };
+ uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 };
+ const int max_tokens = tree->num_symbols;
+ int num_tokens;
+ HuffmanTreeCode huffman_code;
+ huffman_code.num_symbols = CODE_LENGTH_CODES;
+ huffman_code.code_lengths = code_length_bitdepth;
+ huffman_code.codes = code_length_bitdepth_symbols;
+
+ VP8LPutBits(bw, 0, 1);
+ num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens);
+ {
+ uint32_t histogram[CODE_LENGTH_CODES] = { 0 };
+ uint8_t buf_rle[CODE_LENGTH_CODES] = { 0 };
+ int i;
+ for (i = 0; i < num_tokens; ++i) {
+ ++histogram[tokens[i].code];
+ }
+
+ VP8LCreateHuffmanTree(histogram, 7, buf_rle, huff_tree, &huffman_code);
+ }
+
+ StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth);
+ ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code);
+ {
+ int trailing_zero_bits = 0;
+ int trimmed_length = num_tokens;
+ int write_trimmed_length;
+ int length;
+ int i = num_tokens;
+ while (i-- > 0) {
+ const int ix = tokens[i].code;
+ if (ix == 0 || ix == 17 || ix == 18) {
+ --trimmed_length; // discount trailing zeros
+ trailing_zero_bits += code_length_bitdepth[ix];
+ if (ix == 17) {
+ trailing_zero_bits += 3;
+ } else if (ix == 18) {
+ trailing_zero_bits += 7;
+ }
+ } else {
+ break;
+ }
+ }
+ write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12);
+ length = write_trimmed_length ? trimmed_length : num_tokens;
+ VP8LPutBits(bw, write_trimmed_length, 1);
+ if (write_trimmed_length) {
+ if (trimmed_length == 2) {
+ VP8LPutBits(bw, 0, 3 + 2); // nbitpairs=1, trimmed_length=2
+ } else {
+ const int nbits = BitsLog2Floor(trimmed_length - 2);
+ const int nbitpairs = nbits / 2 + 1;
+ assert(trimmed_length > 2);
+ assert(nbitpairs - 1 < 8);
+ VP8LPutBits(bw, nbitpairs - 1, 3);
+ VP8LPutBits(bw, trimmed_length - 2, nbitpairs * 2);
+ }
+ }
+ StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code);
+ }
+}
+
+// 'huff_tree' and 'tokens' are pre-alloacted buffers.
+static void StoreHuffmanCode(VP8LBitWriter* const bw,
+ HuffmanTree* const huff_tree,
+ HuffmanTreeToken* const tokens,
+ const HuffmanTreeCode* const huffman_code) {
+ int i;
+ int count = 0;
+ int symbols[2] = { 0, 0 };
+ const int kMaxBits = 8;
+ const int kMaxSymbol = 1 << kMaxBits;
+
+ // Check whether it's a small tree.
+ for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) {
+ if (huffman_code->code_lengths[i] != 0) {
+ if (count < 2) symbols[count] = i;
+ ++count;
+ }
+ }
+
+ if (count == 0) { // emit minimal tree for empty cases
+ // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0
+ VP8LPutBits(bw, 0x01, 4);
+ } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) {
+ VP8LPutBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols.
+ VP8LPutBits(bw, count - 1, 1);
+ if (symbols[0] <= 1) {
+ VP8LPutBits(bw, 0, 1); // Code bit for small (1 bit) symbol value.
+ VP8LPutBits(bw, symbols[0], 1);
+ } else {
+ VP8LPutBits(bw, 1, 1);
+ VP8LPutBits(bw, symbols[0], 8);
+ }
+ if (count == 2) {
+ VP8LPutBits(bw, symbols[1], 8);
+ }
+ } else {
+ StoreFullHuffmanCode(bw, huff_tree, tokens, huffman_code);
+ }
+}
+
+static WEBP_INLINE void WriteHuffmanCode(VP8LBitWriter* const bw,
+ const HuffmanTreeCode* const code,
+ int code_index) {
+ const int depth = code->code_lengths[code_index];
+ const int symbol = code->codes[code_index];
+ VP8LPutBits(bw, symbol, depth);
+}
+
+static WEBP_INLINE void WriteHuffmanCodeWithExtraBits(
+ VP8LBitWriter* const bw,
+ const HuffmanTreeCode* const code,
+ int code_index,
+ int bits,
+ int n_bits) {
+ const int depth = code->code_lengths[code_index];
+ const int symbol = code->codes[code_index];
+ VP8LPutBits(bw, (bits << depth) | symbol, depth + n_bits);
+}
+
+static WebPEncodingError StoreImageToBitMask(
+ VP8LBitWriter* const bw, int width, int histo_bits,
+ const VP8LBackwardRefs* const refs,
+ const uint16_t* histogram_symbols,
+ const HuffmanTreeCode* const huffman_codes) {
+ const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1;
+ const int tile_mask = (histo_bits == 0) ? 0 : -(1 << histo_bits);
+ // x and y trace the position in the image.
+ int x = 0;
+ int y = 0;
+ int tile_x = x & tile_mask;
+ int tile_y = y & tile_mask;
+ int histogram_ix = histogram_symbols[0];
+ const HuffmanTreeCode* codes = huffman_codes + 5 * histogram_ix;
+ VP8LRefsCursor c = VP8LRefsCursorInit(refs);
+ while (VP8LRefsCursorOk(&c)) {
+ const PixOrCopy* const v = c.cur_pos;
+ if ((tile_x != (x & tile_mask)) || (tile_y != (y & tile_mask))) {
+ tile_x = x & tile_mask;
+ tile_y = y & tile_mask;
+ histogram_ix = histogram_symbols[(y >> histo_bits) * histo_xsize +
+ (x >> histo_bits)];
+ codes = huffman_codes + 5 * histogram_ix;
+ }
+ if (PixOrCopyIsLiteral(v)) {
+ static const uint8_t order[] = { 1, 2, 0, 3 };
+ int k;
+ for (k = 0; k < 4; ++k) {
+ const int code = PixOrCopyLiteral(v, order[k]);
+ WriteHuffmanCode(bw, codes + k, code);
+ }
+ } else if (PixOrCopyIsCacheIdx(v)) {
+ const int code = PixOrCopyCacheIdx(v);
+ const int literal_ix = 256 + NUM_LENGTH_CODES + code;
+ WriteHuffmanCode(bw, codes, literal_ix);
+ } else {
+ int bits, n_bits;
+ int code;
+
+ const int distance = PixOrCopyDistance(v);
+ VP8LPrefixEncode(v->len, &code, &n_bits, &bits);
+ WriteHuffmanCodeWithExtraBits(bw, codes, 256 + code, bits, n_bits);
+
+ // Don't write the distance with the extra bits code since
+ // the distance can be up to 18 bits of extra bits, and the prefix
+ // 15 bits, totaling to 33, and our PutBits only supports up to 32 bits.
+ VP8LPrefixEncode(distance, &code, &n_bits, &bits);
+ WriteHuffmanCode(bw, codes + 4, code);
+ VP8LPutBits(bw, bits, n_bits);
+ }
+ x += PixOrCopyLength(v);
+ while (x >= width) {
+ x -= width;
+ ++y;
+ }
+ VP8LRefsCursorNext(&c);
+ }
+ return bw->error_ ? VP8_ENC_ERROR_OUT_OF_MEMORY : VP8_ENC_OK;
+}
+
+// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
+static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw,
+ const uint32_t* const argb,
+ VP8LHashChain* const hash_chain,
+ VP8LBackwardRefs* const refs_tmp1,
+ VP8LBackwardRefs* const refs_tmp2,
+ int width, int height,
+ int quality, int low_effort) {
+ int i;
+ int max_tokens = 0;
+ WebPEncodingError err = VP8_ENC_OK;
+ VP8LBackwardRefs* refs;
+ HuffmanTreeToken* tokens = NULL;
+ HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
+ const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol
+ int cache_bits = 0;
+ VP8LHistogramSet* histogram_image = NULL;
+ HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
+ 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
+ if (huff_tree == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ // Calculate backward references from ARGB image.
+ if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
+ low_effort)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ refs = VP8LGetBackwardReferences(width, height, argb, quality, 0,
+ kLZ77Standard | kLZ77RLE, &cache_bits,
+ hash_chain, refs_tmp1, refs_tmp2);
+ if (refs == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ histogram_image = VP8LAllocateHistogramSet(1, cache_bits);
+ if (histogram_image == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ // Build histogram image and symbols from backward references.
+ VP8LHistogramStoreRefs(refs, histogram_image->histograms[0]);
+
+ // Create Huffman bit lengths and codes for each histogram image.
+ assert(histogram_image->size == 1);
+ if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ // No color cache, no Huffman image.
+ VP8LPutBits(bw, 0, 1);
+
+ // Find maximum number of symbols for the huffman tree-set.
+ for (i = 0; i < 5; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ if (max_tokens < codes->num_symbols) {
+ max_tokens = codes->num_symbols;
+ }
+ }
+
+ tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
+ if (tokens == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ // Store Huffman codes.
+ for (i = 0; i < 5; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ StoreHuffmanCode(bw, huff_tree, tokens, codes);
+ ClearHuffmanTreeIfOnlyOneSymbol(codes);
+ }
+
+ // Store actual literals.
+ err = StoreImageToBitMask(bw, width, 0, refs, histogram_symbols,
+ huffman_codes);
+
+ Error:
+ WebPSafeFree(tokens);
+ WebPSafeFree(huff_tree);
+ VP8LFreeHistogramSet(histogram_image);
+ WebPSafeFree(huffman_codes[0].codes);
+ return err;
+}
+
+static WebPEncodingError EncodeImageInternal(
+ VP8LBitWriter* const bw, const uint32_t* const argb,
+ VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[3], int width,
+ int height, int quality, int low_effort, int use_cache,
+ const CrunchConfig* const config, int* cache_bits, int histogram_bits,
+ size_t init_byte_position, int* const hdr_size, int* const data_size) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const uint32_t histogram_image_xysize =
+ VP8LSubSampleSize(width, histogram_bits) *
+ VP8LSubSampleSize(height, histogram_bits);
+ VP8LHistogramSet* histogram_image = NULL;
+ VP8LHistogram* tmp_histo = NULL;
+ int histogram_image_size = 0;
+ size_t bit_array_size = 0;
+ HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
+ 3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
+ HuffmanTreeToken* tokens = NULL;
+ HuffmanTreeCode* huffman_codes = NULL;
+ VP8LBackwardRefs* refs_best;
+ VP8LBackwardRefs* refs_tmp;
+ uint16_t* const histogram_symbols =
+ (uint16_t*)WebPSafeMalloc(histogram_image_xysize,
+ sizeof(*histogram_symbols));
+ int lz77s_idx;
+ VP8LBitWriter bw_init = *bw, bw_best;
+ int hdr_size_tmp;
+ assert(histogram_bits >= MIN_HUFFMAN_BITS);
+ assert(histogram_bits <= MAX_HUFFMAN_BITS);
+ assert(hdr_size != NULL);
+ assert(data_size != NULL);
+
+ if (histogram_symbols == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ if (use_cache) {
+ // If the value is different from zero, it has been set during the
+ // palette analysis.
+ if (*cache_bits == 0) *cache_bits = MAX_COLOR_CACHE_BITS;
+ } else {
+ *cache_bits = 0;
+ }
+ // 'best_refs' is the reference to the best backward refs and points to one
+ // of refs_array[0] or refs_array[1].
+ // Calculate backward references from ARGB image.
+ if (huff_tree == NULL ||
+ !VP8LHashChainFill(hash_chain, quality, argb, width, height,
+ low_effort) ||
+ !VP8LBitWriterInit(&bw_best, 0) ||
+ (config->lz77s_types_to_try_size_ > 1 &&
+ !VP8LBitWriterClone(bw, &bw_best))) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ for (lz77s_idx = 0; lz77s_idx < config->lz77s_types_to_try_size_;
+ ++lz77s_idx) {
+ refs_best = VP8LGetBackwardReferences(
+ width, height, argb, quality, low_effort,
+ config->lz77s_types_to_try_[lz77s_idx], cache_bits, hash_chain,
+ &refs_array[0], &refs_array[1]);
+ if (refs_best == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ // Keep the best references aside and use the other element from the first
+ // two as a temporary for later usage.
+ refs_tmp = &refs_array[refs_best == &refs_array[0] ? 1 : 0];
+
+ histogram_image =
+ VP8LAllocateHistogramSet(histogram_image_xysize, *cache_bits);
+ tmp_histo = VP8LAllocateHistogram(*cache_bits);
+ if (histogram_image == NULL || tmp_histo == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ // Build histogram image and symbols from backward references.
+ if (!VP8LGetHistoImageSymbols(width, height, refs_best, quality, low_effort,
+ histogram_bits, *cache_bits, histogram_image,
+ tmp_histo, histogram_symbols)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ // Create Huffman bit lengths and codes for each histogram image.
+ histogram_image_size = histogram_image->size;
+ bit_array_size = 5 * histogram_image_size;
+ huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size,
+ sizeof(*huffman_codes));
+ // Note: some histogram_image entries may point to tmp_histos[], so the
+ // latter need to outlive the following call to GetHuffBitLengthsAndCodes().
+ if (huffman_codes == NULL ||
+ !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ // Free combined histograms.
+ VP8LFreeHistogramSet(histogram_image);
+ histogram_image = NULL;
+
+ // Free scratch histograms.
+ VP8LFreeHistogram(tmp_histo);
+ tmp_histo = NULL;
+
+ // Color Cache parameters.
+ if (*cache_bits > 0) {
+ VP8LPutBits(bw, 1, 1);
+ VP8LPutBits(bw, *cache_bits, 4);
+ } else {
+ VP8LPutBits(bw, 0, 1);
+ }
+
+ // Huffman image + meta huffman.
+ {
+ const int write_histogram_image = (histogram_image_size > 1);
+ VP8LPutBits(bw, write_histogram_image, 1);
+ if (write_histogram_image) {
+ uint32_t* const histogram_argb =
+ (uint32_t*)WebPSafeMalloc(histogram_image_xysize,
+ sizeof(*histogram_argb));
+ int max_index = 0;
+ uint32_t i;
+ if (histogram_argb == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ for (i = 0; i < histogram_image_xysize; ++i) {
+ const int symbol_index = histogram_symbols[i] & 0xffff;
+ histogram_argb[i] = (symbol_index << 8);
+ if (symbol_index >= max_index) {
+ max_index = symbol_index + 1;
+ }
+ }
+ histogram_image_size = max_index;
+
+ VP8LPutBits(bw, histogram_bits - 2, 3);
+ err = EncodeImageNoHuffman(
+ bw, histogram_argb, hash_chain, refs_tmp, &refs_array[2],
+ VP8LSubSampleSize(width, histogram_bits),
+ VP8LSubSampleSize(height, histogram_bits), quality, low_effort);
+ WebPSafeFree(histogram_argb);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+ }
+
+ // Store Huffman codes.
+ {
+ int i;
+ int max_tokens = 0;
+ // Find maximum number of symbols for the huffman tree-set.
+ for (i = 0; i < 5 * histogram_image_size; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ if (max_tokens < codes->num_symbols) {
+ max_tokens = codes->num_symbols;
+ }
+ }
+ tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
+ if (tokens == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ for (i = 0; i < 5 * histogram_image_size; ++i) {
+ HuffmanTreeCode* const codes = &huffman_codes[i];
+ StoreHuffmanCode(bw, huff_tree, tokens, codes);
+ ClearHuffmanTreeIfOnlyOneSymbol(codes);
+ }
+ }
+ // Store actual literals.
+ hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position);
+ err = StoreImageToBitMask(bw, width, histogram_bits, refs_best,
+ histogram_symbols, huffman_codes);
+ // Keep track of the smallest image so far.
+ if (lz77s_idx == 0 ||
+ VP8LBitWriterNumBytes(bw) < VP8LBitWriterNumBytes(&bw_best)) {
+ *hdr_size = hdr_size_tmp;
+ *data_size =
+ (int)(VP8LBitWriterNumBytes(bw) - init_byte_position - *hdr_size);
+ VP8LBitWriterSwap(bw, &bw_best);
+ }
+ // Reset the bit writer for the following iteration if any.
+ if (config->lz77s_types_to_try_size_ > 1) VP8LBitWriterReset(&bw_init, bw);
+ WebPSafeFree(tokens);
+ tokens = NULL;
+ if (huffman_codes != NULL) {
+ WebPSafeFree(huffman_codes->codes);
+ WebPSafeFree(huffman_codes);
+ huffman_codes = NULL;
+ }
+ }
+ VP8LBitWriterSwap(bw, &bw_best);
+
+ Error:
+ WebPSafeFree(tokens);
+ WebPSafeFree(huff_tree);
+ VP8LFreeHistogramSet(histogram_image);
+ VP8LFreeHistogram(tmp_histo);
+ if (huffman_codes != NULL) {
+ WebPSafeFree(huffman_codes->codes);
+ WebPSafeFree(huffman_codes);
+ }
+ WebPSafeFree(histogram_symbols);
+ VP8LBitWriterWipeOut(&bw_best);
+ return err;
+}
+
+// -----------------------------------------------------------------------------
+// Transforms
+
+static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height,
+ VP8LBitWriter* const bw) {
+ VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
+ VP8LPutBits(bw, SUBTRACT_GREEN, 2);
+ VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
+}
+
+static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
+ int width, int height,
+ int quality, int low_effort,
+ int used_subtract_green,
+ VP8LBitWriter* const bw) {
+ const int pred_bits = enc->transform_bits_;
+ const int transform_width = VP8LSubSampleSize(width, pred_bits);
+ const int transform_height = VP8LSubSampleSize(height, pred_bits);
+ // we disable near-lossless quantization if palette is used.
+ const int near_lossless_strength = enc->use_palette_ ? 100
+ : enc->config_->near_lossless;
+
+ VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_,
+ enc->argb_scratch_, enc->transform_data_,
+ near_lossless_strength, enc->config_->exact,
+ used_subtract_green);
+ VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
+ VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2);
+ assert(pred_bits >= 2);
+ VP8LPutBits(bw, pred_bits - 2, 3);
+ return EncodeImageNoHuffman(
+ bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
+ (VP8LBackwardRefs*)&enc->refs_[0], // cast const away
+ (VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
+ quality, low_effort);
+}
+
+static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
+ int width, int height,
+ int quality, int low_effort,
+ VP8LBitWriter* const bw) {
+ const int ccolor_transform_bits = enc->transform_bits_;
+ const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
+ const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
+
+ VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality,
+ enc->argb_, enc->transform_data_);
+ VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
+ VP8LPutBits(bw, CROSS_COLOR_TRANSFORM, 2);
+ assert(ccolor_transform_bits >= 2);
+ VP8LPutBits(bw, ccolor_transform_bits - 2, 3);
+ return EncodeImageNoHuffman(
+ bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
+ (VP8LBackwardRefs*)&enc->refs_[0], // cast const away
+ (VP8LBackwardRefs*)&enc->refs_[1], transform_width, transform_height,
+ quality, low_effort);
+}
+
+// -----------------------------------------------------------------------------
+
+static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
+ size_t riff_size, size_t vp8l_size) {
+ uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
+ 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
+ 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
+ };
+ PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
+ PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
+ if (!pic->writer(riff, sizeof(riff), pic)) {
+ return VP8_ENC_ERROR_BAD_WRITE;
+ }
+ return VP8_ENC_OK;
+}
+
+static int WriteImageSize(const WebPPicture* const pic,
+ VP8LBitWriter* const bw) {
+ const int width = pic->width - 1;
+ const int height = pic->height - 1;
+ assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION);
+
+ VP8LPutBits(bw, width, VP8L_IMAGE_SIZE_BITS);
+ VP8LPutBits(bw, height, VP8L_IMAGE_SIZE_BITS);
+ return !bw->error_;
+}
+
+static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) {
+ VP8LPutBits(bw, has_alpha, 1);
+ VP8LPutBits(bw, VP8L_VERSION, VP8L_VERSION_BITS);
+ return !bw->error_;
+}
+
+static WebPEncodingError WriteImage(const WebPPicture* const pic,
+ VP8LBitWriter* const bw,
+ size_t* const coded_size) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
+ const size_t webpll_size = VP8LBitWriterNumBytes(bw);
+ const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
+ const size_t pad = vp8l_size & 1;
+ const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
+
+ err = WriteRiffHeader(pic, riff_size, vp8l_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ if (!pic->writer(webpll_data, webpll_size, pic)) {
+ err = VP8_ENC_ERROR_BAD_WRITE;
+ goto Error;
+ }
+
+ if (pad) {
+ const uint8_t pad_byte[1] = { 0 };
+ if (!pic->writer(pad_byte, 1, pic)) {
+ err = VP8_ENC_ERROR_BAD_WRITE;
+ goto Error;
+ }
+ }
+ *coded_size = CHUNK_HEADER_SIZE + riff_size;
+ return VP8_ENC_OK;
+
+ Error:
+ return err;
+}
+
+// -----------------------------------------------------------------------------
+
+static void ClearTransformBuffer(VP8LEncoder* const enc) {
+ WebPSafeFree(enc->transform_mem_);
+ enc->transform_mem_ = NULL;
+ enc->transform_mem_size_ = 0;
+}
+
+// Allocates the memory for argb (W x H) buffer, 2 rows of context for
+// prediction and transform data.
+// Flags influencing the memory allocated:
+// enc->transform_bits_
+// enc->use_predict_, enc->use_cross_color_
+static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
+ int width, int height) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const uint64_t image_size = width * height;
+ // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra
+ // pixel in each, plus 2 regular scanlines of bytes.
+ // TODO(skal): Clean up by using arithmetic in bytes instead of words.
+ const uint64_t argb_scratch_size =
+ enc->use_predict_
+ ? (width + 1) * 2 +
+ (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t)
+ : 0;
+ const uint64_t transform_data_size =
+ (enc->use_predict_ || enc->use_cross_color_)
+ ? VP8LSubSampleSize(width, enc->transform_bits_) *
+ VP8LSubSampleSize(height, enc->transform_bits_)
+ : 0;
+ const uint64_t max_alignment_in_words =
+ (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t);
+ const uint64_t mem_size =
+ image_size + max_alignment_in_words +
+ argb_scratch_size + max_alignment_in_words +
+ transform_data_size;
+ uint32_t* mem = enc->transform_mem_;
+ if (mem == NULL || mem_size > enc->transform_mem_size_) {
+ ClearTransformBuffer(enc);
+ mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem));
+ if (mem == NULL) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ enc->transform_mem_ = mem;
+ enc->transform_mem_size_ = (size_t)mem_size;
+ enc->argb_content_ = kEncoderNone;
+ }
+ enc->argb_ = mem;
+ mem = (uint32_t*)WEBP_ALIGN(mem + image_size);
+ enc->argb_scratch_ = mem;
+ mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size);
+ enc->transform_data_ = mem;
+
+ enc->current_width_ = width;
+ Error:
+ return err;
+}
+
+static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const WebPPicture* const picture = enc->pic_;
+ const int width = picture->width;
+ const int height = picture->height;
+ int y;
+ err = AllocateTransformBuffer(enc, width, height);
+ if (err != VP8_ENC_OK) return err;
+ if (enc->argb_content_ == kEncoderARGB) return VP8_ENC_OK;
+ for (y = 0; y < height; ++y) {
+ memcpy(enc->argb_ + y * width,
+ picture->argb + y * picture->argb_stride,
+ width * sizeof(*enc->argb_));
+ }
+ enc->argb_content_ = kEncoderARGB;
+ assert(enc->current_width_ == width);
+ return VP8_ENC_OK;
+}
+
+// -----------------------------------------------------------------------------
+
+static WEBP_INLINE int SearchColorNoIdx(const uint32_t sorted[], uint32_t color,
+ int hi) {
+ int low = 0;
+ if (sorted[low] == color) return low; // loop invariant: sorted[low] != color
+ while (1) {
+ const int mid = (low + hi) >> 1;
+ if (sorted[mid] == color) {
+ return mid;
+ } else if (sorted[mid] < color) {
+ low = mid;
+ } else {
+ hi = mid;
+ }
+ }
+}
+
+#define APPLY_PALETTE_GREEDY_MAX 4
+
+static WEBP_INLINE uint32_t SearchColorGreedy(const uint32_t palette[],
+ int palette_size,
+ uint32_t color) {
+ (void)palette_size;
+ assert(palette_size < APPLY_PALETTE_GREEDY_MAX);
+ assert(3 == APPLY_PALETTE_GREEDY_MAX - 1);
+ if (color == palette[0]) return 0;
+ if (color == palette[1]) return 1;
+ if (color == palette[2]) return 2;
+ return 3;
+}
+
+static WEBP_INLINE uint32_t ApplyPaletteHash0(uint32_t color) {
+ // Focus on the green color.
+ return (color >> 8) & 0xff;
+}
+
+#define PALETTE_INV_SIZE_BITS 11
+#define PALETTE_INV_SIZE (1 << PALETTE_INV_SIZE_BITS)
+
+static WEBP_INLINE uint32_t ApplyPaletteHash1(uint32_t color) {
+ // Forget about alpha.
+ return ((uint32_t)((color & 0x00ffffffu) * 4222244071ull)) >>
+ (32 - PALETTE_INV_SIZE_BITS);
+}
+
+static WEBP_INLINE uint32_t ApplyPaletteHash2(uint32_t color) {
+ // Forget about alpha.
+ return ((uint32_t)((color & 0x00ffffffu) * ((1ull << 31) - 1))) >>
+ (32 - PALETTE_INV_SIZE_BITS);
+}
+
+// Sort palette in increasing order and prepare an inverse mapping array.
+static void PrepareMapToPalette(const uint32_t palette[], int num_colors,
+ uint32_t sorted[], uint32_t idx_map[]) {
+ int i;
+ memcpy(sorted, palette, num_colors * sizeof(*sorted));
+ qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort);
+ for (i = 0; i < num_colors; ++i) {
+ idx_map[SearchColorNoIdx(sorted, palette[i], num_colors)] = i;
+ }
+}
+
+// Use 1 pixel cache for ARGB pixels.
+#define APPLY_PALETTE_FOR(COLOR_INDEX) do { \
+ uint32_t prev_pix = palette[0]; \
+ uint32_t prev_idx = 0; \
+ for (y = 0; y < height; ++y) { \
+ for (x = 0; x < width; ++x) { \
+ const uint32_t pix = src[x]; \
+ if (pix != prev_pix) { \
+ prev_idx = COLOR_INDEX; \
+ prev_pix = pix; \
+ } \
+ tmp_row[x] = prev_idx; \
+ } \
+ VP8LBundleColorMap(tmp_row, width, xbits, dst); \
+ src += src_stride; \
+ dst += dst_stride; \
+ } \
+} while (0)
+
+// Remap argb values in src[] to packed palettes entries in dst[]
+// using 'row' as a temporary buffer of size 'width'.
+// We assume that all src[] values have a corresponding entry in the palette.
+// Note: src[] can be the same as dst[]
+static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride,
+ uint32_t* dst, uint32_t dst_stride,
+ const uint32_t* palette, int palette_size,
+ int width, int height, int xbits) {
+ // TODO(skal): this tmp buffer is not needed if VP8LBundleColorMap() can be
+ // made to work in-place.
+ uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row));
+ int x, y;
+
+ if (tmp_row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+
+ if (palette_size < APPLY_PALETTE_GREEDY_MAX) {
+ APPLY_PALETTE_FOR(SearchColorGreedy(palette, palette_size, pix));
+ } else {
+ int i, j;
+ uint16_t buffer[PALETTE_INV_SIZE];
+ uint32_t (*const hash_functions[])(uint32_t) = {
+ ApplyPaletteHash0, ApplyPaletteHash1, ApplyPaletteHash2
+ };
+
+ // Try to find a perfect hash function able to go from a color to an index
+ // within 1 << PALETTE_INV_SIZE_BITS in order to build a hash map to go
+ // from color to index in palette.
+ for (i = 0; i < 3; ++i) {
+ int use_LUT = 1;
+ // Set each element in buffer to max uint16_t.
+ memset(buffer, 0xff, sizeof(buffer));
+ for (j = 0; j < palette_size; ++j) {
+ const uint32_t ind = hash_functions[i](palette[j]);
+ if (buffer[ind] != 0xffffu) {
+ use_LUT = 0;
+ break;
+ } else {
+ buffer[ind] = j;
+ }
+ }
+ if (use_LUT) break;
+ }
+
+ if (i == 0) {
+ APPLY_PALETTE_FOR(buffer[ApplyPaletteHash0(pix)]);
+ } else if (i == 1) {
+ APPLY_PALETTE_FOR(buffer[ApplyPaletteHash1(pix)]);
+ } else if (i == 2) {
+ APPLY_PALETTE_FOR(buffer[ApplyPaletteHash2(pix)]);
+ } else {
+ uint32_t idx_map[MAX_PALETTE_SIZE];
+ uint32_t palette_sorted[MAX_PALETTE_SIZE];
+ PrepareMapToPalette(palette, palette_size, palette_sorted, idx_map);
+ APPLY_PALETTE_FOR(
+ idx_map[SearchColorNoIdx(palette_sorted, pix, palette_size)]);
+ }
+ }
+ WebPSafeFree(tmp_row);
+ return VP8_ENC_OK;
+}
+#undef APPLY_PALETTE_FOR
+#undef PALETTE_INV_SIZE_BITS
+#undef PALETTE_INV_SIZE
+#undef APPLY_PALETTE_GREEDY_MAX
+
+// Note: Expects "enc->palette_" to be set properly.
+static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc,
+ int in_place) {
+ WebPEncodingError err = VP8_ENC_OK;
+ const WebPPicture* const pic = enc->pic_;
+ const int width = pic->width;
+ const int height = pic->height;
+ const uint32_t* const palette = enc->palette_;
+ const uint32_t* src = in_place ? enc->argb_ : pic->argb;
+ const int src_stride = in_place ? enc->current_width_ : pic->argb_stride;
+ const int palette_size = enc->palette_size_;
+ int xbits;
+
+ // Replace each input pixel by corresponding palette index.
+ // This is done line by line.
+ if (palette_size <= 4) {
+ xbits = (palette_size <= 2) ? 3 : 2;
+ } else {
+ xbits = (palette_size <= 16) ? 1 : 0;
+ }
+
+ err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
+ if (err != VP8_ENC_OK) return err;
+
+ err = ApplyPalette(src, src_stride,
+ enc->argb_, enc->current_width_,
+ palette, palette_size, width, height, xbits);
+ enc->argb_content_ = kEncoderPalette;
+ return err;
+}
+
+// Save palette_[] to bitstream.
+static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
+ VP8LEncoder* const enc) {
+ int i;
+ uint32_t tmp_palette[MAX_PALETTE_SIZE];
+ const int palette_size = enc->palette_size_;
+ const uint32_t* const palette = enc->palette_;
+ VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
+ VP8LPutBits(bw, COLOR_INDEXING_TRANSFORM, 2);
+ assert(palette_size >= 1 && palette_size <= MAX_PALETTE_SIZE);
+ VP8LPutBits(bw, palette_size - 1, 8);
+ for (i = palette_size - 1; i >= 1; --i) {
+ tmp_palette[i] = VP8LSubPixels(palette[i], palette[i - 1]);
+ }
+ tmp_palette[0] = palette[0];
+ return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_,
+ &enc->refs_[0], &enc->refs_[1], palette_size, 1,
+ 20 /* quality */, low_effort);
+}
+
+// -----------------------------------------------------------------------------
+// VP8LEncoder
+
+static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config,
+ const WebPPicture* const picture) {
+ VP8LEncoder* const enc = (VP8LEncoder*)WebPSafeCalloc(1ULL, sizeof(*enc));
+ if (enc == NULL) {
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ return NULL;
+ }
+ enc->config_ = config;
+ enc->pic_ = picture;
+ enc->argb_content_ = kEncoderNone;
+
+ VP8LEncDspInit();
+
+ return enc;
+}
+
+static void VP8LEncoderDelete(VP8LEncoder* enc) {
+ if (enc != NULL) {
+ int i;
+ VP8LHashChainClear(&enc->hash_chain_);
+ for (i = 0; i < 3; ++i) VP8LBackwardRefsClear(&enc->refs_[i]);
+ ClearTransformBuffer(enc);
+ WebPSafeFree(enc);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Main call
+
+typedef struct {
+ const WebPConfig* config_;
+ const WebPPicture* picture_;
+ VP8LBitWriter* bw_;
+ VP8LEncoder* enc_;
+ int use_cache_;
+ CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX];
+ int num_crunch_configs_;
+ int red_and_blue_always_zero_;
+ WebPEncodingError err_;
+ WebPAuxStats* stats_;
+} StreamEncodeContext;
+
+static int EncodeStreamHook(void* input, void* data2) {
+ StreamEncodeContext* const params = (StreamEncodeContext*)input;
+ const WebPConfig* const config = params->config_;
+ const WebPPicture* const picture = params->picture_;
+ VP8LBitWriter* const bw = params->bw_;
+ VP8LEncoder* const enc = params->enc_;
+ const int use_cache = params->use_cache_;
+ const CrunchConfig* const crunch_configs = params->crunch_configs_;
+ const int num_crunch_configs = params->num_crunch_configs_;
+ const int red_and_blue_always_zero = params->red_and_blue_always_zero_;
+#if !defined(WEBP_DISABLE_STATS)
+ WebPAuxStats* const stats = params->stats_;
+#endif
+ WebPEncodingError err = VP8_ENC_OK;
+ const int quality = (int)config->quality;
+ const int low_effort = (config->method == 0);
+#if (WEBP_NEAR_LOSSLESS == 1)
+ const int width = picture->width;
+#endif
+ const int height = picture->height;
+ const size_t byte_position = VP8LBitWriterNumBytes(bw);
+#if (WEBP_NEAR_LOSSLESS == 1)
+ int use_near_lossless = 0;
+#endif
+ int hdr_size = 0;
+ int data_size = 0;
+ int use_delta_palette = 0;
+ int idx;
+ size_t best_size = 0;
+ VP8LBitWriter bw_init = *bw, bw_best;
+ (void)data2;
+
+ if (!VP8LBitWriterInit(&bw_best, 0) ||
+ (num_crunch_configs > 1 && !VP8LBitWriterClone(bw, &bw_best))) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ for (idx = 0; idx < num_crunch_configs; ++idx) {
+ const int entropy_idx = crunch_configs[idx].entropy_idx_;
+ enc->use_palette_ = (entropy_idx == kPalette);
+ enc->use_subtract_green_ =
+ (entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen);
+ enc->use_predict_ =
+ (entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen);
+ if (low_effort) {
+ enc->use_cross_color_ = 0;
+ } else {
+ enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_;
+ }
+ // Reset any parameter in the encoder that is set in the previous iteration.
+ enc->cache_bits_ = 0;
+ VP8LBackwardRefsClear(&enc->refs_[0]);
+ VP8LBackwardRefsClear(&enc->refs_[1]);
+
+#if (WEBP_NEAR_LOSSLESS == 1)
+ // Apply near-lossless preprocessing.
+ use_near_lossless = (config->near_lossless < 100) && !enc->use_palette_ &&
+ !enc->use_predict_;
+ if (use_near_lossless) {
+ err = AllocateTransformBuffer(enc, width, height);
+ if (err != VP8_ENC_OK) goto Error;
+ if ((enc->argb_content_ != kEncoderNearLossless) &&
+ !VP8ApplyNearLossless(picture, config->near_lossless, enc->argb_)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ enc->argb_content_ = kEncoderNearLossless;
+ } else {
+ enc->argb_content_ = kEncoderNone;
+ }
+#else
+ enc->argb_content_ = kEncoderNone;
+#endif
+
+ // Encode palette
+ if (enc->use_palette_) {
+ err = EncodePalette(bw, low_effort, enc);
+ if (err != VP8_ENC_OK) goto Error;
+ err = MapImageFromPalette(enc, use_delta_palette);
+ if (err != VP8_ENC_OK) goto Error;
+ // If using a color cache, do not have it bigger than the number of
+ // colors.
+ if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
+ enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1;
+ }
+ }
+ if (!use_delta_palette) {
+ // In case image is not packed.
+ if (enc->argb_content_ != kEncoderNearLossless &&
+ enc->argb_content_ != kEncoderPalette) {
+ err = MakeInputImageCopy(enc);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+
+ // -----------------------------------------------------------------------
+ // Apply transforms and write transform data.
+
+ if (enc->use_subtract_green_) {
+ ApplySubtractGreen(enc, enc->current_width_, height, bw);
+ }
+
+ if (enc->use_predict_) {
+ err = ApplyPredictFilter(enc, enc->current_width_, height, quality,
+ low_effort, enc->use_subtract_green_, bw);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+
+ if (enc->use_cross_color_) {
+ err = ApplyCrossColorFilter(enc, enc->current_width_, height, quality,
+ low_effort, bw);
+ if (err != VP8_ENC_OK) goto Error;
+ }
+ }
+
+ VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms.
+
+ // -------------------------------------------------------------------------
+ // Encode and write the transformed image.
+ err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
+ enc->current_width_, height, quality, low_effort,
+ use_cache, &crunch_configs[idx],
+ &enc->cache_bits_, enc->histo_bits_,
+ byte_position, &hdr_size, &data_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ // If we are better than what we already have.
+ if (idx == 0 || VP8LBitWriterNumBytes(bw) < best_size) {
+ best_size = VP8LBitWriterNumBytes(bw);
+ // Store the BitWriter.
+ VP8LBitWriterSwap(bw, &bw_best);
+#if !defined(WEBP_DISABLE_STATS)
+ // Update the stats.
+ if (stats != NULL) {
+ stats->lossless_features = 0;
+ if (enc->use_predict_) stats->lossless_features |= 1;
+ if (enc->use_cross_color_) stats->lossless_features |= 2;
+ if (enc->use_subtract_green_) stats->lossless_features |= 4;
+ if (enc->use_palette_) stats->lossless_features |= 8;
+ stats->histogram_bits = enc->histo_bits_;
+ stats->transform_bits = enc->transform_bits_;
+ stats->cache_bits = enc->cache_bits_;
+ stats->palette_size = enc->palette_size_;
+ stats->lossless_size = (int)(best_size - byte_position);
+ stats->lossless_hdr_size = hdr_size;
+ stats->lossless_data_size = data_size;
+ }
+#endif
+ }
+ // Reset the bit writer for the following iteration if any.
+ if (num_crunch_configs > 1) VP8LBitWriterReset(&bw_init, bw);
+ }
+ VP8LBitWriterSwap(&bw_best, bw);
+
+Error:
+ VP8LBitWriterWipeOut(&bw_best);
+ params->err_ = err;
+ // The hook should return false in case of error.
+ return (err == VP8_ENC_OK);
+}
+
+WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
+ const WebPPicture* const picture,
+ VP8LBitWriter* const bw_main,
+ int use_cache) {
+ WebPEncodingError err = VP8_ENC_OK;
+ VP8LEncoder* const enc_main = VP8LEncoderNew(config, picture);
+ VP8LEncoder* enc_side = NULL;
+ CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX];
+ int num_crunch_configs_main, num_crunch_configs_side = 0;
+ int idx;
+ int red_and_blue_always_zero = 0;
+ WebPWorker worker_main, worker_side;
+ StreamEncodeContext params_main, params_side;
+ // The main thread uses picture->stats, the side thread uses stats_side.
+ WebPAuxStats stats_side;
+ VP8LBitWriter bw_side;
+ const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface();
+ int ok_main;
+
+ // Analyze image (entropy, num_palettes etc)
+ if (enc_main == NULL ||
+ !EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
+ &red_and_blue_always_zero) ||
+ !EncoderInit(enc_main) || !VP8LBitWriterInit(&bw_side, 0)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ // Split the configs between the main and side threads (if any).
+ if (config->thread_level > 0) {
+ num_crunch_configs_side = num_crunch_configs_main / 2;
+ for (idx = 0; idx < num_crunch_configs_side; ++idx) {
+ params_side.crunch_configs_[idx] =
+ crunch_configs[num_crunch_configs_main - num_crunch_configs_side +
+ idx];
+ }
+ params_side.num_crunch_configs_ = num_crunch_configs_side;
+ }
+ num_crunch_configs_main -= num_crunch_configs_side;
+ for (idx = 0; idx < num_crunch_configs_main; ++idx) {
+ params_main.crunch_configs_[idx] = crunch_configs[idx];
+ }
+ params_main.num_crunch_configs_ = num_crunch_configs_main;
+
+ // Fill in the parameters for the thread workers.
+ {
+ const int params_size = (num_crunch_configs_side > 0) ? 2 : 1;
+ for (idx = 0; idx < params_size; ++idx) {
+ // Create the parameters for each worker.
+ WebPWorker* const worker = (idx == 0) ? &worker_main : &worker_side;
+ StreamEncodeContext* const param =
+ (idx == 0) ? ¶ms_main : ¶ms_side;
+ param->config_ = config;
+ param->picture_ = picture;
+ param->use_cache_ = use_cache;
+ param->red_and_blue_always_zero_ = red_and_blue_always_zero;
+ if (idx == 0) {
+ param->stats_ = picture->stats;
+ param->bw_ = bw_main;
+ param->enc_ = enc_main;
+ } else {
+ param->stats_ = (picture->stats == NULL) ? NULL : &stats_side;
+ // Create a side bit writer.
+ if (!VP8LBitWriterClone(bw_main, &bw_side)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ param->bw_ = &bw_side;
+ // Create a side encoder.
+ enc_side = VP8LEncoderNew(config, picture);
+ if (enc_side == NULL || !EncoderInit(enc_side)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+ // Copy the values that were computed for the main encoder.
+ enc_side->histo_bits_ = enc_main->histo_bits_;
+ enc_side->transform_bits_ = enc_main->transform_bits_;
+ enc_side->palette_size_ = enc_main->palette_size_;
+ memcpy(enc_side->palette_, enc_main->palette_,
+ sizeof(enc_main->palette_));
+ param->enc_ = enc_side;
+ }
+ // Create the workers.
+ worker_interface->Init(worker);
+ worker->data1 = param;
+ worker->data2 = NULL;
+ worker->hook = EncodeStreamHook;
+ }
+ }
+
+ // Start the second thread if needed.
+ if (num_crunch_configs_side != 0) {
+ if (!worker_interface->Reset(&worker_side)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+#if !defined(WEBP_DISABLE_STATS)
+ // This line is here and not in the param initialization above to remove a
+ // Clang static analyzer warning.
+ if (picture->stats != NULL) {
+ memcpy(&stats_side, picture->stats, sizeof(stats_side));
+ }
+#endif
+ // This line is only useful to remove a Clang static analyzer warning.
+ params_side.err_ = VP8_ENC_OK;
+ worker_interface->Launch(&worker_side);
+ }
+ // Execute the main thread.
+ worker_interface->Execute(&worker_main);
+ ok_main = worker_interface->Sync(&worker_main);
+ worker_interface->End(&worker_main);
+ if (num_crunch_configs_side != 0) {
+ // Wait for the second thread.
+ const int ok_side = worker_interface->Sync(&worker_side);
+ worker_interface->End(&worker_side);
+ if (!ok_main || !ok_side) {
+ err = ok_main ? params_side.err_ : params_main.err_;
+ goto Error;
+ }
+ if (VP8LBitWriterNumBytes(&bw_side) < VP8LBitWriterNumBytes(bw_main)) {
+ VP8LBitWriterSwap(bw_main, &bw_side);
+#if !defined(WEBP_DISABLE_STATS)
+ if (picture->stats != NULL) {
+ memcpy(picture->stats, &stats_side, sizeof(*picture->stats));
+ }
+#endif
+ }
+ } else {
+ if (!ok_main) {
+ err = params_main.err_;
+ goto Error;
+ }
+ }
+
+Error:
+ VP8LBitWriterWipeOut(&bw_side);
+ VP8LEncoderDelete(enc_main);
+ VP8LEncoderDelete(enc_side);
+ return err;
+}
+
+#undef CRUNCH_CONFIGS_MAX
+#undef CRUNCH_CONFIGS_LZ77_MAX
+
+int VP8LEncodeImage(const WebPConfig* const config,
+ const WebPPicture* const picture) {
+ int width, height;
+ int has_alpha;
+ size_t coded_size;
+ int percent = 0;
+ int initial_size;
+ WebPEncodingError err = VP8_ENC_OK;
+ VP8LBitWriter bw;
+
+ if (picture == NULL) return 0;
+
+ if (config == NULL || picture->argb == NULL) {
+ err = VP8_ENC_ERROR_NULL_PARAMETER;
+ WebPEncodingSetError(picture, err);
+ return 0;
+ }
+
+ width = picture->width;
+ height = picture->height;
+ // Initialize BitWriter with size corresponding to 16 bpp to photo images and
+ // 8 bpp for graphical images.
+ initial_size = (config->image_hint == WEBP_HINT_GRAPH) ?
+ width * height : width * height * 2;
+ if (!VP8LBitWriterInit(&bw, initial_size)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ if (!WebPReportProgress(picture, 1, &percent)) {
+ UserAbort:
+ err = VP8_ENC_ERROR_USER_ABORT;
+ goto Error;
+ }
+ // Reset stats (for pure lossless coding)
+ if (picture->stats != NULL) {
+ WebPAuxStats* const stats = picture->stats;
+ memset(stats, 0, sizeof(*stats));
+ stats->PSNR[0] = 99.f;
+ stats->PSNR[1] = 99.f;
+ stats->PSNR[2] = 99.f;
+ stats->PSNR[3] = 99.f;
+ stats->PSNR[4] = 99.f;
+ }
+
+ // Write image size.
+ if (!WriteImageSize(picture, &bw)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ has_alpha = WebPPictureHasTransparency(picture);
+ // Write the non-trivial Alpha flag and lossless version.
+ if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
+ err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ goto Error;
+ }
+
+ if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
+
+ // Encode main image stream.
+ err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/);
+ if (err != VP8_ENC_OK) goto Error;
+
+ if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
+
+ // Finish the RIFF chunk.
+ err = WriteImage(picture, &bw, &coded_size);
+ if (err != VP8_ENC_OK) goto Error;
+
+ if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
+
+#if !defined(WEBP_DISABLE_STATS)
+ // Save size.
+ if (picture->stats != NULL) {
+ picture->stats->coded_size += (int)coded_size;
+ picture->stats->lossless_size = (int)coded_size;
+ }
+#endif
+
+ if (picture->extra_info != NULL) {
+ const int mb_w = (width + 15) >> 4;
+ const int mb_h = (height + 15) >> 4;
+ memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info));
+ }
+
+ Error:
+ if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+ VP8LBitWriterWipeOut(&bw);
+ if (err != VP8_ENC_OK) {
+ WebPEncodingSetError(picture, err);
+ return 0;
+ }
+ return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/enc/vp8li_enc.h b/src/third_party/libwebp/src/enc/vp8li_enc.h
new file mode 100644
index 0000000..298a4a0
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/vp8li_enc.h
@@ -0,0 +1,118 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Lossless encoder: internal header.
+//
+// Author: Vikas Arora (vikaas.arora@gmail.com)
+
+#ifndef WEBP_ENC_VP8LI_ENC_H_
+#define WEBP_ENC_VP8LI_ENC_H_
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+// Either WEBP_NEAR_LOSSLESS is defined as 0 in config.h when compiling to
+// disable near-lossless, or it is enabled by default.
+#ifndef WEBP_NEAR_LOSSLESS
+#define WEBP_NEAR_LOSSLESS 1
+#endif
+
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
+#include "src/utils/bit_writer_utils.h"
+#include "src/webp/encode.h"
+#include "src/webp/format_constants.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// maximum value of transform_bits_ in VP8LEncoder.
+#define MAX_TRANSFORM_BITS 6
+
+typedef enum {
+ kEncoderNone = 0,
+ kEncoderARGB,
+ kEncoderNearLossless,
+ kEncoderPalette
+} VP8LEncoderARGBContent;
+
+typedef struct {
+ const WebPConfig* config_; // user configuration and parameters
+ const WebPPicture* pic_; // input picture.
+
+ uint32_t* argb_; // Transformed argb image data.
+ VP8LEncoderARGBContent argb_content_; // Content type of the argb buffer.
+ uint32_t* argb_scratch_; // Scratch memory for argb rows
+ // (used for prediction).
+ uint32_t* transform_data_; // Scratch memory for transform data.
+ uint32_t* transform_mem_; // Currently allocated memory.
+ size_t transform_mem_size_; // Currently allocated memory size.
+
+ int current_width_; // Corresponds to packed image width.
+
+ // Encoding parameters derived from quality parameter.
+ int histo_bits_;
+ int transform_bits_; // <= MAX_TRANSFORM_BITS.
+ int cache_bits_; // If equal to 0, don't use color cache.
+
+ // Encoding parameters derived from image characteristics.
+ int use_cross_color_;
+ int use_subtract_green_;
+ int use_predict_;
+ int use_palette_;
+ int palette_size_;
+ uint32_t palette_[MAX_PALETTE_SIZE];
+
+ // Some 'scratch' (potentially large) objects.
+ struct VP8LBackwardRefs refs_[3]; // Backward Refs array for temporaries.
+ VP8LHashChain hash_chain_; // HashChain data for constructing
+ // backward references.
+} VP8LEncoder;
+
+//------------------------------------------------------------------------------
+// internal functions. Not public.
+
+// Encodes the picture.
+// Returns 0 if config or picture is NULL or picture doesn't have valid argb
+// input.
+int VP8LEncodeImage(const WebPConfig* const config,
+ const WebPPicture* const picture);
+
+// Encodes the main image stream using the supplied bit writer.
+// If 'use_cache' is false, disables the use of color cache.
+WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
+ const WebPPicture* const picture,
+ VP8LBitWriter* const bw, int use_cache);
+
+#if (WEBP_NEAR_LOSSLESS == 1)
+// in near_lossless.c
+// Near lossless preprocessing in RGB color-space.
+int VP8ApplyNearLossless(const WebPPicture* const picture, int quality,
+ uint32_t* const argb_dst);
+#endif
+
+//------------------------------------------------------------------------------
+// Image transforms in predictor.c.
+
+void VP8LResidualImage(int width, int height, int bits, int low_effort,
+ uint32_t* const argb, uint32_t* const argb_scratch,
+ uint32_t* const image, int near_lossless, int exact,
+ int used_subtract_green);
+
+void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
+ uint32_t* const argb, uint32_t* image);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_ENC_VP8LI_ENC_H_ */
diff --git a/src/third_party/libwebp/src/enc/webp_enc.c b/src/third_party/libwebp/src/enc/webp_enc.c
new file mode 100644
index 0000000..d558652
--- /dev/null
+++ b/src/third_party/libwebp/src/enc/webp_enc.c
@@ -0,0 +1,417 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP encoder: main entry point
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/math_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#endif
+
+#include "src/enc/cost_enc.h"
+#include "src/enc/vp8i_enc.h"
+#include "src/enc/vp8li_enc.h"
+#include "src/utils/utils.h"
+
+// #define PRINT_MEMORY_INFO
+
+#ifdef PRINT_MEMORY_INFO
+#include <stdio.h>
+#endif
+
+//------------------------------------------------------------------------------
+
+int WebPGetEncoderVersion(void) {
+ return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+// VP8Encoder
+//------------------------------------------------------------------------------
+
+static void ResetSegmentHeader(VP8Encoder* const enc) {
+ VP8EncSegmentHeader* const hdr = &enc->segment_hdr_;
+ hdr->num_segments_ = enc->config_->segments;
+ hdr->update_map_ = (hdr->num_segments_ > 1);
+ hdr->size_ = 0;
+}
+
+static void ResetFilterHeader(VP8Encoder* const enc) {
+ VP8EncFilterHeader* const hdr = &enc->filter_hdr_;
+ hdr->simple_ = 1;
+ hdr->level_ = 0;
+ hdr->sharpness_ = 0;
+ hdr->i4x4_lf_delta_ = 0;
+}
+
+static void ResetBoundaryPredictions(VP8Encoder* const enc) {
+ // init boundary values once for all
+ // Note: actually, initializing the preds_[] is only needed for intra4.
+ int i;
+ uint8_t* const top = enc->preds_ - enc->preds_w_;
+ uint8_t* const left = enc->preds_ - 1;
+ for (i = -1; i < 4 * enc->mb_w_; ++i) {
+ top[i] = B_DC_PRED;
+ }
+ for (i = 0; i < 4 * enc->mb_h_; ++i) {
+ left[i * enc->preds_w_] = B_DC_PRED;
+ }
+ enc->nz_[-1] = 0; // constant
+}
+
+// Mapping from config->method_ to coding tools used.
+//-------------------+---+---+---+---+---+---+---+
+// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
+//-------------------+---+---+---+---+---+---+---+
+// fast probe | x | | | x | | | |
+//-------------------+---+---+---+---+---+---+---+
+// dynamic proba | ~ | x | x | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// fast mode analysis|[x]|[x]| | | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// basic rd-opt | | | | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// disto-refine i4/16| x | x | x | | | | |
+//-------------------+---+---+---+---+---+---+---+
+// disto-refine uv | | x | x | | | | |
+//-------------------+---+---+---+---+---+---+---+
+// rd-opt i4/16 | | | ~ | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// token buffer (opt)| | | | x | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+// Trellis | | | | | | x |Ful|
+//-------------------+---+---+---+---+---+---+---+
+// full-SNS | | | | | x | x | x |
+//-------------------+---+---+---+---+---+---+---+
+
+static void MapConfigToTools(VP8Encoder* const enc) {
+ const WebPConfig* const config = enc->config_;
+ const int method = config->method;
+ const int limit = 100 - config->partition_limit;
+ enc->method_ = method;
+ enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL
+ : (method >= 5) ? RD_OPT_TRELLIS
+ : (method >= 3) ? RD_OPT_BASIC
+ : RD_OPT_NONE;
+ enc->max_i4_header_bits_ =
+ 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block
+ (limit * limit) / (100 * 100); // ... modulated with a quadratic curve.
+
+ // partition0 = 512k max.
+ enc->mb_header_limit_ =
+ (score_t)256 * 510 * 8 * 1024 / (enc->mb_w_ * enc->mb_h_);
+
+ enc->thread_level_ = config->thread_level;
+
+ enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
+ if (!config->low_memory) {
+#if !defined(DISABLE_TOKEN_BUFFER)
+ enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC); // need rd stats
+#endif
+ if (enc->use_tokens_) {
+ enc->num_parts_ = 1; // doesn't work with multi-partition
+ }
+ }
+}
+
+// Memory scaling with dimensions:
+// memory (bytes) ~= 2.25 * w + 0.0625 * w * h
+//
+// Typical memory footprint (614x440 picture)
+// encoder: 22111
+// info: 4368
+// preds: 17741
+// top samples: 1263
+// non-zero: 175
+// lf-stats: 0
+// total: 45658
+// Transient object sizes:
+// VP8EncIterator: 3360
+// VP8ModeScore: 872
+// VP8SegmentInfo: 732
+// VP8EncProba: 18352
+// LFStats: 2048
+// Picture size (yuv): 419328
+
+static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
+ WebPPicture* const picture) {
+ VP8Encoder* enc;
+ const int use_filter =
+ (config->filter_strength > 0) || (config->autofilter > 0);
+ const int mb_w = (picture->width + 15) >> 4;
+ const int mb_h = (picture->height + 15) >> 4;
+ const int preds_w = 4 * mb_w + 1;
+ const int preds_h = 4 * mb_h + 1;
+ const size_t preds_size = preds_w * preds_h * sizeof(*enc->preds_);
+ const int top_stride = mb_w * 16;
+ const size_t nz_size = (mb_w + 1) * sizeof(*enc->nz_) + WEBP_ALIGN_CST;
+ const size_t info_size = mb_w * mb_h * sizeof(*enc->mb_info_);
+ const size_t samples_size =
+ 2 * top_stride * sizeof(*enc->y_top_) // top-luma/u/v
+ + WEBP_ALIGN_CST; // align all
+ const size_t lf_stats_size =
+ config->autofilter ? sizeof(*enc->lf_stats_) + WEBP_ALIGN_CST : 0;
+ const size_t top_derr_size =
+ (config->quality <= ERROR_DIFFUSION_QUALITY || config->pass > 1) ?
+ mb_w * sizeof(*enc->top_derr_) : 0;
+ uint8_t* mem;
+ const uint64_t size = (uint64_t)sizeof(*enc) // main struct
+ + WEBP_ALIGN_CST // cache alignment
+ + info_size // modes info
+ + preds_size // prediction modes
+ + samples_size // top/left samples
+ + top_derr_size // top diffusion error
+ + nz_size // coeff context bits
+ + lf_stats_size; // autofilter stats
+
+#ifdef PRINT_MEMORY_INFO
+ printf("===================================\n");
+ printf("Memory used:\n"
+ " encoder: %ld\n"
+ " info: %ld\n"
+ " preds: %ld\n"
+ " top samples: %ld\n"
+ " top diffusion: %ld\n"
+ " non-zero: %ld\n"
+ " lf-stats: %ld\n"
+ " total: %ld\n",
+ sizeof(*enc) + WEBP_ALIGN_CST, info_size,
+ preds_size, samples_size, top_derr_size, nz_size, lf_stats_size, size);
+ printf("Transient object sizes:\n"
+ " VP8EncIterator: %ld\n"
+ " VP8ModeScore: %ld\n"
+ " VP8SegmentInfo: %ld\n"
+ " VP8EncProba: %ld\n"
+ " LFStats: %ld\n",
+ sizeof(VP8EncIterator), sizeof(VP8ModeScore),
+ sizeof(VP8SegmentInfo), sizeof(VP8EncProba),
+ sizeof(LFStats));
+ printf("Picture size (yuv): %ld\n",
+ mb_w * mb_h * 384 * sizeof(uint8_t));
+ printf("===================================\n");
+#endif
+ mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
+ if (mem == NULL) {
+ WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
+ return NULL;
+ }
+ enc = (VP8Encoder*)mem;
+ mem = (uint8_t*)WEBP_ALIGN(mem + sizeof(*enc));
+ memset(enc, 0, sizeof(*enc));
+ enc->num_parts_ = 1 << config->partitions;
+ enc->mb_w_ = mb_w;
+ enc->mb_h_ = mb_h;
+ enc->preds_w_ = preds_w;
+ enc->mb_info_ = (VP8MBInfo*)mem;
+ mem += info_size;
+ enc->preds_ = mem + 1 + enc->preds_w_;
+ mem += preds_size;
+ enc->nz_ = 1 + (uint32_t*)WEBP_ALIGN(mem);
+ mem += nz_size;
+ enc->lf_stats_ = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL;
+ mem += lf_stats_size;
+
+ // top samples (all 16-aligned)
+ mem = (uint8_t*)WEBP_ALIGN(mem);
+ enc->y_top_ = mem;
+ enc->uv_top_ = enc->y_top_ + top_stride;
+ mem += 2 * top_stride;
+ enc->top_derr_ = top_derr_size ? (DError*)mem : NULL;
+ mem += top_derr_size;
+ assert(mem <= (uint8_t*)enc + size);
+
+ enc->config_ = config;
+ enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
+ enc->pic_ = picture;
+ enc->percent_ = 0;
+
+ MapConfigToTools(enc);
+ VP8EncDspInit();
+ VP8DefaultProbas(enc);
+ ResetSegmentHeader(enc);
+ ResetFilterHeader(enc);
+ ResetBoundaryPredictions(enc);
+ VP8EncDspCostInit();
+ VP8EncInitAlpha(enc);
+
+ // lower quality means smaller output -> we modulate a little the page
+ // size based on quality. This is just a crude 1rst-order prediction.
+ {
+ const float scale = 1.f + config->quality * 5.f / 100.f; // in [1,6]
+ VP8TBufferInit(&enc->tokens_, (int)(mb_w * mb_h * 4 * scale));
+ }
+ return enc;
+}
+
+static int DeleteVP8Encoder(VP8Encoder* enc) {
+ int ok = 1;
+ if (enc != NULL) {
+ ok = VP8EncDeleteAlpha(enc);
+ VP8TBufferClear(&enc->tokens_);
+ WebPSafeFree(enc);
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------------
+
+#if !defined(WEBP_DISABLE_STATS)
+static double GetPSNR(uint64_t err, uint64_t size) {
+ return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;
+}
+
+static void FinalizePSNR(const VP8Encoder* const enc) {
+ WebPAuxStats* stats = enc->pic_->stats;
+ const uint64_t size = enc->sse_count_;
+ const uint64_t* const sse = enc->sse_;
+ stats->PSNR[0] = (float)GetPSNR(sse[0], size);
+ stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
+ stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
+ stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
+ stats->PSNR[4] = (float)GetPSNR(sse[3], size);
+}
+#endif // !defined(WEBP_DISABLE_STATS)
+
+static void StoreStats(VP8Encoder* const enc) {
+#if !defined(WEBP_DISABLE_STATS)
+ WebPAuxStats* const stats = enc->pic_->stats;
+ if (stats != NULL) {
+ int i, s;
+ for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
+ stats->segment_level[i] = enc->dqm_[i].fstrength_;
+ stats->segment_quant[i] = enc->dqm_[i].quant_;
+ for (s = 0; s <= 2; ++s) {
+ stats->residual_bytes[s][i] = enc->residual_bytes_[s][i];
+ }
+ }
+ FinalizePSNR(enc);
+ stats->coded_size = enc->coded_size_;
+ for (i = 0; i < 3; ++i) {
+ stats->block_count[i] = enc->block_count_[i];
+ }
+ }
+#else // defined(WEBP_DISABLE_STATS)
+ WebPReportProgress(enc->pic_, 100, &enc->percent_); // done!
+#endif // !defined(WEBP_DISABLE_STATS)
+}
+
+int WebPEncodingSetError(const WebPPicture* const pic,
+ WebPEncodingError error) {
+ assert((int)error < VP8_ENC_ERROR_LAST);
+ assert((int)error >= VP8_ENC_OK);
+ ((WebPPicture*)pic)->error_code = error;
+ return 0;
+}
+
+int WebPReportProgress(const WebPPicture* const pic,
+ int percent, int* const percent_store) {
+ if (percent_store != NULL && percent != *percent_store) {
+ *percent_store = percent;
+ if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
+ // user abort requested
+ WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
+ return 0;
+ }
+ }
+ return 1; // ok
+}
+//------------------------------------------------------------------------------
+
+int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
+ int ok = 0;
+ if (pic == NULL) return 0;
+
+ WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far
+ if (config == NULL) { // bad params
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
+ }
+ if (!WebPValidateConfig(config)) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+ }
+ if (pic->width <= 0 || pic->height <= 0) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
+ }
+ if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) {
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
+ }
+
+ if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
+
+ if (!config->lossless) {
+ VP8Encoder* enc = NULL;
+
+ if (pic->use_argb || pic->y == NULL || pic->u == NULL || pic->v == NULL) {
+ // Make sure we have YUVA samples.
+ if (config->use_sharp_yuv || (config->preprocessing & 4)) {
+ if (!WebPPictureSharpARGBToYUVA(pic)) {
+ return 0;
+ }
+ } else {
+ float dithering = 0.f;
+ if (config->preprocessing & 2) {
+ const float x = config->quality / 100.f;
+ const float x2 = x * x;
+ // slowly decreasing from max dithering at low quality (q->0)
+ // to 0.5 dithering amplitude at high quality (q->100)
+ dithering = 1.0f + (0.5f - 1.0f) * x2 * x2;
+ }
+ if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) {
+ return 0;
+ }
+ }
+ }
+
+ if (!config->exact) {
+ WebPCleanupTransparentArea(pic);
+ }
+
+ enc = InitVP8Encoder(config, pic);
+ if (enc == NULL) return 0; // pic->error is already set.
+ // Note: each of the tasks below account for 20% in the progress report.
+ ok = VP8EncAnalyze(enc);
+
+ // Analysis is done, proceed to actual coding.
+ ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel
+ if (!enc->use_tokens_) {
+ ok = ok && VP8EncLoop(enc);
+ } else {
+ ok = ok && VP8EncTokenLoop(enc);
+ }
+ ok = ok && VP8EncFinishAlpha(enc);
+
+ ok = ok && VP8EncWrite(enc);
+ StoreStats(enc);
+ if (!ok) {
+ VP8EncFreeBitWriters(enc);
+ }
+ ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok
+ } else {
+ // Make sure we have ARGB samples.
+ if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {
+ return 0;
+ }
+
+ if (!config->exact) {
+ WebPCleanupTransparentAreaLossless(pic);
+ }
+
+ ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.
+ }
+
+ return ok;
+}
diff --git a/src/third_party/libwebp/src/libwebp.pc.in b/src/third_party/libwebp/src/libwebp.pc.in
new file mode 100644
index 0000000..733bb6d
--- /dev/null
+++ b/src/third_party/libwebp/src/libwebp.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebp
+Description: Library for the WebP graphics format
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lwebp
+Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/src/third_party/libwebp/src/libwebp.rc b/src/third_party/libwebp/src/libwebp.rc
new file mode 100644
index 0000000..d554124
--- /dev/null
+++ b/src/third_party/libwebp/src/libwebp.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Google, Inc."
+ VALUE "FileDescription", "libwebp DLL"
+ VALUE "FileVersion", "1.0.0"
+ VALUE "InternalName", "libwebp.dll"
+ VALUE "LegalCopyright", "Copyright (C) 2018"
+ VALUE "OriginalFilename", "libwebp.dll"
+ VALUE "ProductName", "WebP Image Codec"
+ VALUE "ProductVersion", "1.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
diff --git a/src/third_party/libwebp/src/libwebpdecoder.pc.in b/src/third_party/libwebp/src/libwebpdecoder.pc.in
new file mode 100644
index 0000000..3ef647a
--- /dev/null
+++ b/src/third_party/libwebp/src/libwebpdecoder.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebpdecoder
+Description: Library for the WebP graphics format (decode only)
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lwebpdecoder
+Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/src/third_party/libwebp/src/libwebpdecoder.rc b/src/third_party/libwebp/src/libwebpdecoder.rc
new file mode 100644
index 0000000..8a2def4
--- /dev/null
+++ b/src/third_party/libwebp/src/libwebpdecoder.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Google, Inc."
+ VALUE "FileDescription", "libwebpdecoder DLL"
+ VALUE "FileVersion", "1.0.0"
+ VALUE "InternalName", "libwebpdecoder.dll"
+ VALUE "LegalCopyright", "Copyright (C) 2018"
+ VALUE "OriginalFilename", "libwebpdecoder.dll"
+ VALUE "ProductName", "WebP Image Decoder"
+ VALUE "ProductVersion", "1.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
diff --git a/src/third_party/libwebp/src/mux/Makefile.am b/src/third_party/libwebp/src/mux/Makefile.am
new file mode 100644
index 0000000..447bcce
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/Makefile.am
@@ -0,0 +1,22 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+lib_LTLIBRARIES = libwebpmux.la
+
+libwebpmux_la_SOURCES =
+libwebpmux_la_SOURCES += anim_encode.c
+libwebpmux_la_SOURCES += animi.h
+libwebpmux_la_SOURCES += muxedit.c
+libwebpmux_la_SOURCES += muxi.h
+libwebpmux_la_SOURCES += muxinternal.c
+libwebpmux_la_SOURCES += muxread.c
+
+libwebpmuxinclude_HEADERS =
+libwebpmuxinclude_HEADERS += ../webp/mux.h
+libwebpmuxinclude_HEADERS += ../webp/mux_types.h
+libwebpmuxinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpmux_la_LIBADD = ../libwebp.la
+libwebpmux_la_LDFLAGS = -no-undefined -version-info 3:2:0 -lm
+libwebpmuxincludedir = $(includedir)/webp
+pkgconfig_DATA = libwebpmux.pc
diff --git a/src/third_party/libwebp/src/mux/anim_encode.c b/src/third_party/libwebp/src/mux/anim_encode.c
new file mode 100644
index 0000000..7be9906
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/anim_encode.c
@@ -0,0 +1,1581 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// AnimEncoder implementation.
+//
+
+#include <assert.h>
+#include <limits.h>
+#include <math.h> // for pow()
+#include <stdio.h>
+#include <stdlib.h> // for abs()
+
+#include "src/mux/animi.h"
+#include "src/utils/utils.h"
+#include "src/webp/decode.h"
+#include "src/webp/encode.h"
+#include "src/webp/format_constants.h"
+#include "src/webp/mux.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+#define ERROR_STR_MAX_LENGTH 100
+
+//------------------------------------------------------------------------------
+// Internal structs.
+
+// Stores frame rectangle dimensions.
+typedef struct {
+ int x_offset_, y_offset_, width_, height_;
+} FrameRectangle;
+
+// Used to store two candidates of encoded data for an animation frame. One of
+// the two will be chosen later.
+typedef struct {
+ WebPMuxFrameInfo sub_frame_; // Encoded frame rectangle.
+ WebPMuxFrameInfo key_frame_; // Encoded frame if it is a key-frame.
+ int is_key_frame_; // True if 'key_frame' has been chosen.
+} EncodedFrame;
+
+struct WebPAnimEncoder {
+ const int canvas_width_; // Canvas width.
+ const int canvas_height_; // Canvas height.
+ const WebPAnimEncoderOptions options_; // Global encoding options.
+
+ FrameRectangle prev_rect_; // Previous WebP frame rectangle.
+ WebPConfig last_config_; // Cached in case a re-encode is needed.
+ WebPConfig last_config_reversed_; // If 'last_config_' uses lossless, then
+ // this config uses lossy and vice versa;
+ // only valid if 'options_.allow_mixed'
+ // is true.
+
+ WebPPicture* curr_canvas_; // Only pointer; we don't own memory.
+
+ // Canvas buffers.
+ WebPPicture curr_canvas_copy_; // Possibly modified current canvas.
+ int curr_canvas_copy_modified_; // True if pixels in 'curr_canvas_copy_'
+ // differ from those in 'curr_canvas_'.
+
+ WebPPicture prev_canvas_; // Previous canvas.
+ WebPPicture prev_canvas_disposed_; // Previous canvas disposed to background.
+
+ // Encoded data.
+ EncodedFrame* encoded_frames_; // Array of encoded frames.
+ size_t size_; // Number of allocated frames.
+ size_t start_; // Frame start index.
+ size_t count_; // Number of valid frames.
+ size_t flush_count_; // If >0, 'flush_count' frames starting from
+ // 'start' are ready to be added to mux.
+
+ // key-frame related.
+ int64_t best_delta_; // min(canvas size - frame size) over the frames.
+ // Can be negative in certain cases due to
+ // transparent pixels in a frame.
+ int keyframe_; // Index of selected key-frame relative to 'start_'.
+ int count_since_key_frame_; // Frames seen since the last key-frame.
+
+ int first_timestamp_; // Timestamp of the first frame.
+ int prev_timestamp_; // Timestamp of the last added frame.
+ int prev_candidate_undecided_; // True if it's not yet decided if previous
+ // frame would be a sub-frame or a key-frame.
+
+ // Misc.
+ int is_first_frame_; // True if first frame is yet to be added/being added.
+ int got_null_frame_; // True if WebPAnimEncoderAdd() has already been called
+ // with a NULL frame.
+
+ size_t in_frame_count_; // Number of input frames processed so far.
+ size_t out_frame_count_; // Number of frames added to mux so far. This may be
+ // different from 'in_frame_count_' due to merging.
+
+ WebPMux* mux_; // Muxer to assemble the WebP bitstream.
+ char error_str_[ERROR_STR_MAX_LENGTH]; // Error string. Empty if no error.
+};
+
+// -----------------------------------------------------------------------------
+// Life of WebPAnimEncoder object.
+
+#define DELTA_INFINITY (1ULL << 32)
+#define KEYFRAME_NONE (-1)
+
+// Reset the counters in the WebPAnimEncoder.
+static void ResetCounters(WebPAnimEncoder* const enc) {
+ enc->start_ = 0;
+ enc->count_ = 0;
+ enc->flush_count_ = 0;
+ enc->best_delta_ = DELTA_INFINITY;
+ enc->keyframe_ = KEYFRAME_NONE;
+}
+
+static void DisableKeyframes(WebPAnimEncoderOptions* const enc_options) {
+ enc_options->kmax = INT_MAX;
+ enc_options->kmin = enc_options->kmax - 1;
+}
+
+#define MAX_CACHED_FRAMES 30
+
+static void SanitizeEncoderOptions(WebPAnimEncoderOptions* const enc_options) {
+ int print_warning = enc_options->verbose;
+
+ if (enc_options->minimize_size) {
+ DisableKeyframes(enc_options);
+ }
+
+ if (enc_options->kmax == 1) { // All frames will be key-frames.
+ enc_options->kmin = 0;
+ enc_options->kmax = 0;
+ return;
+ } else if (enc_options->kmax <= 0) {
+ DisableKeyframes(enc_options);
+ print_warning = 0;
+ }
+
+ if (enc_options->kmin >= enc_options->kmax) {
+ enc_options->kmin = enc_options->kmax - 1;
+ if (print_warning) {
+ fprintf(stderr, "WARNING: Setting kmin = %d, so that kmin < kmax.\n",
+ enc_options->kmin);
+ }
+ } else {
+ const int kmin_limit = enc_options->kmax / 2 + 1;
+ if (enc_options->kmin < kmin_limit && kmin_limit < enc_options->kmax) {
+ // This ensures that enc.keyframe + kmin >= kmax is always true. So, we
+ // can flush all the frames in the 'count_since_key_frame == kmax' case.
+ enc_options->kmin = kmin_limit;
+ if (print_warning) {
+ fprintf(stderr,
+ "WARNING: Setting kmin = %d, so that kmin >= kmax / 2 + 1.\n",
+ enc_options->kmin);
+ }
+ }
+ }
+ // Limit the max number of frames that are allocated.
+ if (enc_options->kmax - enc_options->kmin > MAX_CACHED_FRAMES) {
+ enc_options->kmin = enc_options->kmax - MAX_CACHED_FRAMES;
+ if (print_warning) {
+ fprintf(stderr,
+ "WARNING: Setting kmin = %d, so that kmax - kmin <= %d.\n",
+ enc_options->kmin, MAX_CACHED_FRAMES);
+ }
+ }
+ assert(enc_options->kmin < enc_options->kmax);
+}
+
+#undef MAX_CACHED_FRAMES
+
+static void DefaultEncoderOptions(WebPAnimEncoderOptions* const enc_options) {
+ enc_options->anim_params.loop_count = 0;
+ enc_options->anim_params.bgcolor = 0xffffffff; // White.
+ enc_options->minimize_size = 0;
+ DisableKeyframes(enc_options);
+ enc_options->allow_mixed = 0;
+ enc_options->verbose = 0;
+}
+
+int WebPAnimEncoderOptionsInitInternal(WebPAnimEncoderOptions* enc_options,
+ int abi_version) {
+ if (enc_options == NULL ||
+ WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_MUX_ABI_VERSION)) {
+ return 0;
+ }
+ DefaultEncoderOptions(enc_options);
+ return 1;
+}
+
+// This starting value is more fit to WebPCleanupTransparentAreaLossless().
+#define TRANSPARENT_COLOR 0x00000000
+
+static void ClearRectangle(WebPPicture* const picture,
+ int left, int top, int width, int height) {
+ int j;
+ for (j = top; j < top + height; ++j) {
+ uint32_t* const dst = picture->argb + j * picture->argb_stride;
+ int i;
+ for (i = left; i < left + width; ++i) {
+ dst[i] = TRANSPARENT_COLOR;
+ }
+ }
+}
+
+static void WebPUtilClearPic(WebPPicture* const picture,
+ const FrameRectangle* const rect) {
+ if (rect != NULL) {
+ ClearRectangle(picture, rect->x_offset_, rect->y_offset_,
+ rect->width_, rect->height_);
+ } else {
+ ClearRectangle(picture, 0, 0, picture->width, picture->height);
+ }
+}
+
+static void MarkNoError(WebPAnimEncoder* const enc) {
+ enc->error_str_[0] = '\0'; // Empty string.
+}
+
+static void MarkError(WebPAnimEncoder* const enc, const char* str) {
+ if (snprintf(enc->error_str_, ERROR_STR_MAX_LENGTH, "%s.", str) < 0) {
+ assert(0); // FIX ME!
+ }
+}
+
+static void MarkError2(WebPAnimEncoder* const enc,
+ const char* str, int error_code) {
+ if (snprintf(enc->error_str_, ERROR_STR_MAX_LENGTH, "%s: %d.", str,
+ error_code) < 0) {
+ assert(0); // FIX ME!
+ }
+}
+
+WebPAnimEncoder* WebPAnimEncoderNewInternal(
+ int width, int height, const WebPAnimEncoderOptions* enc_options,
+ int abi_version) {
+ WebPAnimEncoder* enc;
+
+ if (WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_MUX_ABI_VERSION)) {
+ return NULL;
+ }
+ if (width <= 0 || height <= 0 ||
+ (width * (uint64_t)height) >= MAX_IMAGE_AREA) {
+ return NULL;
+ }
+
+ enc = (WebPAnimEncoder*)WebPSafeCalloc(1, sizeof(*enc));
+ if (enc == NULL) return NULL;
+ // sanity inits, so we can call WebPAnimEncoderDelete():
+ enc->encoded_frames_ = NULL;
+ enc->mux_ = NULL;
+ MarkNoError(enc);
+
+ // Dimensions and options.
+ *(int*)&enc->canvas_width_ = width;
+ *(int*)&enc->canvas_height_ = height;
+ if (enc_options != NULL) {
+ *(WebPAnimEncoderOptions*)&enc->options_ = *enc_options;
+ SanitizeEncoderOptions((WebPAnimEncoderOptions*)&enc->options_);
+ } else {
+ DefaultEncoderOptions((WebPAnimEncoderOptions*)&enc->options_);
+ }
+
+ // Canvas buffers.
+ if (!WebPPictureInit(&enc->curr_canvas_copy_) ||
+ !WebPPictureInit(&enc->prev_canvas_) ||
+ !WebPPictureInit(&enc->prev_canvas_disposed_)) {
+ goto Err;
+ }
+ enc->curr_canvas_copy_.width = width;
+ enc->curr_canvas_copy_.height = height;
+ enc->curr_canvas_copy_.use_argb = 1;
+ if (!WebPPictureAlloc(&enc->curr_canvas_copy_) ||
+ !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_) ||
+ !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_disposed_)) {
+ goto Err;
+ }
+ WebPUtilClearPic(&enc->prev_canvas_, NULL);
+ enc->curr_canvas_copy_modified_ = 1;
+
+ // Encoded frames.
+ ResetCounters(enc);
+ // Note: one extra storage is for the previous frame.
+ enc->size_ = enc->options_.kmax - enc->options_.kmin + 1;
+ // We need space for at least 2 frames. But when kmin, kmax are both zero,
+ // enc->size_ will be 1. So we handle that special case below.
+ if (enc->size_ < 2) enc->size_ = 2;
+ enc->encoded_frames_ =
+ (EncodedFrame*)WebPSafeCalloc(enc->size_, sizeof(*enc->encoded_frames_));
+ if (enc->encoded_frames_ == NULL) goto Err;
+
+ enc->mux_ = WebPMuxNew();
+ if (enc->mux_ == NULL) goto Err;
+
+ enc->count_since_key_frame_ = 0;
+ enc->first_timestamp_ = 0;
+ enc->prev_timestamp_ = 0;
+ enc->prev_candidate_undecided_ = 0;
+ enc->is_first_frame_ = 1;
+ enc->got_null_frame_ = 0;
+
+ return enc; // All OK.
+
+ Err:
+ WebPAnimEncoderDelete(enc);
+ return NULL;
+}
+
+// Release the data contained by 'encoded_frame'.
+static void FrameRelease(EncodedFrame* const encoded_frame) {
+ if (encoded_frame != NULL) {
+ WebPDataClear(&encoded_frame->sub_frame_.bitstream);
+ WebPDataClear(&encoded_frame->key_frame_.bitstream);
+ memset(encoded_frame, 0, sizeof(*encoded_frame));
+ }
+}
+
+void WebPAnimEncoderDelete(WebPAnimEncoder* enc) {
+ if (enc != NULL) {
+ WebPPictureFree(&enc->curr_canvas_copy_);
+ WebPPictureFree(&enc->prev_canvas_);
+ WebPPictureFree(&enc->prev_canvas_disposed_);
+ if (enc->encoded_frames_ != NULL) {
+ size_t i;
+ for (i = 0; i < enc->size_; ++i) {
+ FrameRelease(&enc->encoded_frames_[i]);
+ }
+ WebPSafeFree(enc->encoded_frames_);
+ }
+ WebPMuxDelete(enc->mux_);
+ WebPSafeFree(enc);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Frame addition.
+
+// Returns cached frame at the given 'position'.
+static EncodedFrame* GetFrame(const WebPAnimEncoder* const enc,
+ size_t position) {
+ assert(enc->start_ + position < enc->size_);
+ return &enc->encoded_frames_[enc->start_ + position];
+}
+
+typedef int (*ComparePixelsFunc)(const uint32_t*, int, const uint32_t*, int,
+ int, int);
+
+// Returns true if 'length' number of pixels in 'src' and 'dst' are equal,
+// assuming the given step sizes between pixels.
+// 'max_allowed_diff' is unused and only there to allow function pointer use.
+static WEBP_INLINE int ComparePixelsLossless(const uint32_t* src, int src_step,
+ const uint32_t* dst, int dst_step,
+ int length, int max_allowed_diff) {
+ (void)max_allowed_diff;
+ assert(length > 0);
+ while (length-- > 0) {
+ if (*src != *dst) {
+ return 0;
+ }
+ src += src_step;
+ dst += dst_step;
+ }
+ return 1;
+}
+
+// Helper to check if each channel in 'src' and 'dst' is at most off by
+// 'max_allowed_diff'.
+static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
+ int max_allowed_diff) {
+ const int src_a = (src >> 24) & 0xff;
+ const int src_r = (src >> 16) & 0xff;
+ const int src_g = (src >> 8) & 0xff;
+ const int src_b = (src >> 0) & 0xff;
+ const int dst_a = (dst >> 24) & 0xff;
+ const int dst_r = (dst >> 16) & 0xff;
+ const int dst_g = (dst >> 8) & 0xff;
+ const int dst_b = (dst >> 0) & 0xff;
+
+ return (src_a == dst_a) &&
+ (abs(src_r - dst_r) * dst_a <= (max_allowed_diff * 255)) &&
+ (abs(src_g - dst_g) * dst_a <= (max_allowed_diff * 255)) &&
+ (abs(src_b - dst_b) * dst_a <= (max_allowed_diff * 255));
+}
+
+// Returns true if 'length' number of pixels in 'src' and 'dst' are within an
+// error bound, assuming the given step sizes between pixels.
+static WEBP_INLINE int ComparePixelsLossy(const uint32_t* src, int src_step,
+ const uint32_t* dst, int dst_step,
+ int length, int max_allowed_diff) {
+ assert(length > 0);
+ while (length-- > 0) {
+ if (!PixelsAreSimilar(*src, *dst, max_allowed_diff)) {
+ return 0;
+ }
+ src += src_step;
+ dst += dst_step;
+ }
+ return 1;
+}
+
+static int IsEmptyRect(const FrameRectangle* const rect) {
+ return (rect->width_ == 0) || (rect->height_ == 0);
+}
+
+static int QualityToMaxDiff(float quality) {
+ const double val = pow(quality / 100., 0.5);
+ const double max_diff = 31 * (1 - val) + 1 * val;
+ return (int)(max_diff + 0.5);
+}
+
+// Assumes that an initial valid guess of change rectangle 'rect' is passed.
+static void MinimizeChangeRectangle(const WebPPicture* const src,
+ const WebPPicture* const dst,
+ FrameRectangle* const rect,
+ int is_lossless, float quality) {
+ int i, j;
+ const ComparePixelsFunc compare_pixels =
+ is_lossless ? ComparePixelsLossless : ComparePixelsLossy;
+ const int max_allowed_diff_lossy = QualityToMaxDiff(quality);
+ const int max_allowed_diff = is_lossless ? 0 : max_allowed_diff_lossy;
+
+ // Sanity checks.
+ assert(src->width == dst->width && src->height == dst->height);
+ assert(rect->x_offset_ + rect->width_ <= dst->width);
+ assert(rect->y_offset_ + rect->height_ <= dst->height);
+
+ // Left boundary.
+ for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) {
+ const uint32_t* const src_argb =
+ &src->argb[rect->y_offset_ * src->argb_stride + i];
+ const uint32_t* const dst_argb =
+ &dst->argb[rect->y_offset_ * dst->argb_stride + i];
+ if (compare_pixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride,
+ rect->height_, max_allowed_diff)) {
+ --rect->width_; // Redundant column.
+ ++rect->x_offset_;
+ } else {
+ break;
+ }
+ }
+ if (rect->width_ == 0) goto NoChange;
+
+ // Right boundary.
+ for (i = rect->x_offset_ + rect->width_ - 1; i >= rect->x_offset_; --i) {
+ const uint32_t* const src_argb =
+ &src->argb[rect->y_offset_ * src->argb_stride + i];
+ const uint32_t* const dst_argb =
+ &dst->argb[rect->y_offset_ * dst->argb_stride + i];
+ if (compare_pixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride,
+ rect->height_, max_allowed_diff)) {
+ --rect->width_; // Redundant column.
+ } else {
+ break;
+ }
+ }
+ if (rect->width_ == 0) goto NoChange;
+
+ // Top boundary.
+ for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) {
+ const uint32_t* const src_argb =
+ &src->argb[j * src->argb_stride + rect->x_offset_];
+ const uint32_t* const dst_argb =
+ &dst->argb[j * dst->argb_stride + rect->x_offset_];
+ if (compare_pixels(src_argb, 1, dst_argb, 1, rect->width_,
+ max_allowed_diff)) {
+ --rect->height_; // Redundant row.
+ ++rect->y_offset_;
+ } else {
+ break;
+ }
+ }
+ if (rect->height_ == 0) goto NoChange;
+
+ // Bottom boundary.
+ for (j = rect->y_offset_ + rect->height_ - 1; j >= rect->y_offset_; --j) {
+ const uint32_t* const src_argb =
+ &src->argb[j * src->argb_stride + rect->x_offset_];
+ const uint32_t* const dst_argb =
+ &dst->argb[j * dst->argb_stride + rect->x_offset_];
+ if (compare_pixels(src_argb, 1, dst_argb, 1, rect->width_,
+ max_allowed_diff)) {
+ --rect->height_; // Redundant row.
+ } else {
+ break;
+ }
+ }
+ if (rect->height_ == 0) goto NoChange;
+
+ if (IsEmptyRect(rect)) {
+ NoChange:
+ rect->x_offset_ = 0;
+ rect->y_offset_ = 0;
+ rect->width_ = 0;
+ rect->height_ = 0;
+ }
+}
+
+// Snap rectangle to even offsets (and adjust dimensions if needed).
+static WEBP_INLINE void SnapToEvenOffsets(FrameRectangle* const rect) {
+ rect->width_ += (rect->x_offset_ & 1);
+ rect->height_ += (rect->y_offset_ & 1);
+ rect->x_offset_ &= ~1;
+ rect->y_offset_ &= ~1;
+}
+
+typedef struct {
+ int should_try_; // Should try this set of parameters.
+ int empty_rect_allowed_; // Frame with empty rectangle can be skipped.
+ FrameRectangle rect_ll_; // Frame rectangle for lossless compression.
+ WebPPicture sub_frame_ll_; // Sub-frame pic for lossless compression.
+ FrameRectangle rect_lossy_; // Frame rectangle for lossy compression.
+ // Could be smaller than rect_ll_ as pixels
+ // with small diffs can be ignored.
+ WebPPicture sub_frame_lossy_; // Sub-frame pic for lossless compression.
+} SubFrameParams;
+
+static int SubFrameParamsInit(SubFrameParams* const params,
+ int should_try, int empty_rect_allowed) {
+ params->should_try_ = should_try;
+ params->empty_rect_allowed_ = empty_rect_allowed;
+ if (!WebPPictureInit(¶ms->sub_frame_ll_) ||
+ !WebPPictureInit(¶ms->sub_frame_lossy_)) {
+ return 0;
+ }
+ return 1;
+}
+
+static void SubFrameParamsFree(SubFrameParams* const params) {
+ WebPPictureFree(¶ms->sub_frame_ll_);
+ WebPPictureFree(¶ms->sub_frame_lossy_);
+}
+
+// Given previous and current canvas, picks the optimal rectangle for the
+// current frame based on 'is_lossless' and other parameters. Assumes that the
+// initial guess 'rect' is valid.
+static int GetSubRect(const WebPPicture* const prev_canvas,
+ const WebPPicture* const curr_canvas, int is_key_frame,
+ int is_first_frame, int empty_rect_allowed,
+ int is_lossless, float quality,
+ FrameRectangle* const rect,
+ WebPPicture* const sub_frame) {
+ if (!is_key_frame || is_first_frame) { // Optimize frame rectangle.
+ // Note: This behaves as expected for first frame, as 'prev_canvas' is
+ // initialized to a fully transparent canvas in the beginning.
+ MinimizeChangeRectangle(prev_canvas, curr_canvas, rect,
+ is_lossless, quality);
+ }
+
+ if (IsEmptyRect(rect)) {
+ if (empty_rect_allowed) { // No need to get 'sub_frame'.
+ return 1;
+ } else { // Force a 1x1 rectangle.
+ rect->width_ = 1;
+ rect->height_ = 1;
+ assert(rect->x_offset_ == 0);
+ assert(rect->y_offset_ == 0);
+ }
+ }
+
+ SnapToEvenOffsets(rect);
+ return WebPPictureView(curr_canvas, rect->x_offset_, rect->y_offset_,
+ rect->width_, rect->height_, sub_frame);
+}
+
+// Picks optimal frame rectangle for both lossless and lossy compression. The
+// initial guess for frame rectangles will be the full canvas.
+static int GetSubRects(const WebPPicture* const prev_canvas,
+ const WebPPicture* const curr_canvas, int is_key_frame,
+ int is_first_frame, float quality,
+ SubFrameParams* const params) {
+ // Lossless frame rectangle.
+ params->rect_ll_.x_offset_ = 0;
+ params->rect_ll_.y_offset_ = 0;
+ params->rect_ll_.width_ = curr_canvas->width;
+ params->rect_ll_.height_ = curr_canvas->height;
+ if (!GetSubRect(prev_canvas, curr_canvas, is_key_frame, is_first_frame,
+ params->empty_rect_allowed_, 1, quality,
+ ¶ms->rect_ll_, ¶ms->sub_frame_ll_)) {
+ return 0;
+ }
+ // Lossy frame rectangle.
+ params->rect_lossy_ = params->rect_ll_; // seed with lossless rect.
+ return GetSubRect(prev_canvas, curr_canvas, is_key_frame, is_first_frame,
+ params->empty_rect_allowed_, 0, quality,
+ ¶ms->rect_lossy_, ¶ms->sub_frame_lossy_);
+}
+
+static WEBP_INLINE int clip(int v, int min_v, int max_v) {
+ return (v < min_v) ? min_v : (v > max_v) ? max_v : v;
+}
+
+int WebPAnimEncoderRefineRect(
+ const WebPPicture* const prev_canvas, const WebPPicture* const curr_canvas,
+ int is_lossless, float quality, int* const x_offset, int* const y_offset,
+ int* const width, int* const height) {
+ FrameRectangle rect;
+ const int right = clip(*x_offset + *width, 0, curr_canvas->width);
+ const int left = clip(*x_offset, 0, curr_canvas->width - 1);
+ const int bottom = clip(*y_offset + *height, 0, curr_canvas->height);
+ const int top = clip(*y_offset, 0, curr_canvas->height - 1);
+ if (prev_canvas == NULL || curr_canvas == NULL ||
+ prev_canvas->width != curr_canvas->width ||
+ prev_canvas->height != curr_canvas->height ||
+ !prev_canvas->use_argb || !curr_canvas->use_argb) {
+ return 0;
+ }
+ rect.x_offset_ = left;
+ rect.y_offset_ = top;
+ rect.width_ = clip(right - left, 0, curr_canvas->width - rect.x_offset_);
+ rect.height_ = clip(bottom - top, 0, curr_canvas->height - rect.y_offset_);
+ MinimizeChangeRectangle(prev_canvas, curr_canvas, &rect, is_lossless,
+ quality);
+ SnapToEvenOffsets(&rect);
+ *x_offset = rect.x_offset_;
+ *y_offset = rect.y_offset_;
+ *width = rect.width_;
+ *height = rect.height_;
+ return 1;
+}
+
+static void DisposeFrameRectangle(int dispose_method,
+ const FrameRectangle* const rect,
+ WebPPicture* const curr_canvas) {
+ assert(rect != NULL);
+ if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
+ WebPUtilClearPic(curr_canvas, rect);
+ }
+}
+
+static uint32_t RectArea(const FrameRectangle* const rect) {
+ return (uint32_t)rect->width_ * rect->height_;
+}
+
+static int IsLosslessBlendingPossible(const WebPPicture* const src,
+ const WebPPicture* const dst,
+ const FrameRectangle* const rect) {
+ int i, j;
+ assert(src->width == dst->width && src->height == dst->height);
+ assert(rect->x_offset_ + rect->width_ <= dst->width);
+ assert(rect->y_offset_ + rect->height_ <= dst->height);
+ for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) {
+ for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) {
+ const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
+ const uint32_t dst_pixel = dst->argb[j * dst->argb_stride + i];
+ const uint32_t dst_alpha = dst_pixel >> 24;
+ if (dst_alpha != 0xff && src_pixel != dst_pixel) {
+ // In this case, if we use blending, we can't attain the desired
+ // 'dst_pixel' value for this pixel. So, blending is not possible.
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static int IsLossyBlendingPossible(const WebPPicture* const src,
+ const WebPPicture* const dst,
+ const FrameRectangle* const rect,
+ float quality) {
+ const int max_allowed_diff_lossy = QualityToMaxDiff(quality);
+ int i, j;
+ assert(src->width == dst->width && src->height == dst->height);
+ assert(rect->x_offset_ + rect->width_ <= dst->width);
+ assert(rect->y_offset_ + rect->height_ <= dst->height);
+ for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) {
+ for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) {
+ const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
+ const uint32_t dst_pixel = dst->argb[j * dst->argb_stride + i];
+ const uint32_t dst_alpha = dst_pixel >> 24;
+ if (dst_alpha != 0xff &&
+ !PixelsAreSimilar(src_pixel, dst_pixel, max_allowed_diff_lossy)) {
+ // In this case, if we use blending, we can't attain the desired
+ // 'dst_pixel' value for this pixel. So, blending is not possible.
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+// For pixels in 'rect', replace those pixels in 'dst' that are same as 'src' by
+// transparent pixels.
+// Returns true if at least one pixel gets modified.
+static int IncreaseTransparency(const WebPPicture* const src,
+ const FrameRectangle* const rect,
+ WebPPicture* const dst) {
+ int i, j;
+ int modified = 0;
+ assert(src != NULL && dst != NULL && rect != NULL);
+ assert(src->width == dst->width && src->height == dst->height);
+ for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) {
+ const uint32_t* const psrc = src->argb + j * src->argb_stride;
+ uint32_t* const pdst = dst->argb + j * dst->argb_stride;
+ for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) {
+ if (psrc[i] == pdst[i] && pdst[i] != TRANSPARENT_COLOR) {
+ pdst[i] = TRANSPARENT_COLOR;
+ modified = 1;
+ }
+ }
+ }
+ return modified;
+}
+
+#undef TRANSPARENT_COLOR
+
+// Replace similar blocks of pixels by a 'see-through' transparent block
+// with uniform average color.
+// Assumes lossy compression is being used.
+// Returns true if at least one pixel gets modified.
+static int FlattenSimilarBlocks(const WebPPicture* const src,
+ const FrameRectangle* const rect,
+ WebPPicture* const dst, float quality) {
+ const int max_allowed_diff_lossy = QualityToMaxDiff(quality);
+ int i, j;
+ int modified = 0;
+ const int block_size = 8;
+ const int y_start = (rect->y_offset_ + block_size) & ~(block_size - 1);
+ const int y_end = (rect->y_offset_ + rect->height_) & ~(block_size - 1);
+ const int x_start = (rect->x_offset_ + block_size) & ~(block_size - 1);
+ const int x_end = (rect->x_offset_ + rect->width_) & ~(block_size - 1);
+ assert(src != NULL && dst != NULL && rect != NULL);
+ assert(src->width == dst->width && src->height == dst->height);
+ assert((block_size & (block_size - 1)) == 0); // must be a power of 2
+ // Iterate over each block and count similar pixels.
+ for (j = y_start; j < y_end; j += block_size) {
+ for (i = x_start; i < x_end; i += block_size) {
+ int cnt = 0;
+ int avg_r = 0, avg_g = 0, avg_b = 0;
+ int x, y;
+ const uint32_t* const psrc = src->argb + j * src->argb_stride + i;
+ uint32_t* const pdst = dst->argb + j * dst->argb_stride + i;
+ for (y = 0; y < block_size; ++y) {
+ for (x = 0; x < block_size; ++x) {
+ const uint32_t src_pixel = psrc[x + y * src->argb_stride];
+ const int alpha = src_pixel >> 24;
+ if (alpha == 0xff &&
+ PixelsAreSimilar(src_pixel, pdst[x + y * dst->argb_stride],
+ max_allowed_diff_lossy)) {
+ ++cnt;
+ avg_r += (src_pixel >> 16) & 0xff;
+ avg_g += (src_pixel >> 8) & 0xff;
+ avg_b += (src_pixel >> 0) & 0xff;
+ }
+ }
+ }
+ // If we have a fully similar block, we replace it with an
+ // average transparent block. This compresses better in lossy mode.
+ if (cnt == block_size * block_size) {
+ const uint32_t color = (0x00 << 24) |
+ ((avg_r / cnt) << 16) |
+ ((avg_g / cnt) << 8) |
+ ((avg_b / cnt) << 0);
+ for (y = 0; y < block_size; ++y) {
+ for (x = 0; x < block_size; ++x) {
+ pdst[x + y * dst->argb_stride] = color;
+ }
+ }
+ modified = 1;
+ }
+ }
+ }
+ return modified;
+}
+
+static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic,
+ WebPMemoryWriter* const memory) {
+ pic->use_argb = 1;
+ pic->writer = WebPMemoryWrite;
+ pic->custom_ptr = memory;
+ if (!WebPEncode(config, pic)) {
+ return 0;
+ }
+ return 1;
+}
+
+// Struct representing a candidate encoded frame including its metadata.
+typedef struct {
+ WebPMemoryWriter mem_;
+ WebPMuxFrameInfo info_;
+ FrameRectangle rect_;
+ int evaluate_; // True if this candidate should be evaluated.
+} Candidate;
+
+// Generates a candidate encoded frame given a picture and metadata.
+static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame,
+ const FrameRectangle* const rect,
+ const WebPConfig* const encoder_config,
+ int use_blending,
+ Candidate* const candidate) {
+ WebPConfig config = *encoder_config;
+ WebPEncodingError error_code = VP8_ENC_OK;
+ assert(candidate != NULL);
+ memset(candidate, 0, sizeof(*candidate));
+
+ // Set frame rect and info.
+ candidate->rect_ = *rect;
+ candidate->info_.id = WEBP_CHUNK_ANMF;
+ candidate->info_.x_offset = rect->x_offset_;
+ candidate->info_.y_offset = rect->y_offset_;
+ candidate->info_.dispose_method = WEBP_MUX_DISPOSE_NONE; // Set later.
+ candidate->info_.blend_method =
+ use_blending ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
+ candidate->info_.duration = 0; // Set in next call to WebPAnimEncoderAdd().
+
+ // Encode picture.
+ WebPMemoryWriterInit(&candidate->mem_);
+
+ if (!config.lossless && use_blending) {
+ // Disable filtering to avoid blockiness in reconstructed frames at the
+ // time of decoding.
+ config.autofilter = 0;
+ config.filter_strength = 0;
+ }
+ if (!EncodeFrame(&config, sub_frame, &candidate->mem_)) {
+ error_code = sub_frame->error_code;
+ goto Err;
+ }
+
+ candidate->evaluate_ = 1;
+ return error_code;
+
+ Err:
+ WebPMemoryWriterClear(&candidate->mem_);
+ return error_code;
+}
+
+static void CopyCurrentCanvas(WebPAnimEncoder* const enc) {
+ if (enc->curr_canvas_copy_modified_) {
+ WebPCopyPixels(enc->curr_canvas_, &enc->curr_canvas_copy_);
+ enc->curr_canvas_copy_.progress_hook = enc->curr_canvas_->progress_hook;
+ enc->curr_canvas_copy_.user_data = enc->curr_canvas_->user_data;
+ enc->curr_canvas_copy_modified_ = 0;
+ }
+}
+
+enum {
+ LL_DISP_NONE = 0,
+ LL_DISP_BG,
+ LOSSY_DISP_NONE,
+ LOSSY_DISP_BG,
+ CANDIDATE_COUNT
+};
+
+#define MIN_COLORS_LOSSY 31 // Don't try lossy below this threshold.
+#define MAX_COLORS_LOSSLESS 194 // Don't try lossless above this threshold.
+
+// Generates candidates for a given dispose method given pre-filled sub-frame
+// 'params'.
+static WebPEncodingError GenerateCandidates(
+ WebPAnimEncoder* const enc, Candidate candidates[CANDIDATE_COUNT],
+ WebPMuxAnimDispose dispose_method, int is_lossless, int is_key_frame,
+ SubFrameParams* const params,
+ const WebPConfig* const config_ll, const WebPConfig* const config_lossy) {
+ WebPEncodingError error_code = VP8_ENC_OK;
+ const int is_dispose_none = (dispose_method == WEBP_MUX_DISPOSE_NONE);
+ Candidate* const candidate_ll =
+ is_dispose_none ? &candidates[LL_DISP_NONE] : &candidates[LL_DISP_BG];
+ Candidate* const candidate_lossy = is_dispose_none
+ ? &candidates[LOSSY_DISP_NONE]
+ : &candidates[LOSSY_DISP_BG];
+ WebPPicture* const curr_canvas = &enc->curr_canvas_copy_;
+ const WebPPicture* const prev_canvas =
+ is_dispose_none ? &enc->prev_canvas_ : &enc->prev_canvas_disposed_;
+ int use_blending_ll, use_blending_lossy;
+ int evaluate_ll, evaluate_lossy;
+
+ CopyCurrentCanvas(enc);
+ use_blending_ll =
+ !is_key_frame &&
+ IsLosslessBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_ll_);
+ use_blending_lossy =
+ !is_key_frame &&
+ IsLossyBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_lossy_,
+ config_lossy->quality);
+
+ // Pick candidates to be tried.
+ if (!enc->options_.allow_mixed) {
+ evaluate_ll = is_lossless;
+ evaluate_lossy = !is_lossless;
+ } else if (enc->options_.minimize_size) {
+ evaluate_ll = 1;
+ evaluate_lossy = 1;
+ } else { // Use a heuristic for trying lossless and/or lossy compression.
+ const int num_colors = WebPGetColorPalette(¶ms->sub_frame_ll_, NULL);
+ evaluate_ll = (num_colors < MAX_COLORS_LOSSLESS);
+ evaluate_lossy = (num_colors >= MIN_COLORS_LOSSY);
+ }
+
+ // Generate candidates.
+ if (evaluate_ll) {
+ CopyCurrentCanvas(enc);
+ if (use_blending_ll) {
+ enc->curr_canvas_copy_modified_ =
+ IncreaseTransparency(prev_canvas, ¶ms->rect_ll_, curr_canvas);
+ }
+ error_code = EncodeCandidate(¶ms->sub_frame_ll_, ¶ms->rect_ll_,
+ config_ll, use_blending_ll, candidate_ll);
+ if (error_code != VP8_ENC_OK) return error_code;
+ }
+ if (evaluate_lossy) {
+ CopyCurrentCanvas(enc);
+ if (use_blending_lossy) {
+ enc->curr_canvas_copy_modified_ =
+ FlattenSimilarBlocks(prev_canvas, ¶ms->rect_lossy_, curr_canvas,
+ config_lossy->quality);
+ }
+ error_code =
+ EncodeCandidate(¶ms->sub_frame_lossy_, ¶ms->rect_lossy_,
+ config_lossy, use_blending_lossy, candidate_lossy);
+ if (error_code != VP8_ENC_OK) return error_code;
+ enc->curr_canvas_copy_modified_ = 1;
+ }
+ return error_code;
+}
+
+#undef MIN_COLORS_LOSSY
+#undef MAX_COLORS_LOSSLESS
+
+static void GetEncodedData(const WebPMemoryWriter* const memory,
+ WebPData* const encoded_data) {
+ encoded_data->bytes = memory->mem;
+ encoded_data->size = memory->size;
+}
+
+// Sets dispose method of the previous frame to be 'dispose_method'.
+static void SetPreviousDisposeMethod(WebPAnimEncoder* const enc,
+ WebPMuxAnimDispose dispose_method) {
+ const size_t position = enc->count_ - 2;
+ EncodedFrame* const prev_enc_frame = GetFrame(enc, position);
+ assert(enc->count_ >= 2); // As current and previous frames are in enc.
+
+ if (enc->prev_candidate_undecided_) {
+ assert(dispose_method == WEBP_MUX_DISPOSE_NONE);
+ prev_enc_frame->sub_frame_.dispose_method = dispose_method;
+ prev_enc_frame->key_frame_.dispose_method = dispose_method;
+ } else {
+ WebPMuxFrameInfo* const prev_info = prev_enc_frame->is_key_frame_
+ ? &prev_enc_frame->key_frame_
+ : &prev_enc_frame->sub_frame_;
+ prev_info->dispose_method = dispose_method;
+ }
+}
+
+static int IncreasePreviousDuration(WebPAnimEncoder* const enc, int duration) {
+ const size_t position = enc->count_ - 1;
+ EncodedFrame* const prev_enc_frame = GetFrame(enc, position);
+ int new_duration;
+
+ assert(enc->count_ >= 1);
+ assert(prev_enc_frame->sub_frame_.duration ==
+ prev_enc_frame->key_frame_.duration);
+ assert(prev_enc_frame->sub_frame_.duration ==
+ (prev_enc_frame->sub_frame_.duration & (MAX_DURATION - 1)));
+ assert(duration == (duration & (MAX_DURATION - 1)));
+
+ new_duration = prev_enc_frame->sub_frame_.duration + duration;
+ if (new_duration >= MAX_DURATION) { // Special case.
+ // Separate out previous frame from earlier merged frames to avoid overflow.
+ // We add a 1x1 transparent frame for the previous frame, with blending on.
+ const FrameRectangle rect = { 0, 0, 1, 1 };
+ const uint8_t lossless_1x1_bytes[] = {
+ 0x52, 0x49, 0x46, 0x46, 0x14, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
+ 0x56, 0x50, 0x38, 0x4c, 0x08, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00,
+ 0x10, 0x88, 0x88, 0x08
+ };
+ const WebPData lossless_1x1 = {
+ lossless_1x1_bytes, sizeof(lossless_1x1_bytes)
+ };
+ const uint8_t lossy_1x1_bytes[] = {
+ 0x52, 0x49, 0x46, 0x46, 0x40, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
+ 0x56, 0x50, 0x38, 0x58, 0x0a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x4c, 0x50, 0x48, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x56, 0x50, 0x38, 0x20, 0x18, 0x00, 0x00, 0x00,
+ 0x30, 0x01, 0x00, 0x9d, 0x01, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00,
+ 0x34, 0x25, 0xa4, 0x00, 0x03, 0x70, 0x00, 0xfe, 0xfb, 0xfd, 0x50, 0x00
+ };
+ const WebPData lossy_1x1 = { lossy_1x1_bytes, sizeof(lossy_1x1_bytes) };
+ const int can_use_lossless =
+ (enc->last_config_.lossless || enc->options_.allow_mixed);
+ EncodedFrame* const curr_enc_frame = GetFrame(enc, enc->count_);
+ curr_enc_frame->is_key_frame_ = 0;
+ curr_enc_frame->sub_frame_.id = WEBP_CHUNK_ANMF;
+ curr_enc_frame->sub_frame_.x_offset = 0;
+ curr_enc_frame->sub_frame_.y_offset = 0;
+ curr_enc_frame->sub_frame_.dispose_method = WEBP_MUX_DISPOSE_NONE;
+ curr_enc_frame->sub_frame_.blend_method = WEBP_MUX_BLEND;
+ curr_enc_frame->sub_frame_.duration = duration;
+ if (!WebPDataCopy(can_use_lossless ? &lossless_1x1 : &lossy_1x1,
+ &curr_enc_frame->sub_frame_.bitstream)) {
+ return 0;
+ }
+ ++enc->count_;
+ ++enc->count_since_key_frame_;
+ enc->flush_count_ = enc->count_ - 1;
+ enc->prev_candidate_undecided_ = 0;
+ enc->prev_rect_ = rect;
+ } else { // Regular case.
+ // Increase duration of the previous frame by 'duration'.
+ prev_enc_frame->sub_frame_.duration = new_duration;
+ prev_enc_frame->key_frame_.duration = new_duration;
+ }
+ return 1;
+}
+
+// Pick the candidate encoded frame with smallest size and release other
+// candidates.
+// TODO(later): Perhaps a rough SSIM/PSNR produced by the encoder should
+// also be a criteria, in addition to sizes.
+static void PickBestCandidate(WebPAnimEncoder* const enc,
+ Candidate* const candidates, int is_key_frame,
+ EncodedFrame* const encoded_frame) {
+ int i;
+ int best_idx = -1;
+ size_t best_size = ~0;
+ for (i = 0; i < CANDIDATE_COUNT; ++i) {
+ if (candidates[i].evaluate_) {
+ const size_t candidate_size = candidates[i].mem_.size;
+ if (candidate_size < best_size) {
+ best_idx = i;
+ best_size = candidate_size;
+ }
+ }
+ }
+ assert(best_idx != -1);
+ for (i = 0; i < CANDIDATE_COUNT; ++i) {
+ if (candidates[i].evaluate_) {
+ if (i == best_idx) {
+ WebPMuxFrameInfo* const dst = is_key_frame
+ ? &encoded_frame->key_frame_
+ : &encoded_frame->sub_frame_;
+ *dst = candidates[i].info_;
+ GetEncodedData(&candidates[i].mem_, &dst->bitstream);
+ if (!is_key_frame) {
+ // Note: Previous dispose method only matters for non-keyframes.
+ // Also, we don't want to modify previous dispose method that was
+ // selected when a non key-frame was assumed.
+ const WebPMuxAnimDispose prev_dispose_method =
+ (best_idx == LL_DISP_NONE || best_idx == LOSSY_DISP_NONE)
+ ? WEBP_MUX_DISPOSE_NONE
+ : WEBP_MUX_DISPOSE_BACKGROUND;
+ SetPreviousDisposeMethod(enc, prev_dispose_method);
+ }
+ enc->prev_rect_ = candidates[i].rect_; // save for next frame.
+ } else {
+ WebPMemoryWriterClear(&candidates[i].mem_);
+ candidates[i].evaluate_ = 0;
+ }
+ }
+ }
+}
+
+// Depending on the configuration, tries different compressions
+// (lossy/lossless), dispose methods, blending methods etc to encode the current
+// frame and outputs the best one in 'encoded_frame'.
+// 'frame_skipped' will be set to true if this frame should actually be skipped.
+static WebPEncodingError SetFrame(WebPAnimEncoder* const enc,
+ const WebPConfig* const config,
+ int is_key_frame,
+ EncodedFrame* const encoded_frame,
+ int* const frame_skipped) {
+ int i;
+ WebPEncodingError error_code = VP8_ENC_OK;
+ const WebPPicture* const curr_canvas = &enc->curr_canvas_copy_;
+ const WebPPicture* const prev_canvas = &enc->prev_canvas_;
+ Candidate candidates[CANDIDATE_COUNT];
+ const int is_lossless = config->lossless;
+ const int consider_lossless = is_lossless || enc->options_.allow_mixed;
+ const int consider_lossy = !is_lossless || enc->options_.allow_mixed;
+ const int is_first_frame = enc->is_first_frame_;
+
+ // First frame cannot be skipped as there is no 'previous frame' to merge it
+ // to. So, empty rectangle is not allowed for the first frame.
+ const int empty_rect_allowed_none = !is_first_frame;
+
+ // Even if there is exact pixel match between 'disposed previous canvas' and
+ // 'current canvas', we can't skip current frame, as there may not be exact
+ // pixel match between 'previous canvas' and 'current canvas'. So, we don't
+ // allow empty rectangle in this case.
+ const int empty_rect_allowed_bg = 0;
+
+ // If current frame is a key-frame, dispose method of previous frame doesn't
+ // matter, so we don't try dispose to background.
+ // Also, if key-frame insertion is on, and previous frame could be picked as
+ // either a sub-frame or a key-frame, then we can't be sure about what frame
+ // rectangle would be disposed. In that case too, we don't try dispose to
+ // background.
+ const int dispose_bg_possible =
+ !is_key_frame && !enc->prev_candidate_undecided_;
+
+ SubFrameParams dispose_none_params;
+ SubFrameParams dispose_bg_params;
+
+ WebPConfig config_ll = *config;
+ WebPConfig config_lossy = *config;
+ config_ll.lossless = 1;
+ config_lossy.lossless = 0;
+ enc->last_config_ = *config;
+ enc->last_config_reversed_ = config->lossless ? config_lossy : config_ll;
+ *frame_skipped = 0;
+
+ if (!SubFrameParamsInit(&dispose_none_params, 1, empty_rect_allowed_none) ||
+ !SubFrameParamsInit(&dispose_bg_params, 0, empty_rect_allowed_bg)) {
+ return VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ }
+
+ memset(candidates, 0, sizeof(candidates));
+
+ // Change-rectangle assuming previous frame was DISPOSE_NONE.
+ if (!GetSubRects(prev_canvas, curr_canvas, is_key_frame, is_first_frame,
+ config_lossy.quality, &dispose_none_params)) {
+ error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ goto Err;
+ }
+
+ if ((consider_lossless && IsEmptyRect(&dispose_none_params.rect_ll_)) ||
+ (consider_lossy && IsEmptyRect(&dispose_none_params.rect_lossy_))) {
+ // Don't encode the frame at all. Instead, the duration of the previous
+ // frame will be increased later.
+ assert(empty_rect_allowed_none);
+ *frame_skipped = 1;
+ goto End;
+ }
+
+ if (dispose_bg_possible) {
+ // Change-rectangle assuming previous frame was DISPOSE_BACKGROUND.
+ WebPPicture* const prev_canvas_disposed = &enc->prev_canvas_disposed_;
+ WebPCopyPixels(prev_canvas, prev_canvas_disposed);
+ DisposeFrameRectangle(WEBP_MUX_DISPOSE_BACKGROUND, &enc->prev_rect_,
+ prev_canvas_disposed);
+
+ if (!GetSubRects(prev_canvas_disposed, curr_canvas, is_key_frame,
+ is_first_frame, config_lossy.quality,
+ &dispose_bg_params)) {
+ error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ goto Err;
+ }
+ assert(!IsEmptyRect(&dispose_bg_params.rect_ll_));
+ assert(!IsEmptyRect(&dispose_bg_params.rect_lossy_));
+
+ if (enc->options_.minimize_size) { // Try both dispose methods.
+ dispose_bg_params.should_try_ = 1;
+ dispose_none_params.should_try_ = 1;
+ } else if ((is_lossless &&
+ RectArea(&dispose_bg_params.rect_ll_) <
+ RectArea(&dispose_none_params.rect_ll_)) ||
+ (!is_lossless &&
+ RectArea(&dispose_bg_params.rect_lossy_) <
+ RectArea(&dispose_none_params.rect_lossy_))) {
+ dispose_bg_params.should_try_ = 1; // Pick DISPOSE_BACKGROUND.
+ dispose_none_params.should_try_ = 0;
+ }
+ }
+
+ if (dispose_none_params.should_try_) {
+ error_code = GenerateCandidates(
+ enc, candidates, WEBP_MUX_DISPOSE_NONE, is_lossless, is_key_frame,
+ &dispose_none_params, &config_ll, &config_lossy);
+ if (error_code != VP8_ENC_OK) goto Err;
+ }
+
+ if (dispose_bg_params.should_try_) {
+ assert(!enc->is_first_frame_);
+ assert(dispose_bg_possible);
+ error_code = GenerateCandidates(
+ enc, candidates, WEBP_MUX_DISPOSE_BACKGROUND, is_lossless, is_key_frame,
+ &dispose_bg_params, &config_ll, &config_lossy);
+ if (error_code != VP8_ENC_OK) goto Err;
+ }
+
+ PickBestCandidate(enc, candidates, is_key_frame, encoded_frame);
+
+ goto End;
+
+ Err:
+ for (i = 0; i < CANDIDATE_COUNT; ++i) {
+ if (candidates[i].evaluate_) {
+ WebPMemoryWriterClear(&candidates[i].mem_);
+ }
+ }
+
+ End:
+ SubFrameParamsFree(&dispose_none_params);
+ SubFrameParamsFree(&dispose_bg_params);
+ return error_code;
+}
+
+// Calculate the penalty incurred if we encode given frame as a key frame
+// instead of a sub-frame.
+static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) {
+ return ((int64_t)encoded_frame->key_frame_.bitstream.size -
+ encoded_frame->sub_frame_.bitstream.size);
+}
+
+static int CacheFrame(WebPAnimEncoder* const enc,
+ const WebPConfig* const config) {
+ int ok = 0;
+ int frame_skipped = 0;
+ WebPEncodingError error_code = VP8_ENC_OK;
+ const size_t position = enc->count_;
+ EncodedFrame* const encoded_frame = GetFrame(enc, position);
+
+ ++enc->count_;
+
+ if (enc->is_first_frame_) { // Add this as a key-frame.
+ error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped);
+ if (error_code != VP8_ENC_OK) goto End;
+ assert(frame_skipped == 0); // First frame can't be skipped, even if empty.
+ assert(position == 0 && enc->count_ == 1);
+ encoded_frame->is_key_frame_ = 1;
+ enc->flush_count_ = 0;
+ enc->count_since_key_frame_ = 0;
+ enc->prev_candidate_undecided_ = 0;
+ } else {
+ ++enc->count_since_key_frame_;
+ if (enc->count_since_key_frame_ <= enc->options_.kmin) {
+ // Add this as a frame rectangle.
+ error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped);
+ if (error_code != VP8_ENC_OK) goto End;
+ if (frame_skipped) goto Skip;
+ encoded_frame->is_key_frame_ = 0;
+ enc->flush_count_ = enc->count_ - 1;
+ enc->prev_candidate_undecided_ = 0;
+ } else {
+ int64_t curr_delta;
+ FrameRectangle prev_rect_key, prev_rect_sub;
+
+ // Add this as a frame rectangle to enc.
+ error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped);
+ if (error_code != VP8_ENC_OK) goto End;
+ if (frame_skipped) goto Skip;
+ prev_rect_sub = enc->prev_rect_;
+
+
+ // Add this as a key-frame to enc, too.
+ error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped);
+ if (error_code != VP8_ENC_OK) goto End;
+ assert(frame_skipped == 0); // Key-frame cannot be an empty rectangle.
+ prev_rect_key = enc->prev_rect_;
+
+ // Analyze size difference of the two variants.
+ curr_delta = KeyFramePenalty(encoded_frame);
+ if (curr_delta <= enc->best_delta_) { // Pick this as the key-frame.
+ if (enc->keyframe_ != KEYFRAME_NONE) {
+ EncodedFrame* const old_keyframe = GetFrame(enc, enc->keyframe_);
+ assert(old_keyframe->is_key_frame_);
+ old_keyframe->is_key_frame_ = 0;
+ }
+ encoded_frame->is_key_frame_ = 1;
+ enc->prev_candidate_undecided_ = 1;
+ enc->keyframe_ = (int)position;
+ enc->best_delta_ = curr_delta;
+ enc->flush_count_ = enc->count_ - 1; // We can flush previous frames.
+ } else {
+ encoded_frame->is_key_frame_ = 0;
+ enc->prev_candidate_undecided_ = 0;
+ }
+ // Note: We need '>=' below because when kmin and kmax are both zero,
+ // count_since_key_frame will always be > kmax.
+ if (enc->count_since_key_frame_ >= enc->options_.kmax) {
+ enc->flush_count_ = enc->count_ - 1;
+ enc->count_since_key_frame_ = 0;
+ enc->keyframe_ = KEYFRAME_NONE;
+ enc->best_delta_ = DELTA_INFINITY;
+ }
+ if (!enc->prev_candidate_undecided_) {
+ enc->prev_rect_ =
+ encoded_frame->is_key_frame_ ? prev_rect_key : prev_rect_sub;
+ }
+ }
+ }
+
+ // Update previous to previous and previous canvases for next call.
+ WebPCopyPixels(enc->curr_canvas_, &enc->prev_canvas_);
+ enc->is_first_frame_ = 0;
+
+ Skip:
+ ok = 1;
+ ++enc->in_frame_count_;
+
+ End:
+ if (!ok || frame_skipped) {
+ FrameRelease(encoded_frame);
+ // We reset some counters, as the frame addition failed/was skipped.
+ --enc->count_;
+ if (!enc->is_first_frame_) --enc->count_since_key_frame_;
+ if (!ok) {
+ MarkError2(enc, "ERROR adding frame. WebPEncodingError", error_code);
+ }
+ }
+ enc->curr_canvas_->error_code = error_code; // report error_code
+ assert(ok || error_code != VP8_ENC_OK);
+ return ok;
+}
+
+static int FlushFrames(WebPAnimEncoder* const enc) {
+ while (enc->flush_count_ > 0) {
+ WebPMuxError err;
+ EncodedFrame* const curr = GetFrame(enc, 0);
+ const WebPMuxFrameInfo* const info =
+ curr->is_key_frame_ ? &curr->key_frame_ : &curr->sub_frame_;
+ assert(enc->mux_ != NULL);
+ err = WebPMuxPushFrame(enc->mux_, info, 1);
+ if (err != WEBP_MUX_OK) {
+ MarkError2(enc, "ERROR adding frame. WebPMuxError", err);
+ return 0;
+ }
+ if (enc->options_.verbose) {
+ fprintf(stderr, "INFO: Added frame. offset:%d,%d dispose:%d blend:%d\n",
+ info->x_offset, info->y_offset, info->dispose_method,
+ info->blend_method);
+ }
+ ++enc->out_frame_count_;
+ FrameRelease(curr);
+ ++enc->start_;
+ --enc->flush_count_;
+ --enc->count_;
+ if (enc->keyframe_ != KEYFRAME_NONE) --enc->keyframe_;
+ }
+
+ if (enc->count_ == 1 && enc->start_ != 0) {
+ // Move enc->start to index 0.
+ const int enc_start_tmp = (int)enc->start_;
+ EncodedFrame temp = enc->encoded_frames_[0];
+ enc->encoded_frames_[0] = enc->encoded_frames_[enc_start_tmp];
+ enc->encoded_frames_[enc_start_tmp] = temp;
+ FrameRelease(&enc->encoded_frames_[enc_start_tmp]);
+ enc->start_ = 0;
+ }
+ return 1;
+}
+
+#undef DELTA_INFINITY
+#undef KEYFRAME_NONE
+
+int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp,
+ const WebPConfig* encoder_config) {
+ WebPConfig config;
+ int ok;
+
+ if (enc == NULL) {
+ return 0;
+ }
+ MarkNoError(enc);
+
+ if (!enc->is_first_frame_) {
+ // Make sure timestamps are non-decreasing (integer wrap-around is OK).
+ const uint32_t prev_frame_duration =
+ (uint32_t)timestamp - enc->prev_timestamp_;
+ if (prev_frame_duration >= MAX_DURATION) {
+ if (frame != NULL) {
+ frame->error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ }
+ MarkError(enc, "ERROR adding frame: timestamps must be non-decreasing");
+ return 0;
+ }
+ if (!IncreasePreviousDuration(enc, (int)prev_frame_duration)) {
+ return 0;
+ }
+ } else {
+ enc->first_timestamp_ = timestamp;
+ }
+
+ if (frame == NULL) { // Special: last call.
+ enc->got_null_frame_ = 1;
+ enc->prev_timestamp_ = timestamp;
+ return 1;
+ }
+
+ if (frame->width != enc->canvas_width_ ||
+ frame->height != enc->canvas_height_) {
+ frame->error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION;
+ MarkError(enc, "ERROR adding frame: Invalid frame dimensions");
+ return 0;
+ }
+
+ if (!frame->use_argb) { // Convert frame from YUV(A) to ARGB.
+ if (enc->options_.verbose) {
+ fprintf(stderr, "WARNING: Converting frame from YUV(A) to ARGB format; "
+ "this incurs a small loss.\n");
+ }
+ if (!WebPPictureYUVAToARGB(frame)) {
+ MarkError(enc, "ERROR converting frame from YUV(A) to ARGB");
+ return 0;
+ }
+ }
+
+ if (encoder_config != NULL) {
+ if (!WebPValidateConfig(encoder_config)) {
+ MarkError(enc, "ERROR adding frame: Invalid WebPConfig");
+ return 0;
+ }
+ config = *encoder_config;
+ } else {
+ WebPConfigInit(&config);
+ config.lossless = 1;
+ }
+ assert(enc->curr_canvas_ == NULL);
+ enc->curr_canvas_ = frame; // Store reference.
+ assert(enc->curr_canvas_copy_modified_ == 1);
+ CopyCurrentCanvas(enc);
+
+ ok = CacheFrame(enc, &config) && FlushFrames(enc);
+
+ enc->curr_canvas_ = NULL;
+ enc->curr_canvas_copy_modified_ = 1;
+ if (ok) {
+ enc->prev_timestamp_ = timestamp;
+ }
+ return ok;
+}
+
+// -----------------------------------------------------------------------------
+// Bitstream assembly.
+
+static int DecodeFrameOntoCanvas(const WebPMuxFrameInfo* const frame,
+ WebPPicture* const canvas) {
+ const WebPData* const image = &frame->bitstream;
+ WebPPicture sub_image;
+ WebPDecoderConfig config;
+ WebPInitDecoderConfig(&config);
+ WebPUtilClearPic(canvas, NULL);
+ if (WebPGetFeatures(image->bytes, image->size, &config.input) !=
+ VP8_STATUS_OK) {
+ return 0;
+ }
+ if (!WebPPictureView(canvas, frame->x_offset, frame->y_offset,
+ config.input.width, config.input.height, &sub_image)) {
+ return 0;
+ }
+ config.output.is_external_memory = 1;
+ config.output.colorspace = MODE_BGRA;
+ config.output.u.RGBA.rgba = (uint8_t*)sub_image.argb;
+ config.output.u.RGBA.stride = sub_image.argb_stride * 4;
+ config.output.u.RGBA.size = config.output.u.RGBA.stride * sub_image.height;
+
+ if (WebPDecode(image->bytes, image->size, &config) != VP8_STATUS_OK) {
+ return 0;
+ }
+ return 1;
+}
+
+static int FrameToFullCanvas(WebPAnimEncoder* const enc,
+ const WebPMuxFrameInfo* const frame,
+ WebPData* const full_image) {
+ WebPPicture* const canvas_buf = &enc->curr_canvas_copy_;
+ WebPMemoryWriter mem1, mem2;
+ WebPMemoryWriterInit(&mem1);
+ WebPMemoryWriterInit(&mem2);
+
+ if (!DecodeFrameOntoCanvas(frame, canvas_buf)) goto Err;
+ if (!EncodeFrame(&enc->last_config_, canvas_buf, &mem1)) goto Err;
+ GetEncodedData(&mem1, full_image);
+
+ if (enc->options_.allow_mixed) {
+ if (!EncodeFrame(&enc->last_config_reversed_, canvas_buf, &mem2)) goto Err;
+ if (mem2.size < mem1.size) {
+ GetEncodedData(&mem2, full_image);
+ WebPMemoryWriterClear(&mem1);
+ } else {
+ WebPMemoryWriterClear(&mem2);
+ }
+ }
+ return 1;
+
+ Err:
+ WebPMemoryWriterClear(&mem1);
+ WebPMemoryWriterClear(&mem2);
+ return 0;
+}
+
+// Convert a single-frame animation to a non-animated image if appropriate.
+// TODO(urvang): Can we pick one of the two heuristically (based on frame
+// rectangle and/or presence of alpha)?
+static WebPMuxError OptimizeSingleFrame(WebPAnimEncoder* const enc,
+ WebPData* const webp_data) {
+ WebPMuxError err = WEBP_MUX_OK;
+ int canvas_width, canvas_height;
+ WebPMuxFrameInfo frame;
+ WebPData full_image;
+ WebPData webp_data2;
+ WebPMux* const mux = WebPMuxCreate(webp_data, 0);
+ if (mux == NULL) return WEBP_MUX_BAD_DATA;
+ assert(enc->out_frame_count_ == 1);
+ WebPDataInit(&frame.bitstream);
+ WebPDataInit(&full_image);
+ WebPDataInit(&webp_data2);
+
+ err = WebPMuxGetFrame(mux, 1, &frame);
+ if (err != WEBP_MUX_OK) goto End;
+ if (frame.id != WEBP_CHUNK_ANMF) goto End; // Non-animation: nothing to do.
+ err = WebPMuxGetCanvasSize(mux, &canvas_width, &canvas_height);
+ if (err != WEBP_MUX_OK) goto End;
+ if (!FrameToFullCanvas(enc, &frame, &full_image)) {
+ err = WEBP_MUX_BAD_DATA;
+ goto End;
+ }
+ err = WebPMuxSetImage(mux, &full_image, 1);
+ if (err != WEBP_MUX_OK) goto End;
+ err = WebPMuxAssemble(mux, &webp_data2);
+ if (err != WEBP_MUX_OK) goto End;
+
+ if (webp_data2.size < webp_data->size) { // Pick 'webp_data2' if smaller.
+ WebPDataClear(webp_data);
+ *webp_data = webp_data2;
+ WebPDataInit(&webp_data2);
+ }
+
+ End:
+ WebPDataClear(&frame.bitstream);
+ WebPDataClear(&full_image);
+ WebPMuxDelete(mux);
+ WebPDataClear(&webp_data2);
+ return err;
+}
+
+int WebPAnimEncoderAssemble(WebPAnimEncoder* enc, WebPData* webp_data) {
+ WebPMux* mux;
+ WebPMuxError err;
+
+ if (enc == NULL) {
+ return 0;
+ }
+ MarkNoError(enc);
+
+ if (webp_data == NULL) {
+ MarkError(enc, "ERROR assembling: NULL input");
+ return 0;
+ }
+
+ if (enc->in_frame_count_ == 0) {
+ MarkError(enc, "ERROR: No frames to assemble");
+ return 0;
+ }
+
+ if (!enc->got_null_frame_ && enc->in_frame_count_ > 1 && enc->count_ > 0) {
+ // set duration of the last frame to be avg of durations of previous frames.
+ const double delta_time =
+ (uint32_t)enc->prev_timestamp_ - enc->first_timestamp_;
+ const int average_duration = (int)(delta_time / (enc->in_frame_count_ - 1));
+ if (!IncreasePreviousDuration(enc, average_duration)) {
+ return 0;
+ }
+ }
+
+ // Flush any remaining frames.
+ enc->flush_count_ = enc->count_;
+ if (!FlushFrames(enc)) {
+ return 0;
+ }
+
+ // Set definitive canvas size.
+ mux = enc->mux_;
+ err = WebPMuxSetCanvasSize(mux, enc->canvas_width_, enc->canvas_height_);
+ if (err != WEBP_MUX_OK) goto Err;
+
+ err = WebPMuxSetAnimationParams(mux, &enc->options_.anim_params);
+ if (err != WEBP_MUX_OK) goto Err;
+
+ // Assemble into a WebP bitstream.
+ err = WebPMuxAssemble(mux, webp_data);
+ if (err != WEBP_MUX_OK) goto Err;
+
+ if (enc->out_frame_count_ == 1) {
+ err = OptimizeSingleFrame(enc, webp_data);
+ if (err != WEBP_MUX_OK) goto Err;
+ }
+ return 1;
+
+ Err:
+ MarkError2(enc, "ERROR assembling WebP", err);
+ return 0;
+}
+
+const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc) {
+ if (enc == NULL) return NULL;
+ return enc->error_str_;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/mux/animi.h b/src/third_party/libwebp/src/mux/animi.h
new file mode 100644
index 0000000..8889953
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/animi.h
@@ -0,0 +1,43 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Internal header for animation related functions.
+//
+// Author: Hui Su (huisu@google.com)
+
+#ifndef WEBP_MUX_ANIMI_H_
+#define WEBP_MUX_ANIMI_H_
+
+#include "src/webp/mux.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Picks the optimal rectangle between two pictures, starting with initial
+// values of offsets and dimensions that are passed in. The initial
+// values will be clipped, if necessary, to make sure the rectangle is
+// within the canvas. "use_argb" must be true for both pictures.
+// Parameters:
+// prev_canvas, curr_canvas - (in) two input pictures to compare.
+// is_lossless, quality - (in) encoding settings.
+// x_offset, y_offset, width, height - (in/out) rectangle between the two
+// input pictures.
+// Returns true on success.
+int WebPAnimEncoderRefineRect(
+ const struct WebPPicture* const prev_canvas,
+ const struct WebPPicture* const curr_canvas,
+ int is_lossless, float quality, int* const x_offset, int* const y_offset,
+ int* const width, int* const height);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_MUX_ANIMI_H_ */
diff --git a/src/third_party/libwebp/src/mux/libwebpmux.pc.in b/src/third_party/libwebp/src/mux/libwebpmux.pc.in
new file mode 100644
index 0000000..a96fac7
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/libwebpmux.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebpmux
+Description: Library for manipulating the WebP graphics format container
+Version: @PACKAGE_VERSION@
+Requires: libwebp >= 0.2.0
+Cflags: -I${includedir}
+Libs: -L${libdir} -lwebpmux
+Libs.private: -lm
diff --git a/src/third_party/libwebp/src/mux/libwebpmux.rc b/src/third_party/libwebp/src/mux/libwebpmux.rc
new file mode 100644
index 0000000..8c7d5f6
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/libwebpmux.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Google, Inc."
+ VALUE "FileDescription", "libwebpmux DLL"
+ VALUE "FileVersion", "1.0.0"
+ VALUE "InternalName", "libwebpmux.dll"
+ VALUE "LegalCopyright", "Copyright (C) 2018"
+ VALUE "OriginalFilename", "libwebpmux.dll"
+ VALUE "ProductName", "WebP Image Muxer"
+ VALUE "ProductVersion", "1.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
diff --git a/src/third_party/libwebp/src/mux/muxedit.c b/src/third_party/libwebp/src/mux/muxedit.c
new file mode 100644
index 0000000..7a027b3
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/muxedit.c
@@ -0,0 +1,657 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Set and delete APIs for mux.
+//
+// Authors: Urvang (urvang@google.com)
+// Vikas (vikasa@google.com)
+
+#include <assert.h>
+#include "src/mux/muxi.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// Life of a mux object.
+
+static void MuxInit(WebPMux* const mux) {
+ assert(mux != NULL);
+ memset(mux, 0, sizeof(*mux));
+ mux->canvas_width_ = 0; // just to be explicit
+ mux->canvas_height_ = 0;
+}
+
+WebPMux* WebPNewInternal(int version) {
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
+ return NULL;
+ } else {
+ WebPMux* const mux = (WebPMux*)WebPSafeMalloc(1ULL, sizeof(WebPMux));
+ if (mux != NULL) MuxInit(mux);
+ return mux;
+ }
+}
+
+// Delete all images in 'wpi_list'.
+static void DeleteAllImages(WebPMuxImage** const wpi_list) {
+ while (*wpi_list != NULL) {
+ *wpi_list = MuxImageDelete(*wpi_list);
+ }
+}
+
+static void MuxRelease(WebPMux* const mux) {
+ assert(mux != NULL);
+ DeleteAllImages(&mux->images_);
+ ChunkListDelete(&mux->vp8x_);
+ ChunkListDelete(&mux->iccp_);
+ ChunkListDelete(&mux->anim_);
+ ChunkListDelete(&mux->exif_);
+ ChunkListDelete(&mux->xmp_);
+ ChunkListDelete(&mux->unknown_);
+}
+
+void WebPMuxDelete(WebPMux* mux) {
+ if (mux != NULL) {
+ MuxRelease(mux);
+ WebPSafeFree(mux);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Helper method(s).
+
+// Handy MACRO, makes MuxSet() very symmetric to MuxGet().
+#define SWITCH_ID_LIST(INDEX, LIST) \
+ if (idx == (INDEX)) { \
+ err = ChunkAssignData(&chunk, data, copy_data, tag); \
+ if (err == WEBP_MUX_OK) { \
+ err = ChunkSetNth(&chunk, (LIST), nth); \
+ } \
+ return err; \
+ }
+
+static WebPMuxError MuxSet(WebPMux* const mux, uint32_t tag, uint32_t nth,
+ const WebPData* const data, int copy_data) {
+ WebPChunk chunk;
+ WebPMuxError err = WEBP_MUX_NOT_FOUND;
+ const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag);
+ assert(mux != NULL);
+ assert(!IsWPI(kChunks[idx].id));
+
+ ChunkInit(&chunk);
+ SWITCH_ID_LIST(IDX_VP8X, &mux->vp8x_);
+ SWITCH_ID_LIST(IDX_ICCP, &mux->iccp_);
+ SWITCH_ID_LIST(IDX_ANIM, &mux->anim_);
+ SWITCH_ID_LIST(IDX_EXIF, &mux->exif_);
+ SWITCH_ID_LIST(IDX_XMP, &mux->xmp_);
+ SWITCH_ID_LIST(IDX_UNKNOWN, &mux->unknown_);
+ return err;
+}
+#undef SWITCH_ID_LIST
+
+// Create data for frame given image data, offsets and duration.
+static WebPMuxError CreateFrameData(
+ int width, int height, const WebPMuxFrameInfo* const info,
+ WebPData* const frame) {
+ uint8_t* frame_bytes;
+ const size_t frame_size = kChunks[IDX_ANMF].size;
+
+ assert(width > 0 && height > 0 && info->duration >= 0);
+ assert(info->dispose_method == (info->dispose_method & 1));
+ // Note: assertion on upper bounds is done in PutLE24().
+
+ frame_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_size);
+ if (frame_bytes == NULL) return WEBP_MUX_MEMORY_ERROR;
+
+ PutLE24(frame_bytes + 0, info->x_offset / 2);
+ PutLE24(frame_bytes + 3, info->y_offset / 2);
+
+ PutLE24(frame_bytes + 6, width - 1);
+ PutLE24(frame_bytes + 9, height - 1);
+ PutLE24(frame_bytes + 12, info->duration);
+ frame_bytes[15] =
+ (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) |
+ (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0);
+
+ frame->bytes = frame_bytes;
+ frame->size = frame_size;
+ return WEBP_MUX_OK;
+}
+
+// Outputs image data given a bitstream. The bitstream can either be a
+// single-image WebP file or raw VP8/VP8L data.
+// Also outputs 'is_lossless' to be true if the given bitstream is lossless.
+static WebPMuxError GetImageData(const WebPData* const bitstream,
+ WebPData* const image, WebPData* const alpha,
+ int* const is_lossless) {
+ WebPDataInit(alpha); // Default: no alpha.
+ if (bitstream->size < TAG_SIZE ||
+ memcmp(bitstream->bytes, "RIFF", TAG_SIZE)) {
+ // It is NOT webp file data. Return input data as is.
+ *image = *bitstream;
+ } else {
+ // It is webp file data. Extract image data from it.
+ const WebPMuxImage* wpi;
+ WebPMux* const mux = WebPMuxCreate(bitstream, 0);
+ if (mux == NULL) return WEBP_MUX_BAD_DATA;
+ wpi = mux->images_;
+ assert(wpi != NULL && wpi->img_ != NULL);
+ *image = wpi->img_->data_;
+ if (wpi->alpha_ != NULL) {
+ *alpha = wpi->alpha_->data_;
+ }
+ WebPMuxDelete(mux);
+ }
+ *is_lossless = VP8LCheckSignature(image->bytes, image->size);
+ return WEBP_MUX_OK;
+}
+
+static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) {
+ WebPMuxError err = WEBP_MUX_NOT_FOUND;
+ assert(chunk_list);
+ while (*chunk_list) {
+ WebPChunk* const chunk = *chunk_list;
+ if (chunk->tag_ == tag) {
+ *chunk_list = ChunkDelete(chunk);
+ err = WEBP_MUX_OK;
+ } else {
+ chunk_list = &chunk->next_;
+ }
+ }
+ return err;
+}
+
+static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, uint32_t tag) {
+ const WebPChunkId id = ChunkGetIdFromTag(tag);
+ assert(mux != NULL);
+ if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT;
+ return DeleteChunks(MuxGetChunkListFromId(mux, id), tag);
+}
+
+//------------------------------------------------------------------------------
+// Set API(s).
+
+WebPMuxError WebPMuxSetChunk(WebPMux* mux, const char fourcc[4],
+ const WebPData* chunk_data, int copy_data) {
+ uint32_t tag;
+ WebPMuxError err;
+ if (mux == NULL || fourcc == NULL || chunk_data == NULL ||
+ chunk_data->bytes == NULL || chunk_data->size > MAX_CHUNK_PAYLOAD) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ tag = ChunkGetTagFromFourCC(fourcc);
+
+ // Delete existing chunk(s) with the same 'fourcc'.
+ err = MuxDeleteAllNamedData(mux, tag);
+ if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
+
+ // Add the given chunk.
+ return MuxSet(mux, tag, 1, chunk_data, copy_data);
+}
+
+// Creates a chunk from given 'data' and sets it as 1st chunk in 'chunk_list'.
+static WebPMuxError AddDataToChunkList(
+ const WebPData* const data, int copy_data, uint32_t tag,
+ WebPChunk** chunk_list) {
+ WebPChunk chunk;
+ WebPMuxError err;
+ ChunkInit(&chunk);
+ err = ChunkAssignData(&chunk, data, copy_data, tag);
+ if (err != WEBP_MUX_OK) goto Err;
+ err = ChunkSetNth(&chunk, chunk_list, 1);
+ if (err != WEBP_MUX_OK) goto Err;
+ return WEBP_MUX_OK;
+ Err:
+ ChunkRelease(&chunk);
+ return err;
+}
+
+// Extracts image & alpha data from the given bitstream and then sets wpi.alpha_
+// and wpi.img_ appropriately.
+static WebPMuxError SetAlphaAndImageChunks(
+ const WebPData* const bitstream, int copy_data, WebPMuxImage* const wpi) {
+ int is_lossless = 0;
+ WebPData image, alpha;
+ WebPMuxError err = GetImageData(bitstream, &image, &alpha, &is_lossless);
+ const int image_tag =
+ is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag;
+ if (err != WEBP_MUX_OK) return err;
+ if (alpha.bytes != NULL) {
+ err = AddDataToChunkList(&alpha, copy_data, kChunks[IDX_ALPHA].tag,
+ &wpi->alpha_);
+ if (err != WEBP_MUX_OK) return err;
+ }
+ err = AddDataToChunkList(&image, copy_data, image_tag, &wpi->img_);
+ if (err != WEBP_MUX_OK) return err;
+ return MuxImageFinalize(wpi) ? WEBP_MUX_OK : WEBP_MUX_INVALID_ARGUMENT;
+}
+
+WebPMuxError WebPMuxSetImage(WebPMux* mux, const WebPData* bitstream,
+ int copy_data) {
+ WebPMuxImage wpi;
+ WebPMuxError err;
+
+ // Sanity checks.
+ if (mux == NULL || bitstream == NULL || bitstream->bytes == NULL ||
+ bitstream->size > MAX_CHUNK_PAYLOAD) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ if (mux->images_ != NULL) {
+ // Only one 'simple image' can be added in mux. So, remove present images.
+ DeleteAllImages(&mux->images_);
+ }
+
+ MuxImageInit(&wpi);
+ err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi);
+ if (err != WEBP_MUX_OK) goto Err;
+
+ // Add this WebPMuxImage to mux.
+ err = MuxImagePush(&wpi, &mux->images_);
+ if (err != WEBP_MUX_OK) goto Err;
+
+ // All is well.
+ return WEBP_MUX_OK;
+
+ Err: // Something bad happened.
+ MuxImageRelease(&wpi);
+ return err;
+}
+
+WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPMuxFrameInfo* info,
+ int copy_data) {
+ WebPMuxImage wpi;
+ WebPMuxError err;
+ const WebPData* const bitstream = &info->bitstream;
+
+ // Sanity checks.
+ if (mux == NULL || info == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+
+ if (info->id != WEBP_CHUNK_ANMF) return WEBP_MUX_INVALID_ARGUMENT;
+
+ if (bitstream->bytes == NULL || bitstream->size > MAX_CHUNK_PAYLOAD) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ if (mux->images_ != NULL) {
+ const WebPMuxImage* const image = mux->images_;
+ const uint32_t image_id = (image->header_ != NULL) ?
+ ChunkGetIdFromTag(image->header_->tag_) : WEBP_CHUNK_IMAGE;
+ if (image_id != info->id) {
+ return WEBP_MUX_INVALID_ARGUMENT; // Conflicting frame types.
+ }
+ }
+
+ MuxImageInit(&wpi);
+ err = SetAlphaAndImageChunks(bitstream, copy_data, &wpi);
+ if (err != WEBP_MUX_OK) goto Err;
+ assert(wpi.img_ != NULL); // As SetAlphaAndImageChunks() was successful.
+
+ {
+ WebPData frame;
+ const uint32_t tag = kChunks[IDX_ANMF].tag;
+ WebPMuxFrameInfo tmp = *info;
+ tmp.x_offset &= ~1; // Snap offsets to even.
+ tmp.y_offset &= ~1;
+ if (tmp.x_offset < 0 || tmp.x_offset >= MAX_POSITION_OFFSET ||
+ tmp.y_offset < 0 || tmp.y_offset >= MAX_POSITION_OFFSET ||
+ (tmp.duration < 0 || tmp.duration >= MAX_DURATION) ||
+ tmp.dispose_method != (tmp.dispose_method & 1)) {
+ err = WEBP_MUX_INVALID_ARGUMENT;
+ goto Err;
+ }
+ err = CreateFrameData(wpi.width_, wpi.height_, &tmp, &frame);
+ if (err != WEBP_MUX_OK) goto Err;
+ // Add frame chunk (with copy_data = 1).
+ err = AddDataToChunkList(&frame, 1, tag, &wpi.header_);
+ WebPDataClear(&frame); // frame owned by wpi.header_ now.
+ if (err != WEBP_MUX_OK) goto Err;
+ }
+
+ // Add this WebPMuxImage to mux.
+ err = MuxImagePush(&wpi, &mux->images_);
+ if (err != WEBP_MUX_OK) goto Err;
+
+ // All is well.
+ return WEBP_MUX_OK;
+
+ Err: // Something bad happened.
+ MuxImageRelease(&wpi);
+ return err;
+}
+
+WebPMuxError WebPMuxSetAnimationParams(WebPMux* mux,
+ const WebPMuxAnimParams* params) {
+ WebPMuxError err;
+ uint8_t data[ANIM_CHUNK_SIZE];
+ const WebPData anim = { data, ANIM_CHUNK_SIZE };
+
+ if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+ if (params->loop_count < 0 || params->loop_count >= MAX_LOOP_COUNT) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ // Delete any existing ANIM chunk(s).
+ err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
+ if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
+
+ // Set the animation parameters.
+ PutLE32(data, params->bgcolor);
+ PutLE16(data + 4, params->loop_count);
+ return MuxSet(mux, kChunks[IDX_ANIM].tag, 1, &anim, 1);
+}
+
+WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
+ int width, int height) {
+ WebPMuxError err;
+ if (mux == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ if (width < 0 || height < 0 ||
+ width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ if ((width * height) == 0 && (width | height) != 0) {
+ // one of width / height is zero, but not both -> invalid!
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ // If we already assembled a VP8X chunk, invalidate it.
+ err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
+ if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
+
+ mux->canvas_width_ = width;
+ mux->canvas_height_ = height;
+ return WEBP_MUX_OK;
+}
+
+//------------------------------------------------------------------------------
+// Delete API(s).
+
+WebPMuxError WebPMuxDeleteChunk(WebPMux* mux, const char fourcc[4]) {
+ if (mux == NULL || fourcc == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+ return MuxDeleteAllNamedData(mux, ChunkGetTagFromFourCC(fourcc));
+}
+
+WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) {
+ if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+ return MuxImageDeleteNth(&mux->images_, nth);
+}
+
+//------------------------------------------------------------------------------
+// Assembly of the WebP RIFF file.
+
+static WebPMuxError GetFrameInfo(
+ const WebPChunk* const frame_chunk,
+ int* const x_offset, int* const y_offset, int* const duration) {
+ const WebPData* const data = &frame_chunk->data_;
+ const size_t expected_data_size = ANMF_CHUNK_SIZE;
+ assert(frame_chunk->tag_ == kChunks[IDX_ANMF].tag);
+ assert(frame_chunk != NULL);
+ if (data->size != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT;
+
+ *x_offset = 2 * GetLE24(data->bytes + 0);
+ *y_offset = 2 * GetLE24(data->bytes + 3);
+ *duration = GetLE24(data->bytes + 12);
+ return WEBP_MUX_OK;
+}
+
+static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi,
+ int* const x_offset, int* const y_offset,
+ int* const duration,
+ int* const width, int* const height) {
+ const WebPChunk* const frame_chunk = wpi->header_;
+ WebPMuxError err;
+ assert(wpi != NULL);
+ assert(frame_chunk != NULL);
+
+ // Get offsets and duration from ANMF chunk.
+ err = GetFrameInfo(frame_chunk, x_offset, y_offset, duration);
+ if (err != WEBP_MUX_OK) return err;
+
+ // Get width and height from VP8/VP8L chunk.
+ if (width != NULL) *width = wpi->width_;
+ if (height != NULL) *height = wpi->height_;
+ return WEBP_MUX_OK;
+}
+
+// Returns the tightest dimension for the canvas considering the image list.
+static WebPMuxError GetAdjustedCanvasSize(const WebPMux* const mux,
+ int* const width, int* const height) {
+ WebPMuxImage* wpi = NULL;
+ assert(mux != NULL);
+ assert(width != NULL && height != NULL);
+
+ wpi = mux->images_;
+ assert(wpi != NULL);
+ assert(wpi->img_ != NULL);
+
+ if (wpi->next_ != NULL) {
+ int max_x = 0, max_y = 0;
+ // if we have a chain of wpi's, header_ is necessarily set
+ assert(wpi->header_ != NULL);
+ // Aggregate the bounding box for animation frames.
+ for (; wpi != NULL; wpi = wpi->next_) {
+ int x_offset = 0, y_offset = 0, duration = 0, w = 0, h = 0;
+ const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset,
+ &duration, &w, &h);
+ const int max_x_pos = x_offset + w;
+ const int max_y_pos = y_offset + h;
+ if (err != WEBP_MUX_OK) return err;
+ assert(x_offset < MAX_POSITION_OFFSET);
+ assert(y_offset < MAX_POSITION_OFFSET);
+
+ if (max_x_pos > max_x) max_x = max_x_pos;
+ if (max_y_pos > max_y) max_y = max_y_pos;
+ }
+ *width = max_x;
+ *height = max_y;
+ } else {
+ // For a single image, canvas dimensions are same as image dimensions.
+ *width = wpi->width_;
+ *height = wpi->height_;
+ }
+ return WEBP_MUX_OK;
+}
+
+// VP8X format:
+// Total Size : 10,
+// Flags : 4 bytes,
+// Width : 3 bytes,
+// Height : 3 bytes.
+static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
+ WebPMuxError err = WEBP_MUX_OK;
+ uint32_t flags = 0;
+ int width = 0;
+ int height = 0;
+ uint8_t data[VP8X_CHUNK_SIZE];
+ const WebPData vp8x = { data, VP8X_CHUNK_SIZE };
+ const WebPMuxImage* images = NULL;
+
+ assert(mux != NULL);
+ images = mux->images_; // First image.
+ if (images == NULL || images->img_ == NULL ||
+ images->img_->data_.bytes == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ // If VP8X chunk(s) is(are) already present, remove them (and later add new
+ // VP8X chunk with updated flags).
+ err = MuxDeleteAllNamedData(mux, kChunks[IDX_VP8X].tag);
+ if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
+
+ // Set flags.
+ if (mux->iccp_ != NULL && mux->iccp_->data_.bytes != NULL) {
+ flags |= ICCP_FLAG;
+ }
+ if (mux->exif_ != NULL && mux->exif_->data_.bytes != NULL) {
+ flags |= EXIF_FLAG;
+ }
+ if (mux->xmp_ != NULL && mux->xmp_->data_.bytes != NULL) {
+ flags |= XMP_FLAG;
+ }
+ if (images->header_ != NULL) {
+ if (images->header_->tag_ == kChunks[IDX_ANMF].tag) {
+ // This is an image with animation.
+ flags |= ANIMATION_FLAG;
+ }
+ }
+ if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) {
+ flags |= ALPHA_FLAG; // Some images have an alpha channel.
+ }
+
+ err = GetAdjustedCanvasSize(mux, &width, &height);
+ if (err != WEBP_MUX_OK) return err;
+
+ if (width <= 0 || height <= 0) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ if (mux->canvas_width_ != 0 || mux->canvas_height_ != 0) {
+ if (width > mux->canvas_width_ || height > mux->canvas_height_) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ width = mux->canvas_width_;
+ height = mux->canvas_height_;
+ }
+
+ if (flags == 0 && mux->unknown_ == NULL) {
+ // For simple file format, VP8X chunk should not be added.
+ return WEBP_MUX_OK;
+ }
+
+ if (MuxHasAlpha(images)) {
+ // This means some frames explicitly/implicitly contain alpha.
+ // Note: This 'flags' update must NOT be done for a lossless image
+ // without a VP8X chunk!
+ flags |= ALPHA_FLAG;
+ }
+
+ PutLE32(data + 0, flags); // VP8X chunk flags.
+ PutLE24(data + 4, width - 1); // canvas width.
+ PutLE24(data + 7, height - 1); // canvas height.
+
+ return MuxSet(mux, kChunks[IDX_VP8X].tag, 1, &vp8x, 1);
+}
+
+// Cleans up 'mux' by removing any unnecessary chunks.
+static WebPMuxError MuxCleanup(WebPMux* const mux) {
+ int num_frames;
+ int num_anim_chunks;
+
+ // If we have an image with a single frame, and its rectangle
+ // covers the whole canvas, convert it to a non-animated image
+ // (to avoid writing ANMF chunk unnecessarily).
+ WebPMuxError err = WebPMuxNumChunks(mux, kChunks[IDX_ANMF].id, &num_frames);
+ if (err != WEBP_MUX_OK) return err;
+ if (num_frames == 1) {
+ WebPMuxImage* frame = NULL;
+ err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, &frame);
+ assert(err == WEBP_MUX_OK); // We know that one frame does exist.
+ assert(frame != NULL);
+ if (frame->header_ != NULL &&
+ ((mux->canvas_width_ == 0 && mux->canvas_height_ == 0) ||
+ (frame->width_ == mux->canvas_width_ &&
+ frame->height_ == mux->canvas_height_))) {
+ assert(frame->header_->tag_ == kChunks[IDX_ANMF].tag);
+ ChunkDelete(frame->header_); // Removes ANMF chunk.
+ frame->header_ = NULL;
+ num_frames = 0;
+ }
+ }
+ // Remove ANIM chunk if this is a non-animated image.
+ err = WebPMuxNumChunks(mux, kChunks[IDX_ANIM].id, &num_anim_chunks);
+ if (err != WEBP_MUX_OK) return err;
+ if (num_anim_chunks >= 1 && num_frames == 0) {
+ err = MuxDeleteAllNamedData(mux, kChunks[IDX_ANIM].tag);
+ if (err != WEBP_MUX_OK) return err;
+ }
+ return WEBP_MUX_OK;
+}
+
+// Total size of a list of images.
+static size_t ImageListDiskSize(const WebPMuxImage* wpi_list) {
+ size_t size = 0;
+ while (wpi_list != NULL) {
+ size += MuxImageDiskSize(wpi_list);
+ wpi_list = wpi_list->next_;
+ }
+ return size;
+}
+
+// Write out the given list of images into 'dst'.
+static uint8_t* ImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) {
+ while (wpi_list != NULL) {
+ dst = MuxImageEmit(wpi_list, dst);
+ wpi_list = wpi_list->next_;
+ }
+ return dst;
+}
+
+WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) {
+ size_t size = 0;
+ uint8_t* data = NULL;
+ uint8_t* dst = NULL;
+ WebPMuxError err;
+
+ if (assembled_data == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ // Clean up returned data, in case something goes wrong.
+ memset(assembled_data, 0, sizeof(*assembled_data));
+
+ if (mux == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ // Finalize mux.
+ err = MuxCleanup(mux);
+ if (err != WEBP_MUX_OK) return err;
+ err = CreateVP8XChunk(mux);
+ if (err != WEBP_MUX_OK) return err;
+
+ // Allocate data.
+ size = ChunkListDiskSize(mux->vp8x_) + ChunkListDiskSize(mux->iccp_)
+ + ChunkListDiskSize(mux->anim_) + ImageListDiskSize(mux->images_)
+ + ChunkListDiskSize(mux->exif_) + ChunkListDiskSize(mux->xmp_)
+ + ChunkListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE;
+
+ data = (uint8_t*)WebPSafeMalloc(1ULL, size);
+ if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
+
+ // Emit header & chunks.
+ dst = MuxEmitRiffHeader(data, size);
+ dst = ChunkListEmit(mux->vp8x_, dst);
+ dst = ChunkListEmit(mux->iccp_, dst);
+ dst = ChunkListEmit(mux->anim_, dst);
+ dst = ImageListEmit(mux->images_, dst);
+ dst = ChunkListEmit(mux->exif_, dst);
+ dst = ChunkListEmit(mux->xmp_, dst);
+ dst = ChunkListEmit(mux->unknown_, dst);
+ assert(dst == data + size);
+
+ // Validate mux.
+ err = MuxValidate(mux);
+ if (err != WEBP_MUX_OK) {
+ WebPSafeFree(data);
+ data = NULL;
+ size = 0;
+ }
+
+ // Finalize data.
+ assembled_data->bytes = data;
+ assembled_data->size = size;
+
+ return err;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/mux/muxi.h b/src/third_party/libwebp/src/mux/muxi.h
new file mode 100644
index 0000000..f1e1e97
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/muxi.h
@@ -0,0 +1,230 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Internal header for mux library.
+//
+// Author: Urvang (urvang@google.com)
+
+#ifndef WEBP_MUX_MUXI_H_
+#define WEBP_MUX_MUXI_H_
+
+#include <stdlib.h>
+#include "src/dec/vp8i_dec.h"
+#include "src/dec/vp8li_dec.h"
+#include "src/webp/mux.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Defines and constants.
+
+#define MUX_MAJ_VERSION 1
+#define MUX_MIN_VERSION 0
+#define MUX_REV_VERSION 0
+
+// Chunk object.
+typedef struct WebPChunk WebPChunk;
+struct WebPChunk {
+ uint32_t tag_;
+ int owner_; // True if *data_ memory is owned internally.
+ // VP8X, ANIM, and other internally created chunks
+ // like ANMF are always owned.
+ WebPData data_;
+ WebPChunk* next_;
+};
+
+// MuxImage object. Store a full WebP image (including ANMF chunk, ALPH
+// chunk and VP8/VP8L chunk),
+typedef struct WebPMuxImage WebPMuxImage;
+struct WebPMuxImage {
+ WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF.
+ WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA.
+ WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE.
+ WebPChunk* unknown_; // Corresponds to WEBP_CHUNK_UNKNOWN.
+ int width_;
+ int height_;
+ int has_alpha_; // Through ALPH chunk or as part of VP8L.
+ int is_partial_; // True if only some of the chunks are filled.
+ WebPMuxImage* next_;
+};
+
+// Main mux object. Stores data chunks.
+struct WebPMux {
+ WebPMuxImage* images_;
+ WebPChunk* iccp_;
+ WebPChunk* exif_;
+ WebPChunk* xmp_;
+ WebPChunk* anim_;
+ WebPChunk* vp8x_;
+
+ WebPChunk* unknown_;
+ int canvas_width_;
+ int canvas_height_;
+};
+
+// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only.
+// Note: the reason for having two enums ('WebPChunkId' and 'CHUNK_INDEX') is to
+// allow two different chunks to have the same id (e.g. WebPChunkId
+// 'WEBP_CHUNK_IMAGE' can correspond to CHUNK_INDEX 'IDX_VP8' or 'IDX_VP8L').
+typedef enum {
+ IDX_VP8X = 0,
+ IDX_ICCP,
+ IDX_ANIM,
+ IDX_ANMF,
+ IDX_ALPHA,
+ IDX_VP8,
+ IDX_VP8L,
+ IDX_EXIF,
+ IDX_XMP,
+ IDX_UNKNOWN,
+
+ IDX_NIL,
+ IDX_LAST_CHUNK
+} CHUNK_INDEX;
+
+#define NIL_TAG 0x00000000u // To signal void chunk.
+
+typedef struct {
+ uint32_t tag;
+ WebPChunkId id;
+ uint32_t size;
+} ChunkInfo;
+
+extern const ChunkInfo kChunks[IDX_LAST_CHUNK];
+
+//------------------------------------------------------------------------------
+// Chunk object management.
+
+// Initialize.
+void ChunkInit(WebPChunk* const chunk);
+
+// Get chunk index from chunk tag. Returns IDX_UNKNOWN if not found.
+CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag);
+
+// Get chunk id from chunk tag. Returns WEBP_CHUNK_UNKNOWN if not found.
+WebPChunkId ChunkGetIdFromTag(uint32_t tag);
+
+// Convert a fourcc string to a tag.
+uint32_t ChunkGetTagFromFourCC(const char fourcc[4]);
+
+// Get chunk index from fourcc. Returns IDX_UNKNOWN if given fourcc is unknown.
+CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]);
+
+// Search for nth chunk with given 'tag' in the chunk list.
+// nth = 0 means "last of the list".
+WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag);
+
+// Fill the chunk with the given data.
+WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
+ int copy_data, uint32_t tag);
+
+// Sets 'chunk' at nth position in the 'chunk_list'.
+// nth = 0 has the special meaning "last of the list".
+// On success ownership is transferred from 'chunk' to the 'chunk_list'.
+WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
+ uint32_t nth);
+
+// Releases chunk and returns chunk->next_.
+WebPChunk* ChunkRelease(WebPChunk* const chunk);
+
+// Deletes given chunk & returns chunk->next_.
+WebPChunk* ChunkDelete(WebPChunk* const chunk);
+
+// Deletes all chunks in the given chunk list.
+void ChunkListDelete(WebPChunk** const chunk_list);
+
+// Returns size of the chunk including chunk header and padding byte (if any).
+static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
+ return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
+}
+
+// Size of a chunk including header and padding.
+static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) {
+ const size_t data_size = chunk->data_.size;
+ SB_DCHECK(data_size < MAX_CHUNK_PAYLOAD);
+ return SizeWithPadding(data_size);
+}
+
+// Total size of a list of chunks.
+size_t ChunkListDiskSize(const WebPChunk* chunk_list);
+
+// Write out the given list of chunks into 'dst'.
+uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst);
+
+//------------------------------------------------------------------------------
+// MuxImage object management.
+
+// Initialize.
+void MuxImageInit(WebPMuxImage* const wpi);
+
+// Releases image 'wpi' and returns wpi->next.
+WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi);
+
+// Delete image 'wpi' and return the next image in the list or NULL.
+// 'wpi' can be NULL.
+WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi);
+
+// Count number of images matching the given tag id in the 'wpi_list'.
+// If id == WEBP_CHUNK_NIL, all images will be matched.
+int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id);
+
+// Update width/height/has_alpha info from chunks within wpi.
+// Also remove ALPH chunk if not needed.
+int MuxImageFinalize(WebPMuxImage* const wpi);
+
+// Check if given ID corresponds to an image related chunk.
+static WEBP_INLINE int IsWPI(WebPChunkId id) {
+ switch (id) {
+ case WEBP_CHUNK_ANMF:
+ case WEBP_CHUNK_ALPHA:
+ case WEBP_CHUNK_IMAGE: return 1;
+ default: return 0;
+ }
+}
+
+// Pushes 'wpi' at the end of 'wpi_list'.
+WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list);
+
+// Delete nth image in the image list.
+WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth);
+
+// Get nth image in the image list.
+WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
+ WebPMuxImage** wpi);
+
+// Total size of the given image.
+size_t MuxImageDiskSize(const WebPMuxImage* const wpi);
+
+// Write out the given image into 'dst'.
+uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst);
+
+//------------------------------------------------------------------------------
+// Helper methods for mux.
+
+// Checks if the given image list contains at least one image with alpha.
+int MuxHasAlpha(const WebPMuxImage* images);
+
+// Write out RIFF header into 'data', given total data size 'size'.
+uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size);
+
+// Returns the list where chunk with given ID is to be inserted in mux.
+WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id);
+
+// Validates the given mux object.
+WebPMuxError MuxValidate(const WebPMux* const mux);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_MUX_MUXI_H_ */
diff --git a/src/third_party/libwebp/src/mux/muxinternal.c b/src/third_party/libwebp/src/mux/muxinternal.c
new file mode 100644
index 0000000..941222c
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/muxinternal.c
@@ -0,0 +1,556 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Internal objects and utils for mux.
+//
+// Authors: Urvang (urvang@google.com)
+// Vikas (vikasa@google.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#endif
+#include "src/mux/muxi.h"
+#include "src/utils/utils.h"
+
+#define UNDEFINED_CHUNK_SIZE ((uint32_t)(-1))
+
+const ChunkInfo kChunks[] = {
+ { MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
+ { MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE },
+ { MKFOURCC('A', 'N', 'I', 'M'), WEBP_CHUNK_ANIM, ANIM_CHUNK_SIZE },
+ { MKFOURCC('A', 'N', 'M', 'F'), WEBP_CHUNK_ANMF, ANMF_CHUNK_SIZE },
+ { MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE },
+ { MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
+ { MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
+ { MKFOURCC('E', 'X', 'I', 'F'), WEBP_CHUNK_EXIF, UNDEFINED_CHUNK_SIZE },
+ { MKFOURCC('X', 'M', 'P', ' '), WEBP_CHUNK_XMP, UNDEFINED_CHUNK_SIZE },
+ { NIL_TAG, WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE },
+
+ { NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE }
+};
+
+//------------------------------------------------------------------------------
+
+int WebPGetMuxVersion(void) {
+ return (MUX_MAJ_VERSION << 16) | (MUX_MIN_VERSION << 8) | MUX_REV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+// Life of a chunk object.
+
+void ChunkInit(WebPChunk* const chunk) {
+ assert(chunk);
+ memset(chunk, 0, sizeof(*chunk));
+ chunk->tag_ = NIL_TAG;
+}
+
+WebPChunk* ChunkRelease(WebPChunk* const chunk) {
+ WebPChunk* next;
+ if (chunk == NULL) return NULL;
+ if (chunk->owner_) {
+ WebPDataClear(&chunk->data_);
+ }
+ next = chunk->next_;
+ ChunkInit(chunk);
+ return next;
+}
+
+//------------------------------------------------------------------------------
+// Chunk misc methods.
+
+CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag) {
+ int i;
+ for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
+ if (tag == kChunks[i].tag) return (CHUNK_INDEX)i;
+ }
+ return IDX_UNKNOWN;
+}
+
+WebPChunkId ChunkGetIdFromTag(uint32_t tag) {
+ int i;
+ for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
+ if (tag == kChunks[i].tag) return kChunks[i].id;
+ }
+ return WEBP_CHUNK_UNKNOWN;
+}
+
+uint32_t ChunkGetTagFromFourCC(const char fourcc[4]) {
+ return MKFOURCC(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
+}
+
+CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]) {
+ const uint32_t tag = ChunkGetTagFromFourCC(fourcc);
+ return ChunkGetIndexFromTag(tag);
+}
+
+//------------------------------------------------------------------------------
+// Chunk search methods.
+
+// Returns next chunk in the chunk list with the given tag.
+static WebPChunk* ChunkSearchNextInList(WebPChunk* chunk, uint32_t tag) {
+ while (chunk != NULL && chunk->tag_ != tag) {
+ chunk = chunk->next_;
+ }
+ return chunk;
+}
+
+WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) {
+ uint32_t iter = nth;
+ first = ChunkSearchNextInList(first, tag);
+ if (first == NULL) return NULL;
+
+ while (--iter != 0) {
+ WebPChunk* next_chunk = ChunkSearchNextInList(first->next_, tag);
+ if (next_chunk == NULL) break;
+ first = next_chunk;
+ }
+ return ((nth > 0) && (iter > 0)) ? NULL : first;
+}
+
+// Outputs a pointer to 'prev_chunk->next_',
+// where 'prev_chunk' is the pointer to the chunk at position (nth - 1).
+// Returns true if nth chunk was found.
+static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth,
+ WebPChunk*** const location) {
+ uint32_t count = 0;
+ assert(chunk_list != NULL);
+ *location = chunk_list;
+
+ while (*chunk_list != NULL) {
+ WebPChunk* const cur_chunk = *chunk_list;
+ ++count;
+ if (count == nth) return 1; // Found.
+ chunk_list = &cur_chunk->next_;
+ *location = chunk_list;
+ }
+
+ // *chunk_list is ok to be NULL if adding at last location.
+ return (nth == 0 || (count == nth - 1)) ? 1 : 0;
+}
+
+//------------------------------------------------------------------------------
+// Chunk writer methods.
+
+WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
+ int copy_data, uint32_t tag) {
+ // For internally allocated chunks, always copy data & make it owner of data.
+ if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_ANIM].tag) {
+ copy_data = 1;
+ }
+
+ ChunkRelease(chunk);
+
+ if (data != NULL) {
+ if (copy_data) { // Copy data.
+ if (!WebPDataCopy(data, &chunk->data_)) return WEBP_MUX_MEMORY_ERROR;
+ chunk->owner_ = 1; // Chunk is owner of data.
+ } else { // Don't copy data.
+ chunk->data_ = *data;
+ }
+ }
+ chunk->tag_ = tag;
+ return WEBP_MUX_OK;
+}
+
+WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
+ uint32_t nth) {
+ WebPChunk* new_chunk;
+
+ if (!ChunkSearchListToSet(chunk_list, nth, &chunk_list)) {
+ return WEBP_MUX_NOT_FOUND;
+ }
+
+ new_chunk = (WebPChunk*)WebPSafeMalloc(1ULL, sizeof(*new_chunk));
+ if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR;
+ *new_chunk = *chunk;
+ chunk->owner_ = 0;
+ new_chunk->next_ = *chunk_list;
+ *chunk_list = new_chunk;
+ return WEBP_MUX_OK;
+}
+
+//------------------------------------------------------------------------------
+// Chunk deletion method(s).
+
+WebPChunk* ChunkDelete(WebPChunk* const chunk) {
+ WebPChunk* const next = ChunkRelease(chunk);
+ WebPSafeFree(chunk);
+ return next;
+}
+
+void ChunkListDelete(WebPChunk** const chunk_list) {
+ while (*chunk_list != NULL) {
+ *chunk_list = ChunkDelete(*chunk_list);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Chunk serialization methods.
+
+static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) {
+ const size_t chunk_size = chunk->data_.size;
+ assert(chunk);
+ assert(chunk->tag_ != NIL_TAG);
+ PutLE32(dst + 0, chunk->tag_);
+ PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size);
+ assert(chunk_size == (uint32_t)chunk_size);
+ memcpy(dst + CHUNK_HEADER_SIZE, chunk->data_.bytes, chunk_size);
+ if (chunk_size & 1)
+ dst[CHUNK_HEADER_SIZE + chunk_size] = 0; // Add padding.
+ return dst + ChunkDiskSize(chunk);
+}
+
+uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) {
+ while (chunk_list != NULL) {
+ dst = ChunkEmit(chunk_list, dst);
+ chunk_list = chunk_list->next_;
+ }
+ return dst;
+}
+
+size_t ChunkListDiskSize(const WebPChunk* chunk_list) {
+ size_t size = 0;
+ while (chunk_list != NULL) {
+ size += ChunkDiskSize(chunk_list);
+ chunk_list = chunk_list->next_;
+ }
+ return size;
+}
+
+//------------------------------------------------------------------------------
+// Life of a MuxImage object.
+
+void MuxImageInit(WebPMuxImage* const wpi) {
+ assert(wpi);
+ memset(wpi, 0, sizeof(*wpi));
+}
+
+WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
+ WebPMuxImage* next;
+ if (wpi == NULL) return NULL;
+ ChunkDelete(wpi->header_);
+ ChunkDelete(wpi->alpha_);
+ ChunkDelete(wpi->img_);
+ ChunkListDelete(&wpi->unknown_);
+
+ next = wpi->next_;
+ MuxImageInit(wpi);
+ return next;
+}
+
+//------------------------------------------------------------------------------
+// MuxImage search methods.
+
+// Get a reference to appropriate chunk list within an image given chunk tag.
+static WebPChunk** GetChunkListFromId(const WebPMuxImage* const wpi,
+ WebPChunkId id) {
+ assert(wpi != NULL);
+ switch (id) {
+ case WEBP_CHUNK_ANMF: return (WebPChunk**)&wpi->header_;
+ case WEBP_CHUNK_ALPHA: return (WebPChunk**)&wpi->alpha_;
+ case WEBP_CHUNK_IMAGE: return (WebPChunk**)&wpi->img_;
+ default: return NULL;
+ }
+}
+
+int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) {
+ int count = 0;
+ const WebPMuxImage* current;
+ for (current = wpi_list; current != NULL; current = current->next_) {
+ if (id == WEBP_CHUNK_NIL) {
+ ++count; // Special case: count all images.
+ } else {
+ const WebPChunk* const wpi_chunk = *GetChunkListFromId(current, id);
+ if (wpi_chunk != NULL) {
+ const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_);
+ if (wpi_chunk_id == id) ++count; // Count images with a matching 'id'.
+ }
+ }
+ }
+ return count;
+}
+
+// Outputs a pointer to 'prev_wpi->next_',
+// where 'prev_wpi' is the pointer to the image at position (nth - 1).
+// Returns true if nth image was found.
+static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth,
+ WebPMuxImage*** const location) {
+ uint32_t count = 0;
+ assert(wpi_list);
+ *location = wpi_list;
+
+ if (nth == 0) {
+ nth = MuxImageCount(*wpi_list, WEBP_CHUNK_NIL);
+ if (nth == 0) return 0; // Not found.
+ }
+
+ while (*wpi_list != NULL) {
+ WebPMuxImage* const cur_wpi = *wpi_list;
+ ++count;
+ if (count == nth) return 1; // Found.
+ wpi_list = &cur_wpi->next_;
+ *location = wpi_list;
+ }
+ return 0; // Not found.
+}
+
+//------------------------------------------------------------------------------
+// MuxImage writer methods.
+
+WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) {
+ WebPMuxImage* new_wpi;
+
+ while (*wpi_list != NULL) {
+ WebPMuxImage* const cur_wpi = *wpi_list;
+ if (cur_wpi->next_ == NULL) break;
+ wpi_list = &cur_wpi->next_;
+ }
+
+ new_wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*new_wpi));
+ if (new_wpi == NULL) return WEBP_MUX_MEMORY_ERROR;
+ *new_wpi = *wpi;
+ new_wpi->next_ = NULL;
+
+ if (*wpi_list != NULL) {
+ (*wpi_list)->next_ = new_wpi;
+ } else {
+ *wpi_list = new_wpi;
+ }
+ return WEBP_MUX_OK;
+}
+
+//------------------------------------------------------------------------------
+// MuxImage deletion methods.
+
+WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi) {
+ // Delete the components of wpi. If wpi is NULL this is a noop.
+ WebPMuxImage* const next = MuxImageRelease(wpi);
+ WebPSafeFree(wpi);
+ return next;
+}
+
+WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth) {
+ assert(wpi_list);
+ if (!SearchImageToGetOrDelete(wpi_list, nth, &wpi_list)) {
+ return WEBP_MUX_NOT_FOUND;
+ }
+ *wpi_list = MuxImageDelete(*wpi_list);
+ return WEBP_MUX_OK;
+}
+
+//------------------------------------------------------------------------------
+// MuxImage reader methods.
+
+WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
+ WebPMuxImage** wpi) {
+ assert(wpi_list);
+ assert(wpi);
+ if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth,
+ (WebPMuxImage***)&wpi_list)) {
+ return WEBP_MUX_NOT_FOUND;
+ }
+ *wpi = (WebPMuxImage*)*wpi_list;
+ return WEBP_MUX_OK;
+}
+
+//------------------------------------------------------------------------------
+// MuxImage serialization methods.
+
+// Size of an image.
+size_t MuxImageDiskSize(const WebPMuxImage* const wpi) {
+ size_t size = 0;
+ if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_);
+ if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_);
+ if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_);
+ if (wpi->unknown_ != NULL) size += ChunkListDiskSize(wpi->unknown_);
+ return size;
+}
+
+// Special case as ANMF chunk encapsulates other image chunks.
+static uint8_t* ChunkEmitSpecial(const WebPChunk* const header,
+ size_t total_size, uint8_t* dst) {
+ const size_t header_size = header->data_.size;
+ const size_t offset_to_next = total_size - CHUNK_HEADER_SIZE;
+ assert(header->tag_ == kChunks[IDX_ANMF].tag);
+ PutLE32(dst + 0, header->tag_);
+ PutLE32(dst + TAG_SIZE, (uint32_t)offset_to_next);
+ assert(header_size == (uint32_t)header_size);
+ memcpy(dst + CHUNK_HEADER_SIZE, header->data_.bytes, header_size);
+ if (header_size & 1) {
+ dst[CHUNK_HEADER_SIZE + header_size] = 0; // Add padding.
+ }
+ return dst + ChunkDiskSize(header);
+}
+
+uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
+ // Ordering of chunks to be emitted is strictly as follows:
+ // 1. ANMF chunk (if present).
+ // 2. ALPH chunk (if present).
+ // 3. VP8/VP8L chunk.
+ assert(wpi);
+ if (wpi->header_ != NULL) {
+ dst = ChunkEmitSpecial(wpi->header_, MuxImageDiskSize(wpi), dst);
+ }
+ if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst);
+ if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst);
+ if (wpi->unknown_ != NULL) dst = ChunkListEmit(wpi->unknown_, dst);
+ return dst;
+}
+
+//------------------------------------------------------------------------------
+// Helper methods for mux.
+
+int MuxHasAlpha(const WebPMuxImage* images) {
+ while (images != NULL) {
+ if (images->has_alpha_) return 1;
+ images = images->next_;
+ }
+ return 0;
+}
+
+uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) {
+ PutLE32(data + 0, MKFOURCC('R', 'I', 'F', 'F'));
+ PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE);
+ assert(size == (uint32_t)size);
+ PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, MKFOURCC('W', 'E', 'B', 'P'));
+ return data + RIFF_HEADER_SIZE;
+}
+
+WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) {
+ assert(mux != NULL);
+ switch (id) {
+ case WEBP_CHUNK_VP8X: return (WebPChunk**)&mux->vp8x_;
+ case WEBP_CHUNK_ICCP: return (WebPChunk**)&mux->iccp_;
+ case WEBP_CHUNK_ANIM: return (WebPChunk**)&mux->anim_;
+ case WEBP_CHUNK_EXIF: return (WebPChunk**)&mux->exif_;
+ case WEBP_CHUNK_XMP: return (WebPChunk**)&mux->xmp_;
+ default: return (WebPChunk**)&mux->unknown_;
+ }
+}
+
+static int IsNotCompatible(int feature, int num_items) {
+ return (feature != 0) != (num_items > 0);
+}
+
+#define NO_FLAG ((WebPFeatureFlags)0)
+
+// Test basic constraints:
+// retrieval, maximum number of chunks by index (use -1 to skip)
+// and feature incompatibility (use NO_FLAG to skip).
+// On success returns WEBP_MUX_OK and stores the chunk count in *num.
+static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
+ WebPFeatureFlags feature,
+ uint32_t vp8x_flags,
+ int max, int* num) {
+ const WebPMuxError err =
+ WebPMuxNumChunks(mux, kChunks[idx].id, num);
+ if (err != WEBP_MUX_OK) return err;
+ if (max > -1 && *num > max) return WEBP_MUX_INVALID_ARGUMENT;
+ if (feature != NO_FLAG && IsNotCompatible(vp8x_flags & feature, *num)) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ return WEBP_MUX_OK;
+}
+
+WebPMuxError MuxValidate(const WebPMux* const mux) {
+ int num_iccp;
+ int num_exif;
+ int num_xmp;
+ int num_anim;
+ int num_frames;
+ int num_vp8x;
+ int num_images;
+ int num_alpha;
+ uint32_t flags;
+ WebPMuxError err;
+
+ // Verify mux is not NULL.
+ if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+
+ // Verify mux has at least one image.
+ if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+
+ err = WebPMuxGetFeatures(mux, &flags);
+ if (err != WEBP_MUX_OK) return err;
+
+ // At most one color profile chunk.
+ err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp);
+ if (err != WEBP_MUX_OK) return err;
+
+ // At most one EXIF metadata.
+ err = ValidateChunk(mux, IDX_EXIF, EXIF_FLAG, flags, 1, &num_exif);
+ if (err != WEBP_MUX_OK) return err;
+
+ // At most one XMP metadata.
+ err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp);
+ if (err != WEBP_MUX_OK) return err;
+
+ // Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent.
+ // At most one ANIM chunk.
+ err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim);
+ if (err != WEBP_MUX_OK) return err;
+ err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames);
+ if (err != WEBP_MUX_OK) return err;
+
+ {
+ const int has_animation = !!(flags & ANIMATION_FLAG);
+ if (has_animation && (num_anim == 0 || num_frames == 0)) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ if (!has_animation && (num_anim == 1 || num_frames > 0)) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ if (!has_animation) {
+ const WebPMuxImage* images = mux->images_;
+ // There can be only one image.
+ if (images == NULL || images->next_ != NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ // Size must match.
+ if (mux->canvas_width_ > 0) {
+ if (images->width_ != mux->canvas_width_ ||
+ images->height_ != mux->canvas_height_) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ }
+ }
+ }
+
+ // Verify either VP8X chunk is present OR there is only one elem in
+ // mux->images_.
+ err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x);
+ if (err != WEBP_MUX_OK) return err;
+ err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images);
+ if (err != WEBP_MUX_OK) return err;
+ if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;
+
+ // ALPHA_FLAG & alpha chunk(s) are consistent.
+ // Note: ALPHA_FLAG can be set when there is actually no Alpha data present.
+ if (MuxHasAlpha(mux->images_)) {
+ if (num_vp8x > 0) {
+ // VP8X chunk is present, so it should contain ALPHA_FLAG.
+ if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
+ } else {
+ // VP8X chunk is not present, so ALPH chunks should NOT be present either.
+ err = WebPMuxNumChunks(mux, WEBP_CHUNK_ALPHA, &num_alpha);
+ if (err != WEBP_MUX_OK) return err;
+ if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ }
+
+ return WEBP_MUX_OK;
+}
+
+#undef NO_FLAG
+
+//------------------------------------------------------------------------------
+
diff --git a/src/third_party/libwebp/src/mux/muxread.c b/src/third_party/libwebp/src/mux/muxread.c
new file mode 100644
index 0000000..0b55286
--- /dev/null
+++ b/src/third_party/libwebp/src/mux/muxread.c
@@ -0,0 +1,539 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Read APIs for mux.
+//
+// Authors: Urvang (urvang@google.com)
+// Vikas (vikasa@google.com)
+
+#include <assert.h>
+#include "src/mux/muxi.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// Helper method(s).
+
+// Handy MACRO.
+#define SWITCH_ID_LIST(INDEX, LIST) \
+ if (idx == (INDEX)) { \
+ const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \
+ kChunks[(INDEX)].tag); \
+ if (chunk) { \
+ *data = chunk->data_; \
+ return WEBP_MUX_OK; \
+ } else { \
+ return WEBP_MUX_NOT_FOUND; \
+ } \
+ }
+
+static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
+ uint32_t nth, WebPData* const data) {
+ assert(mux != NULL);
+ assert(!IsWPI(kChunks[idx].id));
+ WebPDataInit(data);
+
+ SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_);
+ SWITCH_ID_LIST(IDX_ICCP, mux->iccp_);
+ SWITCH_ID_LIST(IDX_ANIM, mux->anim_);
+ SWITCH_ID_LIST(IDX_EXIF, mux->exif_);
+ SWITCH_ID_LIST(IDX_XMP, mux->xmp_);
+ assert(idx != IDX_UNKNOWN);
+ return WEBP_MUX_NOT_FOUND;
+}
+#undef SWITCH_ID_LIST
+
+// Fill the chunk with the given data (includes chunk header bytes), after some
+// verifications.
+static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
+ const uint8_t* data, size_t data_size,
+ size_t riff_size, int copy_data) {
+ uint32_t chunk_size;
+ WebPData chunk_data;
+
+ // Sanity checks.
+ if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
+ chunk_size = GetLE32(data + TAG_SIZE);
+
+ {
+ const size_t chunk_disk_size = SizeWithPadding(chunk_size);
+ if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA;
+ if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA;
+ }
+
+ // Data assignment.
+ chunk_data.bytes = data + CHUNK_HEADER_SIZE;
+ chunk_data.size = chunk_size;
+ return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
+}
+
+int MuxImageFinalize(WebPMuxImage* const wpi) {
+ const WebPChunk* const img = wpi->img_;
+ const WebPData* const image = &img->data_;
+ const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag);
+ int w, h;
+ int vp8l_has_alpha = 0;
+ const int ok = is_lossless ?
+ VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) :
+ VP8GetInfo(image->bytes, image->size, image->size, &w, &h);
+ assert(img != NULL);
+ if (ok) {
+ // Ignore ALPH chunk accompanying VP8L.
+ if (is_lossless && (wpi->alpha_ != NULL)) {
+ ChunkDelete(wpi->alpha_);
+ wpi->alpha_ = NULL;
+ }
+ wpi->width_ = w;
+ wpi->height_ = h;
+ wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL);
+ }
+ return ok;
+}
+
+static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
+ WebPMuxImage* const wpi) {
+ const uint8_t* bytes = chunk->data_.bytes;
+ size_t size = chunk->data_.size;
+ const uint8_t* const last = bytes + size;
+ WebPChunk subchunk;
+ size_t subchunk_size;
+ ChunkInit(&subchunk);
+
+ assert(chunk->tag_ == kChunks[IDX_ANMF].tag);
+ assert(!wpi->is_partial_);
+
+ // ANMF.
+ {
+ const size_t hdr_size = ANMF_CHUNK_SIZE;
+ const WebPData temp = { bytes, hdr_size };
+ // Each of ANMF chunk contain a header at the beginning. So, its size should
+ // be at least 'hdr_size'.
+ if (size < hdr_size) goto Fail;
+ ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
+ }
+ ChunkSetNth(&subchunk, &wpi->header_, 1);
+ wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
+
+ // Rest of the chunks.
+ subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE;
+ bytes += subchunk_size;
+ size -= subchunk_size;
+
+ while (bytes != last) {
+ ChunkInit(&subchunk);
+ if (ChunkVerifyAndAssign(&subchunk, bytes, size, size,
+ copy_data) != WEBP_MUX_OK) {
+ goto Fail;
+ }
+ switch (ChunkGetIdFromTag(subchunk.tag_)) {
+ case WEBP_CHUNK_ALPHA:
+ if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks.
+ if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail;
+ wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
+ break;
+ case WEBP_CHUNK_IMAGE:
+ if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
+ if (!MuxImageFinalize(wpi)) goto Fail;
+ wpi->is_partial_ = 0; // wpi is completely filled.
+ break;
+ case WEBP_CHUNK_UNKNOWN:
+ if (wpi->is_partial_) goto Fail; // Encountered an unknown chunk
+ // before some image chunks.
+ if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail;
+ break;
+ default:
+ goto Fail;
+ break;
+ }
+ subchunk_size = ChunkDiskSize(&subchunk);
+ bytes += subchunk_size;
+ size -= subchunk_size;
+ }
+ if (wpi->is_partial_) goto Fail;
+ return 1;
+
+ Fail:
+ ChunkRelease(&subchunk);
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// Create a mux object from WebP-RIFF data.
+
+WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
+ int version) {
+ size_t riff_size;
+ uint32_t tag;
+ const uint8_t* end;
+ WebPMux* mux = NULL;
+ WebPMuxImage* wpi = NULL;
+ const uint8_t* data;
+ size_t size;
+ WebPChunk chunk;
+ ChunkInit(&chunk);
+
+ // Sanity checks.
+ if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) {
+ return NULL; // version mismatch
+ }
+ if (bitstream == NULL) return NULL;
+
+ data = bitstream->bytes;
+ size = bitstream->size;
+
+ if (data == NULL) return NULL;
+ if (size < RIFF_HEADER_SIZE) return NULL;
+ if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') ||
+ GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) {
+ return NULL;
+ }
+
+ mux = WebPMuxNew();
+ if (mux == NULL) return NULL;
+
+ if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err;
+
+ tag = GetLE32(data + RIFF_HEADER_SIZE);
+ if (tag != kChunks[IDX_VP8].tag &&
+ tag != kChunks[IDX_VP8L].tag &&
+ tag != kChunks[IDX_VP8X].tag) {
+ goto Err; // First chunk should be VP8, VP8L or VP8X.
+ }
+
+ riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE));
+ if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) {
+ goto Err;
+ } else {
+ if (riff_size < size) { // Redundant data after last chunk.
+ size = riff_size; // To make sure we don't read any data beyond mux_size.
+ }
+ }
+
+ end = data + size;
+ data += RIFF_HEADER_SIZE;
+ size -= RIFF_HEADER_SIZE;
+
+ wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*wpi));
+ if (wpi == NULL) goto Err;
+ MuxImageInit(wpi);
+
+ // Loop over chunks.
+ while (data != end) {
+ size_t data_size;
+ WebPChunkId id;
+ WebPChunk** chunk_list;
+ if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
+ copy_data) != WEBP_MUX_OK) {
+ goto Err;
+ }
+ data_size = ChunkDiskSize(&chunk);
+ id = ChunkGetIdFromTag(chunk.tag_);
+ switch (id) {
+ case WEBP_CHUNK_ALPHA:
+ if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks.
+ if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err;
+ wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
+ break;
+ case WEBP_CHUNK_IMAGE:
+ if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
+ if (!MuxImageFinalize(wpi)) goto Err;
+ wpi->is_partial_ = 0; // wpi is completely filled.
+ PushImage:
+ // Add this to mux->images_ list.
+ if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err;
+ MuxImageInit(wpi); // Reset for reading next image.
+ break;
+ case WEBP_CHUNK_ANMF:
+ if (wpi->is_partial_) goto Err; // Previous wpi is still incomplete.
+ if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err;
+ ChunkRelease(&chunk);
+ goto PushImage;
+ break;
+ default: // A non-image chunk.
+ if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
+ // getting all chunks of an image.
+ chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
+ if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
+ if (id == WEBP_CHUNK_VP8X) { // grab global specs
+ mux->canvas_width_ = GetLE24(data + 12) + 1;
+ mux->canvas_height_ = GetLE24(data + 15) + 1;
+ }
+ break;
+ }
+ data += data_size;
+ size -= data_size;
+ ChunkInit(&chunk);
+ }
+
+ // Incomplete image.
+ if (wpi->is_partial_) goto Err;
+
+ // Validate mux if complete.
+ if (MuxValidate(mux) != WEBP_MUX_OK) goto Err;
+
+ MuxImageDelete(wpi);
+ return mux; // All OK;
+
+ Err: // Something bad happened.
+ ChunkRelease(&chunk);
+ MuxImageDelete(wpi);
+ WebPMuxDelete(mux);
+ return NULL;
+}
+
+//------------------------------------------------------------------------------
+// Get API(s).
+
+// Validates that the given mux has a single image.
+static WebPMuxError ValidateForSingleImage(const WebPMux* const mux) {
+ const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE);
+ const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_ANMF);
+
+ if (num_images == 0) {
+ // No images in mux.
+ return WEBP_MUX_NOT_FOUND;
+ } else if (num_images == 1 && num_frames == 0) {
+ // Valid case (single image).
+ return WEBP_MUX_OK;
+ } else {
+ // Frame case OR an invalid mux.
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+}
+
+// Get the canvas width, height and flags after validating that VP8X/VP8/VP8L
+// chunk and canvas size are valid.
+static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux,
+ int* width, int* height, uint32_t* flags) {
+ int w, h;
+ uint32_t f = 0;
+ WebPData data;
+ assert(mux != NULL);
+
+ // Check if VP8X chunk is present.
+ if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) {
+ if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA;
+ f = GetLE32(data.bytes + 0);
+ w = GetLE24(data.bytes + 4) + 1;
+ h = GetLE24(data.bytes + 7) + 1;
+ } else {
+ const WebPMuxImage* const wpi = mux->images_;
+ // Grab user-forced canvas size as default.
+ w = mux->canvas_width_;
+ h = mux->canvas_height_;
+ if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) {
+ // single image and not forced canvas size => use dimension of first frame
+ assert(wpi != NULL);
+ w = wpi->width_;
+ h = wpi->height_;
+ }
+ if (wpi != NULL) {
+ if (wpi->has_alpha_) f |= ALPHA_FLAG;
+ }
+ }
+ if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
+
+ if (width != NULL) *width = w;
+ if (height != NULL) *height = h;
+ if (flags != NULL) *flags = f;
+ return WEBP_MUX_OK;
+}
+
+WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux, int* width, int* height) {
+ if (mux == NULL || width == NULL || height == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ return MuxGetCanvasInfo(mux, width, height, NULL);
+}
+
+WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) {
+ if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+ return MuxGetCanvasInfo(mux, NULL, NULL, flags);
+}
+
+static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width,
+ int height, uint32_t flags) {
+ const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
+ assert(width >= 1 && height >= 1);
+ assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE);
+ assert(width * (uint64_t)height < MAX_IMAGE_AREA);
+ PutLE32(dst, MKFOURCC('V', 'P', '8', 'X'));
+ PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE);
+ PutLE32(dst + CHUNK_HEADER_SIZE, flags);
+ PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1);
+ PutLE24(dst + CHUNK_HEADER_SIZE + 7, height - 1);
+ return dst + vp8x_size;
+}
+
+// Assemble a single image WebP bitstream from 'wpi'.
+static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi,
+ WebPData* const bitstream) {
+ uint8_t* dst;
+
+ // Allocate data.
+ const int need_vp8x = (wpi->alpha_ != NULL);
+ const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0;
+ const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0;
+ // Note: No need to output ANMF chunk for a single image.
+ const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size +
+ ChunkDiskSize(wpi->img_);
+ uint8_t* const data = (uint8_t*)WebPSafeMalloc(1ULL, size);
+ if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
+
+ // Main RIFF header.
+ dst = MuxEmitRiffHeader(data, size);
+
+ if (need_vp8x) {
+ dst = EmitVP8XChunk(dst, wpi->width_, wpi->height_, ALPHA_FLAG); // VP8X.
+ dst = ChunkListEmit(wpi->alpha_, dst); // ALPH.
+ }
+
+ // Bitstream.
+ dst = ChunkListEmit(wpi->img_, dst);
+ assert(dst == data + size);
+
+ // Output.
+ bitstream->bytes = data;
+ bitstream->size = size;
+ return WEBP_MUX_OK;
+}
+
+WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4],
+ WebPData* chunk_data) {
+ CHUNK_INDEX idx;
+ if (mux == NULL || fourcc == NULL || chunk_data == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+ idx = ChunkGetIndexFromFourCC(fourcc);
+ if (IsWPI(kChunks[idx].id)) { // An image chunk.
+ return WEBP_MUX_INVALID_ARGUMENT;
+ } else if (idx != IDX_UNKNOWN) { // A known chunk type.
+ return MuxGet(mux, idx, 1, chunk_data);
+ } else { // An unknown chunk type.
+ const WebPChunk* const chunk =
+ ChunkSearchList(mux->unknown_, 1, ChunkGetTagFromFourCC(fourcc));
+ if (chunk == NULL) return WEBP_MUX_NOT_FOUND;
+ *chunk_data = chunk->data_;
+ return WEBP_MUX_OK;
+ }
+}
+
+static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi,
+ WebPMuxFrameInfo* const info) {
+ // Set some defaults for unrelated fields.
+ info->x_offset = 0;
+ info->y_offset = 0;
+ info->duration = 1;
+ info->dispose_method = WEBP_MUX_DISPOSE_NONE;
+ info->blend_method = WEBP_MUX_BLEND;
+ // Extract data for related fields.
+ info->id = ChunkGetIdFromTag(wpi->img_->tag_);
+ return SynthesizeBitstream(wpi, &info->bitstream);
+}
+
+static WebPMuxError MuxGetFrameInternal(const WebPMuxImage* const wpi,
+ WebPMuxFrameInfo* const frame) {
+ const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag);
+ const WebPData* frame_data;
+ if (!is_frame) return WEBP_MUX_INVALID_ARGUMENT;
+ assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame().
+ // Get frame chunk.
+ frame_data = &wpi->header_->data_;
+ if (frame_data->size < kChunks[IDX_ANMF].size) return WEBP_MUX_BAD_DATA;
+ // Extract info.
+ frame->x_offset = 2 * GetLE24(frame_data->bytes + 0);
+ frame->y_offset = 2 * GetLE24(frame_data->bytes + 3);
+ {
+ const uint8_t bits = frame_data->bytes[15];
+ frame->duration = GetLE24(frame_data->bytes + 12);
+ frame->dispose_method =
+ (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE;
+ frame->blend_method = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND;
+ }
+ frame->id = ChunkGetIdFromTag(wpi->header_->tag_);
+ return SynthesizeBitstream(wpi, &frame->bitstream);
+}
+
+WebPMuxError WebPMuxGetFrame(
+ const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame) {
+ WebPMuxError err;
+ WebPMuxImage* wpi;
+
+ // Sanity checks.
+ if (mux == NULL || frame == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ // Get the nth WebPMuxImage.
+ err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, &wpi);
+ if (err != WEBP_MUX_OK) return err;
+
+ // Get frame info.
+ if (wpi->header_ == NULL) {
+ return MuxGetImageInternal(wpi, frame);
+ } else {
+ return MuxGetFrameInternal(wpi, frame);
+ }
+}
+
+WebPMuxError WebPMuxGetAnimationParams(const WebPMux* mux,
+ WebPMuxAnimParams* params) {
+ WebPData anim;
+ WebPMuxError err;
+
+ if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
+
+ err = MuxGet(mux, IDX_ANIM, 1, &anim);
+ if (err != WEBP_MUX_OK) return err;
+ if (anim.size < kChunks[WEBP_CHUNK_ANIM].size) return WEBP_MUX_BAD_DATA;
+ params->bgcolor = GetLE32(anim.bytes);
+ params->loop_count = GetLE16(anim.bytes + 4);
+
+ return WEBP_MUX_OK;
+}
+
+// Get chunk index from chunk id. Returns IDX_NIL if not found.
+static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) {
+ int i;
+ for (i = 0; kChunks[i].id != WEBP_CHUNK_NIL; ++i) {
+ if (id == kChunks[i].id) return (CHUNK_INDEX)i;
+ }
+ return IDX_NIL;
+}
+
+// Count number of chunks matching 'tag' in the 'chunk_list'.
+// If tag == NIL_TAG, any tag will be matched.
+static int CountChunks(const WebPChunk* const chunk_list, uint32_t tag) {
+ int count = 0;
+ const WebPChunk* current;
+ for (current = chunk_list; current != NULL; current = current->next_) {
+ if (tag == NIL_TAG || current->tag_ == tag) {
+ count++; // Count chunks whose tags match.
+ }
+ }
+ return count;
+}
+
+WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
+ WebPChunkId id, int* num_elements) {
+ if (mux == NULL || num_elements == NULL) {
+ return WEBP_MUX_INVALID_ARGUMENT;
+ }
+
+ if (IsWPI(id)) {
+ *num_elements = MuxImageCount(mux->images_, id);
+ } else {
+ WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id);
+ const CHUNK_INDEX idx = ChunkGetIndexFromId(id);
+ *num_elements = CountChunks(*chunk_list, kChunks[idx].tag);
+ }
+
+ return WEBP_MUX_OK;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/utils/Makefile.am b/src/third_party/libwebp/src/utils/Makefile.am
new file mode 100644
index 0000000..fbb0fe7
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/Makefile.am
@@ -0,0 +1,51 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES = libwebputils.la
+
+if BUILD_LIBWEBPDECODER
+ noinst_LTLIBRARIES += libwebputilsdecode.la
+endif
+
+common_HEADERS = ../webp/types.h
+commondir = $(includedir)/webp
+
+noinst_HEADERS =
+noinst_HEADERS += ../dsp/dsp.h
+noinst_HEADERS += ../webp/decode.h
+noinst_HEADERS += ../webp/encode.h
+noinst_HEADERS += ../webp/format_constants.h
+
+COMMON_SOURCES =
+COMMON_SOURCES += bit_reader_utils.c
+COMMON_SOURCES += bit_reader_utils.h
+COMMON_SOURCES += bit_reader_inl_utils.h
+COMMON_SOURCES += color_cache_utils.c
+COMMON_SOURCES += color_cache_utils.h
+COMMON_SOURCES += endian_inl_utils.h
+COMMON_SOURCES += filters_utils.c
+COMMON_SOURCES += filters_utils.h
+COMMON_SOURCES += huffman_utils.c
+COMMON_SOURCES += huffman_utils.h
+COMMON_SOURCES += quant_levels_dec_utils.c
+COMMON_SOURCES += quant_levels_dec_utils.h
+COMMON_SOURCES += rescaler_utils.c
+COMMON_SOURCES += rescaler_utils.h
+COMMON_SOURCES += random_utils.c
+COMMON_SOURCES += random_utils.h
+COMMON_SOURCES += thread_utils.c
+COMMON_SOURCES += thread_utils.h
+COMMON_SOURCES += utils.c
+COMMON_SOURCES += utils.h
+
+ENC_SOURCES =
+ENC_SOURCES += bit_writer_utils.c
+ENC_SOURCES += bit_writer_utils.h
+ENC_SOURCES += huffman_encode_utils.c
+ENC_SOURCES += huffman_encode_utils.h
+ENC_SOURCES += quant_levels_utils.c
+ENC_SOURCES += quant_levels_utils.h
+
+libwebputils_la_SOURCES = $(COMMON_SOURCES) $(ENC_SOURCES)
+
+if BUILD_LIBWEBPDECODER
+ libwebputilsdecode_la_SOURCES = $(COMMON_SOURCES)
+endif
diff --git a/src/third_party/libwebp/src/utils/bit_reader_inl_utils.h b/src/third_party/libwebp/src/utils/bit_reader_inl_utils.h
new file mode 100644
index 0000000..5b50a3c
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/bit_reader_inl_utils.h
@@ -0,0 +1,192 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Specific inlined methods for boolean decoder [VP8GetBit() ...]
+// This file should be included by the .c sources that actually need to call
+// these methods.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_UTILS_BIT_READER_INL_UTILS_H_
+#define WEBP_UTILS_BIT_READER_INL_UTILS_H_
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#if !defined(STARBOARD)
+#include <string.h> // for memcpy
+#endif
+
+#include "src/dsp/dsp.h"
+#include "src/utils/bit_reader_utils.h"
+#include "src/utils/endian_inl_utils.h"
+#include "src/utils/utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Derived type lbit_t = natural type for memory I/O
+
+#if (BITS > 32)
+typedef uint64_t lbit_t;
+#elif (BITS > 16)
+typedef uint32_t lbit_t;
+#elif (BITS > 8)
+typedef uint16_t lbit_t;
+#else
+typedef uint8_t lbit_t;
+#endif
+
+extern const uint8_t kVP8Log2Range[128];
+extern const uint8_t kVP8NewRange[128];
+
+// special case for the tail byte-reading
+void VP8LoadFinalBytes(VP8BitReader* const br);
+
+//------------------------------------------------------------------------------
+// Inlined critical functions
+
+// makes sure br->value_ has at least BITS bits worth of data
+static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE
+void VP8LoadNewBytes(VP8BitReader* const br) {
+ SB_DCHECK(br != NULL && br->buf_ != NULL);
+ // Read 'BITS' bits at a time if possible.
+ if (br->buf_ < br->buf_max_) {
+ // convert memory type to register type (with some zero'ing!)
+ bit_t bits;
+#if defined(WEBP_USE_MIPS32)
+ // This is needed because of un-aligned read.
+ lbit_t in_bits;
+ lbit_t* p_buf_ = (lbit_t*)br->buf_;
+ __asm__ volatile(
+ ".set push \n\t"
+ ".set at \n\t"
+ ".set macro \n\t"
+ "ulw %[in_bits], 0(%[p_buf_]) \n\t"
+ ".set pop \n\t"
+ : [in_bits]"=r"(in_bits)
+ : [p_buf_]"r"(p_buf_)
+ : "memory", "at"
+ );
+#else
+ lbit_t in_bits;
+ SbMemoryCopy(&in_bits, br->buf_, sizeof(in_bits));
+#endif
+ br->buf_ += BITS >> 3;
+#if !defined(WORDS_BIGENDIAN)
+#if (BITS > 32)
+ bits = BSwap64(in_bits);
+ bits >>= 64 - BITS;
+#elif (BITS >= 24)
+ bits = BSwap32(in_bits);
+ bits >>= (32 - BITS);
+#elif (BITS == 16)
+ bits = BSwap16(in_bits);
+#else // BITS == 8
+ bits = (bit_t)in_bits;
+#endif // BITS > 32
+#else // WORDS_BIGENDIAN
+ bits = (bit_t)in_bits;
+ if (BITS != 8 * sizeof(bit_t)) bits >>= (8 * sizeof(bit_t) - BITS);
+#endif
+ br->value_ = bits | (br->value_ << BITS);
+ br->bits_ += BITS;
+ } else {
+ VP8LoadFinalBytes(br); // no need to be inlined
+ }
+}
+
+// Read a bit with proba 'prob'. Speed-critical function!
+static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) {
+ // Don't move this declaration! It makes a big speed difference to store
+ // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't
+ // alter br->range_ value.
+ range_t range = br->range_;
+ if (br->bits_ < 0) {
+ VP8LoadNewBytes(br);
+ }
+ {
+ const int pos = br->bits_;
+ const range_t split = (range * prob) >> 8;
+ const range_t value = (range_t)(br->value_ >> pos);
+ const int bit = (value > split);
+ if (bit) {
+ range -= split;
+ br->value_ -= (bit_t)(split + 1) << pos;
+ } else {
+ range = split + 1;
+ }
+ {
+ const int shift = 7 ^ BitsLog2Floor(range);
+ range <<= shift;
+ br->bits_ -= shift;
+ }
+ br->range_ = range - 1;
+ return bit;
+ }
+}
+
+// simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here)
+static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
+int VP8GetSigned(VP8BitReader* const br, int v) {
+ if (br->bits_ < 0) {
+ VP8LoadNewBytes(br);
+ }
+ {
+ const int pos = br->bits_;
+ const range_t split = br->range_ >> 1;
+ const range_t value = (range_t)(br->value_ >> pos);
+ const int32_t mask = (int32_t)(split - value) >> 31; // -1 or 0
+ br->bits_ -= 1;
+ br->range_ += mask;
+ br->range_ |= 1;
+ br->value_ -= (bit_t)((split + 1) & mask) << pos;
+ return (v ^ mask) - mask;
+ }
+}
+
+static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br, int prob) {
+ // Don't move this declaration! It makes a big speed difference to store
+ // 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't
+ // alter br->range_ value.
+ range_t range = br->range_;
+ if (br->bits_ < 0) {
+ VP8LoadNewBytes(br);
+ }
+ {
+ const int pos = br->bits_;
+ const range_t split = (range * prob) >> 8;
+ const range_t value = (range_t)(br->value_ >> pos);
+ int bit; // Don't use 'const int bit = (value > split);", it's slower.
+ if (value > split) {
+ range -= split + 1;
+ br->value_ -= (bit_t)(split + 1) << pos;
+ bit = 1;
+ } else {
+ range = split;
+ bit = 0;
+ }
+ if (range <= (range_t)0x7e) {
+ const int shift = kVP8Log2Range[range];
+ range = kVP8NewRange[range];
+ br->bits_ -= shift;
+ }
+ br->range_ = range;
+ return bit;
+ }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_UTILS_BIT_READER_INL_UTILS_H_
diff --git a/src/third_party/libwebp/src/utils/bit_reader_utils.c b/src/third_party/libwebp/src/utils/bit_reader_utils.c
new file mode 100644
index 0000000..17bbe8e
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/bit_reader_utils.c
@@ -0,0 +1,226 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Boolean decoder non-inlined methods
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#endif
+
+#include "src/utils/bit_reader_inl_utils.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// VP8BitReader
+
+void VP8BitReaderSetBuffer(VP8BitReader* const br,
+ const uint8_t* const start,
+ size_t size) {
+ br->buf_ = start;
+ br->buf_end_ = start + size;
+ br->buf_max_ =
+ (size >= sizeof(lbit_t)) ? start + size - sizeof(lbit_t) + 1
+ : start;
+}
+
+void VP8InitBitReader(VP8BitReader* const br,
+ const uint8_t* const start, size_t size) {
+ assert(br != NULL);
+ assert(start != NULL);
+ assert(size < (1u << 31)); // limit ensured by format and upstream checks
+ br->range_ = 255 - 1;
+ br->value_ = 0;
+ br->bits_ = -8; // to load the very first 8bits
+ br->eof_ = 0;
+ VP8BitReaderSetBuffer(br, start, size);
+ VP8LoadNewBytes(br);
+}
+
+void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
+ if (br->buf_ != NULL) {
+ br->buf_ += offset;
+ br->buf_end_ += offset;
+ br->buf_max_ += offset;
+ }
+}
+
+const uint8_t kVP8Log2Range[128] = {
+ 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0
+};
+
+// range = ((range - 1) << kVP8Log2Range[range]) + 1
+const uint8_t kVP8NewRange[128] = {
+ 127, 127, 191, 127, 159, 191, 223, 127,
+ 143, 159, 175, 191, 207, 223, 239, 127,
+ 135, 143, 151, 159, 167, 175, 183, 191,
+ 199, 207, 215, 223, 231, 239, 247, 127,
+ 131, 135, 139, 143, 147, 151, 155, 159,
+ 163, 167, 171, 175, 179, 183, 187, 191,
+ 195, 199, 203, 207, 211, 215, 219, 223,
+ 227, 231, 235, 239, 243, 247, 251, 127,
+ 129, 131, 133, 135, 137, 139, 141, 143,
+ 145, 147, 149, 151, 153, 155, 157, 159,
+ 161, 163, 165, 167, 169, 171, 173, 175,
+ 177, 179, 181, 183, 185, 187, 189, 191,
+ 193, 195, 197, 199, 201, 203, 205, 207,
+ 209, 211, 213, 215, 217, 219, 221, 223,
+ 225, 227, 229, 231, 233, 235, 237, 239,
+ 241, 243, 245, 247, 249, 251, 253, 127
+};
+
+void VP8LoadFinalBytes(VP8BitReader* const br) {
+ assert(br != NULL && br->buf_ != NULL);
+ // Only read 8bits at a time
+ if (br->buf_ < br->buf_end_) {
+ br->bits_ += 8;
+ br->value_ = (bit_t)(*br->buf_++) | (br->value_ << 8);
+ } else if (!br->eof_) {
+ br->value_ <<= 8;
+ br->bits_ += 8;
+ br->eof_ = 1;
+ } else {
+ br->bits_ = 0; // This is to avoid undefined behaviour with shifts.
+ }
+}
+
+//------------------------------------------------------------------------------
+// Higher-level calls
+
+uint32_t VP8GetValue(VP8BitReader* const br, int bits) {
+ uint32_t v = 0;
+ while (bits-- > 0) {
+ v |= VP8GetBit(br, 0x80) << bits;
+ }
+ return v;
+}
+
+int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) {
+ const int value = VP8GetValue(br, bits);
+ return VP8Get(br) ? -value : value;
+}
+
+//------------------------------------------------------------------------------
+// VP8LBitReader
+
+#define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits.
+
+#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \
+ defined(__i386__) || defined(_M_IX86) || \
+ defined(__x86_64__) || defined(_M_X64)
+#define VP8L_USE_FAST_LOAD
+#endif
+
+static const uint32_t kBitMask[VP8L_MAX_NUM_BIT_READ + 1] = {
+ 0,
+ 0x000001, 0x000003, 0x000007, 0x00000f,
+ 0x00001f, 0x00003f, 0x00007f, 0x0000ff,
+ 0x0001ff, 0x0003ff, 0x0007ff, 0x000fff,
+ 0x001fff, 0x003fff, 0x007fff, 0x00ffff,
+ 0x01ffff, 0x03ffff, 0x07ffff, 0x0fffff,
+ 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff
+};
+
+void VP8LInitBitReader(VP8LBitReader* const br, const uint8_t* const start,
+ size_t length) {
+ size_t i;
+ vp8l_val_t value = 0;
+ assert(br != NULL);
+ assert(start != NULL);
+ assert(length < 0xfffffff8u); // can't happen with a RIFF chunk.
+
+ br->len_ = length;
+ br->val_ = 0;
+ br->bit_pos_ = 0;
+ br->eos_ = 0;
+
+ if (length > sizeof(br->val_)) {
+ length = sizeof(br->val_);
+ }
+ for (i = 0; i < length; ++i) {
+ value |= (vp8l_val_t)start[i] << (8 * i);
+ }
+ br->val_ = value;
+ br->pos_ = length;
+ br->buf_ = start;
+}
+
+void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
+ const uint8_t* const buf, size_t len) {
+ assert(br != NULL);
+ assert(buf != NULL);
+ assert(len < 0xfffffff8u); // can't happen with a RIFF chunk.
+ br->buf_ = buf;
+ br->len_ = len;
+ // pos_ > len_ should be considered a param error.
+ br->eos_ = (br->pos_ > br->len_) || VP8LIsEndOfStream(br);
+}
+
+static void VP8LSetEndOfStream(VP8LBitReader* const br) {
+ br->eos_ = 1;
+ br->bit_pos_ = 0; // To avoid undefined behaviour with shifts.
+}
+
+// If not at EOS, reload up to VP8L_LBITS byte-by-byte
+static void ShiftBytes(VP8LBitReader* const br) {
+ while (br->bit_pos_ >= 8 && br->pos_ < br->len_) {
+ br->val_ >>= 8;
+ br->val_ |= ((vp8l_val_t)br->buf_[br->pos_]) << (VP8L_LBITS - 8);
+ ++br->pos_;
+ br->bit_pos_ -= 8;
+ }
+ if (VP8LIsEndOfStream(br)) {
+ VP8LSetEndOfStream(br);
+ }
+}
+
+void VP8LDoFillBitWindow(VP8LBitReader* const br) {
+ assert(br->bit_pos_ >= VP8L_WBITS);
+#if defined(VP8L_USE_FAST_LOAD)
+ if (br->pos_ + sizeof(br->val_) < br->len_) {
+ br->val_ >>= VP8L_WBITS;
+ br->bit_pos_ -= VP8L_WBITS;
+ br->val_ |= (vp8l_val_t)HToLE32(WebPMemToUint32(br->buf_ + br->pos_)) <<
+ (VP8L_LBITS - VP8L_WBITS);
+ br->pos_ += VP8L_LOG8_WBITS;
+ return;
+ }
+#endif
+ ShiftBytes(br); // Slow path.
+}
+
+uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) {
+ assert(n_bits >= 0);
+ // Flag an error if end_of_stream or n_bits is more than allowed limit.
+ if (!br->eos_ && n_bits <= VP8L_MAX_NUM_BIT_READ) {
+ const uint32_t val = VP8LPrefetchBits(br) & kBitMask[n_bits];
+ const int new_bits = br->bit_pos_ + n_bits;
+ br->bit_pos_ = new_bits;
+ ShiftBytes(br);
+ return val;
+ } else {
+ VP8LSetEndOfStream(br);
+ return 0;
+ }
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/utils/bit_reader_utils.h b/src/third_party/libwebp/src/utils/bit_reader_utils.h
new file mode 100644
index 0000000..24e4fe7
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/bit_reader_utils.h
@@ -0,0 +1,180 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Boolean decoder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+// Vikas Arora (vikaas.arora@gmail.com)
+
+#ifndef WEBP_UTILS_BIT_READER_UTILS_H_
+#define WEBP_UTILS_BIT_READER_UTILS_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#else
+#include <assert.h>
+#ifdef _MSC_VER
+#include <stdlib.h> // _byteswap_ulong
+#endif
+#endif
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The Boolean decoder needs to maintain infinite precision on the value_ field.
+// However, since range_ is only 8bit, we only need an active window of 8 bits
+// for value_. Left bits (MSB) gets zeroed and shifted away when value_ falls
+// below 128, range_ is updated, and fresh bits read from the bitstream are
+// brought in as LSB. To avoid reading the fresh bits one by one (slow), we
+// cache BITS of them ahead. The total of (BITS + 8) bits must fit into a
+// natural register (with type bit_t). To fetch BITS bits from bitstream we
+// use a type lbit_t.
+//
+// BITS can be any multiple of 8 from 8 to 56 (inclusive).
+// Pick values that fit natural register size.
+
+#if defined(__i386__) || defined(_M_IX86) // x86 32bit
+#define BITS 24
+#elif defined(__x86_64__) || defined(_M_X64) // x86 64bit
+#define BITS 56
+#elif defined(__arm__) || defined(_M_ARM) // ARM
+#define BITS 24
+#elif defined(__aarch64__) // ARM 64bit
+#define BITS 56
+#elif defined(__mips__) // MIPS
+#define BITS 24
+#else // reasonable default
+#define BITS 24
+#endif
+
+//------------------------------------------------------------------------------
+// Derived types and constants:
+// bit_t = natural register type for storing 'value_' (which is BITS+8 bits)
+// range_t = register for 'range_' (which is 8bits only)
+
+#if (BITS > 24)
+typedef uint64_t bit_t;
+#else
+typedef uint32_t bit_t;
+#endif
+
+typedef uint32_t range_t;
+
+//------------------------------------------------------------------------------
+// Bitreader
+
+typedef struct VP8BitReader VP8BitReader;
+struct VP8BitReader {
+ // boolean decoder (keep the field ordering as is!)
+ bit_t value_; // current value
+ range_t range_; // current range minus 1. In [127, 254] interval.
+ int bits_; // number of valid bits left
+ // read buffer
+ const uint8_t* buf_; // next byte to be read
+ const uint8_t* buf_end_; // end of read buffer
+ const uint8_t* buf_max_; // max packed-read position on buffer
+ int eof_; // true if input is exhausted
+};
+
+// Initialize the bit reader and the boolean decoder.
+void VP8InitBitReader(VP8BitReader* const br,
+ const uint8_t* const start, size_t size);
+// Sets the working read buffer.
+void VP8BitReaderSetBuffer(VP8BitReader* const br,
+ const uint8_t* const start, size_t size);
+
+// Update internal pointers to displace the byte buffer by the
+// relative offset 'offset'.
+void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset);
+
+// return the next value made of 'num_bits' bits
+uint32_t VP8GetValue(VP8BitReader* const br, int num_bits);
+static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) {
+ return VP8GetValue(br, 1);
+}
+
+// return the next value with sign-extension.
+int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits);
+
+// bit_reader_inl.h will implement the following methods:
+// static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob)
+// static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v)
+// and should be included by the .c files that actually need them.
+// This is to avoid recompiling the whole library whenever this file is touched,
+// and also allowing platform-specific ad-hoc hacks.
+
+// -----------------------------------------------------------------------------
+// Bitreader for lossless format
+
+// maximum number of bits (inclusive) the bit-reader can handle:
+#define VP8L_MAX_NUM_BIT_READ 24
+
+#define VP8L_LBITS 64 // Number of bits prefetched (= bit-size of vp8l_val_t).
+#define VP8L_WBITS 32 // Minimum number of bytes ready after VP8LFillBitWindow.
+
+typedef uint64_t vp8l_val_t; // right now, this bit-reader can only use 64bit.
+
+typedef struct {
+ vp8l_val_t val_; // pre-fetched bits
+ const uint8_t* buf_; // input byte buffer
+ size_t len_; // buffer length
+ size_t pos_; // byte position in buf_
+ int bit_pos_; // current bit-reading position in val_
+ int eos_; // true if a bit was read past the end of buffer
+} VP8LBitReader;
+
+void VP8LInitBitReader(VP8LBitReader* const br,
+ const uint8_t* const start,
+ size_t length);
+
+// Sets a new data buffer.
+void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
+ const uint8_t* const buffer, size_t length);
+
+// Reads the specified number of bits from read buffer.
+// Flags an error in case end_of_stream or n_bits is more than the allowed limit
+// of VP8L_MAX_NUM_BIT_READ (inclusive).
+// Flags eos_ if this read attempt is going to cross the read buffer.
+uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits);
+
+// Return the prefetched bits, so they can be looked up.
+static WEBP_INLINE uint32_t VP8LPrefetchBits(VP8LBitReader* const br) {
+ return (uint32_t)(br->val_ >> (br->bit_pos_ & (VP8L_LBITS - 1)));
+}
+
+// Returns true if there was an attempt at reading bit past the end of
+// the buffer. Doesn't set br->eos_ flag.
+static WEBP_INLINE int VP8LIsEndOfStream(const VP8LBitReader* const br) {
+ SB_DCHECK(br->pos_ <= br->len_);
+ return br->eos_ || ((br->pos_ == br->len_) && (br->bit_pos_ > VP8L_LBITS));
+}
+
+// For jumping over a number of bits in the bit stream when accessed with
+// VP8LPrefetchBits and VP8LFillBitWindow.
+// This function does *not* set br->eos_, since it's speed-critical.
+// Use with extreme care!
+static WEBP_INLINE void VP8LSetBitPos(VP8LBitReader* const br, int val) {
+ br->bit_pos_ = val;
+}
+
+// Advances the read buffer by 4 bytes to make room for reading next 32 bits.
+// Speed critical, but infrequent part of the code can be non-inlined.
+extern void VP8LDoFillBitWindow(VP8LBitReader* const br);
+static WEBP_INLINE void VP8LFillBitWindow(VP8LBitReader* const br) {
+ if (br->bit_pos_ >= VP8L_WBITS) VP8LDoFillBitWindow(br);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_BIT_READER_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/bit_writer_utils.c b/src/third_party/libwebp/src/utils/bit_writer_utils.c
new file mode 100644
index 0000000..a7f871a
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/bit_writer_utils.c
@@ -0,0 +1,352 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Bit writing and boolean coder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+// Vikas Arora (vikaas.arora@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <string.h> // for memcpy()
+#include <stdlib.h>
+#endif
+
+#include "src/utils/bit_writer_utils.h"
+#include "src/utils/endian_inl_utils.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// VP8BitWriter
+
+static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) {
+ uint8_t* new_buf;
+ size_t new_size;
+ const uint64_t needed_size_64b = (uint64_t)bw->pos_ + extra_size;
+ const size_t needed_size = (size_t)needed_size_64b;
+ if (needed_size_64b != needed_size) {
+ bw->error_ = 1;
+ return 0;
+ }
+ if (needed_size <= bw->max_pos_) return 1;
+ // If the following line wraps over 32bit, the test just after will catch it.
+ new_size = 2 * bw->max_pos_;
+ if (new_size < needed_size) new_size = needed_size;
+ if (new_size < 1024) new_size = 1024;
+ new_buf = (uint8_t*)WebPSafeMalloc(1ULL, new_size);
+ if (new_buf == NULL) {
+ bw->error_ = 1;
+ return 0;
+ }
+ if (bw->pos_ > 0) {
+ assert(bw->buf_ != NULL);
+ memcpy(new_buf, bw->buf_, bw->pos_);
+ }
+ WebPSafeFree(bw->buf_);
+ bw->buf_ = new_buf;
+ bw->max_pos_ = new_size;
+ return 1;
+}
+
+static void Flush(VP8BitWriter* const bw) {
+ const int s = 8 + bw->nb_bits_;
+ const int32_t bits = bw->value_ >> s;
+ assert(bw->nb_bits_ >= 0);
+ bw->value_ -= bits << s;
+ bw->nb_bits_ -= 8;
+ if ((bits & 0xff) != 0xff) {
+ size_t pos = bw->pos_;
+ if (!BitWriterResize(bw, bw->run_ + 1)) {
+ return;
+ }
+ if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's
+ if (pos > 0) bw->buf_[pos - 1]++;
+ }
+ if (bw->run_ > 0) {
+ const int value = (bits & 0x100) ? 0x00 : 0xff;
+ for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value;
+ }
+ bw->buf_[pos++] = bits;
+ bw->pos_ = pos;
+ } else {
+ bw->run_++; // delay writing of bytes 0xff, pending eventual carry.
+ }
+}
+
+//------------------------------------------------------------------------------
+// renormalization
+
+static const uint8_t kNorm[128] = { // renorm_sizes[i] = 8 - log2(i)
+ 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0
+};
+
+// range = ((range + 1) << kVP8Log2Range[range]) - 1
+static const uint8_t kNewRange[128] = {
+ 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239,
+ 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239,
+ 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179,
+ 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239,
+ 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149,
+ 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179,
+ 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209,
+ 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239,
+ 241, 243, 245, 247, 249, 251, 253, 127
+};
+
+int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) {
+ const int split = (bw->range_ * prob) >> 8;
+ if (bit) {
+ bw->value_ += split + 1;
+ bw->range_ -= split + 1;
+ } else {
+ bw->range_ = split;
+ }
+ if (bw->range_ < 127) { // emit 'shift' bits out and renormalize
+ const int shift = kNorm[bw->range_];
+ bw->range_ = kNewRange[bw->range_];
+ bw->value_ <<= shift;
+ bw->nb_bits_ += shift;
+ if (bw->nb_bits_ > 0) Flush(bw);
+ }
+ return bit;
+}
+
+int VP8PutBitUniform(VP8BitWriter* const bw, int bit) {
+ const int split = bw->range_ >> 1;
+ if (bit) {
+ bw->value_ += split + 1;
+ bw->range_ -= split + 1;
+ } else {
+ bw->range_ = split;
+ }
+ if (bw->range_ < 127) {
+ bw->range_ = kNewRange[bw->range_];
+ bw->value_ <<= 1;
+ bw->nb_bits_ += 1;
+ if (bw->nb_bits_ > 0) Flush(bw);
+ }
+ return bit;
+}
+
+void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits) {
+ uint32_t mask;
+ assert(nb_bits > 0 && nb_bits < 32);
+ for (mask = 1u << (nb_bits - 1); mask; mask >>= 1) {
+ VP8PutBitUniform(bw, value & mask);
+ }
+}
+
+void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits) {
+ if (!VP8PutBitUniform(bw, value != 0)) return;
+ if (value < 0) {
+ VP8PutBits(bw, ((-value) << 1) | 1, nb_bits + 1);
+ } else {
+ VP8PutBits(bw, value << 1, nb_bits + 1);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size) {
+ bw->range_ = 255 - 1;
+ bw->value_ = 0;
+ bw->run_ = 0;
+ bw->nb_bits_ = -8;
+ bw->pos_ = 0;
+ bw->max_pos_ = 0;
+ bw->error_ = 0;
+ bw->buf_ = NULL;
+ return (expected_size > 0) ? BitWriterResize(bw, expected_size) : 1;
+}
+
+uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) {
+ VP8PutBits(bw, 0, 9 - bw->nb_bits_);
+ bw->nb_bits_ = 0; // pad with zeroes
+ Flush(bw);
+ return bw->buf_;
+}
+
+int VP8BitWriterAppend(VP8BitWriter* const bw,
+ const uint8_t* data, size_t size) {
+ assert(data != NULL);
+ if (bw->nb_bits_ != -8) return 0; // Flush() must have been called
+ if (!BitWriterResize(bw, size)) return 0;
+ memcpy(bw->buf_ + bw->pos_, data, size);
+ bw->pos_ += size;
+ return 1;
+}
+
+void VP8BitWriterWipeOut(VP8BitWriter* const bw) {
+ if (bw != NULL) {
+ WebPSafeFree(bw->buf_);
+ memset(bw, 0, sizeof(*bw));
+ }
+}
+
+//------------------------------------------------------------------------------
+// VP8LBitWriter
+
+// This is the minimum amount of size the memory buffer is guaranteed to grow
+// when extra space is needed.
+#define MIN_EXTRA_SIZE (32768ULL)
+
+// Returns 1 on success.
+static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) {
+ uint8_t* allocated_buf;
+ size_t allocated_size;
+ const size_t max_bytes = bw->end_ - bw->buf_;
+ const size_t current_size = bw->cur_ - bw->buf_;
+ const uint64_t size_required_64b = (uint64_t)current_size + extra_size;
+ const size_t size_required = (size_t)size_required_64b;
+ if (size_required != size_required_64b) {
+ bw->error_ = 1;
+ return 0;
+ }
+ if (max_bytes > 0 && size_required <= max_bytes) return 1;
+ allocated_size = (3 * max_bytes) >> 1;
+ if (allocated_size < size_required) allocated_size = size_required;
+ // make allocated size multiple of 1k
+ allocated_size = (((allocated_size >> 10) + 1) << 10);
+ allocated_buf = (uint8_t*)WebPSafeMalloc(1ULL, allocated_size);
+ if (allocated_buf == NULL) {
+ bw->error_ = 1;
+ return 0;
+ }
+ if (current_size > 0) {
+ memcpy(allocated_buf, bw->buf_, current_size);
+ }
+ WebPSafeFree(bw->buf_);
+ bw->buf_ = allocated_buf;
+ bw->cur_ = bw->buf_ + current_size;
+ bw->end_ = bw->buf_ + allocated_size;
+ return 1;
+}
+
+int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) {
+ memset(bw, 0, sizeof(*bw));
+ return VP8LBitWriterResize(bw, expected_size);
+}
+
+int VP8LBitWriterClone(const VP8LBitWriter* const src,
+ VP8LBitWriter* const dst) {
+ const size_t current_size = src->cur_ - src->buf_;
+ assert(src->cur_ >= src->buf_ && src->cur_ <= src->end_);
+ if (!VP8LBitWriterResize(dst, current_size)) return 0;
+ memcpy(dst->buf_, src->buf_, current_size);
+ dst->bits_ = src->bits_;
+ dst->used_ = src->used_;
+ dst->error_ = src->error_;
+ return 1;
+}
+
+void VP8LBitWriterWipeOut(VP8LBitWriter* const bw) {
+ if (bw != NULL) {
+ WebPSafeFree(bw->buf_);
+ memset(bw, 0, sizeof(*bw));
+ }
+}
+
+void VP8LBitWriterReset(const VP8LBitWriter* const bw_init,
+ VP8LBitWriter* const bw) {
+ bw->bits_ = bw_init->bits_;
+ bw->used_ = bw_init->used_;
+ bw->cur_ = bw->buf_ + (bw_init->cur_ - bw_init->buf_);
+ assert(bw->cur_ <= bw->end_);
+ bw->error_ = bw_init->error_;
+}
+
+void VP8LBitWriterSwap(VP8LBitWriter* const src, VP8LBitWriter* const dst) {
+ const VP8LBitWriter tmp = *src;
+ *src = *dst;
+ *dst = tmp;
+}
+
+void VP8LPutBitsFlushBits(VP8LBitWriter* const bw) {
+ // If needed, make some room by flushing some bits out.
+ if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) {
+ const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE;
+ if (extra_size != (size_t)extra_size ||
+ !VP8LBitWriterResize(bw, (size_t)extra_size)) {
+ bw->cur_ = bw->buf_;
+ bw->error_ = 1;
+ return;
+ }
+ }
+ *(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)bw->bits_);
+ bw->cur_ += VP8L_WRITER_BYTES;
+ bw->bits_ >>= VP8L_WRITER_BITS;
+ bw->used_ -= VP8L_WRITER_BITS;
+}
+
+void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits) {
+ assert(n_bits <= 32);
+ // That's the max we can handle:
+ assert(sizeof(vp8l_wtype_t) == 2);
+ if (n_bits > 0) {
+ vp8l_atype_t lbits = bw->bits_;
+ int used = bw->used_;
+ // Special case of overflow handling for 32bit accumulator (2-steps flush).
+#if VP8L_WRITER_BITS == 16
+ if (used + n_bits >= VP8L_WRITER_MAX_BITS) {
+ // Fill up all the VP8L_WRITER_MAX_BITS so it can be flushed out below.
+ const int shift = VP8L_WRITER_MAX_BITS - used;
+ lbits |= (vp8l_atype_t)bits << used;
+ used = VP8L_WRITER_MAX_BITS;
+ n_bits -= shift;
+ bits >>= shift;
+ assert(n_bits <= VP8L_WRITER_MAX_BITS);
+ }
+#endif
+ // If needed, make some room by flushing some bits out.
+ while (used >= VP8L_WRITER_BITS) {
+ if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) {
+ const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE;
+ if (extra_size != (size_t)extra_size ||
+ !VP8LBitWriterResize(bw, (size_t)extra_size)) {
+ bw->cur_ = bw->buf_;
+ bw->error_ = 1;
+ return;
+ }
+ }
+ *(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)lbits);
+ bw->cur_ += VP8L_WRITER_BYTES;
+ lbits >>= VP8L_WRITER_BITS;
+ used -= VP8L_WRITER_BITS;
+ }
+ bw->bits_ = lbits | ((vp8l_atype_t)bits << used);
+ bw->used_ = used + n_bits;
+ }
+}
+
+uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) {
+ // flush leftover bits
+ if (VP8LBitWriterResize(bw, (bw->used_ + 7) >> 3)) {
+ while (bw->used_ > 0) {
+ *bw->cur_++ = (uint8_t)bw->bits_;
+ bw->bits_ >>= 8;
+ bw->used_ -= 8;
+ }
+ bw->used_ = 0;
+ }
+ return bw->buf_;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/utils/bit_writer_utils.h b/src/third_party/libwebp/src/utils/bit_writer_utils.h
new file mode 100644
index 0000000..2cf5976
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/bit_writer_utils.h
@@ -0,0 +1,154 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Bit writing and boolean coder
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_UTILS_BIT_WRITER_UTILS_H_
+#define WEBP_UTILS_BIT_WRITER_UTILS_H_
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Bit-writing
+
+typedef struct VP8BitWriter VP8BitWriter;
+struct VP8BitWriter {
+ int32_t range_; // range-1
+ int32_t value_;
+ int run_; // number of outstanding bits
+ int nb_bits_; // number of pending bits
+ uint8_t* buf_; // internal buffer. Re-allocated regularly. Not owned.
+ size_t pos_;
+ size_t max_pos_;
+ int error_; // true in case of error
+};
+
+// Initialize the object. Allocates some initial memory based on expected_size.
+int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size);
+// Finalize the bitstream coding. Returns a pointer to the internal buffer.
+uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw);
+// Release any pending memory and zeroes the object. Not a mandatory call.
+// Only useful in case of error, when the internal buffer hasn't been grabbed!
+void VP8BitWriterWipeOut(VP8BitWriter* const bw);
+
+int VP8PutBit(VP8BitWriter* const bw, int bit, int prob);
+int VP8PutBitUniform(VP8BitWriter* const bw, int bit);
+void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits);
+void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits);
+
+// Appends some bytes to the internal buffer. Data is copied.
+int VP8BitWriterAppend(VP8BitWriter* const bw,
+ const uint8_t* data, size_t size);
+
+// return approximate write position (in bits)
+static WEBP_INLINE uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) {
+ const uint64_t nb_bits = 8 + bw->nb_bits_; // bw->nb_bits_ is <= 0, note
+ return (bw->pos_ + bw->run_) * 8 + nb_bits;
+}
+
+// Returns a pointer to the internal buffer.
+static WEBP_INLINE uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) {
+ return bw->buf_;
+}
+// Returns the size of the internal buffer.
+static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) {
+ return bw->pos_;
+}
+
+//------------------------------------------------------------------------------
+// VP8LBitWriter
+
+#if defined(__x86_64__) || defined(_M_X64) // 64bit
+typedef uint64_t vp8l_atype_t; // accumulator type
+typedef uint32_t vp8l_wtype_t; // writing type
+#define WSWAP HToLE32
+#define VP8L_WRITER_BYTES 4 // sizeof(vp8l_wtype_t)
+#define VP8L_WRITER_BITS 32 // 8 * sizeof(vp8l_wtype_t)
+#define VP8L_WRITER_MAX_BITS 64 // 8 * sizeof(vp8l_atype_t)
+#else
+typedef uint32_t vp8l_atype_t;
+typedef uint16_t vp8l_wtype_t;
+#define WSWAP HToLE16
+#define VP8L_WRITER_BYTES 2
+#define VP8L_WRITER_BITS 16
+#define VP8L_WRITER_MAX_BITS 32
+#endif
+
+typedef struct {
+ vp8l_atype_t bits_; // bit accumulator
+ int used_; // number of bits used in accumulator
+ uint8_t* buf_; // start of buffer
+ uint8_t* cur_; // current write position
+ uint8_t* end_; // end of buffer
+
+ // After all bits are written (VP8LBitWriterFinish()), the caller must observe
+ // the state of error_. A value of 1 indicates that a memory allocation
+ // failure has happened during bit writing. A value of 0 indicates successful
+ // writing of bits.
+ int error_;
+} VP8LBitWriter;
+
+static WEBP_INLINE size_t VP8LBitWriterNumBytes(const VP8LBitWriter* const bw) {
+ return (bw->cur_ - bw->buf_) + ((bw->used_ + 7) >> 3);
+}
+
+// Returns false in case of memory allocation error.
+int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size);
+// Returns false in case of memory allocation error.
+int VP8LBitWriterClone(const VP8LBitWriter* const src,
+ VP8LBitWriter* const dst);
+// Finalize the bitstream coding. Returns a pointer to the internal buffer.
+uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw);
+// Release any pending memory and zeroes the object.
+void VP8LBitWriterWipeOut(VP8LBitWriter* const bw);
+// Resets the cursor of the BitWriter bw to when it was like in bw_init.
+void VP8LBitWriterReset(const VP8LBitWriter* const bw_init,
+ VP8LBitWriter* const bw);
+// Swaps the memory held by two BitWriters.
+void VP8LBitWriterSwap(VP8LBitWriter* const src, VP8LBitWriter* const dst);
+
+// Internal function for VP8LPutBits flushing 32 bits from the written state.
+void VP8LPutBitsFlushBits(VP8LBitWriter* const bw);
+
+// PutBits internal function used in the 16 bit vp8l_wtype_t case.
+void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits);
+
+// This function writes bits into bytes in increasing addresses (little endian),
+// and within a byte least-significant-bit first.
+// This function can write up to 32 bits in one go, but VP8LBitReader can only
+// read 24 bits max (VP8L_MAX_NUM_BIT_READ).
+// VP8LBitWriter's error_ flag is set in case of memory allocation error.
+static WEBP_INLINE void VP8LPutBits(VP8LBitWriter* const bw,
+ uint32_t bits, int n_bits) {
+ if (sizeof(vp8l_wtype_t) == 4) {
+ if (n_bits > 0) {
+ if (bw->used_ >= 32) {
+ VP8LPutBitsFlushBits(bw);
+ }
+ bw->bits_ |= (vp8l_atype_t)bits << bw->used_;
+ bw->used_ += n_bits;
+ }
+ } else {
+ VP8LPutBitsInternal(bw, bits, n_bits);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_BIT_WRITER_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/color_cache_utils.c b/src/third_party/libwebp/src/utils/color_cache_utils.c
new file mode 100644
index 0000000..ee54811
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/color_cache_utils.c
@@ -0,0 +1,55 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Color Cache for WebP Lossless
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+#include "src/utils/color_cache_utils.h"
+#include "src/utils/utils.h"
+
+//------------------------------------------------------------------------------
+// VP8LColorCache.
+
+int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
+ const int hash_size = 1 << hash_bits;
+ assert(cc != NULL);
+ assert(hash_bits > 0);
+ cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size,
+ sizeof(*cc->colors_));
+ if (cc->colors_ == NULL) return 0;
+ cc->hash_shift_ = 32 - hash_bits;
+ cc->hash_bits_ = hash_bits;
+ return 1;
+}
+
+void VP8LColorCacheClear(VP8LColorCache* const cc) {
+ if (cc != NULL) {
+ WebPSafeFree(cc->colors_);
+ cc->colors_ = NULL;
+ }
+}
+
+void VP8LColorCacheCopy(const VP8LColorCache* const src,
+ VP8LColorCache* const dst) {
+ assert(src != NULL);
+ assert(dst != NULL);
+ assert(src->hash_bits_ == dst->hash_bits_);
+ memcpy(dst->colors_, src->colors_,
+ ((size_t)1u << dst->hash_bits_) * sizeof(*dst->colors_));
+}
diff --git a/src/third_party/libwebp/src/utils/color_cache_utils.h b/src/third_party/libwebp/src/utils/color_cache_utils.h
new file mode 100644
index 0000000..44cac8c
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/color_cache_utils.h
@@ -0,0 +1,91 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Color Cache for WebP Lossless
+//
+// Authors: Jyrki Alakuijala (jyrki@google.com)
+// Urvang Joshi (urvang@google.com)
+
+#ifndef WEBP_UTILS_COLOR_CACHE_UTILS_H_
+#define WEBP_UTILS_COLOR_CACHE_UTILS_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#else
+#include <assert.h>
+#endif
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Main color cache struct.
+typedef struct {
+ uint32_t *colors_; // color entries
+ int hash_shift_; // Hash shift: 32 - hash_bits_.
+ int hash_bits_;
+} VP8LColorCache;
+
+static const uint64_t kHashMul = 0x1e35a7bdull;
+
+static WEBP_INLINE int VP8LHashPix(uint32_t argb, int shift) {
+ return (int)(((argb * kHashMul) & 0xffffffffu) >> shift);
+}
+
+static WEBP_INLINE uint32_t VP8LColorCacheLookup(
+ const VP8LColorCache* const cc, uint32_t key) {
+ SB_DCHECK((key >> cc->hash_bits_) == 0u);
+ return cc->colors_[key];
+}
+
+static WEBP_INLINE void VP8LColorCacheSet(const VP8LColorCache* const cc,
+ uint32_t key, uint32_t argb) {
+ SB_DCHECK((key >> cc->hash_bits_) == 0u);
+ cc->colors_[key] = argb;
+}
+
+static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc,
+ uint32_t argb) {
+ const int key = VP8LHashPix(argb, cc->hash_shift_);
+ cc->colors_[key] = argb;
+}
+
+static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc,
+ uint32_t argb) {
+ return VP8LHashPix(argb, cc->hash_shift_);
+}
+
+// Return the key if cc contains argb, and -1 otherwise.
+static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc,
+ uint32_t argb) {
+ const int key = VP8LHashPix(argb, cc->hash_shift_);
+ return (cc->colors_[key] == argb) ? key : -1;
+}
+
+//------------------------------------------------------------------------------
+
+// Initializes the color cache with 'hash_bits' bits for the keys.
+// Returns false in case of memory error.
+int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits);
+
+void VP8LColorCacheCopy(const VP8LColorCache* const src,
+ VP8LColorCache* const dst);
+
+// Delete the memory associated to color cache.
+void VP8LColorCacheClear(VP8LColorCache* const color_cache);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WEBP_UTILS_COLOR_CACHE_UTILS_H_
diff --git a/src/third_party/libwebp/src/utils/endian_inl_utils.h b/src/third_party/libwebp/src/utils/endian_inl_utils.h
new file mode 100644
index 0000000..8ca9fb7
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/endian_inl_utils.h
@@ -0,0 +1,109 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Endian related functions.
+
+#ifndef WEBP_UTILS_ENDIAN_INL_UTILS_H_
+#define WEBP_UTILS_ENDIAN_INL_UTILS_H_
+
+#if defined(STARBOARD)
+#include "starboard/byte_swap.h"
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#include "src/dsp/dsp.h"
+#include "src/webp/types.h"
+
+#if defined(WORDS_BIGENDIAN)
+#define HToLE32 BSwap32
+#define HToLE16 BSwap16
+#else
+#define HToLE32(x) (x)
+#define HToLE16(x) (x)
+#endif
+
+#if !defined(HAVE_CONFIG_H)
+#if LOCAL_GCC_PREREQ(4,8) || __has_builtin(__builtin_bswap16)
+#define HAVE_BUILTIN_BSWAP16
+#endif
+#if LOCAL_GCC_PREREQ(4,3) || __has_builtin(__builtin_bswap32)
+#define HAVE_BUILTIN_BSWAP32
+#endif
+#if LOCAL_GCC_PREREQ(4,3) || __has_builtin(__builtin_bswap64)
+#define HAVE_BUILTIN_BSWAP64
+#endif
+#endif // !HAVE_CONFIG_H
+
+static WEBP_INLINE uint16_t BSwap16(uint16_t x) {
+#if defined(STARBOARD)
+ return SbByteSwapU16(x);
+#else
+#if defined(HAVE_BUILTIN_BSWAP16)
+ return __builtin_bswap16(x);
+#elif defined(_MSC_VER)
+ return _byteswap_ushort(x);
+#else
+ // gcc will recognize a 'rorw $8, ...' here:
+ return (x >> 8) | ((x & 0xff) << 8);
+#endif // HAVE_BUILTIN_BSWAP16
+#endif // defined(STARBOARD)
+}
+
+static WEBP_INLINE uint32_t BSwap32(uint32_t x) {
+#if defined(STARBOARD)
+ return SbByteSwapU32(x);
+#else
+#if defined(WEBP_USE_MIPS32_R2)
+ uint32_t ret;
+ __asm__ volatile (
+ "wsbh %[ret], %[x] \n\t"
+ "rotr %[ret], %[ret], 16 \n\t"
+ : [ret]"=r"(ret)
+ : [x]"r"(x)
+ );
+ return ret;
+#elif defined(HAVE_BUILTIN_BSWAP32)
+ return __builtin_bswap32(x);
+#elif defined(__i386__) || defined(__x86_64__)
+ uint32_t swapped_bytes;
+ __asm__ volatile("bswap %0" : "=r"(swapped_bytes) : "0"(x));
+ return swapped_bytes;
+#elif defined(_MSC_VER)
+ return (uint32_t)_byteswap_ulong(x);
+#else
+ return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24);
+#endif // HAVE_BUILTIN_BSWAP32
+#endif // defined(STARBOARD)
+}
+
+static WEBP_INLINE uint64_t BSwap64(uint64_t x) {
+#if defined(STARBOARD)
+ return SbByteSwapU64(x);
+#else
+#if defined(HAVE_BUILTIN_BSWAP64)
+ return __builtin_bswap64(x);
+#elif defined(__x86_64__)
+ uint64_t swapped_bytes;
+ __asm__ volatile("bswapq %0" : "=r"(swapped_bytes) : "0"(x));
+ return swapped_bytes;
+#elif defined(_MSC_VER)
+ return (uint64_t)_byteswap_uint64(x);
+#else // generic code for swapping 64-bit values (suggested by bdb@)
+ x = ((x & 0xffffffff00000000ull) >> 32) | ((x & 0x00000000ffffffffull) << 32);
+ x = ((x & 0xffff0000ffff0000ull) >> 16) | ((x & 0x0000ffff0000ffffull) << 16);
+ x = ((x & 0xff00ff00ff00ff00ull) >> 8) | ((x & 0x00ff00ff00ff00ffull) << 8);
+ return x;
+#endif // HAVE_BUILTIN_BSWAP64
+#endif // defined(STARBOARD)
+}
+
+#endif // WEBP_UTILS_ENDIAN_INL_UTILS_H_
diff --git a/src/third_party/libwebp/src/utils/filters_utils.c b/src/third_party/libwebp/src/utils/filters_utils.c
new file mode 100644
index 0000000..bbc2c34
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/filters_utils.c
@@ -0,0 +1,76 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// filter estimation
+//
+// Author: Urvang (urvang@google.com)
+
+#include "src/utils/filters_utils.h"
+#include <stdlib.h>
+#include <string.h>
+
+// -----------------------------------------------------------------------------
+// Quick estimate of a potentially interesting filter mode to try.
+
+#define SMAX 16
+#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX)
+
+static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
+ const int g = a + b - c;
+ return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
+}
+
+WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data,
+ int width, int height, int stride) {
+ int i, j;
+ int bins[WEBP_FILTER_LAST][SMAX];
+ memset(bins, 0, sizeof(bins));
+
+ // We only sample every other pixels. That's enough.
+ for (j = 2; j < height - 1; j += 2) {
+ const uint8_t* const p = data + j * stride;
+ int mean = p[0];
+ for (i = 2; i < width - 1; i += 2) {
+ const int diff0 = SDIFF(p[i], mean);
+ const int diff1 = SDIFF(p[i], p[i - 1]);
+ const int diff2 = SDIFF(p[i], p[i - width]);
+ const int grad_pred =
+ GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]);
+ const int diff3 = SDIFF(p[i], grad_pred);
+ bins[WEBP_FILTER_NONE][diff0] = 1;
+ bins[WEBP_FILTER_HORIZONTAL][diff1] = 1;
+ bins[WEBP_FILTER_VERTICAL][diff2] = 1;
+ bins[WEBP_FILTER_GRADIENT][diff3] = 1;
+ mean = (3 * mean + p[i] + 2) >> 2;
+ }
+ }
+ {
+ int filter;
+ WEBP_FILTER_TYPE best_filter = WEBP_FILTER_NONE;
+ int best_score = 0x7fffffff;
+ for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) {
+ int score = 0;
+ for (i = 0; i < SMAX; ++i) {
+ if (bins[filter][i] > 0) {
+ score += i;
+ }
+ }
+ if (score < best_score) {
+ best_score = score;
+ best_filter = (WEBP_FILTER_TYPE)filter;
+ }
+ }
+ return best_filter;
+ }
+}
+
+#undef SMAX
+#undef SDIFF
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/utils/filters_utils.h b/src/third_party/libwebp/src/utils/filters_utils.h
new file mode 100644
index 0000000..410f2fc
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/filters_utils.h
@@ -0,0 +1,32 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Spatial prediction using various filters
+//
+// Author: Urvang (urvang@google.com)
+
+#ifndef WEBP_UTILS_FILTERS_UTILS_H_
+#define WEBP_UTILS_FILTERS_UTILS_H_
+
+#include "src/webp/types.h"
+#include "src/dsp/dsp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Fast estimate of a potentially good filter.
+WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data,
+ int width, int height, int stride);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_FILTERS_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/huffman_encode_utils.c b/src/third_party/libwebp/src/utils/huffman_encode_utils.c
new file mode 100644
index 0000000..6f3b1bb
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/huffman_encode_utils.c
@@ -0,0 +1,417 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+// Entropy encoding (Huffman) for webp lossless.
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "src/utils/huffman_encode_utils.h"
+#include "src/utils/utils.h"
+#include "src/webp/format_constants.h"
+
+// -----------------------------------------------------------------------------
+// Util function to optimize the symbol map for RLE coding
+
+// Heuristics for selecting the stride ranges to collapse.
+static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) {
+ return abs(a - b) < 4;
+}
+
+// Change the population counts in a way that the consequent
+// Huffman tree compression, especially its RLE-part, give smaller output.
+static void OptimizeHuffmanForRle(int length, uint8_t* const good_for_rle,
+ uint32_t* const counts) {
+ // 1) Let's make the Huffman code more compatible with rle encoding.
+ int i;
+ for (; length >= 0; --length) {
+ if (length == 0) {
+ return; // All zeros.
+ }
+ if (counts[length - 1] != 0) {
+ // Now counts[0..length - 1] does not have trailing zeros.
+ break;
+ }
+ }
+ // 2) Let's mark all population counts that already can be encoded
+ // with an rle code.
+ {
+ // Let's not spoil any of the existing good rle codes.
+ // Mark any seq of 0's that is longer as 5 as a good_for_rle.
+ // Mark any seq of non-0's that is longer as 7 as a good_for_rle.
+ uint32_t symbol = counts[0];
+ int stride = 0;
+ for (i = 0; i < length + 1; ++i) {
+ if (i == length || counts[i] != symbol) {
+ if ((symbol == 0 && stride >= 5) ||
+ (symbol != 0 && stride >= 7)) {
+ int k;
+ for (k = 0; k < stride; ++k) {
+ good_for_rle[i - k - 1] = 1;
+ }
+ }
+ stride = 1;
+ if (i != length) {
+ symbol = counts[i];
+ }
+ } else {
+ ++stride;
+ }
+ }
+ }
+ // 3) Let's replace those population counts that lead to more rle codes.
+ {
+ uint32_t stride = 0;
+ uint32_t limit = counts[0];
+ uint32_t sum = 0;
+ for (i = 0; i < length + 1; ++i) {
+ if (i == length || good_for_rle[i] ||
+ (i != 0 && good_for_rle[i - 1]) ||
+ !ValuesShouldBeCollapsedToStrideAverage(counts[i], limit)) {
+ if (stride >= 4 || (stride >= 3 && sum == 0)) {
+ uint32_t k;
+ // The stride must end, collapse what we have, if we have enough (4).
+ uint32_t count = (sum + stride / 2) / stride;
+ if (count < 1) {
+ count = 1;
+ }
+ if (sum == 0) {
+ // Don't make an all zeros stride to be upgraded to ones.
+ count = 0;
+ }
+ for (k = 0; k < stride; ++k) {
+ // We don't want to change value at counts[i],
+ // that is already belonging to the next stride. Thus - 1.
+ counts[i - k - 1] = count;
+ }
+ }
+ stride = 0;
+ sum = 0;
+ if (i < length - 3) {
+ // All interesting strides have a count of at least 4,
+ // at least when non-zeros.
+ limit = (counts[i] + counts[i + 1] +
+ counts[i + 2] + counts[i + 3] + 2) / 4;
+ } else if (i < length) {
+ limit = counts[i];
+ } else {
+ limit = 0;
+ }
+ }
+ ++stride;
+ if (i != length) {
+ sum += counts[i];
+ if (stride >= 4) {
+ limit = (sum + stride / 2) / stride;
+ }
+ }
+ }
+ }
+}
+
+// A comparer function for two Huffman trees: sorts first by 'total count'
+// (more comes first), and then by 'value' (more comes first).
+static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) {
+ const HuffmanTree* const t1 = (const HuffmanTree*)ptr1;
+ const HuffmanTree* const t2 = (const HuffmanTree*)ptr2;
+ if (t1->total_count_ > t2->total_count_) {
+ return -1;
+ } else if (t1->total_count_ < t2->total_count_) {
+ return 1;
+ } else {
+ assert(t1->value_ != t2->value_);
+ return (t1->value_ < t2->value_) ? -1 : 1;
+ }
+}
+
+static void SetBitDepths(const HuffmanTree* const tree,
+ const HuffmanTree* const pool,
+ uint8_t* const bit_depths, int level) {
+ if (tree->pool_index_left_ >= 0) {
+ SetBitDepths(&pool[tree->pool_index_left_], pool, bit_depths, level + 1);
+ SetBitDepths(&pool[tree->pool_index_right_], pool, bit_depths, level + 1);
+ } else {
+ bit_depths[tree->value_] = level;
+ }
+}
+
+// Create an optimal Huffman tree.
+//
+// (data,length): population counts.
+// tree_limit: maximum bit depth (inclusive) of the codes.
+// bit_depths[]: how many bits are used for the symbol.
+//
+// Returns 0 when an error has occurred.
+//
+// The catch here is that the tree cannot be arbitrarily deep
+//
+// count_limit is the value that is to be faked as the minimum value
+// and this minimum value is raised until the tree matches the
+// maximum length requirement.
+//
+// This algorithm is not of excellent performance for very long data blocks,
+// especially when population counts are longer than 2**tree_limit, but
+// we are not planning to use this with extremely long blocks.
+//
+// See http://en.wikipedia.org/wiki/Huffman_coding
+static void GenerateOptimalTree(const uint32_t* const histogram,
+ int histogram_size,
+ HuffmanTree* tree, int tree_depth_limit,
+ uint8_t* const bit_depths) {
+ uint32_t count_min;
+ HuffmanTree* tree_pool;
+ int tree_size_orig = 0;
+ int i;
+
+ for (i = 0; i < histogram_size; ++i) {
+ if (histogram[i] != 0) {
+ ++tree_size_orig;
+ }
+ }
+
+ if (tree_size_orig == 0) { // pretty optimal already!
+ return;
+ }
+
+ tree_pool = tree + tree_size_orig;
+
+ // For block sizes with less than 64k symbols we never need to do a
+ // second iteration of this loop.
+ // If we actually start running inside this loop a lot, we would perhaps
+ // be better off with the Katajainen algorithm.
+ assert(tree_size_orig <= (1 << (tree_depth_limit - 1)));
+ for (count_min = 1; ; count_min *= 2) {
+ int tree_size = tree_size_orig;
+ // We need to pack the Huffman tree in tree_depth_limit bits.
+ // So, we try by faking histogram entries to be at least 'count_min'.
+ int idx = 0;
+ int j;
+ for (j = 0; j < histogram_size; ++j) {
+ if (histogram[j] != 0) {
+ const uint32_t count =
+ (histogram[j] < count_min) ? count_min : histogram[j];
+ tree[idx].total_count_ = count;
+ tree[idx].value_ = j;
+ tree[idx].pool_index_left_ = -1;
+ tree[idx].pool_index_right_ = -1;
+ ++idx;
+ }
+ }
+
+ // Build the Huffman tree.
+ qsort(tree, tree_size, sizeof(*tree), CompareHuffmanTrees);
+
+ if (tree_size > 1) { // Normal case.
+ int tree_pool_size = 0;
+ while (tree_size > 1) { // Finish when we have only one root.
+ uint32_t count;
+ tree_pool[tree_pool_size++] = tree[tree_size - 1];
+ tree_pool[tree_pool_size++] = tree[tree_size - 2];
+ count = tree_pool[tree_pool_size - 1].total_count_ +
+ tree_pool[tree_pool_size - 2].total_count_;
+ tree_size -= 2;
+ {
+ // Search for the insertion point.
+ int k;
+ for (k = 0; k < tree_size; ++k) {
+ if (tree[k].total_count_ <= count) {
+ break;
+ }
+ }
+ memmove(tree + (k + 1), tree + k, (tree_size - k) * sizeof(*tree));
+ tree[k].total_count_ = count;
+ tree[k].value_ = -1;
+
+ tree[k].pool_index_left_ = tree_pool_size - 1;
+ tree[k].pool_index_right_ = tree_pool_size - 2;
+ tree_size = tree_size + 1;
+ }
+ }
+ SetBitDepths(&tree[0], tree_pool, bit_depths, 0);
+ } else if (tree_size == 1) { // Trivial case: only one element.
+ bit_depths[tree[0].value_] = 1;
+ }
+
+ {
+ // Test if this Huffman tree satisfies our 'tree_depth_limit' criteria.
+ int max_depth = bit_depths[0];
+ for (j = 1; j < histogram_size; ++j) {
+ if (max_depth < bit_depths[j]) {
+ max_depth = bit_depths[j];
+ }
+ }
+ if (max_depth <= tree_depth_limit) {
+ break;
+ }
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Coding of the Huffman tree values
+
+static HuffmanTreeToken* CodeRepeatedValues(int repetitions,
+ HuffmanTreeToken* tokens,
+ int value, int prev_value) {
+ assert(value <= MAX_ALLOWED_CODE_LENGTH);
+ if (value != prev_value) {
+ tokens->code = value;
+ tokens->extra_bits = 0;
+ ++tokens;
+ --repetitions;
+ }
+ while (repetitions >= 1) {
+ if (repetitions < 3) {
+ int i;
+ for (i = 0; i < repetitions; ++i) {
+ tokens->code = value;
+ tokens->extra_bits = 0;
+ ++tokens;
+ }
+ break;
+ } else if (repetitions < 7) {
+ tokens->code = 16;
+ tokens->extra_bits = repetitions - 3;
+ ++tokens;
+ break;
+ } else {
+ tokens->code = 16;
+ tokens->extra_bits = 3;
+ ++tokens;
+ repetitions -= 6;
+ }
+ }
+ return tokens;
+}
+
+static HuffmanTreeToken* CodeRepeatedZeros(int repetitions,
+ HuffmanTreeToken* tokens) {
+ while (repetitions >= 1) {
+ if (repetitions < 3) {
+ int i;
+ for (i = 0; i < repetitions; ++i) {
+ tokens->code = 0; // 0-value
+ tokens->extra_bits = 0;
+ ++tokens;
+ }
+ break;
+ } else if (repetitions < 11) {
+ tokens->code = 17;
+ tokens->extra_bits = repetitions - 3;
+ ++tokens;
+ break;
+ } else if (repetitions < 139) {
+ tokens->code = 18;
+ tokens->extra_bits = repetitions - 11;
+ ++tokens;
+ break;
+ } else {
+ tokens->code = 18;
+ tokens->extra_bits = 0x7f; // 138 repeated 0s
+ ++tokens;
+ repetitions -= 138;
+ }
+ }
+ return tokens;
+}
+
+int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
+ HuffmanTreeToken* tokens, int max_tokens) {
+ HuffmanTreeToken* const starting_token = tokens;
+ HuffmanTreeToken* const ending_token = tokens + max_tokens;
+ const int depth_size = tree->num_symbols;
+ int prev_value = 8; // 8 is the initial value for rle.
+ int i = 0;
+ assert(tokens != NULL);
+ while (i < depth_size) {
+ const int value = tree->code_lengths[i];
+ int k = i + 1;
+ int runs;
+ while (k < depth_size && tree->code_lengths[k] == value) ++k;
+ runs = k - i;
+ if (value == 0) {
+ tokens = CodeRepeatedZeros(runs, tokens);
+ } else {
+ tokens = CodeRepeatedValues(runs, tokens, value, prev_value);
+ prev_value = value;
+ }
+ i += runs;
+ assert(tokens <= ending_token);
+ }
+ (void)ending_token; // suppress 'unused variable' warning
+ return (int)(tokens - starting_token);
+}
+
+// -----------------------------------------------------------------------------
+
+// Pre-reversed 4-bit values.
+static const uint8_t kReversedBits[16] = {
+ 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
+};
+
+static uint32_t ReverseBits(int num_bits, uint32_t bits) {
+ uint32_t retval = 0;
+ int i = 0;
+ while (i < num_bits) {
+ i += 4;
+ retval |= kReversedBits[bits & 0xf] << (MAX_ALLOWED_CODE_LENGTH + 1 - i);
+ bits >>= 4;
+ }
+ retval >>= (MAX_ALLOWED_CODE_LENGTH + 1 - num_bits);
+ return retval;
+}
+
+// Get the actual bit values for a tree of bit depths.
+static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) {
+ // 0 bit-depth means that the symbol does not exist.
+ int i;
+ int len;
+ uint32_t next_code[MAX_ALLOWED_CODE_LENGTH + 1];
+ int depth_count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 };
+
+ assert(tree != NULL);
+ len = tree->num_symbols;
+ for (i = 0; i < len; ++i) {
+ const int code_length = tree->code_lengths[i];
+ assert(code_length <= MAX_ALLOWED_CODE_LENGTH);
+ ++depth_count[code_length];
+ }
+ depth_count[0] = 0; // ignore unused symbol
+ next_code[0] = 0;
+ {
+ uint32_t code = 0;
+ for (i = 1; i <= MAX_ALLOWED_CODE_LENGTH; ++i) {
+ code = (code + depth_count[i - 1]) << 1;
+ next_code[i] = code;
+ }
+ }
+ for (i = 0; i < len; ++i) {
+ const int code_length = tree->code_lengths[i];
+ tree->codes[i] = ReverseBits(code_length, next_code[code_length]++);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Main entry point
+
+void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit,
+ uint8_t* const buf_rle,
+ HuffmanTree* const huff_tree,
+ HuffmanTreeCode* const huff_code) {
+ const int num_symbols = huff_code->num_symbols;
+ memset(buf_rle, 0, num_symbols * sizeof(*buf_rle));
+ OptimizeHuffmanForRle(num_symbols, buf_rle, histogram);
+ GenerateOptimalTree(histogram, num_symbols, huff_tree, tree_depth_limit,
+ huff_code->code_lengths);
+ // Create the actual bit codes for the bit lengths.
+ ConvertBitDepthsToSymbols(huff_code);
+}
diff --git a/src/third_party/libwebp/src/utils/huffman_encode_utils.h b/src/third_party/libwebp/src/utils/huffman_encode_utils.h
new file mode 100644
index 0000000..3e6763c
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/huffman_encode_utils.h
@@ -0,0 +1,60 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Author: Jyrki Alakuijala (jyrki@google.com)
+//
+// Entropy encoding (Huffman) for webp lossless
+
+#ifndef WEBP_UTILS_HUFFMAN_ENCODE_UTILS_H_
+#define WEBP_UTILS_HUFFMAN_ENCODE_UTILS_H_
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Struct for holding the tree header in coded form.
+typedef struct {
+ uint8_t code; // value (0..15) or escape code (16,17,18)
+ uint8_t extra_bits; // extra bits for escape codes
+} HuffmanTreeToken;
+
+// Struct to represent the tree codes (depth and bits array).
+typedef struct {
+ int num_symbols; // Number of symbols.
+ uint8_t* code_lengths; // Code lengths of the symbols.
+ uint16_t* codes; // Symbol Codes.
+} HuffmanTreeCode;
+
+// Struct to represent the Huffman tree.
+typedef struct {
+ uint32_t total_count_; // Symbol frequency.
+ int value_; // Symbol value.
+ int pool_index_left_; // Index for the left sub-tree.
+ int pool_index_right_; // Index for the right sub-tree.
+} HuffmanTree;
+
+// Turn the Huffman tree into a token sequence.
+// Returns the number of tokens used.
+int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
+ HuffmanTreeToken* tokens, int max_tokens);
+
+// Create an optimized tree, and tokenize it.
+// 'buf_rle' and 'huff_tree' are pre-allocated and the 'tree' is the constructed
+// huffman code tree.
+void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit,
+ uint8_t* const buf_rle, HuffmanTree* const huff_tree,
+ HuffmanTreeCode* const tree);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // WEBP_UTILS_HUFFMAN_ENCODE_UTILS_H_
diff --git a/src/third_party/libwebp/src/utils/huffman_utils.c b/src/third_party/libwebp/src/utils/huffman_utils.c
new file mode 100644
index 0000000..7a69963
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/huffman_utils.c
@@ -0,0 +1,223 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for building and looking up Huffman trees.
+//
+// Author: Urvang Joshi (urvang@google.com)
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "src/utils/huffman_utils.h"
+#include "src/utils/utils.h"
+#include "src/webp/format_constants.h"
+
+// Huffman data read via DecodeImageStream is represented in two (red and green)
+// bytes.
+#define MAX_HTREE_GROUPS 0x10000
+
+HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups) {
+ HTreeGroup* const htree_groups =
+ (HTreeGroup*)WebPSafeMalloc(num_htree_groups, sizeof(*htree_groups));
+ if (htree_groups == NULL) {
+ return NULL;
+ }
+ assert(num_htree_groups <= MAX_HTREE_GROUPS);
+ return htree_groups;
+}
+
+void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups) {
+ if (htree_groups != NULL) {
+ WebPSafeFree(htree_groups);
+ }
+}
+
+// Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the
+// bit-wise reversal of the len least significant bits of key.
+static WEBP_INLINE uint32_t GetNextKey(uint32_t key, int len) {
+ uint32_t step = 1 << (len - 1);
+ while (key & step) {
+ step >>= 1;
+ }
+ return step ? (key & (step - 1)) + step : key;
+}
+
+// Stores code in table[0], table[step], table[2*step], ..., table[end].
+// Assumes that end is an integer multiple of step.
+static WEBP_INLINE void ReplicateValue(HuffmanCode* table,
+ int step, int end,
+ HuffmanCode code) {
+ assert(end % step == 0);
+ do {
+ end -= step;
+ table[end] = code;
+ } while (end > 0);
+}
+
+// Returns the table width of the next 2nd level table. count is the histogram
+// of bit lengths for the remaining symbols, len is the code length of the next
+// processed symbol
+static WEBP_INLINE int NextTableBitSize(const int* const count,
+ int len, int root_bits) {
+ int left = 1 << (len - root_bits);
+ while (len < MAX_ALLOWED_CODE_LENGTH) {
+ left -= count[len];
+ if (left <= 0) break;
+ ++len;
+ left <<= 1;
+ }
+ return len - root_bits;
+}
+
+// sorted[code_lengths_size] is a pre-allocated array for sorting symbols
+// by code length.
+static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
+ const int code_lengths[], int code_lengths_size,
+ uint16_t sorted[]) {
+ HuffmanCode* table = root_table; // next available space in table
+ int total_size = 1 << root_bits; // total size root table + 2nd level table
+ int len; // current code length
+ int symbol; // symbol index in original or sorted table
+ // number of codes of each length:
+ int count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 };
+ // offsets in sorted table for each length:
+ int offset[MAX_ALLOWED_CODE_LENGTH + 1];
+
+ assert(code_lengths_size != 0);
+ assert(code_lengths != NULL);
+ assert(root_table != NULL);
+ assert(root_bits > 0);
+
+ // Build histogram of code lengths.
+ for (symbol = 0; symbol < code_lengths_size; ++symbol) {
+ if (code_lengths[symbol] > MAX_ALLOWED_CODE_LENGTH) {
+ return 0;
+ }
+ ++count[code_lengths[symbol]];
+ }
+
+ // Error, all code lengths are zeros.
+ if (count[0] == code_lengths_size) {
+ return 0;
+ }
+
+ // Generate offsets into sorted symbol table by code length.
+ offset[1] = 0;
+ for (len = 1; len < MAX_ALLOWED_CODE_LENGTH; ++len) {
+ if (count[len] > (1 << len)) {
+ return 0;
+ }
+ offset[len + 1] = offset[len] + count[len];
+ }
+
+ // Sort symbols by length, by symbol order within each length.
+ for (symbol = 0; symbol < code_lengths_size; ++symbol) {
+ const int symbol_code_length = code_lengths[symbol];
+ if (code_lengths[symbol] > 0) {
+ sorted[offset[symbol_code_length]++] = symbol;
+ }
+ }
+
+ // Special case code with only one value.
+ if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) {
+ HuffmanCode code;
+ code.bits = 0;
+ code.value = (uint16_t)sorted[0];
+ ReplicateValue(table, 1, total_size, code);
+ return total_size;
+ }
+
+ {
+ int step; // step size to replicate values in current table
+ uint32_t low = -1; // low bits for current root entry
+ uint32_t mask = total_size - 1; // mask for low bits
+ uint32_t key = 0; // reversed prefix code
+ int num_nodes = 1; // number of Huffman tree nodes
+ int num_open = 1; // number of open branches in current tree level
+ int table_bits = root_bits; // key length of current table
+ int table_size = 1 << table_bits; // size of current table
+ symbol = 0;
+ // Fill in root table.
+ for (len = 1, step = 2; len <= root_bits; ++len, step <<= 1) {
+ num_open <<= 1;
+ num_nodes += num_open;
+ num_open -= count[len];
+ if (num_open < 0) {
+ return 0;
+ }
+ for (; count[len] > 0; --count[len]) {
+ HuffmanCode code;
+ code.bits = (uint8_t)len;
+ code.value = (uint16_t)sorted[symbol++];
+ ReplicateValue(&table[key], step, table_size, code);
+ key = GetNextKey(key, len);
+ }
+ }
+
+ // Fill in 2nd level tables and add pointers to root table.
+ for (len = root_bits + 1, step = 2; len <= MAX_ALLOWED_CODE_LENGTH;
+ ++len, step <<= 1) {
+ num_open <<= 1;
+ num_nodes += num_open;
+ num_open -= count[len];
+ if (num_open < 0) {
+ return 0;
+ }
+ for (; count[len] > 0; --count[len]) {
+ HuffmanCode code;
+ if ((key & mask) != low) {
+ table += table_size;
+ table_bits = NextTableBitSize(count, len, root_bits);
+ table_size = 1 << table_bits;
+ total_size += table_size;
+ low = key & mask;
+ root_table[low].bits = (uint8_t)(table_bits + root_bits);
+ root_table[low].value = (uint16_t)((table - root_table) - low);
+ }
+ code.bits = (uint8_t)(len - root_bits);
+ code.value = (uint16_t)sorted[symbol++];
+ ReplicateValue(&table[key >> root_bits], step, table_size, code);
+ key = GetNextKey(key, len);
+ }
+ }
+
+ // Check if tree is full.
+ if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) {
+ return 0;
+ }
+ }
+
+ return total_size;
+}
+
+// Maximum code_lengths_size is 2328 (reached for 11-bit color_cache_bits).
+// More commonly, the value is around ~280.
+#define MAX_CODE_LENGTHS_SIZE \
+ ((1 << MAX_CACHE_BITS) + NUM_LITERAL_CODES + NUM_LENGTH_CODES)
+// Cut-off value for switching between heap and stack allocation.
+#define SORTED_SIZE_CUTOFF 512
+int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
+ const int code_lengths[], int code_lengths_size) {
+ int total_size;
+ assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
+ if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
+ // use local stack-allocated array.
+ uint16_t sorted[SORTED_SIZE_CUTOFF];
+ total_size = BuildHuffmanTable(root_table, root_bits,
+ code_lengths, code_lengths_size, sorted);
+ } else { // rare case. Use heap allocation.
+ uint16_t* const sorted =
+ (uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
+ if (sorted == NULL) return 0;
+ total_size = BuildHuffmanTable(root_table, root_bits,
+ code_lengths, code_lengths_size, sorted);
+ WebPSafeFree(sorted);
+ }
+ return total_size;
+}
diff --git a/src/third_party/libwebp/src/utils/huffman_utils.h b/src/third_party/libwebp/src/utils/huffman_utils.h
new file mode 100644
index 0000000..da1c797
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/huffman_utils.h
@@ -0,0 +1,92 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for building and looking up Huffman trees.
+//
+// Author: Urvang Joshi (urvang@google.com)
+
+#ifndef WEBP_UTILS_HUFFMAN_UTILS_H_
+#define WEBP_UTILS_HUFFMAN_UTILS_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#else
+#include <assert.h>
+#endif
+#include "src/webp/format_constants.h"
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define HUFFMAN_TABLE_BITS 8
+#define HUFFMAN_TABLE_MASK ((1 << HUFFMAN_TABLE_BITS) - 1)
+
+#define LENGTHS_TABLE_BITS 7
+#define LENGTHS_TABLE_MASK ((1 << LENGTHS_TABLE_BITS) - 1)
+
+
+// Huffman lookup table entry
+typedef struct {
+ uint8_t bits; // number of bits used for this symbol
+ uint16_t value; // symbol value or table offset
+} HuffmanCode;
+
+// long version for holding 32b values
+typedef struct {
+ int bits; // number of bits used for this symbol,
+ // or an impossible value if not a literal code.
+ uint32_t value; // 32b packed ARGB value if literal,
+ // or non-literal symbol otherwise
+} HuffmanCode32;
+
+#define HUFFMAN_PACKED_BITS 6
+#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)
+
+// Huffman table group.
+// Includes special handling for the following cases:
+// - is_trivial_literal: one common literal base for RED/BLUE/ALPHA (not GREEN)
+// - is_trivial_code: only 1 code (no bit is read from bitstream)
+// - use_packed_table: few enough literal symbols, so all the bit codes
+// can fit into a small look-up table packed_table[]
+// The common literal base, if applicable, is stored in 'literal_arb'.
+typedef struct HTreeGroup HTreeGroup;
+struct HTreeGroup {
+ HuffmanCode* htrees[HUFFMAN_CODES_PER_META_CODE];
+ int is_trivial_literal; // True, if huffman trees for Red, Blue & Alpha
+ // Symbols are trivial (have a single code).
+ uint32_t literal_arb; // If is_trivial_literal is true, this is the
+ // ARGB value of the pixel, with Green channel
+ // being set to zero.
+ int is_trivial_code; // true if is_trivial_literal with only one code
+ int use_packed_table; // use packed table below for short literal code
+ // table mapping input bits to a packed values, or escape case to literal code
+ HuffmanCode32 packed_table[HUFFMAN_PACKED_TABLE_SIZE];
+};
+
+// Creates the instance of HTreeGroup with specified number of tree-groups.
+HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups);
+
+// Releases the memory allocated for HTreeGroup.
+void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
+
+// Builds Huffman lookup table assuming code lengths are in symbol order.
+// The 'code_lengths' is pre-allocated temporary memory buffer used for creating
+// the huffman table.
+// Returns built table size or 0 in case of error (invalid tree or
+// memory error).
+int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
+ const int code_lengths[], int code_lengths_size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBP_UTILS_HUFFMAN_UTILS_H_
diff --git a/src/third_party/libwebp/src/utils/quant_levels_dec_utils.c b/src/third_party/libwebp/src/utils/quant_levels_dec_utils.c
new file mode 100644
index 0000000..3818a78
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/quant_levels_dec_utils.c
@@ -0,0 +1,285 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Implement gradient smoothing: we replace a current alpha value by its
+// surrounding average if it's close enough (that is: the change will be less
+// than the minimum distance between two quantized level).
+// We use sliding window for computing the 2d moving average.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "src/utils/quant_levels_dec_utils.h"
+
+#include <string.h> // for memset
+
+#include "src/utils/utils.h"
+
+// #define USE_DITHERING // uncomment to enable ordered dithering (not vital)
+
+#define FIX 16 // fix-point precision for averaging
+#define LFIX 2 // extra precision for look-up table
+#define LUT_SIZE ((1 << (8 + LFIX)) - 1) // look-up table size
+
+#if defined(USE_DITHERING)
+
+#define DFIX 4 // extra precision for ordered dithering
+#define DSIZE 4 // dithering size (must be a power of two)
+// cf. http://en.wikipedia.org/wiki/Ordered_dithering
+static const uint8_t kOrderedDither[DSIZE][DSIZE] = {
+ { 0, 8, 2, 10 }, // coefficients are in DFIX fixed-point precision
+ { 12, 4, 14, 6 },
+ { 3, 11, 1, 9 },
+ { 15, 7, 13, 5 }
+};
+
+#else
+#define DFIX 0
+#endif
+
+typedef struct {
+ int width_, height_; // dimension
+ int stride_; // stride in bytes
+ int row_; // current input row being processed
+ uint8_t* src_; // input pointer
+ uint8_t* dst_; // output pointer
+
+ int radius_; // filter radius (=delay)
+ int scale_; // normalization factor, in FIX bits precision
+
+ void* mem_; // all memory
+
+ // various scratch buffers
+ uint16_t* start_;
+ uint16_t* cur_;
+ uint16_t* end_;
+ uint16_t* top_;
+ uint16_t* average_;
+
+ // input levels distribution
+ int num_levels_; // number of quantized levels
+ int min_, max_; // min and max level values
+ int min_level_dist_; // smallest distance between two consecutive levels
+
+ int16_t* correction_; // size = 1 + 2*LUT_SIZE -> ~4k memory
+} SmoothParams;
+
+//------------------------------------------------------------------------------
+
+#define CLIP_8b_MASK (int)(~0U << (8 + DFIX))
+static WEBP_INLINE uint8_t clip_8b(int v) {
+ return (!(v & CLIP_8b_MASK)) ? (uint8_t)(v >> DFIX) : (v < 0) ? 0u : 255u;
+}
+#undef CLIP_8b_MASK
+
+// vertical accumulation
+static void VFilter(SmoothParams* const p) {
+ const uint8_t* src = p->src_;
+ const int w = p->width_;
+ uint16_t* const cur = p->cur_;
+ const uint16_t* const top = p->top_;
+ uint16_t* const out = p->end_;
+ uint16_t sum = 0; // all arithmetic is modulo 16bit
+ int x;
+
+ for (x = 0; x < w; ++x) {
+ uint16_t new_value;
+ sum += src[x];
+ new_value = top[x] + sum;
+ out[x] = new_value - cur[x]; // vertical sum of 'r' pixels.
+ cur[x] = new_value;
+ }
+ // move input pointers one row down
+ p->top_ = p->cur_;
+ p->cur_ += w;
+ if (p->cur_ == p->end_) p->cur_ = p->start_; // roll-over
+ // We replicate edges, as it's somewhat easier as a boundary condition.
+ // That's why we don't update the 'src' pointer on top/bottom area:
+ if (p->row_ >= 0 && p->row_ < p->height_ - 1) {
+ p->src_ += p->stride_;
+ }
+}
+
+// horizontal accumulation. We use mirror replication of missing pixels, as it's
+// a little easier to implement (surprisingly).
+static void HFilter(SmoothParams* const p) {
+ const uint16_t* const in = p->end_;
+ uint16_t* const out = p->average_;
+ const uint32_t scale = p->scale_;
+ const int w = p->width_;
+ const int r = p->radius_;
+
+ int x;
+ for (x = 0; x <= r; ++x) { // left mirroring
+ const uint16_t delta = in[x + r - 1] + in[r - x];
+ out[x] = (delta * scale) >> FIX;
+ }
+ for (; x < w - r; ++x) { // bulk middle run
+ const uint16_t delta = in[x + r] - in[x - r - 1];
+ out[x] = (delta * scale) >> FIX;
+ }
+ for (; x < w; ++x) { // right mirroring
+ const uint16_t delta =
+ 2 * in[w - 1] - in[2 * w - 2 - r - x] - in[x - r - 1];
+ out[x] = (delta * scale) >> FIX;
+ }
+}
+
+// emit one filtered output row
+static void ApplyFilter(SmoothParams* const p) {
+ const uint16_t* const average = p->average_;
+ const int w = p->width_;
+ const int16_t* const correction = p->correction_;
+#if defined(USE_DITHERING)
+ const uint8_t* const dither = kOrderedDither[p->row_ % DSIZE];
+#endif
+ uint8_t* const dst = p->dst_;
+ int x;
+ for (x = 0; x < w; ++x) {
+ const int v = dst[x];
+ if (v < p->max_ && v > p->min_) {
+ const int c = (v << DFIX) + correction[average[x] - (v << LFIX)];
+#if defined(USE_DITHERING)
+ dst[x] = clip_8b(c + dither[x % DSIZE]);
+#else
+ dst[x] = clip_8b(c);
+#endif
+ }
+ }
+ p->dst_ += p->stride_; // advance output pointer
+}
+
+//------------------------------------------------------------------------------
+// Initialize correction table
+
+static void InitCorrectionLUT(int16_t* const lut, int min_dist) {
+ // The correction curve is:
+ // f(x) = x for x <= threshold2
+ // f(x) = 0 for x >= threshold1
+ // and a linear interpolation for range x=[threshold2, threshold1]
+ // (along with f(-x) = -f(x) symmetry).
+ // Note that: threshold2 = 3/4 * threshold1
+ const int threshold1 = min_dist << LFIX;
+ const int threshold2 = (3 * threshold1) >> 2;
+ const int max_threshold = threshold2 << DFIX;
+ const int delta = threshold1 - threshold2;
+ int i;
+ for (i = 1; i <= LUT_SIZE; ++i) {
+ int c = (i <= threshold2) ? (i << DFIX)
+ : (i < threshold1) ? max_threshold * (threshold1 - i) / delta
+ : 0;
+ c >>= LFIX;
+ lut[+i] = +c;
+ lut[-i] = -c;
+ }
+ lut[0] = 0;
+}
+
+static void CountLevels(SmoothParams* const p) {
+ int i, j, last_level;
+ uint8_t used_levels[256] = { 0 };
+ const uint8_t* data = p->src_;
+ p->min_ = 255;
+ p->max_ = 0;
+ for (j = 0; j < p->height_; ++j) {
+ for (i = 0; i < p->width_; ++i) {
+ const int v = data[i];
+ if (v < p->min_) p->min_ = v;
+ if (v > p->max_) p->max_ = v;
+ used_levels[v] = 1;
+ }
+ data += p->stride_;
+ }
+ // Compute the mininum distance between two non-zero levels.
+ p->min_level_dist_ = p->max_ - p->min_;
+ last_level = -1;
+ for (i = 0; i < 256; ++i) {
+ if (used_levels[i]) {
+ ++p->num_levels_;
+ if (last_level >= 0) {
+ const int level_dist = i - last_level;
+ if (level_dist < p->min_level_dist_) {
+ p->min_level_dist_ = level_dist;
+ }
+ }
+ last_level = i;
+ }
+ }
+}
+
+// Initialize all params.
+static int InitParams(uint8_t* const data, int width, int height, int stride,
+ int radius, SmoothParams* const p) {
+ const int R = 2 * radius + 1; // total size of the kernel
+
+ const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start_);
+ const size_t size_m = width * sizeof(*p->average_);
+ const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction_);
+ const size_t total_size = size_scratch_m + size_m + size_lut;
+ uint8_t* mem = (uint8_t*)WebPSafeMalloc(1U, total_size);
+
+ if (mem == NULL) return 0;
+ p->mem_ = (void*)mem;
+
+ p->start_ = (uint16_t*)mem;
+ p->cur_ = p->start_;
+ p->end_ = p->start_ + R * width;
+ p->top_ = p->end_ - width;
+ memset(p->top_, 0, width * sizeof(*p->top_));
+ mem += size_scratch_m;
+
+ p->average_ = (uint16_t*)mem;
+ mem += size_m;
+
+ p->width_ = width;
+ p->height_ = height;
+ p->stride_ = stride;
+ p->src_ = data;
+ p->dst_ = data;
+ p->radius_ = radius;
+ p->scale_ = (1 << (FIX + LFIX)) / (R * R); // normalization constant
+ p->row_ = -radius;
+
+ // analyze the input distribution so we can best-fit the threshold
+ CountLevels(p);
+
+ // correction table
+ p->correction_ = ((int16_t*)mem) + LUT_SIZE;
+ InitCorrectionLUT(p->correction_, p->min_level_dist_);
+
+ return 1;
+}
+
+static void CleanupParams(SmoothParams* const p) {
+ WebPSafeFree(p->mem_);
+}
+
+int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride,
+ int strength) {
+ const int radius = 4 * strength / 100;
+ if (strength < 0 || strength > 100) return 0;
+ if (data == NULL || width <= 0 || height <= 0) return 0; // bad params
+ if (radius > 0) {
+ SmoothParams p;
+ memset(&p, 0, sizeof(p));
+ if (!InitParams(data, width, height, stride, radius, &p)) return 0;
+ if (p.num_levels_ > 2) {
+ for (; p.row_ < p.height_; ++p.row_) {
+ VFilter(&p); // accumulate average of input
+ // Need to wait few rows in order to prime the filter,
+ // before emitting some output.
+ if (p.row_ >= p.radius_) {
+ HFilter(&p);
+ ApplyFilter(&p);
+ }
+ }
+ }
+ CleanupParams(&p);
+ }
+ return 1;
+}
diff --git a/src/third_party/libwebp/src/utils/quant_levels_dec_utils.h b/src/third_party/libwebp/src/utils/quant_levels_dec_utils.h
new file mode 100644
index 0000000..f822107
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/quant_levels_dec_utils.h
@@ -0,0 +1,35 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Alpha plane de-quantization utility
+//
+// Author: Vikas Arora (vikasa@google.com)
+
+#ifndef WEBP_UTILS_QUANT_LEVELS_DEC_UTILS_H_
+#define WEBP_UTILS_QUANT_LEVELS_DEC_UTILS_H_
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Apply post-processing to input 'data' of size 'width'x'height' assuming that
+// the source was quantized to a reduced number of levels. 'stride' is in bytes.
+// Strength is in [0..100] and controls the amount of dithering applied.
+// Returns false in case of error (data is NULL, invalid parameters,
+// malloc failure, ...).
+int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride,
+ int strength);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_QUANT_LEVELS_DEC_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/quant_levels_utils.c b/src/third_party/libwebp/src/utils/quant_levels_utils.c
new file mode 100644
index 0000000..d65ad3c
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/quant_levels_utils.c
@@ -0,0 +1,140 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Quantize levels for specified number of quantization-levels ([2, 256]).
+// Min and max values are preserved (usual 0 and 255 for alpha plane).
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+
+#include "src/utils/quant_levels_utils.h"
+
+#define NUM_SYMBOLS 256
+
+#define MAX_ITER 6 // Maximum number of convergence steps.
+#define ERROR_THRESHOLD 1e-4 // MSE stopping criterion.
+
+// -----------------------------------------------------------------------------
+// Quantize levels.
+
+int QuantizeLevels(uint8_t* const data, int width, int height,
+ int num_levels, uint64_t* const sse) {
+ int freq[NUM_SYMBOLS] = { 0 };
+ int q_level[NUM_SYMBOLS] = { 0 };
+ double inv_q_level[NUM_SYMBOLS] = { 0 };
+ int min_s = 255, max_s = 0;
+ const size_t data_size = height * width;
+ int i, num_levels_in, iter;
+ double last_err = 1.e38, err = 0.;
+ const double err_threshold = ERROR_THRESHOLD * data_size;
+
+ if (data == NULL) {
+ return 0;
+ }
+
+ if (width <= 0 || height <= 0) {
+ return 0;
+ }
+
+ if (num_levels < 2 || num_levels > 256) {
+ return 0;
+ }
+
+ {
+ size_t n;
+ num_levels_in = 0;
+ for (n = 0; n < data_size; ++n) {
+ num_levels_in += (freq[data[n]] == 0);
+ if (min_s > data[n]) min_s = data[n];
+ if (max_s < data[n]) max_s = data[n];
+ ++freq[data[n]];
+ }
+ }
+
+ if (num_levels_in <= num_levels) goto End; // nothing to do!
+
+ // Start with uniformly spread centroids.
+ for (i = 0; i < num_levels; ++i) {
+ inv_q_level[i] = min_s + (double)(max_s - min_s) * i / (num_levels - 1);
+ }
+
+ // Fixed values. Won't be changed.
+ q_level[min_s] = 0;
+ q_level[max_s] = num_levels - 1;
+ assert(inv_q_level[0] == min_s);
+ assert(inv_q_level[num_levels - 1] == max_s);
+
+ // k-Means iterations.
+ for (iter = 0; iter < MAX_ITER; ++iter) {
+ double q_sum[NUM_SYMBOLS] = { 0 };
+ double q_count[NUM_SYMBOLS] = { 0 };
+ int s, slot = 0;
+
+ // Assign classes to representatives.
+ for (s = min_s; s <= max_s; ++s) {
+ // Keep track of the nearest neighbour 'slot'
+ while (slot < num_levels - 1 &&
+ 2 * s > inv_q_level[slot] + inv_q_level[slot + 1]) {
+ ++slot;
+ }
+ if (freq[s] > 0) {
+ q_sum[slot] += s * freq[s];
+ q_count[slot] += freq[s];
+ }
+ q_level[s] = slot;
+ }
+
+ // Assign new representatives to classes.
+ if (num_levels > 2) {
+ for (slot = 1; slot < num_levels - 1; ++slot) {
+ const double count = q_count[slot];
+ if (count > 0.) {
+ inv_q_level[slot] = q_sum[slot] / count;
+ }
+ }
+ }
+
+ // Compute convergence error.
+ err = 0.;
+ for (s = min_s; s <= max_s; ++s) {
+ const double error = s - inv_q_level[q_level[s]];
+ err += freq[s] * error * error;
+ }
+
+ // Check for convergence: we stop as soon as the error is no
+ // longer improving.
+ if (last_err - err < err_threshold) break;
+ last_err = err;
+ }
+
+ // Remap the alpha plane to quantized values.
+ {
+ // double->int rounding operation can be costly, so we do it
+ // once for all before remapping. We also perform the data[] -> slot
+ // mapping, while at it (avoid one indirection in the final loop).
+ uint8_t map[NUM_SYMBOLS];
+ int s;
+ size_t n;
+ for (s = min_s; s <= max_s; ++s) {
+ const int slot = q_level[s];
+ map[s] = (uint8_t)(inv_q_level[slot] + .5);
+ }
+ // Final pass.
+ for (n = 0; n < data_size; ++n) {
+ data[n] = map[data[n]];
+ }
+ }
+ End:
+ // Store sum of squared error if needed.
+ if (sse != NULL) *sse = (uint64_t)err;
+
+ return 1;
+}
+
diff --git a/src/third_party/libwebp/src/utils/quant_levels_utils.h b/src/third_party/libwebp/src/utils/quant_levels_utils.h
new file mode 100644
index 0000000..75df2ba
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/quant_levels_utils.h
@@ -0,0 +1,36 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Alpha plane quantization utility
+//
+// Author: Vikas Arora (vikasa@google.com)
+
+#ifndef WEBP_UTILS_QUANT_LEVELS_UTILS_H_
+#define WEBP_UTILS_QUANT_LEVELS_UTILS_H_
+
+#include <stdlib.h>
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Replace the input 'data' of size 'width'x'height' with 'num-levels'
+// quantized values. If not NULL, 'sse' will contain the sum of squared error.
+// Valid range for 'num_levels' is [2, 256].
+// Returns false in case of error (data is NULL, or parameters are invalid).
+int QuantizeLevels(uint8_t* const data, int width, int height, int num_levels,
+ uint64_t* const sse);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_QUANT_LEVELS_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/random_utils.c b/src/third_party/libwebp/src/utils/random_utils.c
new file mode 100644
index 0000000..d8d26ae
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/random_utils.c
@@ -0,0 +1,47 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Pseudo-random utilities
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <string.h>
+#endif
+#include "src/utils/random_utils.h"
+
+//------------------------------------------------------------------------------
+
+// 31b-range values
+static const uint32_t kRandomTable[VP8_RANDOM_TABLE_SIZE] = {
+ 0x0de15230, 0x03b31886, 0x775faccb, 0x1c88626a, 0x68385c55, 0x14b3b828,
+ 0x4a85fef8, 0x49ddb84b, 0x64fcf397, 0x5c550289, 0x4a290000, 0x0d7ec1da,
+ 0x5940b7ab, 0x5492577d, 0x4e19ca72, 0x38d38c69, 0x0c01ee65, 0x32a1755f,
+ 0x5437f652, 0x5abb2c32, 0x0faa57b1, 0x73f533e7, 0x685feeda, 0x7563cce2,
+ 0x6e990e83, 0x4730a7ed, 0x4fc0d9c6, 0x496b153c, 0x4f1403fa, 0x541afb0c,
+ 0x73990b32, 0x26d7cb1c, 0x6fcc3706, 0x2cbb77d8, 0x75762f2a, 0x6425ccdd,
+ 0x24b35461, 0x0a7d8715, 0x220414a8, 0x141ebf67, 0x56b41583, 0x73e502e3,
+ 0x44cab16f, 0x28264d42, 0x73baaefb, 0x0a50ebed, 0x1d6ab6fb, 0x0d3ad40b,
+ 0x35db3b68, 0x2b081e83, 0x77ce6b95, 0x5181e5f0, 0x78853bbc, 0x009f9494,
+ 0x27e5ed3c
+};
+
+void VP8InitRandom(VP8Random* const rg, float dithering) {
+ memcpy(rg->tab_, kRandomTable, sizeof(rg->tab_));
+ rg->index1_ = 0;
+ rg->index2_ = 31;
+ rg->amp_ = (dithering < 0.0) ? 0
+ : (dithering > 1.0) ? (1 << VP8_RANDOM_DITHER_FIX)
+ : (uint32_t)((1 << VP8_RANDOM_DITHER_FIX) * dithering);
+}
+
+//------------------------------------------------------------------------------
+
diff --git a/src/third_party/libwebp/src/utils/random_utils.h b/src/third_party/libwebp/src/utils/random_utils.h
new file mode 100644
index 0000000..32d5d41
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/random_utils.h
@@ -0,0 +1,67 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Pseudo-random utilities
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_UTILS_RANDOM_UTILS_H_
+#define WEBP_UTILS_RANDOM_UTILS_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#else
+#include <assert.h>
+#endif
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VP8_RANDOM_DITHER_FIX 8 // fixed-point precision for dithering
+#define VP8_RANDOM_TABLE_SIZE 55
+
+typedef struct {
+ int index1_, index2_;
+ uint32_t tab_[VP8_RANDOM_TABLE_SIZE];
+ int amp_;
+} VP8Random;
+
+// Initializes random generator with an amplitude 'dithering' in range [0..1].
+void VP8InitRandom(VP8Random* const rg, float dithering);
+
+// Returns a centered pseudo-random number with 'num_bits' amplitude.
+// (uses D.Knuth's Difference-based random generator).
+// 'amp' is in VP8_RANDOM_DITHER_FIX fixed-point precision.
+static WEBP_INLINE int VP8RandomBits2(VP8Random* const rg, int num_bits,
+ int amp) {
+ int diff;
+ SB_DCHECK(num_bits + VP8_RANDOM_DITHER_FIX <= 31);
+ diff = rg->tab_[rg->index1_] - rg->tab_[rg->index2_];
+ if (diff < 0) diff += (1u << 31);
+ rg->tab_[rg->index1_] = diff;
+ if (++rg->index1_ == VP8_RANDOM_TABLE_SIZE) rg->index1_ = 0;
+ if (++rg->index2_ == VP8_RANDOM_TABLE_SIZE) rg->index2_ = 0;
+ // sign-extend, 0-center
+ diff = (int)((uint32_t)diff << 1) >> (32 - num_bits);
+ diff = (diff * amp) >> VP8_RANDOM_DITHER_FIX; // restrict range
+ diff += 1 << (num_bits - 1); // shift back to 0.5-center
+ return diff;
+}
+
+static WEBP_INLINE int VP8RandomBits(VP8Random* const rg, int num_bits) {
+ return VP8RandomBits2(rg, num_bits, rg->amp_);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_RANDOM_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/rescaler_utils.c b/src/third_party/libwebp/src/utils/rescaler_utils.c
new file mode 100644
index 0000000..90e2ea7
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/rescaler_utils.c
@@ -0,0 +1,148 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Rescaling functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "src/dsp/dsp.h"
+#include "src/utils/rescaler_utils.h"
+
+//------------------------------------------------------------------------------
+
+void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
+ uint8_t* const dst,
+ int dst_width, int dst_height, int dst_stride,
+ int num_channels, rescaler_t* const work) {
+ const int x_add = src_width, x_sub = dst_width;
+ const int y_add = src_height, y_sub = dst_height;
+ wrk->x_expand = (src_width < dst_width);
+ wrk->y_expand = (src_height < dst_height);
+ wrk->src_width = src_width;
+ wrk->src_height = src_height;
+ wrk->dst_width = dst_width;
+ wrk->dst_height = dst_height;
+ wrk->src_y = 0;
+ wrk->dst_y = 0;
+ wrk->dst = dst;
+ wrk->dst_stride = dst_stride;
+ wrk->num_channels = num_channels;
+
+ // for 'x_expand', we use bilinear interpolation
+ wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add;
+ wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub;
+ if (!wrk->x_expand) { // fx_scale is not used otherwise
+ wrk->fx_scale = WEBP_RESCALER_FRAC(1, wrk->x_sub);
+ }
+ // vertical scaling parameters
+ wrk->y_add = wrk->y_expand ? y_add - 1 : y_add;
+ wrk->y_sub = wrk->y_expand ? y_sub - 1 : y_sub;
+ wrk->y_accum = wrk->y_expand ? wrk->y_sub : wrk->y_add;
+ if (!wrk->y_expand) {
+ // This is WEBP_RESCALER_FRAC(dst_height, x_add * y_add) without the cast.
+ // Its value is <= WEBP_RESCALER_ONE, because dst_height <= wrk->y_add, and
+ // wrk->x_add >= 1;
+ const uint64_t ratio =
+ (uint64_t)dst_height * WEBP_RESCALER_ONE / (wrk->x_add * wrk->y_add);
+ if (ratio != (uint32_t)ratio) {
+ // When ratio == WEBP_RESCALER_ONE, we can't represent the ratio with the
+ // current fixed-point precision. This happens when src_height ==
+ // wrk->y_add (which == src_height), and wrk->x_add == 1.
+ // => We special-case fxy_scale = 0, in WebPRescalerExportRow().
+ wrk->fxy_scale = 0;
+ } else {
+ wrk->fxy_scale = (uint32_t)ratio;
+ }
+ wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->y_sub);
+ } else {
+ wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->x_add);
+ // wrk->fxy_scale is unused here.
+ }
+ wrk->irow = work;
+ wrk->frow = work + num_channels * dst_width;
+ memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
+
+ WebPRescalerDspInit();
+}
+
+int WebPRescalerGetScaledDimensions(int src_width, int src_height,
+ int* const scaled_width,
+ int* const scaled_height) {
+ assert(scaled_width != NULL);
+ assert(scaled_height != NULL);
+ {
+ int width = *scaled_width;
+ int height = *scaled_height;
+
+ // if width is unspecified, scale original proportionally to height ratio.
+ if (width == 0) {
+ width =
+ (int)(((uint64_t)src_width * height + src_height / 2) / src_height);
+ }
+ // if height is unspecified, scale original proportionally to width ratio.
+ if (height == 0) {
+ height =
+ (int)(((uint64_t)src_height * width + src_width / 2) / src_width);
+ }
+ // Check if the overall dimensions still make sense.
+ if (width <= 0 || height <= 0) {
+ return 0;
+ }
+
+ *scaled_width = width;
+ *scaled_height = height;
+ return 1;
+ }
+}
+
+//------------------------------------------------------------------------------
+// all-in-one calls
+
+int WebPRescaleNeededLines(const WebPRescaler* const wrk, int max_num_lines) {
+ const int num_lines = (wrk->y_accum + wrk->y_sub - 1) / wrk->y_sub;
+ return (num_lines > max_num_lines) ? max_num_lines : num_lines;
+}
+
+int WebPRescalerImport(WebPRescaler* const wrk, int num_lines,
+ const uint8_t* src, int src_stride) {
+ int total_imported = 0;
+ while (total_imported < num_lines && !WebPRescalerHasPendingOutput(wrk)) {
+ if (wrk->y_expand) {
+ rescaler_t* const tmp = wrk->irow;
+ wrk->irow = wrk->frow;
+ wrk->frow = tmp;
+ }
+ WebPRescalerImportRow(wrk, src);
+ if (!wrk->y_expand) { // Accumulate the contribution of the new row.
+ int x;
+ for (x = 0; x < wrk->num_channels * wrk->dst_width; ++x) {
+ wrk->irow[x] += wrk->frow[x];
+ }
+ }
+ ++wrk->src_y;
+ src += src_stride;
+ ++total_imported;
+ wrk->y_accum -= wrk->y_sub;
+ }
+ return total_imported;
+}
+
+int WebPRescalerExport(WebPRescaler* const rescaler) {
+ int total_exported = 0;
+ while (WebPRescalerHasPendingOutput(rescaler)) {
+ WebPRescalerExportRow(rescaler);
+ ++total_exported;
+ }
+ return total_exported;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/utils/rescaler_utils.h b/src/third_party/libwebp/src/utils/rescaler_utils.h
new file mode 100644
index 0000000..8890e6f
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/rescaler_utils.h
@@ -0,0 +1,101 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Rescaling functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_UTILS_RESCALER_UTILS_H_
+#define WEBP_UTILS_RESCALER_UTILS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "src/webp/types.h"
+
+#define WEBP_RESCALER_RFIX 32 // fixed-point precision for multiplies
+#define WEBP_RESCALER_ONE (1ull << WEBP_RESCALER_RFIX)
+#define WEBP_RESCALER_FRAC(x, y) \
+ ((uint32_t)(((uint64_t)(x) << WEBP_RESCALER_RFIX) / (y)))
+
+// Structure used for on-the-fly rescaling
+typedef uint32_t rescaler_t; // type for side-buffer
+typedef struct WebPRescaler WebPRescaler;
+struct WebPRescaler {
+ int x_expand; // true if we're expanding in the x direction
+ int y_expand; // true if we're expanding in the y direction
+ int num_channels; // bytes to jump between pixels
+ uint32_t fx_scale; // fixed-point scaling factors
+ uint32_t fy_scale; // ''
+ uint32_t fxy_scale; // ''
+ int y_accum; // vertical accumulator
+ int y_add, y_sub; // vertical increments
+ int x_add, x_sub; // horizontal increments
+ int src_width, src_height; // source dimensions
+ int dst_width, dst_height; // destination dimensions
+ int src_y, dst_y; // row counters for input and output
+ uint8_t* dst;
+ int dst_stride;
+ rescaler_t* irow, *frow; // work buffer
+};
+
+// Initialize a rescaler given scratch area 'work' and dimensions of src & dst.
+void WebPRescalerInit(WebPRescaler* const rescaler,
+ int src_width, int src_height,
+ uint8_t* const dst,
+ int dst_width, int dst_height, int dst_stride,
+ int num_channels,
+ rescaler_t* const work);
+
+// If either 'scaled_width' or 'scaled_height' (but not both) is 0 the value
+// will be calculated preserving the aspect ratio, otherwise the values are
+// left unmodified. Returns true on success, false if either value is 0 after
+// performing the scaling calculation.
+int WebPRescalerGetScaledDimensions(int src_width, int src_height,
+ int* const scaled_width,
+ int* const scaled_height);
+
+// Returns the number of input lines needed next to produce one output line,
+// considering that the maximum available input lines are 'max_num_lines'.
+int WebPRescaleNeededLines(const WebPRescaler* const rescaler,
+ int max_num_lines);
+
+// Import multiple rows over all channels, until at least one row is ready to
+// be exported. Returns the actual number of lines that were imported.
+int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows,
+ const uint8_t* src, int src_stride);
+
+// Export as many rows as possible. Return the numbers of rows written.
+int WebPRescalerExport(WebPRescaler* const rescaler);
+
+// Return true if input is finished
+static WEBP_INLINE
+int WebPRescalerInputDone(const WebPRescaler* const rescaler) {
+ return (rescaler->src_y >= rescaler->src_height);
+}
+// Return true if output is finished
+static WEBP_INLINE
+int WebPRescalerOutputDone(const WebPRescaler* const rescaler) {
+ return (rescaler->dst_y >= rescaler->dst_height);
+}
+
+// Return true if there are pending output rows ready.
+static WEBP_INLINE
+int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) {
+ return !WebPRescalerOutputDone(rescaler) && (rescaler->y_accum <= 0);
+}
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_RESCALER_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/thread_utils.c b/src/third_party/libwebp/src/utils/thread_utils.c
new file mode 100644
index 0000000..2052b6b
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/thread_utils.c
@@ -0,0 +1,359 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Multi-threaded worker
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <string.h> // for memset()
+#include "src/utils/thread_utils.h"
+#include "src/utils/utils.h"
+
+#ifdef WEBP_USE_THREAD
+
+#if defined(_WIN32)
+
+#include <windows.h>
+typedef HANDLE pthread_t;
+typedef CRITICAL_SECTION pthread_mutex_t;
+
+#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater
+#define USE_WINDOWS_CONDITION_VARIABLE
+typedef CONDITION_VARIABLE pthread_cond_t;
+#else
+typedef struct {
+ HANDLE waiting_sem_;
+ HANDLE received_sem_;
+ HANDLE signal_event_;
+} pthread_cond_t;
+#endif // _WIN32_WINNT >= 0x600
+
+#ifndef WINAPI_FAMILY_PARTITION
+#define WINAPI_PARTITION_DESKTOP 1
+#define WINAPI_FAMILY_PARTITION(x) x
+#endif
+
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#define USE_CREATE_THREAD
+#endif
+
+#else // !_WIN32
+
+#include <pthread.h>
+
+#endif // _WIN32
+
+typedef struct {
+ pthread_mutex_t mutex_;
+ pthread_cond_t condition_;
+ pthread_t thread_;
+} WebPWorkerImpl;
+
+#if defined(_WIN32)
+
+//------------------------------------------------------------------------------
+// simplistic pthread emulation layer
+
+#include <process.h>
+
+// _beginthreadex requires __stdcall
+#define THREADFN unsigned int __stdcall
+#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val)
+
+#if _WIN32_WINNT >= 0x0501 // Windows XP or greater
+#define WaitForSingleObject(obj, timeout) \
+ WaitForSingleObjectEx(obj, timeout, FALSE /*bAlertable*/)
+#endif
+
+static int pthread_create(pthread_t* const thread, const void* attr,
+ unsigned int (__stdcall *start)(void*), void* arg) {
+ (void)attr;
+#ifdef USE_CREATE_THREAD
+ *thread = CreateThread(NULL, /* lpThreadAttributes */
+ 0, /* dwStackSize */
+ start,
+ arg,
+ 0, /* dwStackSize */
+ NULL); /* lpThreadId */
+#else
+ *thread = (pthread_t)_beginthreadex(NULL, /* void *security */
+ 0, /* unsigned stack_size */
+ start,
+ arg,
+ 0, /* unsigned initflag */
+ NULL); /* unsigned *thrdaddr */
+#endif
+ if (*thread == NULL) return 1;
+ SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL);
+ return 0;
+}
+
+static int pthread_join(pthread_t thread, void** value_ptr) {
+ (void)value_ptr;
+ return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 ||
+ CloseHandle(thread) == 0);
+}
+
+// Mutex
+static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) {
+ (void)mutexattr;
+#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater
+ InitializeCriticalSectionEx(mutex, 0 /*dwSpinCount*/, 0 /*Flags*/);
+#else
+ InitializeCriticalSection(mutex);
+#endif
+ return 0;
+}
+
+static int pthread_mutex_lock(pthread_mutex_t* const mutex) {
+ EnterCriticalSection(mutex);
+ return 0;
+}
+
+static int pthread_mutex_unlock(pthread_mutex_t* const mutex) {
+ LeaveCriticalSection(mutex);
+ return 0;
+}
+
+static int pthread_mutex_destroy(pthread_mutex_t* const mutex) {
+ DeleteCriticalSection(mutex);
+ return 0;
+}
+
+// Condition
+static int pthread_cond_destroy(pthread_cond_t* const condition) {
+ int ok = 1;
+#ifdef USE_WINDOWS_CONDITION_VARIABLE
+ (void)condition;
+#else
+ ok &= (CloseHandle(condition->waiting_sem_) != 0);
+ ok &= (CloseHandle(condition->received_sem_) != 0);
+ ok &= (CloseHandle(condition->signal_event_) != 0);
+#endif
+ return !ok;
+}
+
+static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) {
+ (void)cond_attr;
+#ifdef USE_WINDOWS_CONDITION_VARIABLE
+ InitializeConditionVariable(condition);
+#else
+ condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
+ condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
+ condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (condition->waiting_sem_ == NULL ||
+ condition->received_sem_ == NULL ||
+ condition->signal_event_ == NULL) {
+ pthread_cond_destroy(condition);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static int pthread_cond_signal(pthread_cond_t* const condition) {
+ int ok = 1;
+#ifdef USE_WINDOWS_CONDITION_VARIABLE
+ WakeConditionVariable(condition);
+#else
+ if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) {
+ // a thread is waiting in pthread_cond_wait: allow it to be notified
+ ok = SetEvent(condition->signal_event_);
+ // wait until the event is consumed so the signaler cannot consume
+ // the event via its own pthread_cond_wait.
+ ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) !=
+ WAIT_OBJECT_0);
+ }
+#endif
+ return !ok;
+}
+
+static int pthread_cond_wait(pthread_cond_t* const condition,
+ pthread_mutex_t* const mutex) {
+ int ok;
+#ifdef USE_WINDOWS_CONDITION_VARIABLE
+ ok = SleepConditionVariableCS(condition, mutex, INFINITE);
+#else
+ // note that there is a consumer available so the signal isn't dropped in
+ // pthread_cond_signal
+ if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL)) return 1;
+ // now unlock the mutex so pthread_cond_signal may be issued
+ pthread_mutex_unlock(mutex);
+ ok = (WaitForSingleObject(condition->signal_event_, INFINITE) ==
+ WAIT_OBJECT_0);
+ ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL);
+ pthread_mutex_lock(mutex);
+#endif
+ return !ok;
+}
+
+#else // !_WIN32
+# define THREADFN void*
+# define THREAD_RETURN(val) val
+#endif // _WIN32
+
+//------------------------------------------------------------------------------
+
+static THREADFN ThreadLoop(void* ptr) {
+ WebPWorker* const worker = (WebPWorker*)ptr;
+ WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_;
+ int done = 0;
+ while (!done) {
+ pthread_mutex_lock(&impl->mutex_);
+ while (worker->status_ == OK) { // wait in idling mode
+ pthread_cond_wait(&impl->condition_, &impl->mutex_);
+ }
+ if (worker->status_ == WORK) {
+ WebPGetWorkerInterface()->Execute(worker);
+ worker->status_ = OK;
+ } else if (worker->status_ == NOT_OK) { // finish the worker
+ done = 1;
+ }
+ // signal to the main thread that we're done (for Sync())
+ pthread_cond_signal(&impl->condition_);
+ pthread_mutex_unlock(&impl->mutex_);
+ }
+ return THREAD_RETURN(NULL); // Thread is finished
+}
+
+// main thread state control
+static void ChangeState(WebPWorker* const worker, WebPWorkerStatus new_status) {
+ // No-op when attempting to change state on a thread that didn't come up.
+ // Checking status_ without acquiring the lock first would result in a data
+ // race.
+ WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_;
+ if (impl == NULL) return;
+
+ pthread_mutex_lock(&impl->mutex_);
+ if (worker->status_ >= OK) {
+ // wait for the worker to finish
+ while (worker->status_ != OK) {
+ pthread_cond_wait(&impl->condition_, &impl->mutex_);
+ }
+ // assign new status and release the working thread if needed
+ if (new_status != OK) {
+ worker->status_ = new_status;
+ pthread_cond_signal(&impl->condition_);
+ }
+ }
+ pthread_mutex_unlock(&impl->mutex_);
+}
+
+#endif // WEBP_USE_THREAD
+
+//------------------------------------------------------------------------------
+
+static void Init(WebPWorker* const worker) {
+ memset(worker, 0, sizeof(*worker));
+ worker->status_ = NOT_OK;
+}
+
+static int Sync(WebPWorker* const worker) {
+#ifdef WEBP_USE_THREAD
+ ChangeState(worker, OK);
+#endif
+ assert(worker->status_ <= OK);
+ return !worker->had_error;
+}
+
+static int Reset(WebPWorker* const worker) {
+ int ok = 1;
+ worker->had_error = 0;
+ if (worker->status_ < OK) {
+#ifdef WEBP_USE_THREAD
+ WebPWorkerImpl* const impl =
+ (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(WebPWorkerImpl));
+ worker->impl_ = (void*)impl;
+ if (worker->impl_ == NULL) {
+ return 0;
+ }
+ if (pthread_mutex_init(&impl->mutex_, NULL)) {
+ goto Error;
+ }
+ if (pthread_cond_init(&impl->condition_, NULL)) {
+ pthread_mutex_destroy(&impl->mutex_);
+ goto Error;
+ }
+ pthread_mutex_lock(&impl->mutex_);
+ ok = !pthread_create(&impl->thread_, NULL, ThreadLoop, worker);
+ if (ok) worker->status_ = OK;
+ pthread_mutex_unlock(&impl->mutex_);
+ if (!ok) {
+ pthread_mutex_destroy(&impl->mutex_);
+ pthread_cond_destroy(&impl->condition_);
+ Error:
+ WebPSafeFree(impl);
+ worker->impl_ = NULL;
+ return 0;
+ }
+#else
+ worker->status_ = OK;
+#endif
+ } else if (worker->status_ > OK) {
+ ok = Sync(worker);
+ }
+ assert(!ok || (worker->status_ == OK));
+ return ok;
+}
+
+static void Execute(WebPWorker* const worker) {
+ if (worker->hook != NULL) {
+ worker->had_error |= !worker->hook(worker->data1, worker->data2);
+ }
+}
+
+static void Launch(WebPWorker* const worker) {
+#ifdef WEBP_USE_THREAD
+ ChangeState(worker, WORK);
+#else
+ Execute(worker);
+#endif
+}
+
+static void End(WebPWorker* const worker) {
+#ifdef WEBP_USE_THREAD
+ if (worker->impl_ != NULL) {
+ WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_;
+ ChangeState(worker, NOT_OK);
+ pthread_join(impl->thread_, NULL);
+ pthread_mutex_destroy(&impl->mutex_);
+ pthread_cond_destroy(&impl->condition_);
+ WebPSafeFree(impl);
+ worker->impl_ = NULL;
+ }
+#else
+ worker->status_ = NOT_OK;
+ assert(worker->impl_ == NULL);
+#endif
+ assert(worker->status_ == NOT_OK);
+}
+
+//------------------------------------------------------------------------------
+
+static WebPWorkerInterface g_worker_interface = {
+ Init, Reset, Sync, Launch, Execute, End
+};
+
+int WebPSetWorkerInterface(const WebPWorkerInterface* const winterface) {
+ if (winterface == NULL ||
+ winterface->Init == NULL || winterface->Reset == NULL ||
+ winterface->Sync == NULL || winterface->Launch == NULL ||
+ winterface->Execute == NULL || winterface->End == NULL) {
+ return 0;
+ }
+ g_worker_interface = *winterface;
+ return 1;
+}
+
+const WebPWorkerInterface* WebPGetWorkerInterface(void) {
+ return &g_worker_interface;
+}
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/utils/thread_utils.h b/src/third_party/libwebp/src/utils/thread_utils.h
new file mode 100644
index 0000000..c8ae6c9
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/thread_utils.h
@@ -0,0 +1,90 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Multi-threaded worker
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_UTILS_THREAD_UTILS_H_
+#define WEBP_UTILS_THREAD_UTILS_H_
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// State of the worker thread object
+typedef enum {
+ NOT_OK = 0, // object is unusable
+ OK, // ready to work
+ WORK // busy finishing the current task
+} WebPWorkerStatus;
+
+// Function to be called by the worker thread. Takes two opaque pointers as
+// arguments (data1 and data2), and should return false in case of error.
+typedef int (*WebPWorkerHook)(void*, void*);
+
+// Synchronization object used to launch job in the worker thread
+typedef struct {
+ void* impl_; // platform-dependent implementation worker details
+ WebPWorkerStatus status_;
+ WebPWorkerHook hook; // hook to call
+ void* data1; // first argument passed to 'hook'
+ void* data2; // second argument passed to 'hook'
+ int had_error; // return value of the last call to 'hook'
+} WebPWorker;
+
+// The interface for all thread-worker related functions. All these functions
+// must be implemented.
+typedef struct {
+ // Must be called first, before any other method.
+ void (*Init)(WebPWorker* const worker);
+ // Must be called to initialize the object and spawn the thread. Re-entrant.
+ // Will potentially launch the thread. Returns false in case of error.
+ int (*Reset)(WebPWorker* const worker);
+ // Makes sure the previous work is finished. Returns true if worker->had_error
+ // was not set and no error condition was triggered by the working thread.
+ int (*Sync)(WebPWorker* const worker);
+ // Triggers the thread to call hook() with data1 and data2 arguments. These
+ // hook/data1/data2 values can be changed at any time before calling this
+ // function, but not be changed afterward until the next call to Sync().
+ void (*Launch)(WebPWorker* const worker);
+ // This function is similar to Launch() except that it calls the
+ // hook directly instead of using a thread. Convenient to bypass the thread
+ // mechanism while still using the WebPWorker structs. Sync() must
+ // still be called afterward (for error reporting).
+ void (*Execute)(WebPWorker* const worker);
+ // Kill the thread and terminate the object. To use the object again, one
+ // must call Reset() again.
+ void (*End)(WebPWorker* const worker);
+} WebPWorkerInterface;
+
+// Install a new set of threading functions, overriding the defaults. This
+// should be done before any workers are started, i.e., before any encoding or
+// decoding takes place. The contents of the interface struct are copied, it
+// is safe to free the corresponding memory after this call. This function is
+// not thread-safe. Return false in case of invalid pointer or methods.
+WEBP_EXTERN int WebPSetWorkerInterface(
+ const WebPWorkerInterface* const winterface);
+
+// Retrieve the currently set thread worker interface.
+WEBP_EXTERN const WebPWorkerInterface* WebPGetWorkerInterface(void);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_THREAD_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/utils/utils.c b/src/third_party/libwebp/src/utils/utils.c
new file mode 100644
index 0000000..8d6486a
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/utils.c
@@ -0,0 +1,339 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Misc. common utility functions
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#if defined(STARBOARD)
+#include "starboard/client_porting/poem/assert_poem.h"
+#include "starboard/client_porting/poem/stdio_poem.h"
+#include "starboard/client_porting/poem/stdlib_poem.h"
+#include "starboard/client_porting/poem/string_poem.h"
+#else
+#include <stdlib.h>
+#include <string.h> // for memcpy()
+#endif
+#include "src/webp/decode.h"
+#include "src/webp/encode.h"
+#include "src/webp/format_constants.h" // for MAX_PALETTE_SIZE
+#include "src/utils/color_cache_utils.h"
+#include "src/utils/utils.h"
+
+// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
+// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
+// and not multi-thread safe!).
+// An interesting alternative is valgrind's 'massif' tool:
+// http://valgrind.org/docs/manual/ms-manual.html
+// Here is an example command line:
+/* valgrind --tool=massif --massif-out-file=massif.out \
+ --stacks=yes --alloc-fn=WebPSafeMalloc --alloc-fn=WebPSafeCalloc
+ ms_print massif.out
+*/
+// In addition:
+// * if PRINT_MEM_TRAFFIC is defined, all the details of the malloc/free cycles
+// are printed.
+// * if MALLOC_FAIL_AT is defined, the global environment variable
+// $MALLOC_FAIL_AT is used to simulate a memory error when calloc or malloc
+// is called for the nth time. Example usage:
+// export MALLOC_FAIL_AT=50 && ./examples/cwebp input.png
+// * if MALLOC_LIMIT is defined, the global environment variable $MALLOC_LIMIT
+// sets the maximum amount of memory (in bytes) made available to libwebp.
+// This can be used to emulate environment with very limited memory.
+// Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp
+
+// #define PRINT_MEM_INFO
+// #define PRINT_MEM_TRAFFIC
+// #define MALLOC_FAIL_AT
+// #define MALLOC_LIMIT
+
+//------------------------------------------------------------------------------
+// Checked memory allocation
+
+#if defined(PRINT_MEM_INFO)
+
+#if !defined(STARBOARD)
+#include <stdio.h>
+#endif
+
+static int num_malloc_calls = 0;
+static int num_calloc_calls = 0;
+static int num_free_calls = 0;
+static int countdown_to_fail = 0; // 0 = off
+
+typedef struct MemBlock MemBlock;
+struct MemBlock {
+ void* ptr_;
+ size_t size_;
+ MemBlock* next_;
+};
+
+static MemBlock* all_blocks = NULL;
+static size_t total_mem = 0;
+static size_t total_mem_allocated = 0;
+static size_t high_water_mark = 0;
+static size_t mem_limit = 0;
+
+static int exit_registered = 0;
+
+static void PrintMemInfo(void) {
+ fprintf(stderr, "\nMEMORY INFO:\n");
+ fprintf(stderr, "num calls to: malloc = %4d\n", num_malloc_calls);
+ fprintf(stderr, " calloc = %4d\n", num_calloc_calls);
+ fprintf(stderr, " free = %4d\n", num_free_calls);
+ fprintf(stderr, "total_mem: %u\n", (uint32_t)total_mem);
+ fprintf(stderr, "total_mem allocated: %u\n", (uint32_t)total_mem_allocated);
+ fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark);
+ while (all_blocks != NULL) {
+ MemBlock* b = all_blocks;
+ all_blocks = b->next_;
+ free(b);
+ }
+}
+
+static void Increment(int* const v) {
+ if (!exit_registered) {
+#if defined(MALLOC_FAIL_AT)
+ {
+ const char* const malloc_fail_at_str = getenv("MALLOC_FAIL_AT");
+ if (malloc_fail_at_str != NULL) {
+ countdown_to_fail = atoi(malloc_fail_at_str);
+ }
+ }
+#endif
+#if defined(MALLOC_LIMIT)
+ {
+ const char* const malloc_limit_str = getenv("MALLOC_LIMIT");
+ if (malloc_limit_str != NULL) {
+ mem_limit = atoi(malloc_limit_str);
+ }
+ }
+#endif
+ (void)countdown_to_fail;
+ (void)mem_limit;
+ atexit(PrintMemInfo);
+ exit_registered = 1;
+ }
+ ++*v;
+}
+
+static void AddMem(void* ptr, size_t size) {
+ if (ptr != NULL) {
+ MemBlock* const b = (MemBlock*)malloc(sizeof(*b));
+ if (b == NULL) abort();
+ b->next_ = all_blocks;
+ all_blocks = b;
+ b->ptr_ = ptr;
+ b->size_ = size;
+ total_mem += size;
+ total_mem_allocated += size;
+#if defined(PRINT_MEM_TRAFFIC)
+#if defined(MALLOC_FAIL_AT)
+ fprintf(stderr, "fail-count: %5d [mem=%u]\n",
+ num_malloc_calls + num_calloc_calls, (uint32_t)total_mem);
+#else
+ fprintf(stderr, "Mem: %u (+%u)\n", (uint32_t)total_mem, (uint32_t)size);
+#endif
+#endif
+ if (total_mem > high_water_mark) high_water_mark = total_mem;
+ }
+}
+
+static void SubMem(void* ptr) {
+ if (ptr != NULL) {
+ MemBlock** b = &all_blocks;
+ // Inefficient search, but that's just for debugging.
+ while (*b != NULL && (*b)->ptr_ != ptr) b = &(*b)->next_;
+ if (*b == NULL) {
+ fprintf(stderr, "Invalid pointer free! (%p)\n", ptr);
+ abort();
+ }
+ {
+ MemBlock* const block = *b;
+ *b = block->next_;
+ total_mem -= block->size_;
+#if defined(PRINT_MEM_TRAFFIC)
+ fprintf(stderr, "Mem: %u (-%u)\n",
+ (uint32_t)total_mem, (uint32_t)block->size_);
+#endif
+ free(block);
+ }
+ }
+}
+
+#else
+#define Increment(v) do {} while (0)
+#define AddMem(p, s) do {} while (0)
+#define SubMem(p) do {} while (0)
+#endif
+
+// Returns 0 in case of overflow of nmemb * size.
+static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
+ const uint64_t total_size = nmemb * size;
+ if (nmemb == 0) return 1;
+ if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
+ if (total_size != (size_t)total_size) return 0;
+#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
+ if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
+ return 0; // fake fail!
+ }
+#endif
+#if defined(MALLOC_LIMIT)
+ if (mem_limit > 0) {
+ const uint64_t new_total_mem = (uint64_t)total_mem + total_size;
+ if (new_total_mem != (size_t)new_total_mem ||
+ new_total_mem > mem_limit) {
+ return 0; // fake fail!
+ }
+ }
+#endif
+
+ return 1;
+}
+
+void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
+ void* ptr;
+ Increment(&num_malloc_calls);
+ if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
+ assert(nmemb * size > 0);
+ ptr = malloc((size_t)(nmemb * size));
+ AddMem(ptr, (size_t)(nmemb * size));
+ return ptr;
+}
+
+void* WebPSafeCalloc(uint64_t nmemb, size_t size) {
+ void* ptr;
+ Increment(&num_calloc_calls);
+ if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
+ assert(nmemb * size > 0);
+ ptr = calloc((size_t)nmemb, size);
+ AddMem(ptr, (size_t)(nmemb * size));
+ return ptr;
+}
+
+void WebPSafeFree(void* const ptr) {
+ if (ptr != NULL) {
+ Increment(&num_free_calls);
+ SubMem(ptr);
+ }
+ free(ptr);
+}
+
+// Public API function.
+void WebPFree(void* ptr) {
+ free(ptr);
+}
+
+//------------------------------------------------------------------------------
+
+void WebPCopyPlane(const uint8_t* src, int src_stride,
+ uint8_t* dst, int dst_stride, int width, int height) {
+ assert(src != NULL && dst != NULL);
+ assert(src_stride >= width && dst_stride >= width);
+ while (height-- > 0) {
+ memcpy(dst, src, width);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
+ assert(src != NULL && dst != NULL);
+ assert(src->width == dst->width && src->height == dst->height);
+ assert(src->use_argb && dst->use_argb);
+ WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
+ 4 * dst->argb_stride, 4 * src->width, src->height);
+}
+
+//------------------------------------------------------------------------------
+
+#define COLOR_HASH_SIZE (MAX_PALETTE_SIZE * 4)
+#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE).
+
+int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
+ int i;
+ int x, y;
+ int num_colors = 0;
+ uint8_t in_use[COLOR_HASH_SIZE] = { 0 };
+ uint32_t colors[COLOR_HASH_SIZE];
+ const uint32_t* argb = pic->argb;
+ const int width = pic->width;
+ const int height = pic->height;
+ uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0]
+ assert(pic != NULL);
+ assert(pic->use_argb);
+
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ int key;
+ if (argb[x] == last_pix) {
+ continue;
+ }
+ last_pix = argb[x];
+ key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
+ while (1) {
+ if (!in_use[key]) {
+ colors[key] = last_pix;
+ in_use[key] = 1;
+ ++num_colors;
+ if (num_colors > MAX_PALETTE_SIZE) {
+ return MAX_PALETTE_SIZE + 1; // Exact count not needed.
+ }
+ break;
+ } else if (colors[key] == last_pix) {
+ break; // The color is already there.
+ } else {
+ // Some other color sits here, so do linear conflict resolution.
+ ++key;
+ key &= (COLOR_HASH_SIZE - 1); // Key mask.
+ }
+ }
+ }
+ argb += pic->argb_stride;
+ }
+
+ if (palette != NULL) { // Fill the colors into palette.
+ num_colors = 0;
+ for (i = 0; i < COLOR_HASH_SIZE; ++i) {
+ if (in_use[i]) {
+ palette[num_colors] = colors[i];
+ ++num_colors;
+ }
+ }
+ }
+ return num_colors;
+}
+
+#undef COLOR_HASH_SIZE
+#undef COLOR_HASH_RIGHT_SHIFT
+
+//------------------------------------------------------------------------------
+
+#if defined(WEBP_NEED_LOG_TABLE_8BIT)
+const uint8_t WebPLogTable8bit[256] = { // 31 ^ clz(i)
+ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+};
+#endif
+
+//------------------------------------------------------------------------------
diff --git a/src/third_party/libwebp/src/utils/utils.h b/src/third_party/libwebp/src/utils/utils.h
new file mode 100644
index 0000000..69b9af5
--- /dev/null
+++ b/src/third_party/libwebp/src/utils/utils.h
@@ -0,0 +1,186 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Misc. common utility functions
+//
+// Authors: Skal (pascal.massimino@gmail.com)
+// Urvang (urvang@google.com)
+
+#ifndef WEBP_UTILS_UTILS_H_
+#define WEBP_UTILS_UTILS_H_
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#else
+#include <assert.h>
+#include <limits.h>
+#endif
+
+#include "src/dsp/dsp.h"
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// Memory allocation
+
+// This is the maximum memory amount that libwebp will ever try to allocate.
+#ifndef WEBP_MAX_ALLOCABLE_MEMORY
+#if SIZE_MAX > (1ULL << 34)
+#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 34)
+#else
+// For 32-bit targets keep this below INT_MAX to avoid valgrind warnings.
+#define WEBP_MAX_ALLOCABLE_MEMORY ((1ULL << 31) - (1 << 16))
+#endif
+#endif // WEBP_MAX_ALLOCABLE_MEMORY
+
+// size-checking safe malloc/calloc: verify that the requested size is not too
+// large, or return NULL. You don't need to call these for constructs like
+// malloc(sizeof(foo)), but only if there's picture-dependent size involved
+// somewhere (like: malloc(num_pixels * sizeof(*something))). That's why this
+// safe malloc() borrows the signature from calloc(), pointing at the dangerous
+// underlying multiply involved.
+WEBP_EXTERN void* WebPSafeMalloc(uint64_t nmemb, size_t size);
+// Note that WebPSafeCalloc() expects the second argument type to be 'size_t'
+// in order to favor the "calloc(num_foo, sizeof(foo))" pattern.
+WEBP_EXTERN void* WebPSafeCalloc(uint64_t nmemb, size_t size);
+
+// Companion deallocation function to the above allocations.
+WEBP_EXTERN void WebPSafeFree(void* const ptr);
+
+//------------------------------------------------------------------------------
+// Alignment
+
+#define WEBP_ALIGN_CST 31
+#define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & ~WEBP_ALIGN_CST)
+
+#if !defined(STARBOARD)
+#include <string.h>
+#endif
+
+// memcpy() is the safe way of moving potentially unaligned 32b memory.
+static WEBP_INLINE uint32_t WebPMemToUint32(const uint8_t* const ptr) {
+ uint32_t A;
+ SbMemoryCopy(&A, ptr, sizeof(A));
+ return A;
+}
+static WEBP_INLINE void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) {
+ SbMemoryCopy(ptr, &val, sizeof(val));
+}
+
+//------------------------------------------------------------------------------
+// Reading/writing data.
+
+// Read 16, 24 or 32 bits stored in little-endian order.
+static WEBP_INLINE int GetLE16(const uint8_t* const data) {
+ return (int)(data[0] << 0) | (data[1] << 8);
+}
+
+static WEBP_INLINE int GetLE24(const uint8_t* const data) {
+ return GetLE16(data) | (data[2] << 16);
+}
+
+static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) {
+ return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16);
+}
+
+// Store 16, 24 or 32 bits in little-endian order.
+static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
+ SB_DCHECK(val < (1 << 16));
+ data[0] = (val >> 0);
+ data[1] = (val >> 8);
+}
+
+static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
+ SB_DCHECK(val < (1 << 24));
+ PutLE16(data, val & 0xffff);
+ data[2] = (val >> 16);
+}
+
+static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
+ PutLE16(data, (int)(val & 0xffff));
+ PutLE16(data + 2, (int)(val >> 16));
+}
+
+// Returns 31 ^ clz(n) = log2(n). This is the default C-implementation, either
+// based on table or not. Can be used as fallback if clz() is not available.
+#define WEBP_NEED_LOG_TABLE_8BIT
+extern const uint8_t WebPLogTable8bit[256];
+static WEBP_INLINE int WebPLog2FloorC(uint32_t n) {
+ int log_value = 0;
+ while (n >= 256) {
+ log_value += 8;
+ n >>= 8;
+ }
+ return log_value + WebPLogTable8bit[n];
+}
+
+// Returns (int)floor(log2(n)). n must be > 0.
+// use GNU builtins where available.
+#if !defined(STARBOARD) && defined(__GNUC__) && \
+ ((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4)
+static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
+ return 31 ^ __builtin_clz(n);
+}
+#elif !defined(STARBOARD) && defined(_MSC_VER) && _MSC_VER > 1310 && \
+ (defined(_M_X64) || defined(_M_IX86))
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse)
+
+static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
+ unsigned long first_set_bit;
+ _BitScanReverse(&first_set_bit, n);
+ return first_set_bit;
+}
+#else // default: use the C-version.
+static WEBP_INLINE int BitsLog2Floor(uint32_t n) { return WebPLog2FloorC(n); }
+#endif
+
+//------------------------------------------------------------------------------
+// Pixel copying.
+
+struct WebPPicture;
+
+// Copy width x height pixels from 'src' to 'dst' honoring the strides.
+WEBP_EXTERN void WebPCopyPlane(const uint8_t* src, int src_stride,
+ uint8_t* dst, int dst_stride,
+ int width, int height);
+
+// Copy ARGB pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are
+// assumed to be already allocated and using ARGB data.
+WEBP_EXTERN void WebPCopyPixels(const struct WebPPicture* const src,
+ struct WebPPicture* const dst);
+
+//------------------------------------------------------------------------------
+// Unique colors.
+
+// Returns count of unique colors in 'pic', assuming pic->use_argb is true.
+// If the unique color count is more than MAX_PALETTE_SIZE, returns
+// MAX_PALETTE_SIZE+1.
+// If 'palette' is not NULL and number of unique colors is less than or equal to
+// MAX_PALETTE_SIZE, also outputs the actual unique colors into 'palette'.
+// Note: 'palette' is assumed to be an array already allocated with at least
+// MAX_PALETTE_SIZE elements.
+WEBP_EXTERN int WebPGetColorPalette(const struct WebPPicture* const pic,
+ uint32_t* const palette);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_UTILS_UTILS_H_ */
diff --git a/src/third_party/libwebp/src/webp/decode.h b/src/third_party/libwebp/src/webp/decode.h
new file mode 100644
index 0000000..2165e96
--- /dev/null
+++ b/src/third_party/libwebp/src/webp/decode.h
@@ -0,0 +1,494 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Main decoding functions for WebP images.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_WEBP_DECODE_H_
+#define WEBP_WEBP_DECODE_H_
+
+#include "./types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WEBP_DECODER_ABI_VERSION 0x0208 // MAJOR(8b) + MINOR(8b)
+
+// Note: forward declaring enumerations is not allowed in (strict) C and C++,
+// the types are left here for reference.
+// typedef enum VP8StatusCode VP8StatusCode;
+// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE;
+typedef struct WebPRGBABuffer WebPRGBABuffer;
+typedef struct WebPYUVABuffer WebPYUVABuffer;
+typedef struct WebPDecBuffer WebPDecBuffer;
+typedef struct WebPIDecoder WebPIDecoder;
+typedef struct WebPBitstreamFeatures WebPBitstreamFeatures;
+typedef struct WebPDecoderOptions WebPDecoderOptions;
+typedef struct WebPDecoderConfig WebPDecoderConfig;
+
+// Return the decoder's version number, packed in hexadecimal using 8bits for
+// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
+WEBP_EXTERN int WebPGetDecoderVersion(void);
+
+// Retrieve basic header information: width, height.
+// This function will also validate the header, returning true on success,
+// false otherwise. '*width' and '*height' are only valid on successful return.
+// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
+WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
+// with the dimensions in *width and *height. The ordering of samples in
+// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
+// The returned pointer should be deleted calling WebPFree().
+// Returns NULL in case of error.
+WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.
+WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.
+WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.
+// If the bitstream contains transparency, it is ignored.
+WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.
+WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+
+// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
+// returned is the Y samples buffer. Upon return, *u and *v will point to
+// the U and V chroma data. These U and V buffers need NOT be passed to
+// WebPFree(), unlike the returned Y luma one. The dimension of the U and V
+// planes are both (*width + 1) / 2 and (*height + 1)/ 2.
+// Upon return, the Y buffer has a stride returned as '*stride', while U and V
+// have a common stride returned as '*uv_stride'.
+// Return NULL in case of error.
+// (*) Also named Y'CbCr. See: http://en.wikipedia.org/wiki/YCbCr
+WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
+ int* width, int* height,
+ uint8_t** u, uint8_t** v,
+ int* stride, int* uv_stride);
+
+// Releases memory returned by the WebPDecode*() functions above.
+WEBP_EXTERN void WebPFree(void* ptr);
+
+// These five functions are variants of the above ones, that decode the image
+// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
+// available in this buffer is indicated by 'output_buffer_size'. If this
+// storage is not sufficient (or an error occurred), NULL is returned.
+// Otherwise, output_buffer is returned, for convenience.
+// The parameter 'output_stride' specifies the distance (in bytes)
+// between scanlines. Hence, output_buffer_size is expected to be at least
+// output_stride x picture-height.
+WEBP_EXTERN uint8_t* WebPDecodeRGBAInto(
+ const uint8_t* data, size_t data_size,
+ uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
+WEBP_EXTERN uint8_t* WebPDecodeARGBInto(
+ const uint8_t* data, size_t data_size,
+ uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
+WEBP_EXTERN uint8_t* WebPDecodeBGRAInto(
+ const uint8_t* data, size_t data_size,
+ uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
+
+// RGB and BGR variants. Here too the transparency information, if present,
+// will be dropped and ignored.
+WEBP_EXTERN uint8_t* WebPDecodeRGBInto(
+ const uint8_t* data, size_t data_size,
+ uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
+WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
+ const uint8_t* data, size_t data_size,
+ uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
+
+// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly
+// into pre-allocated luma/chroma plane buffers. This function requires the
+// strides to be passed: one for the luma plane and one for each of the
+// chroma ones. The size of each plane buffer is passed as 'luma_size',
+// 'u_size' and 'v_size' respectively.
+// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred
+// during decoding (or because some buffers were found to be too small).
+WEBP_EXTERN uint8_t* WebPDecodeYUVInto(
+ const uint8_t* data, size_t data_size,
+ uint8_t* luma, size_t luma_size, int luma_stride,
+ uint8_t* u, size_t u_size, int u_stride,
+ uint8_t* v, size_t v_size, int v_stride);
+
+//------------------------------------------------------------------------------
+// Output colorspaces and buffer
+
+// Colorspaces
+// Note: the naming describes the byte-ordering of packed samples in memory.
+// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,...
+// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels.
+// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order:
+// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ...
+// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ...
+// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for
+// these two modes:
+// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ...
+// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ...
+
+typedef enum WEBP_CSP_MODE {
+ MODE_RGB = 0, MODE_RGBA = 1,
+ MODE_BGR = 2, MODE_BGRA = 3,
+ MODE_ARGB = 4, MODE_RGBA_4444 = 5,
+ MODE_RGB_565 = 6,
+ // RGB-premultiplied transparent modes (alpha value is preserved)
+ MODE_rgbA = 7,
+ MODE_bgrA = 8,
+ MODE_Argb = 9,
+ MODE_rgbA_4444 = 10,
+ // YUV modes must come after RGB ones.
+ MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0
+ MODE_LAST = 13
+} WEBP_CSP_MODE;
+
+// Some useful macros:
+static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) {
+ return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb ||
+ mode == MODE_rgbA_4444);
+}
+
+static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) {
+ return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
+ mode == MODE_RGBA_4444 || mode == MODE_YUVA ||
+ WebPIsPremultipliedMode(mode));
+}
+
+static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) {
+ return (mode < MODE_YUV);
+}
+
+//------------------------------------------------------------------------------
+// WebPDecBuffer: Generic structure for describing the output sample buffer.
+
+struct WebPRGBABuffer { // view as RGBA
+ uint8_t* rgba; // pointer to RGBA samples
+ int stride; // stride in bytes from one scanline to the next.
+ size_t size; // total size of the *rgba buffer.
+};
+
+struct WebPYUVABuffer { // view as YUVA
+ uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples
+ int y_stride; // luma stride
+ int u_stride, v_stride; // chroma strides
+ int a_stride; // alpha stride
+ size_t y_size; // luma plane size
+ size_t u_size, v_size; // chroma planes size
+ size_t a_size; // alpha-plane size
+};
+
+// Output buffer
+struct WebPDecBuffer {
+ WEBP_CSP_MODE colorspace; // Colorspace.
+ int width, height; // Dimensions.
+ int is_external_memory; // If non-zero, 'internal_memory' pointer is not
+ // used. If value is '2' or more, the external
+ // memory is considered 'slow' and multiple
+ // read/write will be avoided.
+ union {
+ WebPRGBABuffer RGBA;
+ WebPYUVABuffer YUVA;
+ } u; // Nameless union of buffer parameters.
+ uint32_t pad[4]; // padding for later use
+
+ uint8_t* private_memory; // Internally allocated memory (only when
+ // is_external_memory is 0). Should not be used
+ // externally, but accessed via the buffer union.
+};
+
+// Internal, version-checked, entry point
+WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int);
+
+// Initialize the structure as empty. Must be called before any other use.
+// Returns false in case of version mismatch
+static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
+ return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);
+}
+
+// Free any memory associated with the buffer. Must always be called last.
+// Note: doesn't free the 'buffer' structure itself.
+WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer);
+
+//------------------------------------------------------------------------------
+// Enumeration of the status codes
+
+typedef enum VP8StatusCode {
+ VP8_STATUS_OK = 0,
+ VP8_STATUS_OUT_OF_MEMORY,
+ VP8_STATUS_INVALID_PARAM,
+ VP8_STATUS_BITSTREAM_ERROR,
+ VP8_STATUS_UNSUPPORTED_FEATURE,
+ VP8_STATUS_SUSPENDED,
+ VP8_STATUS_USER_ABORT,
+ VP8_STATUS_NOT_ENOUGH_DATA
+} VP8StatusCode;
+
+//------------------------------------------------------------------------------
+// Incremental decoding
+//
+// This API allows streamlined decoding of partial data.
+// Picture can be incrementally decoded as data become available thanks to the
+// WebPIDecoder object. This object can be left in a SUSPENDED state if the
+// picture is only partially decoded, pending additional input.
+// Code example:
+//
+// WebPInitDecBuffer(&output_buffer);
+// output_buffer.colorspace = mode;
+// ...
+// WebPIDecoder* idec = WebPINewDecoder(&output_buffer);
+// while (additional_data_is_available) {
+// // ... (get additional data in some new_data[] buffer)
+// status = WebPIAppend(idec, new_data, new_data_size);
+// if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
+// break; // an error occurred.
+// }
+//
+// // The above call decodes the current available buffer.
+// // Part of the image can now be refreshed by calling
+// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
+// }
+// WebPIDelete(idec);
+
+// Creates a new incremental decoder with the supplied buffer parameter.
+// This output_buffer can be passed NULL, in which case a default output buffer
+// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer'
+// is kept, which means that the lifespan of 'output_buffer' must be larger than
+// that of the returned WebPIDecoder object.
+// The supplied 'output_buffer' content MUST NOT be changed between calls to
+// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is
+// not set to 0. In such a case, it is allowed to modify the pointers, size and
+// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain
+// within valid bounds.
+// All other fields of WebPDecBuffer MUST remain constant between calls.
+// Returns NULL if the allocation failed.
+WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer);
+
+// This function allocates and initializes an incremental-decoder object, which
+// will output the RGB/A samples specified by 'csp' into a preallocated
+// buffer 'output_buffer'. The size of this buffer is at least
+// 'output_buffer_size' and the stride (distance in bytes between two scanlines)
+// is specified by 'output_stride'.
+// Additionally, output_buffer can be passed NULL in which case the output
+// buffer will be allocated automatically when the decoding starts. The
+// colorspace 'csp' is taken into account for allocating this buffer. All other
+// parameters are ignored.
+// Returns NULL if the allocation failed, or if some parameters are invalid.
+WEBP_EXTERN WebPIDecoder* WebPINewRGB(
+ WEBP_CSP_MODE csp,
+ uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
+
+// This function allocates and initializes an incremental-decoder object, which
+// will output the raw luma/chroma samples into a preallocated planes if
+// supplied. The luma plane is specified by its pointer 'luma', its size
+// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane
+// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v
+// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer
+// can be pass NULL in case one is not interested in the transparency plane.
+// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied.
+// In this case, the output buffer will be automatically allocated (using
+// MODE_YUVA) when decoding starts. All parameters are then ignored.
+// Returns NULL if the allocation failed or if a parameter is invalid.
+WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
+ uint8_t* luma, size_t luma_size, int luma_stride,
+ uint8_t* u, size_t u_size, int u_stride,
+ uint8_t* v, size_t v_size, int v_stride,
+ uint8_t* a, size_t a_size, int a_stride);
+
+// Deprecated version of the above, without the alpha plane.
+// Kept for backward compatibility.
+WEBP_EXTERN WebPIDecoder* WebPINewYUV(
+ uint8_t* luma, size_t luma_size, int luma_stride,
+ uint8_t* u, size_t u_size, int u_stride,
+ uint8_t* v, size_t v_size, int v_stride);
+
+// Deletes the WebPIDecoder object and associated memory. Must always be called
+// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded.
+WEBP_EXTERN void WebPIDelete(WebPIDecoder* idec);
+
+// Copies and decodes the next available data. Returns VP8_STATUS_OK when
+// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more
+// data is expected. Returns error in other cases.
+WEBP_EXTERN VP8StatusCode WebPIAppend(
+ WebPIDecoder* idec, const uint8_t* data, size_t data_size);
+
+// A variant of the above function to be used when data buffer contains
+// partial data from the beginning. In this case data buffer is not copied
+// to the internal memory.
+// Note that the value of the 'data' pointer can change between calls to
+// WebPIUpdate, for instance when the data buffer is resized to fit larger data.
+WEBP_EXTERN VP8StatusCode WebPIUpdate(
+ WebPIDecoder* idec, const uint8_t* data, size_t data_size);
+
+// Returns the RGB/A image decoded so far. Returns NULL if output params
+// are not initialized yet. The RGB/A output type corresponds to the colorspace
+// specified during call to WebPINewDecoder() or WebPINewRGB().
+// *last_y is the index of last decoded row in raster scan order. Some pointers
+// (*last_y, *width etc.) can be NULL if corresponding information is not
+// needed. The values in these pointers are only valid on successful (non-NULL)
+// return.
+WEBP_EXTERN uint8_t* WebPIDecGetRGB(
+ const WebPIDecoder* idec, int* last_y,
+ int* width, int* height, int* stride);
+
+// Same as above function to get a YUVA image. Returns pointer to the luma
+// plane or NULL in case of error. If there is no alpha information
+// the alpha pointer '*a' will be returned NULL.
+WEBP_EXTERN uint8_t* WebPIDecGetYUVA(
+ const WebPIDecoder* idec, int* last_y,
+ uint8_t** u, uint8_t** v, uint8_t** a,
+ int* width, int* height, int* stride, int* uv_stride, int* a_stride);
+
+// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
+// alpha information (if present). Kept for backward compatibility.
+static WEBP_INLINE uint8_t* WebPIDecGetYUV(
+ const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
+ int* width, int* height, int* stride, int* uv_stride) {
+ return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
+ stride, uv_stride, NULL);
+}
+
+// Generic call to retrieve information about the displayable area.
+// If non NULL, the left/right/width/height pointers are filled with the visible
+// rectangular area so far.
+// Returns NULL in case the incremental decoder object is in an invalid state.
+// Otherwise returns the pointer to the internal representation. This structure
+// is read-only, tied to WebPIDecoder's lifespan and should not be modified.
+WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
+ const WebPIDecoder* idec, int* left, int* top, int* width, int* height);
+
+//------------------------------------------------------------------------------
+// Advanced decoding parametrization
+//
+// Code sample for using the advanced decoding API
+/*
+ // 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', if needed
+ config.no_fancy_upsampling = 1;
+ config.output.colorspace = MODE_BGRA;
+ // etc.
+
+ // Note that you can also make config.output point to an externally
+ // supplied memory buffer, provided it's big enough to store the decoded
+ // picture. Otherwise, config.output will just be used to allocate memory
+ // and store the decoded picture.
+
+ // D) Decode!
+ CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
+
+ // E) Decoded image is now in config.output (and config.output.u.RGBA)
+
+ // F) 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);
+*/
+
+// Features gathered from the bitstream
+struct WebPBitstreamFeatures {
+ int width; // Width in pixels, as read from the bitstream.
+ int height; // Height in pixels, as read from the bitstream.
+ int has_alpha; // True if the bitstream contains an alpha channel.
+ int has_animation; // True if the bitstream is an animation.
+ int format; // 0 = undefined (/mixed), 1 = lossy, 2 = lossless
+
+ uint32_t pad[5]; // padding for later use
+};
+
+// Internal, version-checked, entry point
+WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(
+ const uint8_t*, size_t, WebPBitstreamFeatures*, int);
+
+// Retrieve features from the bitstream. The *features structure is filled
+// with information gathered from the bitstream.
+// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
+// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
+// features from headers. Returns error in other cases.
+static WEBP_INLINE VP8StatusCode WebPGetFeatures(
+ const uint8_t* data, size_t data_size,
+ WebPBitstreamFeatures* features) {
+ return WebPGetFeaturesInternal(data, data_size, features,
+ WEBP_DECODER_ABI_VERSION);
+}
+
+// Decoding options
+struct WebPDecoderOptions {
+ int bypass_filtering; // if true, skip the in-loop filtering
+ int no_fancy_upsampling; // if true, use faster pointwise upsampler
+ int use_cropping; // if true, cropping is applied _first_
+ int crop_left, crop_top; // top-left position for cropping.
+ // Will be snapped to even values.
+ int crop_width, crop_height; // dimension of the cropping area
+ int use_scaling; // if true, scaling is applied _afterward_
+ int scaled_width, scaled_height; // final resolution
+ int use_threads; // if true, use multi-threaded decoding
+ int dithering_strength; // dithering strength (0=Off, 100=full)
+ int flip; // flip output vertically
+ int alpha_dithering_strength; // alpha dithering strength in [0..100]
+
+ uint32_t pad[5]; // padding for later use
+};
+
+// Main object storing the configuration for advanced decoding.
+struct WebPDecoderConfig {
+ WebPBitstreamFeatures input; // Immutable bitstream features (optional)
+ WebPDecBuffer output; // Output buffer (can point to external mem)
+ WebPDecoderOptions options; // Decoding options
+};
+
+// Internal, version-checked, entry point
+WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
+
+// Initialize the configuration as empty. This function must always be
+// called first, unless WebPGetFeatures() is to be called.
+// Returns false in case of mismatched version.
+static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
+ return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION);
+}
+
+// Instantiate a new incremental decoder object with the requested
+// configuration. The bitstream can be passed using 'data' and 'data_size'
+// parameter, in which case the features will be parsed and stored into
+// config->input. Otherwise, 'data' can be NULL and no parsing will occur.
+// Note that 'config' can be NULL too, in which case a default configuration
+// is used. If 'config' is not NULL, it must outlive the WebPIDecoder object
+// as some references to its fields will be used. No internal copy of 'config'
+// is made.
+// The return WebPIDecoder object must always be deleted calling WebPIDelete().
+// Returns NULL in case of error (and config->status will then reflect
+// the error condition, if available).
+WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
+ WebPDecoderConfig* config);
+
+// Non-incremental version. This version decodes the full data at once, taking
+// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
+// if the decoding was successful). Note that 'config' cannot be NULL.
+WEBP_EXTERN VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
+ WebPDecoderConfig* config);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_WEBP_DECODE_H_ */
diff --git a/src/third_party/libwebp/src/webp/demux.h b/src/third_party/libwebp/src/webp/demux.h
new file mode 100644
index 0000000..555d641
--- /dev/null
+++ b/src/third_party/libwebp/src/webp/demux.h
@@ -0,0 +1,363 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Demux API.
+// Enables extraction of image and extended format data from WebP files.
+
+// 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);
+*/
+
+#ifndef WEBP_WEBP_DEMUX_H_
+#define WEBP_WEBP_DEMUX_H_
+
+#include "./decode.h" // for WEBP_CSP_MODE
+#include "./mux_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WEBP_DEMUX_ABI_VERSION 0x0107 // MAJOR(8b) + MINOR(8b)
+
+// Note: forward declaring enumerations is not allowed in (strict) C and C++,
+// the types are left here for reference.
+// typedef enum WebPDemuxState WebPDemuxState;
+// typedef enum WebPFormatFeature WebPFormatFeature;
+typedef struct WebPDemuxer WebPDemuxer;
+typedef struct WebPIterator WebPIterator;
+typedef struct WebPChunkIterator WebPChunkIterator;
+typedef struct WebPAnimInfo WebPAnimInfo;
+typedef struct WebPAnimDecoderOptions WebPAnimDecoderOptions;
+
+//------------------------------------------------------------------------------
+
+// Returns the version number of the demux library, packed in hexadecimal using
+// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
+WEBP_EXTERN int WebPGetDemuxVersion(void);
+
+//------------------------------------------------------------------------------
+// Life of a Demux object
+
+typedef enum WebPDemuxState {
+ WEBP_DEMUX_PARSE_ERROR = -1, // An error occurred while parsing.
+ WEBP_DEMUX_PARSING_HEADER = 0, // Not enough data to parse full header.
+ WEBP_DEMUX_PARSED_HEADER = 1, // Header parsing complete,
+ // data may be available.
+ WEBP_DEMUX_DONE = 2 // Entire file has been parsed.
+} WebPDemuxState;
+
+// Internal, version-checked, entry point
+WEBP_EXTERN WebPDemuxer* WebPDemuxInternal(
+ const WebPData*, int, WebPDemuxState*, int);
+
+// Parses the full WebP file given by 'data'. For single images the WebP file
+// header alone or the file header and the chunk header may be absent.
+// Returns a WebPDemuxer object on successful parse, NULL otherwise.
+static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
+ return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION);
+}
+
+// Parses the possibly incomplete WebP file given by 'data'.
+// If 'state' is non-NULL it will be set to indicate the status of the demuxer.
+// Returns NULL in case of error or if there isn't enough data to start parsing;
+// and a WebPDemuxer object on successful parse.
+// Note that WebPDemuxer keeps internal pointers to 'data' memory segment.
+// If this data is volatile, the demuxer object should be deleted (by calling
+// WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data.
+// This is usually an inexpensive operation.
+static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
+ const WebPData* data, WebPDemuxState* state) {
+ return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION);
+}
+
+// Frees memory associated with 'dmux'.
+WEBP_EXTERN void WebPDemuxDelete(WebPDemuxer* dmux);
+
+//------------------------------------------------------------------------------
+// Data/information extraction.
+
+typedef enum WebPFormatFeature {
+ WEBP_FF_FORMAT_FLAGS, // bit-wise combination of WebPFeatureFlags
+ // corresponding to the 'VP8X' chunk (if present).
+ WEBP_FF_CANVAS_WIDTH,
+ WEBP_FF_CANVAS_HEIGHT,
+ WEBP_FF_LOOP_COUNT, // only relevant for animated file
+ WEBP_FF_BACKGROUND_COLOR, // idem.
+ WEBP_FF_FRAME_COUNT // Number of frames present in the demux object.
+ // In case of a partial demux, this is the number
+ // of frames seen so far, with the last frame
+ // possibly being partial.
+} WebPFormatFeature;
+
+// Get the 'feature' value from the 'dmux'.
+// NOTE: values are only valid if WebPDemux() was used or WebPDemuxPartial()
+// returned a state > WEBP_DEMUX_PARSING_HEADER.
+// If 'feature' is WEBP_FF_FORMAT_FLAGS, the returned value is a bit-wise
+// combination of WebPFeatureFlags values.
+// If 'feature' is WEBP_FF_LOOP_COUNT, WEBP_FF_BACKGROUND_COLOR, the returned
+// value is only meaningful if the bitstream is animated.
+WEBP_EXTERN uint32_t WebPDemuxGetI(
+ const WebPDemuxer* dmux, WebPFormatFeature feature);
+
+//------------------------------------------------------------------------------
+// Frame iteration.
+
+struct WebPIterator {
+ int frame_num;
+ int num_frames; // equivalent to WEBP_FF_FRAME_COUNT.
+ int x_offset, y_offset; // offset relative to the canvas.
+ int width, height; // dimensions of this frame.
+ int duration; // display duration in milliseconds.
+ WebPMuxAnimDispose dispose_method; // dispose method for the frame.
+ int complete; // true if 'fragment' contains a full frame. partial images
+ // may still be decoded with the WebP incremental decoder.
+ WebPData fragment; // The frame given by 'frame_num'. Note for historical
+ // reasons this is called a fragment.
+ int has_alpha; // True if the frame contains transparency.
+ WebPMuxAnimBlend blend_method; // Blend operation for the frame.
+
+ uint32_t pad[2]; // padding for later use.
+ void* private_; // for internal use only.
+};
+
+// Retrieves frame 'frame_number' from 'dmux'.
+// 'iter->fragment' points to the frame on return from this function.
+// Setting 'frame_number' equal to 0 will return the last frame of the image.
+// Returns false if 'dmux' is NULL or frame 'frame_number' is not present.
+// Call WebPDemuxReleaseIterator() when use of the iterator is complete.
+// NOTE: 'dmux' must persist for the lifetime of 'iter'.
+WEBP_EXTERN int WebPDemuxGetFrame(
+ const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
+
+// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or
+// previous ('iter->frame_num' - 1) frame. These functions do not loop.
+// Returns true on success, false otherwise.
+WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter);
+WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter);
+
+// Releases any memory associated with 'iter'.
+// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same
+// iter. Also, must be called before destroying the associated WebPDemuxer with
+// WebPDemuxDelete().
+WEBP_EXTERN void WebPDemuxReleaseIterator(WebPIterator* iter);
+
+//------------------------------------------------------------------------------
+// Chunk iteration.
+
+struct WebPChunkIterator {
+ // The current and total number of chunks with the fourcc given to
+ // WebPDemuxGetChunk().
+ int chunk_num;
+ int num_chunks;
+ WebPData chunk; // The payload of the chunk.
+
+ uint32_t pad[6]; // padding for later use
+ void* private_;
+};
+
+// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from
+// 'dmux'.
+// 'fourcc' is a character array containing the fourcc of the chunk to return,
+// e.g., "ICCP", "XMP ", "EXIF", etc.
+// Setting 'chunk_number' equal to 0 will return the last chunk in a set.
+// Returns true if the chunk is found, false otherwise. Image related chunk
+// payloads are accessed through WebPDemuxGetFrame() and related functions.
+// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete.
+// NOTE: 'dmux' must persist for the lifetime of the iterator.
+WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux,
+ const char fourcc[4], int chunk_number,
+ WebPChunkIterator* iter);
+
+// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous
+// ('iter->chunk_num' - 1) chunk. These functions do not loop.
+// Returns true on success, false otherwise.
+WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter);
+WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter);
+
+// Releases any memory associated with 'iter'.
+// Must be called before destroying the associated WebPDemuxer with
+// WebPDemuxDelete().
+WEBP_EXTERN void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter);
+
+//------------------------------------------------------------------------------
+// WebPAnimDecoder API
+//
+// This API allows decoding (possibly) animated WebP images.
+//
+// Code Example:
+/*
+ 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, ×tamp);
+ // ... (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);
+*/
+
+typedef struct WebPAnimDecoder WebPAnimDecoder; // Main opaque object.
+
+// Global options.
+struct WebPAnimDecoderOptions {
+ // Output colorspace. Only the following modes are supported:
+ // MODE_RGBA, MODE_BGRA, MODE_rgbA and MODE_bgrA.
+ WEBP_CSP_MODE color_mode;
+ int use_threads; // If true, use multi-threaded decoding.
+ uint32_t padding[7]; // Padding for later use.
+};
+
+// Internal, version-checked, entry point.
+WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
+ WebPAnimDecoderOptions*, int);
+
+// Should always be called, to initialize a fresh WebPAnimDecoderOptions
+// structure before modification. Returns false in case of version mismatch.
+// WebPAnimDecoderOptionsInit() must have succeeded before using the
+// 'dec_options' object.
+static WEBP_INLINE int WebPAnimDecoderOptionsInit(
+ WebPAnimDecoderOptions* dec_options) {
+ return WebPAnimDecoderOptionsInitInternal(dec_options,
+ WEBP_DEMUX_ABI_VERSION);
+}
+
+// Internal, version-checked, entry point.
+WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
+ const WebPData*, const WebPAnimDecoderOptions*, int);
+
+// Creates and initializes a WebPAnimDecoder object.
+// Parameters:
+// webp_data - (in) WebP bitstream. This should remain unchanged during the
+// lifetime of the output WebPAnimDecoder object.
+// dec_options - (in) decoding options. Can be passed NULL to choose
+// reasonable defaults (in particular, color mode MODE_RGBA
+// will be picked).
+// Returns:
+// A pointer to the newly created WebPAnimDecoder object, or NULL in case of
+// parsing error, invalid option or memory error.
+static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
+ const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
+ return WebPAnimDecoderNewInternal(webp_data, dec_options,
+ WEBP_DEMUX_ABI_VERSION);
+}
+
+// Global information about the animation..
+struct WebPAnimInfo {
+ uint32_t canvas_width;
+ uint32_t canvas_height;
+ uint32_t loop_count;
+ uint32_t bgcolor;
+ uint32_t frame_count;
+ uint32_t pad[4]; // padding for later use
+};
+
+// Get global information about the animation.
+// Parameters:
+// dec - (in) decoder instance to get information from.
+// info - (out) global information fetched from the animation.
+// Returns:
+// True on success.
+WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
+ WebPAnimInfo* info);
+
+// Fetch the next frame from 'dec' based on options supplied to
+// WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size
+// 'canvas_width * 4 * canvas_height', and not just the frame sub-rectangle. The
+// returned buffer 'buf' is valid only until the next call to
+// WebPAnimDecoderGetNext(), WebPAnimDecoderReset() or WebPAnimDecoderDelete().
+// Parameters:
+// dec - (in/out) decoder instance from which the next frame is to be fetched.
+// buf - (out) decoded frame.
+// timestamp - (out) timestamp of the frame in milliseconds.
+// Returns:
+// False if any of the arguments are NULL, or if there is a parsing or
+// decoding error, or if there are no more frames. Otherwise, returns true.
+WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
+ uint8_t** buf, int* timestamp);
+
+// Check if there are more frames left to decode.
+// Parameters:
+// dec - (in) decoder instance to be checked.
+// Returns:
+// True if 'dec' is not NULL and some frames are yet to be decoded.
+// Otherwise, returns false.
+WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec);
+
+// Resets the WebPAnimDecoder object, so that next call to
+// WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be
+// helpful when all frames need to be decoded multiple times (e.g.
+// info.loop_count times) without destroying and recreating the 'dec' object.
+// Parameters:
+// dec - (in/out) decoder instance to be reset
+WEBP_EXTERN void WebPAnimDecoderReset(WebPAnimDecoder* dec);
+
+// Grab the internal demuxer object.
+// Getting the demuxer object can be useful if one wants to use operations only
+// available through demuxer; e.g. to get XMP/EXIF/ICC metadata. The returned
+// demuxer object is owned by 'dec' and is valid only until the next call to
+// WebPAnimDecoderDelete().
+//
+// Parameters:
+// dec - (in) decoder instance from which the demuxer object is to be fetched.
+WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer(
+ const WebPAnimDecoder* dec);
+
+// Deletes the WebPAnimDecoder object.
+// Parameters:
+// dec - (in/out) decoder instance to be deleted
+WEBP_EXTERN void WebPAnimDecoderDelete(WebPAnimDecoder* dec);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_WEBP_DEMUX_H_ */
diff --git a/src/third_party/libwebp/src/webp/encode.h b/src/third_party/libwebp/src/webp/encode.h
new file mode 100644
index 0000000..7ec3543
--- /dev/null
+++ b/src/third_party/libwebp/src/webp/encode.h
@@ -0,0 +1,545 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP encoder: main interface
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_WEBP_ENCODE_H_
+#define WEBP_WEBP_ENCODE_H_
+
+#include "./types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WEBP_ENCODER_ABI_VERSION 0x020e // MAJOR(8b) + MINOR(8b)
+
+// Note: forward declaring enumerations is not allowed in (strict) C and C++,
+// the types are left here for reference.
+// typedef enum WebPImageHint WebPImageHint;
+// typedef enum WebPEncCSP WebPEncCSP;
+// typedef enum WebPPreset WebPPreset;
+// typedef enum WebPEncodingError WebPEncodingError;
+typedef struct WebPConfig WebPConfig;
+typedef struct WebPPicture WebPPicture; // main structure for I/O
+typedef struct WebPAuxStats WebPAuxStats;
+typedef struct WebPMemoryWriter WebPMemoryWriter;
+
+// Return the encoder's version number, packed in hexadecimal using 8bits for
+// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
+WEBP_EXTERN int WebPGetEncoderVersion(void);
+
+//------------------------------------------------------------------------------
+// One-stop-shop call! No questions asked:
+
+// Returns the size of the compressed data (pointed to by *output), or 0 if
+// an error occurred. The compressed data must be released by the caller
+// using the call 'WebPFree(*output)'.
+// These functions compress using the lossy format, and the quality_factor
+// can go from 0 (smaller output, lower quality) to 100 (best quality,
+// larger output).
+WEBP_EXTERN size_t WebPEncodeRGB(const uint8_t* rgb,
+ int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+WEBP_EXTERN size_t WebPEncodeBGR(const uint8_t* bgr,
+ int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+WEBP_EXTERN size_t WebPEncodeRGBA(const uint8_t* rgba,
+ int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+WEBP_EXTERN size_t WebPEncodeBGRA(const uint8_t* bgra,
+ int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+
+// These functions are the equivalent of the above, but compressing in a
+// lossless manner. Files are usually larger than lossy format, but will
+// not suffer any compression loss.
+WEBP_EXTERN size_t WebPEncodeLosslessRGB(const uint8_t* rgb,
+ int width, int height, int stride,
+ uint8_t** output);
+WEBP_EXTERN size_t WebPEncodeLosslessBGR(const uint8_t* bgr,
+ int width, int height, int stride,
+ uint8_t** output);
+WEBP_EXTERN size_t WebPEncodeLosslessRGBA(const uint8_t* rgba,
+ int width, int height, int stride,
+ uint8_t** output);
+WEBP_EXTERN size_t WebPEncodeLosslessBGRA(const uint8_t* bgra,
+ int width, int height, int stride,
+ uint8_t** output);
+
+// Releases memory returned by the WebPEncode*() functions above.
+WEBP_EXTERN void WebPFree(void* ptr);
+
+//------------------------------------------------------------------------------
+// Coding parameters
+
+// Image characteristics hint for the underlying encoder.
+typedef enum WebPImageHint {
+ WEBP_HINT_DEFAULT = 0, // default preset.
+ WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
+ WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting
+ WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc).
+ WEBP_HINT_LAST
+} WebPImageHint;
+
+// Compression parameters.
+struct WebPConfig {
+ int lossless; // Lossless encoding (0=lossy(default), 1=lossless).
+ float quality; // between 0 and 100. For lossy, 0 gives the smallest
+ // size and 100 the largest. For lossless, this
+ // parameter is the amount of effort put into the
+ // compression: 0 is the fastest but gives larger
+ // files compared to the slowest, but best, 100.
+ int method; // quality/speed trade-off (0=fast, 6=slower-better)
+
+ WebPImageHint image_hint; // Hint for image type (lossless only for now).
+
+ int target_size; // if non-zero, set the desired target size in bytes.
+ // Takes precedence over the 'compression' parameter.
+ float target_PSNR; // if non-zero, specifies the minimal distortion to
+ // try to achieve. Takes precedence over target_size.
+ int segments; // maximum number of segments to use, in [1..4]
+ int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum.
+ int filter_strength; // range: [0 = off .. 100 = strongest]
+ int filter_sharpness; // range: [0 = off .. 7 = least sharp]
+ int filter_type; // filtering type: 0 = simple, 1 = strong (only used
+ // if filter_strength > 0 or autofilter > 0)
+ int autofilter; // Auto adjust filter's strength [0 = off, 1 = on]
+ int alpha_compression; // Algorithm for encoding the alpha plane (0 = none,
+ // 1 = compressed with WebP lossless). Default is 1.
+ int alpha_filtering; // Predictive filtering method for alpha plane.
+ // 0: none, 1: fast, 2: best. Default if 1.
+ int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
+ // Default is 100.
+ int pass; // number of entropy-analysis passes (in [1..10]).
+
+ int show_compressed; // if true, export the compressed picture back.
+ // In-loop filtering is not applied.
+ int preprocessing; // preprocessing filter:
+ // 0=none, 1=segment-smooth, 2=pseudo-random dithering
+ int partitions; // log2(number of token partitions) in [0..3]. Default
+ // is set to 0 for easier progressive decoding.
+ int partition_limit; // quality degradation allowed to fit the 512k limit
+ // on prediction modes coding (0: no degradation,
+ // 100: maximum possible degradation).
+ int emulate_jpeg_size; // If true, compression parameters will be remapped
+ // to better match the expected output size from
+ // JPEG compression. Generally, the output size will
+ // be similar but the degradation will be lower.
+ int thread_level; // If non-zero, try and use multi-threaded encoding.
+ int low_memory; // If set, reduce memory usage (but increase CPU use).
+
+ int near_lossless; // Near lossless encoding [0 = max loss .. 100 = off
+ // (default)].
+ int exact; // if non-zero, preserve the exact RGB values under
+ // transparent area. Otherwise, discard this invisible
+ // RGB information for better compression. The default
+ // value is 0.
+
+ int use_delta_palette; // reserved for future lossless feature
+ int use_sharp_yuv; // if needed, use sharp (and slow) RGB->YUV conversion
+
+ uint32_t pad[2]; // padding for later use
+};
+
+// Enumerate some predefined settings for WebPConfig, depending on the type
+// of source picture. These presets are used when calling WebPConfigPreset().
+typedef enum WebPPreset {
+ WEBP_PRESET_DEFAULT = 0, // default preset.
+ WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot
+ WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting
+ WEBP_PRESET_DRAWING, // hand or line drawing, with high-contrast details
+ WEBP_PRESET_ICON, // small-sized colorful images
+ WEBP_PRESET_TEXT // text-like
+} WebPPreset;
+
+// Internal, version-checked, entry point
+WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int);
+
+// Should always be called, to initialize a fresh WebPConfig structure before
+// modification. Returns false in case of version mismatch. WebPConfigInit()
+// must have succeeded before using the 'config' object.
+// Note that the default values are lossless=0 and quality=75.
+static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
+ return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f,
+ WEBP_ENCODER_ABI_VERSION);
+}
+
+// This function will initialize the configuration according to a predefined
+// set of parameters (referred to by 'preset') and a given quality factor.
+// This function can be called as a replacement to WebPConfigInit(). Will
+// return false in case of error.
+static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
+ WebPPreset preset, float quality) {
+ return WebPConfigInitInternal(config, preset, quality,
+ WEBP_ENCODER_ABI_VERSION);
+}
+
+// Activate the lossless compression mode with the desired efficiency level
+// between 0 (fastest, lowest compression) and 9 (slower, best compression).
+// A good default level is '6', providing a fair tradeoff between compression
+// speed and final compressed size.
+// This function will overwrite several fields from config: 'method', 'quality'
+// and 'lossless'. Returns false in case of parameter error.
+WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, int level);
+
+// Returns true if 'config' is non-NULL and all configuration parameters are
+// within their valid ranges.
+WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config);
+
+//------------------------------------------------------------------------------
+// Input / Output
+// Structure for storing auxiliary statistics.
+
+struct WebPAuxStats {
+ int coded_size; // final size
+
+ float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha
+ int block_count[3]; // number of intra4/intra16/skipped macroblocks
+ int header_bytes[2]; // approximate number of bytes spent for header
+ // and mode-partition #0
+ int residual_bytes[3][4]; // approximate number of bytes spent for
+ // DC/AC/uv coefficients for each (0..3) segments.
+ int segment_size[4]; // number of macroblocks in each segments
+ int segment_quant[4]; // quantizer values for each segments
+ int segment_level[4]; // filtering strength for each segments [0..63]
+
+ int alpha_data_size; // size of the transparency data
+ int layer_data_size; // size of the enhancement layer data
+
+ // lossless encoder statistics
+ uint32_t lossless_features; // bit0:predictor bit1:cross-color transform
+ // bit2:subtract-green bit3:color indexing
+ int histogram_bits; // number of precision bits of histogram
+ int transform_bits; // precision bits for transform
+ int cache_bits; // number of bits for color cache lookup
+ int palette_size; // number of color in palette, if used
+ int lossless_size; // final lossless size
+ int lossless_hdr_size; // lossless header (transform, huffman etc) size
+ int lossless_data_size; // lossless image data size
+
+ uint32_t pad[2]; // padding for later use
+};
+
+// Signature for output function. Should return true if writing was successful.
+// data/data_size is the segment of data to write, and 'picture' is for
+// reference (and so one can make use of picture->custom_ptr).
+typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size,
+ const WebPPicture* picture);
+
+// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using
+// the following WebPMemoryWriter object (to be set as a custom_ptr).
+struct WebPMemoryWriter {
+ uint8_t* mem; // final buffer (of size 'max_size', larger than 'size').
+ size_t size; // final size
+ size_t max_size; // total capacity
+ uint32_t pad[1]; // padding for later use
+};
+
+// The following must be called first before any use.
+WEBP_EXTERN void WebPMemoryWriterInit(WebPMemoryWriter* writer);
+
+// The following must be called to deallocate writer->mem memory. The 'writer'
+// object itself is not deallocated.
+WEBP_EXTERN void WebPMemoryWriterClear(WebPMemoryWriter* writer);
+// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon
+// completion, writer.mem and writer.size will hold the coded data.
+// writer.mem must be freed by calling WebPMemoryWriterClear.
+WEBP_EXTERN int WebPMemoryWrite(const uint8_t* data, size_t data_size,
+ const WebPPicture* picture);
+
+// Progress hook, called from time to time to report progress. It can return
+// false to request an abort of the encoding process, or true otherwise if
+// everything is OK.
+typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture);
+
+// Color spaces.
+typedef enum WebPEncCSP {
+ // chroma sampling
+ WEBP_YUV420 = 0, // 4:2:0
+ WEBP_YUV420A = 4, // alpha channel variant
+ WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors
+ WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present
+} WebPEncCSP;
+
+// Encoding error conditions.
+typedef enum WebPEncodingError {
+ VP8_ENC_OK = 0,
+ VP8_ENC_ERROR_OUT_OF_MEMORY, // memory error allocating objects
+ VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, // memory error while flushing bits
+ VP8_ENC_ERROR_NULL_PARAMETER, // a pointer parameter is NULL
+ VP8_ENC_ERROR_INVALID_CONFIGURATION, // configuration is invalid
+ VP8_ENC_ERROR_BAD_DIMENSION, // picture has invalid width/height
+ VP8_ENC_ERROR_PARTITION0_OVERFLOW, // partition is bigger than 512k
+ VP8_ENC_ERROR_PARTITION_OVERFLOW, // partition is bigger than 16M
+ VP8_ENC_ERROR_BAD_WRITE, // error while flushing bytes
+ VP8_ENC_ERROR_FILE_TOO_BIG, // file is bigger than 4G
+ VP8_ENC_ERROR_USER_ABORT, // abort request by user
+ VP8_ENC_ERROR_LAST // list terminator. always last.
+} WebPEncodingError;
+
+// maximum width/height allowed (inclusive), in pixels
+#define WEBP_MAX_DIMENSION 16383
+
+// Main exchange structure (input samples, output bytes, statistics)
+struct WebPPicture {
+ // INPUT
+ //////////////
+ // Main flag for encoder selecting between ARGB or YUV input.
+ // It is recommended to use ARGB input (*argb, argb_stride) for lossless
+ // compression, and YUV input (*y, *u, *v, etc.) for lossy compression
+ // since these are the respective native colorspace for these formats.
+ int use_argb;
+
+ // YUV input (mostly used for input to lossy compression)
+ WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr).
+ int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION)
+ uint8_t *y, *u, *v; // pointers to luma/chroma planes.
+ int y_stride, uv_stride; // luma/chroma strides.
+ uint8_t* a; // pointer to the alpha plane
+ int a_stride; // stride of the alpha plane
+ uint32_t pad1[2]; // padding for later use
+
+ // ARGB input (mostly used for input to lossless compression)
+ uint32_t* argb; // Pointer to argb (32 bit) plane.
+ int argb_stride; // This is stride in pixels units, not bytes.
+ uint32_t pad2[3]; // padding for later use
+
+ // OUTPUT
+ ///////////////
+ // Byte-emission hook, to store compressed bytes as they are ready.
+ WebPWriterFunction writer; // can be NULL
+ void* custom_ptr; // can be used by the writer.
+
+ // map for extra information (only for lossy compression mode)
+ int extra_info_type; // 1: intra type, 2: segment, 3: quant
+ // 4: intra-16 prediction mode,
+ // 5: chroma prediction mode,
+ // 6: bit cost, 7: distortion
+ uint8_t* extra_info; // if not NULL, points to an array of size
+ // ((width + 15) / 16) * ((height + 15) / 16) that
+ // will be filled with a macroblock map, depending
+ // on extra_info_type.
+
+ // STATS AND REPORTS
+ ///////////////////////////
+ // Pointer to side statistics (updated only if not NULL)
+ WebPAuxStats* stats;
+
+ // Error code for the latest error encountered during encoding
+ WebPEncodingError error_code;
+
+ // If not NULL, report progress during encoding.
+ WebPProgressHook progress_hook;
+
+ void* user_data; // this field is free to be set to any value and
+ // used during callbacks (like progress-report e.g.).
+
+ uint32_t pad3[3]; // padding for later use
+
+ // Unused for now
+ uint8_t *pad4, *pad5;
+ uint32_t pad6[8]; // padding for later use
+
+ // PRIVATE FIELDS
+ ////////////////////
+ void* memory_; // row chunk of memory for yuva planes
+ void* memory_argb_; // and for argb too.
+ void* pad7[2]; // padding for later use
+};
+
+// Internal, version-checked, entry point
+WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int);
+
+// Should always be called, to initialize the structure. Returns false in case
+// of version mismatch. WebPPictureInit() must have succeeded before using the
+// 'picture' object.
+// Note that, by default, use_argb is false and colorspace is WEBP_YUV420.
+static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
+ return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION);
+}
+
+//------------------------------------------------------------------------------
+// WebPPicture utils
+
+// Convenience allocation / deallocation based on picture->width/height:
+// Allocate y/u/v buffers as per colorspace/width/height specification.
+// Note! This function will free the previous buffer if needed.
+// Returns false in case of memory error.
+WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture);
+
+// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*().
+// Note that this function does _not_ free the memory used by the 'picture'
+// object itself.
+// Besides memory (which is reclaimed) all other fields of 'picture' are
+// preserved.
+WEBP_EXTERN void WebPPictureFree(WebPPicture* picture);
+
+// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, *dst
+// will fully own the copied pixels (this is not a view). The 'dst' picture need
+// not be initialized as its content is overwritten.
+// Returns false in case of memory allocation error.
+WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
+
+// Compute the single distortion for packed planes of samples.
+// 'src' will be compared to 'ref', and the raw distortion stored into
+// '*distortion'. The refined metric (log(MSE), log(1 - ssim),...' will be
+// stored in '*result'.
+// 'x_step' is the horizontal stride (in bytes) between samples.
+// 'src/ref_stride' is the byte distance between rows.
+// Returns false in case of error (bad parameter, memory allocation error, ...).
+WEBP_EXTERN int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
+ const uint8_t* ref, size_t ref_stride,
+ int width, int height,
+ size_t x_step,
+ int type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
+ float* distortion, float* result);
+
+// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results
+// are in dB, stored in result[] in the B/G/R/A/All order. The distortion is
+// always performed using ARGB samples. Hence if the input is YUV(A), the
+// picture will be internally converted to ARGB (just for the measurement).
+// Warning: this function is rather CPU-intensive.
+WEBP_EXTERN int WebPPictureDistortion(
+ const WebPPicture* src, const WebPPicture* ref,
+ int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
+ float result[5]);
+
+// self-crops a picture to the rectangle defined by top/left/width/height.
+// Returns false in case of memory allocation error, or if the rectangle is
+// outside of the source picture.
+// The rectangle for the view is defined by the top-left corner pixel
+// coordinates (left, top) as well as its width and height. This rectangle
+// must be fully be comprised inside the 'src' source picture. If the source
+// picture uses the YUV420 colorspace, the top and left coordinates will be
+// snapped to even values.
+WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture,
+ int left, int top, int width, int height);
+
+// Extracts a view from 'src' picture into 'dst'. The rectangle for the view
+// is defined by the top-left corner pixel coordinates (left, top) as well
+// as its width and height. This rectangle must be fully be comprised inside
+// the 'src' source picture. If the source picture uses the YUV420 colorspace,
+// the top and left coordinates will be snapped to even values.
+// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed
+// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so,
+// the original dimension will be lost). Picture 'dst' need not be initialized
+// with WebPPictureInit() if it is different from 'src', since its content will
+// be overwritten.
+// Returns false in case of memory allocation error or invalid parameters.
+WEBP_EXTERN int WebPPictureView(const WebPPicture* src,
+ int left, int top, int width, int height,
+ WebPPicture* dst);
+
+// Returns true if the 'picture' is actually a view and therefore does
+// not own the memory for pixels.
+WEBP_EXTERN int WebPPictureIsView(const WebPPicture* picture);
+
+// Rescale a picture to new dimension width x height.
+// If either 'width' or 'height' (but not both) is 0 the corresponding
+// dimension will be calculated preserving the aspect ratio.
+// No gamma correction is applied.
+// Returns false in case of error (invalid parameter or insufficient memory).
+WEBP_EXTERN int WebPPictureRescale(WebPPicture* pic, int width, int height);
+
+// Colorspace conversion function to import RGB samples.
+// Previous buffer will be free'd, if any.
+// *rgb buffer should have a size of at least height * rgb_stride.
+// Returns false in case of memory error.
+WEBP_EXTERN int WebPPictureImportRGB(
+ WebPPicture* picture, const uint8_t* rgb, int rgb_stride);
+// Same, but for RGBA buffer.
+WEBP_EXTERN int WebPPictureImportRGBA(
+ WebPPicture* picture, const uint8_t* rgba, int rgba_stride);
+// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format
+// input buffer ignoring the alpha channel. Avoids needing to copy the data
+// to a temporary 24-bit RGB buffer to import the RGB only.
+WEBP_EXTERN int WebPPictureImportRGBX(
+ WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride);
+
+// Variants of the above, but taking BGR(A|X) input.
+WEBP_EXTERN int WebPPictureImportBGR(
+ WebPPicture* picture, const uint8_t* bgr, int bgr_stride);
+WEBP_EXTERN int WebPPictureImportBGRA(
+ WebPPicture* picture, const uint8_t* bgra, int bgra_stride);
+WEBP_EXTERN int WebPPictureImportBGRX(
+ WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride);
+
+// Converts picture->argb data to the YUV420A format. The 'colorspace'
+// parameter is deprecated and should be equal to WEBP_YUV420.
+// Upon return, picture->use_argb is set to false. The presence of real
+// non-opaque transparent values is detected, and 'colorspace' will be
+// adjusted accordingly. Note that this method is lossy.
+// Returns false in case of error.
+WEBP_EXTERN int WebPPictureARGBToYUVA(WebPPicture* picture,
+ WebPEncCSP /*colorspace = WEBP_YUV420*/);
+
+// Same as WebPPictureARGBToYUVA(), but the conversion is done using
+// pseudo-random dithering with a strength 'dithering' between
+// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful
+// for photographic picture.
+WEBP_EXTERN int WebPPictureARGBToYUVADithered(
+ WebPPicture* picture, WebPEncCSP colorspace, float dithering);
+
+// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion.
+// Downsampling is handled with extra care in case of color clipping. This
+// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better
+// and sharper YUV representation.
+// Returns false in case of error.
+WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture);
+// kept for backward compatibility:
+WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
+
+// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
+// The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to
+// ARGB incurs a small loss too.
+// Note that the use of this colorspace is discouraged if one has access to the
+// raw ARGB samples, since using YUV420 is comparatively lossy.
+// Returns false in case of error.
+WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture);
+
+// Helper function: given a width x height plane of RGBA or YUV(A) samples
+// clean-up or smoothen the YUV or RGB samples under fully transparent area,
+// to help compressibility (no guarantee, though).
+WEBP_EXTERN void WebPCleanupTransparentArea(WebPPicture* picture);
+
+// Scan the picture 'picture' for the presence of non fully opaque alpha values.
+// Returns true in such case. Otherwise returns false (indicating that the
+// alpha plane can be ignored altogether e.g.).
+WEBP_EXTERN int WebPPictureHasTransparency(const WebPPicture* picture);
+
+// Remove the transparency information (if present) by blending the color with
+// the background color 'background_rgb' (specified as 24bit RGB triplet).
+// After this call, all alpha values are reset to 0xff.
+WEBP_EXTERN void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb);
+
+//------------------------------------------------------------------------------
+// Main call
+
+// Main encoding call, after config and picture have been initialized.
+// 'picture' must be less than 16384x16384 in dimension (cf WEBP_MAX_DIMENSION),
+// and the 'config' object must be a valid one.
+// Returns false in case of error, true otherwise.
+// In case of error, picture->error_code is updated accordingly.
+// 'picture' can hold the source samples in both YUV(A) or ARGB input, depending
+// on the value of 'picture->use_argb'. It is highly recommended to use
+// the former for lossy encoding, and the latter for lossless encoding
+// (when config.lossless is true). Automatic conversion from one format to
+// another is provided but they both incur some loss.
+WEBP_EXTERN int WebPEncode(const WebPConfig* config, WebPPicture* picture);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_WEBP_ENCODE_H_ */
diff --git a/src/third_party/libwebp/src/webp/format_constants.h b/src/third_party/libwebp/src/webp/format_constants.h
new file mode 100644
index 0000000..329fc8a
--- /dev/null
+++ b/src/third_party/libwebp/src/webp/format_constants.h
@@ -0,0 +1,87 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Internal header for constants related to WebP file format.
+//
+// Author: Urvang (urvang@google.com)
+
+#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_
+#define WEBP_WEBP_FORMAT_CONSTANTS_H_
+
+// Create fourcc of the chunk from the chunk tag characters.
+#define MKFOURCC(a, b, c, d) ((a) | (b) << 8 | (c) << 16 | (uint32_t)(d) << 24)
+
+// VP8 related constants.
+#define VP8_SIGNATURE 0x9d012a // Signature in VP8 data.
+#define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition
+#define VP8_MAX_PARTITION_SIZE (1 << 24) // max size for token partition
+#define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data.
+
+// VP8L related constants.
+#define VP8L_SIGNATURE_SIZE 1 // VP8L signature size.
+#define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte.
+#define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store
+ // width and height.
+#define VP8L_VERSION_BITS 3 // 3 bits reserved for version.
+#define VP8L_VERSION 0 // version 0
+#define VP8L_FRAME_HEADER_SIZE 5 // Size of the VP8L frame header.
+
+#define MAX_PALETTE_SIZE 256
+#define MAX_CACHE_BITS 11
+#define HUFFMAN_CODES_PER_META_CODE 5
+#define ARGB_BLACK 0xff000000
+
+#define DEFAULT_CODE_LENGTH 8
+#define MAX_ALLOWED_CODE_LENGTH 15
+
+#define NUM_LITERAL_CODES 256
+#define NUM_LENGTH_CODES 24
+#define NUM_DISTANCE_CODES 40
+#define CODE_LENGTH_CODES 19
+
+#define MIN_HUFFMAN_BITS 2 // min number of Huffman bits
+#define MAX_HUFFMAN_BITS 9 // max number of Huffman bits
+
+#define TRANSFORM_PRESENT 1 // The bit to be written when next data
+ // to be read is a transform.
+#define NUM_TRANSFORMS 4 // Maximum number of allowed transform
+ // in a bitstream.
+typedef enum {
+ PREDICTOR_TRANSFORM = 0,
+ CROSS_COLOR_TRANSFORM = 1,
+ SUBTRACT_GREEN = 2,
+ COLOR_INDEXING_TRANSFORM = 3
+} VP8LImageTransformType;
+
+// Alpha related constants.
+#define ALPHA_HEADER_LEN 1
+#define ALPHA_NO_COMPRESSION 0
+#define ALPHA_LOSSLESS_COMPRESSION 1
+#define ALPHA_PREPROCESSED_LEVELS 1
+
+// Mux related constants.
+#define TAG_SIZE 4 // Size of a chunk tag (e.g. "VP8L").
+#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size.
+#define CHUNK_HEADER_SIZE 8 // Size of a chunk header.
+#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP").
+#define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk.
+#define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk.
+#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk.
+
+#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height.
+#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height.
+#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count
+#define MAX_DURATION (1 << 24) // maximum duration
+#define MAX_POSITION_OFFSET (1 << 24) // maximum frame x/y offset
+
+// Maximum chunk payload is such that adding the header and padding won't
+// overflow a uint32_t.
+#define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1)
+
+#endif /* WEBP_WEBP_FORMAT_CONSTANTS_H_ */
diff --git a/src/third_party/libwebp/src/webp/mux.h b/src/third_party/libwebp/src/webp/mux.h
new file mode 100644
index 0000000..28bb4a4
--- /dev/null
+++ b/src/third_party/libwebp/src/webp/mux.h
@@ -0,0 +1,530 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// RIFF container manipulation and encoding for WebP images.
+//
+// Authors: Urvang (urvang@google.com)
+// Vikas (vikasa@google.com)
+
+#ifndef WEBP_WEBP_MUX_H_
+#define WEBP_WEBP_MUX_H_
+
+#include "./mux_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WEBP_MUX_ABI_VERSION 0x0108 // MAJOR(8b) + MINOR(8b)
+
+//------------------------------------------------------------------------------
+// Mux API
+//
+// This API allows manipulation of WebP container images containing features
+// like color profile, metadata, animation.
+//
+// Code Example#1: Create 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 ICCP color 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);
+*/
+
+// Code Example#2: 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_data).
+ WebPMuxDelete(mux);
+ free(data);
+*/
+
+// Note: forward declaring enumerations is not allowed in (strict) C and C++,
+// the types are left here for reference.
+// typedef enum WebPMuxError WebPMuxError;
+// typedef enum WebPChunkId WebPChunkId;
+typedef struct WebPMux WebPMux; // main opaque object.
+typedef struct WebPMuxFrameInfo WebPMuxFrameInfo;
+typedef struct WebPMuxAnimParams WebPMuxAnimParams;
+typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions;
+
+// Error codes
+typedef enum WebPMuxError {
+ WEBP_MUX_OK = 1,
+ WEBP_MUX_NOT_FOUND = 0,
+ WEBP_MUX_INVALID_ARGUMENT = -1,
+ WEBP_MUX_BAD_DATA = -2,
+ WEBP_MUX_MEMORY_ERROR = -3,
+ WEBP_MUX_NOT_ENOUGH_DATA = -4
+} WebPMuxError;
+
+// IDs for different types of chunks.
+typedef enum WebPChunkId {
+ WEBP_CHUNK_VP8X, // VP8X
+ WEBP_CHUNK_ICCP, // ICCP
+ WEBP_CHUNK_ANIM, // ANIM
+ WEBP_CHUNK_ANMF, // ANMF
+ WEBP_CHUNK_DEPRECATED, // (deprecated from FRGM)
+ WEBP_CHUNK_ALPHA, // ALPH
+ WEBP_CHUNK_IMAGE, // VP8/VP8L
+ WEBP_CHUNK_EXIF, // EXIF
+ WEBP_CHUNK_XMP, // XMP
+ WEBP_CHUNK_UNKNOWN, // Other chunks.
+ WEBP_CHUNK_NIL
+} WebPChunkId;
+
+//------------------------------------------------------------------------------
+
+// Returns the version number of the mux library, packed in hexadecimal using
+// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
+WEBP_EXTERN int WebPGetMuxVersion(void);
+
+//------------------------------------------------------------------------------
+// Life of a Mux object
+
+// Internal, version-checked, entry point
+WEBP_EXTERN WebPMux* WebPNewInternal(int);
+
+// Creates an empty mux object.
+// Returns:
+// A pointer to the newly created empty mux object.
+// Or NULL in case of memory error.
+static WEBP_INLINE WebPMux* WebPMuxNew(void) {
+ return WebPNewInternal(WEBP_MUX_ABI_VERSION);
+}
+
+// Deletes the mux object.
+// Parameters:
+// mux - (in/out) object to be deleted
+WEBP_EXTERN void WebPMuxDelete(WebPMux* mux);
+
+//------------------------------------------------------------------------------
+// Mux creation.
+
+// Internal, version-checked, entry point
+WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, int);
+
+// Creates a mux object from raw data given in WebP RIFF format.
+// Parameters:
+// bitstream - (in) the bitstream data in WebP RIFF format
+// copy_data - (in) value 1 indicates given data WILL be copied to the mux
+// object and value 0 indicates data will NOT be copied.
+// Returns:
+// A pointer to the mux object created from given data - on success.
+// NULL - In case of invalid data or memory error.
+static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
+ int copy_data) {
+ return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION);
+}
+
+//------------------------------------------------------------------------------
+// Non-image chunks.
+
+// Note: Only non-image related chunks should be managed through chunk APIs.
+// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH").
+// To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(),
+// WebPMuxGetFrame() and WebPMuxDeleteFrame().
+
+// Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object.
+// Any existing chunk(s) with the same id will be removed.
+// Parameters:
+// mux - (in/out) object to which the chunk is to be added
+// fourcc - (in) a character array containing the fourcc of the given chunk;
+// e.g., "ICCP", "XMP ", "EXIF" etc.
+// chunk_data - (in) the chunk data to be added
+// copy_data - (in) value 1 indicates given data WILL be copied to the mux
+// object and value 0 indicates data will NOT be copied.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
+// or if fourcc corresponds to an image chunk.
+// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxSetChunk(
+ WebPMux* mux, const char fourcc[4], const WebPData* chunk_data,
+ int copy_data);
+
+// Gets a reference to the data of the chunk with id 'fourcc' in the mux object.
+// The caller should NOT free the returned data.
+// Parameters:
+// mux - (in) object from which the chunk data is to be fetched
+// fourcc - (in) a character array containing the fourcc of the chunk;
+// e.g., "ICCP", "XMP ", "EXIF" etc.
+// chunk_data - (out) returned chunk data
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
+// or if fourcc corresponds to an image chunk.
+// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given id.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxGetChunk(
+ const WebPMux* mux, const char fourcc[4], WebPData* chunk_data);
+
+// Deletes the chunk with the given 'fourcc' from the mux object.
+// Parameters:
+// mux - (in/out) object from which the chunk is to be deleted
+// fourcc - (in) a character array containing the fourcc of the chunk;
+// e.g., "ICCP", "XMP ", "EXIF" etc.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux or fourcc is NULL
+// or if fourcc corresponds to an image chunk.
+// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given fourcc.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxDeleteChunk(
+ WebPMux* mux, const char fourcc[4]);
+
+//------------------------------------------------------------------------------
+// Images.
+
+// Encapsulates data about a single frame.
+struct WebPMuxFrameInfo {
+ WebPData bitstream; // image data: can be a raw VP8/VP8L bitstream
+ // or a single-image WebP file.
+ int x_offset; // x-offset of the frame.
+ int y_offset; // y-offset of the frame.
+ int duration; // duration of the frame (in milliseconds).
+
+ WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF
+ // or WEBP_CHUNK_IMAGE
+ WebPMuxAnimDispose dispose_method; // Disposal method for the frame.
+ WebPMuxAnimBlend blend_method; // Blend operation for the frame.
+ uint32_t pad[1]; // padding for later use
+};
+
+// Sets the (non-animated) image in the mux object.
+// Note: Any existing images (including frames) will be removed.
+// Parameters:
+// mux - (in/out) object in which the image is to be set
+// bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image
+// WebP file (non-animated)
+// copy_data - (in) value 1 indicates given data WILL be copied to the mux
+// object and value 0 indicates data will NOT be copied.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL.
+// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxSetImage(
+ WebPMux* mux, const WebPData* bitstream, int copy_data);
+
+// Adds a frame at the end of the mux object.
+// Notes: (1) frame.id should be WEBP_CHUNK_ANMF
+// (2) For setting a non-animated image, use WebPMuxSetImage() instead.
+// (3) Type of frame being pushed must be same as the frames in mux.
+// (4) As WebP only supports even offsets, any odd offset will be snapped
+// to an even location using: offset &= ~1
+// Parameters:
+// mux - (in/out) object to which the frame is to be added
+// frame - (in) frame data.
+// copy_data - (in) value 1 indicates given data WILL be copied to the mux
+// object and value 0 indicates data will NOT be copied.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL
+// or if content of 'frame' is invalid.
+// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxPushFrame(
+ WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data);
+
+// Gets the nth frame from the mux object.
+// The content of 'frame->bitstream' is allocated using malloc(), and NOT
+// owned by the 'mux' object. It MUST be deallocated by the caller by calling
+// WebPDataClear().
+// nth=0 has a special meaning - last position.
+// Parameters:
+// mux - (in) object from which the info is to be fetched
+// nth - (in) index of the frame in the mux object
+// frame - (out) data of the returned frame
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL.
+// WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object.
+// WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid.
+// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxGetFrame(
+ const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame);
+
+// Deletes a frame from the mux object.
+// nth=0 has a special meaning - last position.
+// Parameters:
+// mux - (in/out) object from which a frame is to be deleted
+// nth - (in) The position from which the frame is to be deleted
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL.
+// WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object
+// before deletion.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth);
+
+//------------------------------------------------------------------------------
+// Animation.
+
+// Animation parameters.
+struct WebPMuxAnimParams {
+ uint32_t bgcolor; // Background color of the canvas stored (in MSB order) as:
+ // Bits 00 to 07: Alpha.
+ // Bits 08 to 15: Red.
+ // Bits 16 to 23: Green.
+ // Bits 24 to 31: Blue.
+ int loop_count; // Number of times to repeat the animation [0 = infinite].
+};
+
+// Sets the animation parameters in the mux object. Any existing ANIM chunks
+// will be removed.
+// Parameters:
+// mux - (in/out) object in which ANIM chunk is to be set/added
+// params - (in) animation parameters.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
+// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxSetAnimationParams(
+ WebPMux* mux, const WebPMuxAnimParams* params);
+
+// Gets the animation parameters from the mux object.
+// Parameters:
+// mux - (in) object from which the animation parameters to be fetched
+// params - (out) animation parameters extracted from the ANIM chunk
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
+// WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxGetAnimationParams(
+ const WebPMux* mux, WebPMuxAnimParams* params);
+
+//------------------------------------------------------------------------------
+// Misc Utilities.
+
+// Sets the canvas size for the mux object. The width and height can be
+// specified explicitly or left as zero (0, 0).
+// * When width and height are specified explicitly, then this frame bound is
+// enforced during subsequent calls to WebPMuxAssemble() and an error is
+// reported if any animated frame does not completely fit within the canvas.
+// * When unspecified (0, 0), the constructed canvas will get the frame bounds
+// from the bounding-box over all frames after calling WebPMuxAssemble().
+// Parameters:
+// mux - (in) object to which the canvas size is to be set
+// width - (in) canvas width
+// height - (in) canvas height
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or
+// width or height are invalid or out of bounds
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
+ int width, int height);
+
+// Gets the canvas size from the mux object.
+// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
+// That is, the mux object hasn't been modified since the last call to
+// WebPMuxAssemble() or WebPMuxCreate().
+// Parameters:
+// mux - (in) object from which the canvas size is to be fetched
+// width - (out) canvas width
+// height - (out) canvas height
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux, width or height is NULL.
+// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux,
+ int* width, int* height);
+
+// Gets the feature flags from the mux object.
+// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
+// That is, the mux object hasn't been modified since the last call to
+// WebPMuxAssemble() or WebPMuxCreate().
+// Parameters:
+// mux - (in) object from which the features are to be fetched
+// flags - (out) the flags specifying which features are present in the
+// mux object. This will be an OR of various flag values.
+// Enum 'WebPFeatureFlags' can be used to test individual flag values.
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL.
+// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxGetFeatures(const WebPMux* mux,
+ uint32_t* flags);
+
+// Gets number of chunks with the given 'id' in the mux object.
+// Parameters:
+// mux - (in) object from which the info is to be fetched
+// id - (in) chunk id specifying the type of chunk
+// num_elements - (out) number of chunks with the given chunk id
+// Returns:
+// WEBP_MUX_INVALID_ARGUMENT - if mux, or num_elements is NULL.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
+ WebPChunkId id, int* num_elements);
+
+// Assembles all chunks in WebP RIFF format and returns in 'assembled_data'.
+// This function also validates the mux object.
+// Note: The content of 'assembled_data' will be ignored and overwritten.
+// Also, the content of 'assembled_data' is allocated using malloc(), and NOT
+// owned by the 'mux' object. It MUST be deallocated by the caller by calling
+// WebPDataClear(). It's always safe to call WebPDataClear() upon return,
+// even in case of error.
+// Parameters:
+// mux - (in/out) object whose chunks are to be assembled
+// assembled_data - (out) assembled WebP data
+// Returns:
+// WEBP_MUX_BAD_DATA - if mux object is invalid.
+// WEBP_MUX_INVALID_ARGUMENT - if mux or assembled_data is NULL.
+// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
+// WEBP_MUX_OK - on success.
+WEBP_EXTERN WebPMuxError WebPMuxAssemble(WebPMux* mux,
+ WebPData* assembled_data);
+
+//------------------------------------------------------------------------------
+// WebPAnimEncoder API
+//
+// This API allows encoding (possibly) 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, timestamp_ms, &config);
+ }
+ WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
+ WebPAnimEncoderAssemble(enc, webp_data);
+ WebPAnimEncoderDelete(enc);
+ // Write the 'webp_data' to a file, or re-mux it further.
+*/
+
+typedef struct WebPAnimEncoder WebPAnimEncoder; // Main opaque object.
+
+// Forward declarations. Defined in encode.h.
+struct WebPPicture;
+struct WebPConfig;
+
+// Global options.
+struct WebPAnimEncoderOptions {
+ WebPMuxAnimParams anim_params; // Animation parameters.
+ int minimize_size; // If true, minimize the output size (slow). Implicitly
+ // disables key-frame insertion.
+ int kmin;
+ int kmax; // Minimum and maximum distance between consecutive key
+ // frames in the output. The library may insert some key
+ // frames as needed to satisfy this criteria.
+ // Note that these conditions should hold: kmax > kmin
+ // and kmin >= kmax / 2 + 1. Also, if kmax <= 0, then
+ // key-frame insertion is disabled; and if kmax == 1,
+ // then all frames will be key-frames (kmin value does
+ // not matter for these special cases).
+ int allow_mixed; // If true, use mixed compression mode; may choose
+ // either lossy and lossless for each frame.
+ int verbose; // If true, print info and warning messages to stderr.
+
+ uint32_t padding[4]; // Padding for later use.
+};
+
+// Internal, version-checked, entry point.
+WEBP_EXTERN int WebPAnimEncoderOptionsInitInternal(
+ WebPAnimEncoderOptions*, int);
+
+// Should always be called, to initialize a fresh WebPAnimEncoderOptions
+// structure before modification. Returns false in case of version mismatch.
+// WebPAnimEncoderOptionsInit() must have succeeded before using the
+// 'enc_options' object.
+static WEBP_INLINE int WebPAnimEncoderOptionsInit(
+ WebPAnimEncoderOptions* enc_options) {
+ return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION);
+}
+
+// Internal, version-checked, entry point.
+WEBP_EXTERN WebPAnimEncoder* WebPAnimEncoderNewInternal(
+ int, int, const WebPAnimEncoderOptions*, int);
+
+// Creates and initializes a WebPAnimEncoder object.
+// Parameters:
+// width/height - (in) canvas width and height of the animation.
+// enc_options - (in) encoding options; can be passed NULL to pick
+// reasonable defaults.
+// Returns:
+// A pointer to the newly created WebPAnimEncoder object.
+// Or NULL in case of memory error.
+static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew(
+ int width, int height, const WebPAnimEncoderOptions* enc_options) {
+ return WebPAnimEncoderNewInternal(width, height, enc_options,
+ WEBP_MUX_ABI_VERSION);
+}
+
+// Optimize the given frame for WebP, encode it and add it to the
+// WebPAnimEncoder object.
+// The last call to 'WebPAnimEncoderAdd' should be with frame = NULL, which
+// indicates that no more frames are to be added. This call is also used to
+// determine the duration of the last frame.
+// Parameters:
+// enc - (in/out) object to which the frame is to be added.
+// frame - (in/out) frame data in ARGB or YUV(A) format. If it is in YUV(A)
+// format, it will be converted to ARGB, which incurs a small loss.
+// timestamp_ms - (in) timestamp of this frame in milliseconds.
+// Duration of a frame would be calculated as
+// "timestamp of next frame - timestamp of this frame".
+// Hence, timestamps should be in non-decreasing order.
+// config - (in) encoding options; can be passed NULL to pick
+// reasonable defaults.
+// Returns:
+// On error, returns false and frame->error_code is set appropriately.
+// Otherwise, returns true.
+WEBP_EXTERN int WebPAnimEncoderAdd(
+ WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms,
+ const struct WebPConfig* config);
+
+// Assemble all frames added so far into a WebP bitstream.
+// This call should be preceded by a call to 'WebPAnimEncoderAdd' with
+// frame = NULL; if not, the duration of the last frame will be internally
+// estimated.
+// Parameters:
+// enc - (in/out) object from which the frames are to be assembled.
+// webp_data - (out) generated WebP bitstream.
+// Returns:
+// True on success.
+WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
+ WebPData* webp_data);
+
+// Get error string corresponding to the most recent call using 'enc'. The
+// returned string is owned by 'enc' and is valid only until the next call to
+// WebPAnimEncoderAdd() or WebPAnimEncoderAssemble() or WebPAnimEncoderDelete().
+// Parameters:
+// enc - (in/out) object from which the error string is to be fetched.
+// Returns:
+// NULL if 'enc' is NULL. Otherwise, returns the error string if the last call
+// to 'enc' had an error, or an empty string if the last call was a success.
+WEBP_EXTERN const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc);
+
+// Deletes the WebPAnimEncoder object.
+// Parameters:
+// enc - (in/out) object to be deleted
+WEBP_EXTERN void WebPAnimEncoderDelete(WebPAnimEncoder* enc);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_WEBP_MUX_H_ */
diff --git a/src/third_party/libwebp/src/webp/mux_types.h b/src/third_party/libwebp/src/webp/mux_types.h
new file mode 100644
index 0000000..0ebd06f
--- /dev/null
+++ b/src/third_party/libwebp/src/webp/mux_types.h
@@ -0,0 +1,103 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Data-types common to the mux and demux libraries.
+//
+// Author: Urvang (urvang@google.com)
+
+#ifndef WEBP_WEBP_MUX_TYPES_H_
+#define WEBP_WEBP_MUX_TYPES_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#include "starboard/memory.h"
+#else
+#include <stdlib.h> // free()
+#include <string.h> // memset()
+#endif
+#include "./types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: forward declaring enumerations is not allowed in (strict) C and C++,
+// the types are left here for reference.
+// typedef enum WebPFeatureFlags WebPFeatureFlags;
+// typedef enum WebPMuxAnimDispose WebPMuxAnimDispose;
+// typedef enum WebPMuxAnimBlend WebPMuxAnimBlend;
+typedef struct WebPData WebPData;
+
+// VP8X Feature Flags.
+typedef enum WebPFeatureFlags {
+ ANIMATION_FLAG = 0x00000002,
+ XMP_FLAG = 0x00000004,
+ EXIF_FLAG = 0x00000008,
+ ALPHA_FLAG = 0x00000010,
+ ICCP_FLAG = 0x00000020,
+
+ ALL_VALID_FLAGS = 0x0000003e
+} WebPFeatureFlags;
+
+// Dispose method (animation only). Indicates how the area used by the current
+// frame is to be treated before rendering the next frame on the canvas.
+typedef enum WebPMuxAnimDispose {
+ WEBP_MUX_DISPOSE_NONE, // Do not dispose.
+ WEBP_MUX_DISPOSE_BACKGROUND // Dispose to background color.
+} WebPMuxAnimDispose;
+
+// Blend operation (animation only). Indicates how transparent pixels of the
+// current frame are blended with those of the previous canvas.
+typedef enum WebPMuxAnimBlend {
+ WEBP_MUX_BLEND, // Blend.
+ WEBP_MUX_NO_BLEND // Do not blend.
+} WebPMuxAnimBlend;
+
+// Data type used to describe 'raw' data, e.g., chunk data
+// (ICC profile, metadata) and WebP compressed image data.
+struct WebPData {
+ const uint8_t* bytes;
+ size_t size;
+};
+
+// Initializes the contents of the 'webp_data' object with default values.
+static WEBP_INLINE void WebPDataInit(WebPData* webp_data) {
+ if (webp_data != NULL) {
+ SbMemorySet(webp_data, 0, sizeof(*webp_data));
+ }
+}
+
+// Clears the contents of the 'webp_data' object by calling free(). Does not
+// deallocate the object itself.
+static WEBP_INLINE void WebPDataClear(WebPData* webp_data) {
+ if (webp_data != NULL) {
+ SbMemoryDeallocate((void*)webp_data->bytes);
+ WebPDataInit(webp_data);
+ }
+}
+
+// Allocates necessary storage for 'dst' and copies the contents of 'src'.
+// Returns true on success.
+static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) {
+ if (src == NULL || dst == NULL) return 0;
+ WebPDataInit(dst);
+ if (src->bytes != NULL && src->size != 0) {
+ dst->bytes = (uint8_t*)SbMemoryAllocate(src->size);
+ if (dst->bytes == NULL) return 0;
+ SbMemoryCopy((void*)dst->bytes, src->bytes, src->size);
+ dst->size = src->size;
+ }
+ return 1;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* WEBP_WEBP_MUX_TYPES_H_ */
diff --git a/src/third_party/libwebp/src/webp/types.h b/src/third_party/libwebp/src/webp/types.h
new file mode 100644
index 0000000..12738fa
--- /dev/null
+++ b/src/third_party/libwebp/src/webp/types.h
@@ -0,0 +1,58 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Common types
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_WEBP_TYPES_H_
+#define WEBP_WEBP_TYPES_H_
+
+#if defined(STARBOARD)
+#include "starboard/log.h"
+#include "starboard/types.h"
+#define WEBP_INLINE SB_C_INLINE
+#else
+#include <stddef.h> // for size_t
+
+#ifndef _MSC_VER
+#include <inttypes.h>
+#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
+ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+#define WEBP_INLINE inline
+#else
+#define WEBP_INLINE
+#endif
+#else
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+typedef signed int int32_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long int uint64_t;
+typedef long long int int64_t;
+#define WEBP_INLINE __forceinline
+#endif /* _MSC_VER */
+#endif /* defined(STARBOARD) */
+
+#ifndef WEBP_EXTERN
+// This explicitly marks library functions and allows for changing the
+// signature for e.g., Windows DLL builds.
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define WEBP_EXTERN extern __attribute__ ((visibility ("default")))
+# else
+# define WEBP_EXTERN extern
+# endif /* __GNUC__ >= 4 */
+#endif /* WEBP_EXTERN */
+
+// Macro to check ABI compatibility (same major revision number)
+#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8))
+
+#endif /* WEBP_WEBP_TYPES_H_ */
diff --git a/src/third_party/libwebp/swig/README b/src/third_party/libwebp/swig/README
new file mode 100644
index 0000000..725c071
--- /dev/null
+++ b/src/third_party/libwebp/swig/README
@@ -0,0 +1,56 @@
+Building:
+=========
+
+JNI SWIG bindings:
+------------------
+ $ gcc -shared -fPIC -fno-strict-aliasing -O2 \
+ -I/path/to/your/jdk/includes \
+ libwebp_java_wrap.c \
+ -lwebp \
+ -o libwebp_jni.so
+
+-------------------------------------- BEGIN PSEUDO EXAMPLE
+import com.google.webp.libwebp;
+
+import java.lang.reflect.Method;
+
+public class libwebp_jni_example {
+ static {
+ System.loadLibrary("webp_jni");
+ }
+
+ /**
+ * usage: java -cp libwebp.jar:. libwebp_jni_example
+ */
+ public static void main(String argv[]) {
+ final int version = libwebp.WebPGetDecoderVersion();
+ System.out.println("libwebp version: " + Integer.toHexString(version));
+
+ System.out.println("libwebp methods:");
+ final Method[] libwebpMethods = libwebp.class.getDeclaredMethods();
+ for (int i = 0; i < libwebpMethods.length; i++) {
+ System.out.println(libwebpMethods[i]);
+ }
+ }
+}
+-------------------------------------- END PSEUDO EXAMPLE
+
+ $ javac -cp libwebp.jar libwebp_jni_example.java
+ $ java -Djava.library.path=. -cp libwebp.jar:. libwebp_jni_example
+
+Python SWIG bindings:
+---------------------
+ $ python setup.py build_ext
+ $ python setup.py install --prefix=pylocal
+
+-------------------------------------- BEGIN PSEUDO EXAMPLE
+import glob
+import sys
+sys.path.append(glob.glob('pylocal/lib/python*/site-packages')[0])
+
+from com.google.webp import libwebp
+print "libwebp decoder version: %x" % libwebp.WebPGetDecoderVersion()
+
+print "libwebp attributes:"
+for attr in dir(libwebp): print attr
+-------------------------------------- END PSEUDO EXAMPLE
diff --git a/src/third_party/libwebp/swig/libwebp.go b/src/third_party/libwebp/swig/libwebp.go
new file mode 100644
index 0000000..df205aa
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp.go
@@ -0,0 +1,45 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.10
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package libwebp
+
+import _ "runtime/cgo"
+import "unsafe"
+
+type _ unsafe.Pointer
+
+type _swig_fnptr *byte
+type _swig_memberptr *byte
+
+//extern libwebpSwigCgocall
+func SwigCgocall()
+
+//extern libwebpSwigCgocallDone
+func SwigCgocallDone()
+
+//extern libwebpSwigCgocallBack
+func SwigCgocallBack()
+
+//extern libwebpSwigCgocallBackDone
+func SwigCgocallBackDone()
+
+func WebPGetDecoderVersion() int
+func Wrapped_WebPGetInfo(string, []int, []int) int
+
+// WebPGetInfo has 2 output parameters, provide a version in the more natural
+// go idiom:
+func WebPGetInfo(webp []byte) (ok bool, width int, height int) {
+ w := []int{0}
+ h := []int{0}
+ ok = Wrapped_WebPGetInfo(string(webp), w, h) != 0
+ width = w[0]
+ height = h[0]
+ return
+}
diff --git a/src/third_party/libwebp/swig/libwebp.jar b/src/third_party/libwebp/swig/libwebp.jar
new file mode 100644
index 0000000..2fc502b
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp.jar
Binary files differ
diff --git a/src/third_party/libwebp/swig/libwebp.py b/src/third_party/libwebp/swig/libwebp.py
new file mode 100644
index 0000000..4ff11f8
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp.py
@@ -0,0 +1,198 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 2.0.4
+#
+# Do not make changes to this file unless you know what you are doing--modify
+# the SWIG interface file instead.
+
+
+
+from sys import version_info
+if version_info >= (2,6,0):
+ def swig_import_helper():
+ from os.path import dirname
+ import imp
+ fp = None
+ try:
+ fp, pathname, description = imp.find_module('_libwebp', [dirname(__file__)])
+ except ImportError:
+ import _libwebp
+ return _libwebp
+ if fp is not None:
+ try:
+ _mod = imp.load_module('_libwebp', fp, pathname, description)
+ finally:
+ fp.close()
+ return _mod
+ _libwebp = swig_import_helper()
+ del swig_import_helper
+else:
+ import _libwebp
+del version_info
+try:
+ _swig_property = property
+except NameError:
+ pass # Python < 2.2 doesn't have 'property'.
+def _swig_setattr_nondynamic(self,class_type,name,value,static=1):
+ if (name == "thisown"): return self.this.own(value)
+ if (name == "this"):
+ if type(value).__name__ == 'SwigPyObject':
+ self.__dict__[name] = value
+ return
+ method = class_type.__swig_setmethods__.get(name,None)
+ if method: return method(self,value)
+ if (not static):
+ self.__dict__[name] = value
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+
+def _swig_setattr(self,class_type,name,value):
+ return _swig_setattr_nondynamic(self,class_type,name,value,0)
+
+def _swig_getattr(self,class_type,name):
+ if (name == "thisown"): return self.this.own()
+ method = class_type.__swig_getmethods__.get(name,None)
+ if method: return method(self)
+ raise AttributeError(name)
+
+def _swig_repr(self):
+ try: strthis = "proxy of " + self.this.__repr__()
+ except: strthis = ""
+ return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+try:
+ _object = object
+ _newclass = 1
+except AttributeError:
+ class _object : pass
+ _newclass = 0
+
+
+
+def WebPGetDecoderVersion():
+ """WebPGetDecoderVersion() -> int"""
+ return _libwebp.WebPGetDecoderVersion()
+
+def WebPGetInfo(*args):
+ """WebPGetInfo(uint8_t data) -> (width, height)"""
+ return _libwebp.WebPGetInfo(*args)
+
+def WebPDecodeRGB(*args):
+ """WebPDecodeRGB(uint8_t data) -> (rgb, width, height)"""
+ return _libwebp.WebPDecodeRGB(*args)
+
+def WebPDecodeRGBA(*args):
+ """WebPDecodeRGBA(uint8_t data) -> (rgb, width, height)"""
+ return _libwebp.WebPDecodeRGBA(*args)
+
+def WebPDecodeARGB(*args):
+ """WebPDecodeARGB(uint8_t data) -> (rgb, width, height)"""
+ return _libwebp.WebPDecodeARGB(*args)
+
+def WebPDecodeBGR(*args):
+ """WebPDecodeBGR(uint8_t data) -> (rgb, width, height)"""
+ return _libwebp.WebPDecodeBGR(*args)
+
+def WebPDecodeBGRA(*args):
+ """WebPDecodeBGRA(uint8_t data) -> (rgb, width, height)"""
+ return _libwebp.WebPDecodeBGRA(*args)
+
+def WebPGetEncoderVersion():
+ """WebPGetEncoderVersion() -> int"""
+ return _libwebp.WebPGetEncoderVersion()
+
+def wrap_WebPEncodeRGB(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeRGB(*args)
+
+def wrap_WebPEncodeBGR(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeBGR(*args)
+
+def wrap_WebPEncodeRGBA(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeRGBA(*args)
+
+def wrap_WebPEncodeBGRA(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeBGRA(*args)
+
+def wrap_WebPEncodeLosslessRGB(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeLosslessRGB(*args)
+
+def wrap_WebPEncodeLosslessBGR(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeLosslessBGR(*args)
+
+def wrap_WebPEncodeLosslessRGBA(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeLosslessRGBA(*args)
+
+def wrap_WebPEncodeLosslessBGRA(*args):
+ """private, do not call directly."""
+ return _libwebp.wrap_WebPEncodeLosslessBGRA(*args)
+_UNUSED = 1
+
+def WebPEncodeRGB(rgb, width, height, stride, quality_factor):
+ """WebPEncodeRGB(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+ webp = wrap_WebPEncodeRGB(
+ rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+def WebPEncodeRGBA(rgb, width, height, stride, quality_factor):
+ """WebPEncodeRGBA(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+ webp = wrap_WebPEncodeRGBA(
+ rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+def WebPEncodeBGR(rgb, width, height, stride, quality_factor):
+ """WebPEncodeBGR(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+ webp = wrap_WebPEncodeBGR(
+ rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+def WebPEncodeBGRA(rgb, width, height, stride, quality_factor):
+ """WebPEncodeBGRA(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+ webp = wrap_WebPEncodeBGRA(
+ rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+def WebPEncodeLosslessRGB(rgb, width, height, stride):
+ """WebPEncodeLosslessRGB(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+ webp = wrap_WebPEncodeLosslessRGB(rgb, _UNUSED, _UNUSED, width, height, stride)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+def WebPEncodeLosslessRGBA(rgb, width, height, stride):
+ """WebPEncodeLosslessRGBA(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+ webp = wrap_WebPEncodeLosslessRGBA(rgb, _UNUSED, _UNUSED, width, height, stride)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+def WebPEncodeLosslessBGR(rgb, width, height, stride):
+ """WebPEncodeLosslessBGR(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+ webp = wrap_WebPEncodeLosslessBGR(rgb, _UNUSED, _UNUSED, width, height, stride)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+def WebPEncodeLosslessBGRA(rgb, width, height, stride):
+ """WebPEncodeLosslessBGRA(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+ webp = wrap_WebPEncodeLosslessBGRA(rgb, _UNUSED, _UNUSED, width, height, stride)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+
+# This file is compatible with both classic and new-style classes.
+
+
diff --git a/src/third_party/libwebp/swig/libwebp.swig b/src/third_party/libwebp/swig/libwebp.swig
new file mode 100644
index 0000000..17748b9
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp.swig
@@ -0,0 +1,435 @@
+// Copyright 2011 Google Inc.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// libwebp swig interface definition
+//
+// Author: James Zern (jzern@google.com)
+
+/*
+ Go bindings:
+ $ swig -go \
+ -outdir . \
+ -o libwebp_go_wrap.c libwebp.swig
+
+ Java bindings:
+ $ mkdir -p java/com/google/webp
+ $ swig -java \
+ -package com.google.webp \
+ -outdir java/com/google/webp \
+ -o libwebp_java_wrap.c libwebp.swig
+
+ Python bindings:
+ $ swig -python \
+ -outdir . \
+ -o libwebp_python_wrap.c libwebp.swig
+*/
+
+#ifdef SWIGPYTHON
+%module(package="com.google.webp") libwebp
+#else
+%module libwebp
+#endif /* SWIGPYTHON */
+
+%include "constraints.i"
+%include "typemaps.i"
+
+#ifdef SWIGGO
+%apply (char* STRING, size_t LENGTH) { (const uint8_t* data, size_t data_size) }
+
+%rename(wrapped_WebPGetInfo) WebPGetInfo(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+#endif /* SWIGGO */
+
+#ifdef SWIGJAVA
+%include "arrays_java.i";
+%include "enums.swg" /*NB: requires JDK-1.5+
+ See: http://www.swig.org/Doc1.3/Java.html#enumerations */
+
+// map uint8_t* such that a byte[] is used
+%{
+#include "webp/types.h"
+%}
+// from arrays_java.i (signed char)
+JAVA_ARRAYS_DECL(uint8_t, jbyte, Byte, Uint8)
+JAVA_ARRAYS_IMPL(uint8_t, jbyte, Byte, Uint8)
+JAVA_ARRAYS_TYPEMAPS(uint8_t, byte, jbyte, Uint8, "[B")
+%apply uint8_t[] { uint8_t* }
+#endif /* SWIGJAVA */
+
+#ifdef SWIGPYTHON
+%apply (char* STRING, size_t LENGTH) { (const uint8_t* data, size_t data_size) }
+%typemap(out) uint8_t* {
+ $result = PyString_FromStringAndSize(
+ (const char*)$1,
+ ($1 == NULL) ? 0 : ReturnedBufferSize("$symname", arg3, arg4));
+}
+
+%typemap (in) const uint8_t* rgb (Py_buffer rgb_buffer) {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer($input, (const void**)(&$1), &unused);
+ if (!PyObject_CheckBuffer($input)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method '$symname', argument $argnum"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer($input, &rgb_buffer, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method '$symname', unable to get buffer view");
+ }
+ $1 = ($1_ltype)rgb_buffer.buf;
+}
+
+%typemap(freearg) const uint8_t* rgb {
+ PyBuffer_Release(&rgb_buffer$argnum);
+}
+
+%define DECODE_AUTODOC(func)
+%feature("autodoc", #func "(uint8_t data) -> (rgb, width, height)") func;
+%enddef
+
+%feature("autodoc", "1");
+DECODE_AUTODOC(WebPDecodeRGB);
+DECODE_AUTODOC(WebPDecodeRGBA);
+DECODE_AUTODOC(WebPDecodeARGB);
+DECODE_AUTODOC(WebPDecodeBGR);
+DECODE_AUTODOC(WebPDecodeBGRA);
+%feature("autodoc", "WebPGetInfo(uint8_t data) -> (width, height)") WebPGetInfo;
+#endif /* SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Decoder specific
+
+%apply int* OUTPUT { int* width, int* height }
+
+int WebPGetDecoderVersion(void);
+int WebPGetInfo(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+
+// free the buffer returned by these functions after copying into
+// the native type
+%newobject WebPDecodeRGB;
+%newobject WebPDecodeRGBA;
+%newobject WebPDecodeARGB;
+%newobject WebPDecodeBGR;
+%newobject WebPDecodeBGRA;
+%typemap(newfree) uint8_t* "free($1);"
+
+uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
+ int* width, int* height);
+
+#endif /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Encoder specific
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+
+int WebPGetEncoderVersion(void);
+
+#endif /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Wrapper code additions
+
+%{
+#include "webp/decode.h"
+#include "webp/encode.h"
+%}
+
+#ifdef SWIGJAVA
+%{
+#define FillMeInAsSizeCannotBeDeterminedAutomatically \
+ (result ? (jint)ReturnedBufferSize(__FUNCTION__, arg3, arg4) : 0)
+%}
+#endif /* SWIGJAVA */
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+%{
+static size_t ReturnedBufferSize(
+ const char* function, int* width, int* height) {
+ static const struct sizemap {
+ const char* function;
+ int size_multiplier;
+ } size_map[] = {
+#ifdef SWIGJAVA
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB", 3 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR", 3 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 },
+#endif
+#ifdef SWIGPYTHON
+ { "WebPDecodeRGB", 3 },
+ { "WebPDecodeRGBA", 4 },
+ { "WebPDecodeARGB", 4 },
+ { "WebPDecodeBGR", 3 },
+ { "WebPDecodeBGRA", 4 },
+ { "wrap_WebPEncodeRGB", 1 },
+ { "wrap_WebPEncodeBGR", 1 },
+ { "wrap_WebPEncodeRGBA", 1 },
+ { "wrap_WebPEncodeBGRA", 1 },
+ { "wrap_WebPEncodeLosslessRGB", 1 },
+ { "wrap_WebPEncodeLosslessBGR", 1 },
+ { "wrap_WebPEncodeLosslessRGBA", 1 },
+ { "wrap_WebPEncodeLosslessBGRA", 1 },
+#endif
+ { NULL, 0 }
+ };
+ const struct sizemap* p;
+ size_t size = 0;
+
+ for (p = size_map; p->function; ++p) {
+ if (!strcmp(function, p->function)) {
+ size = *width * *height * p->size_multiplier;
+ break;
+ }
+ }
+
+ return size;
+}
+%}
+
+%{
+typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb,
+ int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb,
+ int width, int height, int stride,
+ uint8_t** output);
+
+static uint8_t* EncodeLossy(const uint8_t* rgb,
+ int width, int height, int stride,
+ float quality_factor,
+ WebPEncodeFunction encfn,
+ int* output_size, int* unused) {
+ uint8_t* output = NULL;
+ const size_t image_size =
+ encfn(rgb, width, height, stride, quality_factor, &output);
+ // the values of following two will be interpreted by ReturnedBufferSize()
+ // as 'width' and 'height' in the size calculation.
+ *output_size = image_size;
+ *unused = 1;
+ return image_size ? output : NULL;
+}
+
+static uint8_t* EncodeLossless(const uint8_t* rgb,
+ int width, int height, int stride,
+ WebPEncodeLosslessFunction encfn,
+ int* output_size, int* unused) {
+ uint8_t* output = NULL;
+ const size_t image_size = encfn(rgb, width, height, stride, &output);
+ // the values of the following two will be interpreted by
+ // ReturnedBufferSize() as 'width' and 'height' in the size calculation.
+ *output_size = image_size;
+ *unused = 1;
+ return image_size ? output : NULL;
+}
+%}
+
+#endif /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// libwebp/encode wrapper functions
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+
+%apply int* INPUT { int* unused1, int* unused2 }
+%apply int* OUTPUT { int* output_size }
+
+// free the buffer returned by these functions after copying into
+// the native type
+%newobject wrap_WebPEncodeRGB;
+%newobject wrap_WebPEncodeBGR;
+%newobject wrap_WebPEncodeRGBA;
+%newobject wrap_WebPEncodeBGRA;
+%newobject wrap_WebPEncodeLosslessRGB;
+%newobject wrap_WebPEncodeLosslessBGR;
+%newobject wrap_WebPEncodeLosslessRGBA;
+%newobject wrap_WebPEncodeLosslessBGRA;
+
+#ifdef SWIGJAVA
+// There's no reason to call these directly
+%javamethodmodifiers wrap_WebPEncodeRGB "private";
+%javamethodmodifiers wrap_WebPEncodeBGR "private";
+%javamethodmodifiers wrap_WebPEncodeRGBA "private";
+%javamethodmodifiers wrap_WebPEncodeBGRA "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessRGB "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessBGR "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessRGBA "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessBGRA "private";
+#endif /* SWIGJAVA */
+
+#ifdef SWIGPYTHON
+// This autodoc will serve as a catch-all for wrap_*.
+%feature("autodoc", "private, do not call directly.");
+#endif
+
+%inline %{
+// Changes the return type of WebPEncode* to more closely match Decode*.
+// This also makes it easier to wrap the output buffer in a native type rather
+// than dealing with the return pointer.
+// The additional parameters are to allow reuse of ReturnedBufferSize(),
+// unused2 and output_size will be used in this case.
+#define LOSSY_WRAPPER(FUNC) \
+ static uint8_t* wrap_##FUNC( \
+ const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+ int width, int height, int stride, float quality_factor) { \
+ return EncodeLossy(rgb, width, height, stride, quality_factor, \
+ FUNC, output_size, unused2); \
+ } \
+
+LOSSY_WRAPPER(WebPEncodeRGB)
+LOSSY_WRAPPER(WebPEncodeBGR)
+LOSSY_WRAPPER(WebPEncodeRGBA)
+LOSSY_WRAPPER(WebPEncodeBGRA)
+
+#undef LOSSY_WRAPPER
+
+#define LOSSLESS_WRAPPER(FUNC) \
+ static uint8_t* wrap_##FUNC( \
+ const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+ int width, int height, int stride) { \
+ return EncodeLossless(rgb, width, height, stride, \
+ FUNC, output_size, unused2); \
+ } \
+
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+
+#undef LOSSLESS_WRAPPER
+
+%}
+
+#endif /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Language specific
+
+#ifdef SWIGGO
+%insert(go_wrapper) %{
+
+// WebPGetInfo has 2 output parameters, provide a version in the more natural
+// go idiom:
+func WebPGetInfo(webp []byte) (ok bool, width int, height int) {
+ w := []int{0}
+ h := []int{0}
+ ok = Wrapped_WebPGetInfo(string(webp), w, h) != 0
+ width = w[0]
+ height = h[0]
+ return
+}
+
+%}
+#endif /* SWIGGO */
+
+#ifdef SWIGJAVA
+%{
+/* Work around broken gcj jni.h */
+#ifdef __GCJ_JNI_H__
+# undef JNIEXPORT
+# define JNIEXPORT
+# undef JNICALL
+# define JNICALL
+#endif
+%}
+
+%pragma(java) modulecode=%{
+ private static final int UNUSED = 1;
+ private static int outputSize[] = { 0 };
+%}
+
+
+%define CALL_ENCODE_LOSSY_WRAPPER(func)
+%pragma(java) modulecode=%{
+ public static byte[] func(
+ byte[] rgb, int width, int height, int stride, float quality_factor) {
+ return wrap_##func(
+ rgb, UNUSED, UNUSED, outputSize, width, height, stride, quality_factor);
+ }
+%}
+%enddef
+
+%define CALL_ENCODE_LOSSLESS_WRAPPER(func)
+%pragma(java) modulecode=%{
+ public static byte[] func(
+ byte[] rgb, int width, int height, int stride) {
+ return wrap_##func(
+ rgb, UNUSED, UNUSED, outputSize, width, height, stride);
+ }
+%}
+%enddef
+
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGB)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGBA)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGR)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGRA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+#endif /* SWIGJAVA */
+
+#ifdef SWIGPYTHON
+%pythoncode %{
+_UNUSED = 1
+%}
+
+%define CALL_ENCODE_LOSSY_WRAPPER(func)
+%pythoncode %{
+def func(rgb, width, height, stride, quality_factor):
+ """func(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+ webp = wrap_##func(
+ rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+%}
+%enddef
+
+%define CALL_ENCODE_LOSSLESS_WRAPPER(func)
+%pythoncode %{
+def func(rgb, width, height, stride):
+ """func(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+ webp = wrap_##func(rgb, _UNUSED, _UNUSED, width, height, stride)
+ if len(webp[0]) == 0:
+ return None
+ return webp[0]
+%}
+%enddef
+
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGB)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGBA)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGR)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGRA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+#endif /* SWIGPYTHON */
diff --git a/src/third_party/libwebp/swig/libwebp_gc.c b/src/third_party/libwebp/swig/libwebp_gc.c
new file mode 100644
index 0000000..308b7f8
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp_gc.c
@@ -0,0 +1,52 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.10
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+/* This file should be compiled with 6c/8c. */
+#pragma dynimport _ _ "libwebp_go.so"
+
+#include "runtime.h"
+#include "cgocall.h"
+
+#ifdef _64BIT
+#define SWIG_PARM_SIZE 8
+#else
+#define SWIG_PARM_SIZE 4
+#endif
+
+#pragma dynimport _wrap_WebPGetDecoderVersion _wrap_WebPGetDecoderVersion ""
+extern void (*_wrap_WebPGetDecoderVersion)(void*);
+static void (*x_wrap_WebPGetDecoderVersion)(void*) = _wrap_WebPGetDecoderVersion;
+
+void
+·WebPGetDecoderVersion(struct {
+ uint8 x[SWIG_PARM_SIZE];
+} p)
+
+{
+ runtime·cgocall(x_wrap_WebPGetDecoderVersion, &p);
+}
+
+
+
+#pragma dynimport _wrap_wrapped_WebPGetInfo _wrap_wrapped_WebPGetInfo ""
+extern void (*_wrap_wrapped_WebPGetInfo)(void*);
+static void (*x_wrap_wrapped_WebPGetInfo)(void*) = _wrap_wrapped_WebPGetInfo;
+
+void
+·Wrapped_WebPGetInfo(struct {
+ uint8 x[(2 * SWIG_PARM_SIZE) + (3 * SWIG_PARM_SIZE) + (3 * SWIG_PARM_SIZE) + SWIG_PARM_SIZE];
+} p)
+
+{
+ runtime·cgocall(x_wrap_wrapped_WebPGetInfo, &p);
+}
+
+
+
diff --git a/src/third_party/libwebp/swig/libwebp_go_wrap.c b/src/third_party/libwebp/swig/libwebp_go_wrap.c
new file mode 100644
index 0000000..351d523
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp_go_wrap.c
@@ -0,0 +1,274 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.10
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+#define SWIGMODULE libwebp
+/* -----------------------------------------------------------------------------
+ * This section contains generic SWIG labels for method/variable
+ * declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+# define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+# define SWIGINLINE inline
+# else
+# define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+# elif defined(__ICC)
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+# pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+# define SWIGUNUSEDPARM(p)
+# else
+# define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# ifndef GCC_HASCLASSVISIBILITY
+# define GCC_HASCLASSVISIBILITY
+# endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# if defined(STATIC_LINKED)
+# define SWIGEXPORT
+# else
+# define SWIGEXPORT __declspec(dllexport)
+# endif
+# else
+# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+# define SWIGEXPORT __attribute__ ((visibility("default")))
+# else
+# define SWIGEXPORT
+# endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# define SWIGSTDCALL __stdcall
+# else
+# define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+
+
+typedef long long intgo;
+typedef unsigned long long uintgo;
+
+
+
+typedef struct { char *p; intgo n; } _gostring_;
+typedef struct { void* array; intgo len; intgo cap; } _goslice_;
+
+
+
+
+#define swiggo_size_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1];
+#define swiggo_size_assert(t, n) swiggo_size_assert_eq(sizeof(t), n, swiggo_sizeof_##t##_is_not_##n)
+
+swiggo_size_assert(char, 1)
+swiggo_size_assert(short, 2)
+swiggo_size_assert(int, 4)
+typedef long long swiggo_long_long;
+swiggo_size_assert(swiggo_long_long, 8)
+swiggo_size_assert(float, 4)
+swiggo_size_assert(double, 8)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void crosscall2(void (*fn)(void *, int), void *, int);
+extern void _cgo_allocate(void *, int);
+extern void _cgo_panic(void *, int);
+#ifdef __cplusplus
+}
+#endif
+
+static void *_swig_goallocate(size_t len) {
+ struct {
+ size_t len;
+ void *ret;
+ } a;
+ a.len = len;
+ crosscall2(_cgo_allocate, &a, (int) sizeof a);
+ return a.ret;
+}
+
+static void _swig_gopanic(const char *p) {
+ struct {
+ const char *p;
+ } a;
+ a.p = p;
+ crosscall2(_cgo_panic, &a, (int) sizeof a);
+}
+
+
+
+
+static _gostring_ _swig_makegostring(const char *p, size_t l) {
+ _gostring_ ret;
+ ret.p = (char*)_swig_goallocate(l + 1);
+ memcpy(ret.p, p, l);
+ ret.n = l;
+ return ret;
+}
+
+#define SWIG_contract_assert(expr, msg) \
+ if (!(expr)) { _swig_gopanic(msg); } else
+
+
+#define SWIG_exception(code, msg) _swig_gopanic(msg)
+
+
+#include "webp/decode.h"
+#include "webp/encode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+_wrap_WebPGetDecoderVersion(void *swig_v)
+{
+ int result;
+
+ struct swigargs {
+ long : 0;
+ intgo result;
+ } *swig_a = (struct swigargs *) swig_v;
+
+
+ result = (int)WebPGetDecoderVersion();
+ swig_a->result = result;
+}
+
+
+void
+_wrap_wrapped_WebPGetInfo(void *swig_v)
+{
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int temp3 ;
+ int temp4 ;
+ int result;
+
+ struct swigargs {
+ _gostring_ arg1;
+ _goslice_ arg3;
+ _goslice_ arg4;
+ long : 0;
+ intgo result;
+ } *swig_a = (struct swigargs *) swig_v;
+
+
+ arg1 = (uint8_t *)swig_a->arg1.p;
+ arg2 = (size_t)swig_a->arg1.n;
+
+ {
+ if (swig_a->arg3.len == 0) {
+ _swig_gopanic("array must contain at least 1 element");
+ }
+ arg3 = &temp3;
+ }
+ {
+ if (swig_a->arg4.len == 0) {
+ _swig_gopanic("array must contain at least 1 element");
+ }
+ arg4 = &temp4;
+ }
+
+ result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4);
+ swig_a->result = result;
+ {
+ int* a = (int *) swig_a->arg3.array;
+ a[0] = temp3;
+ }
+ {
+ int* a = (int *) swig_a->arg4.array;
+ a[0] = temp4;
+ }
+
+
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/src/third_party/libwebp/swig/libwebp_java_wrap.c b/src/third_party/libwebp/swig/libwebp_java_wrap.c
new file mode 100644
index 0000000..c8d4b13
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp_java_wrap.c
@@ -0,0 +1,1765 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.4
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+#define SWIGJAVA
+
+/* -----------------------------------------------------------------------------
+ * This section contains generic SWIG labels for method/variable
+ * declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+# define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+# define SWIGINLINE inline
+# else
+# define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+# elif defined(__ICC)
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+# pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+# define SWIGUNUSEDPARM(p)
+# else
+# define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# ifndef GCC_HASCLASSVISIBILITY
+# define GCC_HASCLASSVISIBILITY
+# endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# if defined(STATIC_LINKED)
+# define SWIGEXPORT
+# else
+# define SWIGEXPORT __declspec(dllexport)
+# endif
+# else
+# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+# define SWIGEXPORT __attribute__ ((visibility("default")))
+# else
+# define SWIGEXPORT
+# endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# define SWIGSTDCALL __stdcall
+# else
+# define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+
+
+/* Fix for jlong on some versions of gcc on Windows */
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+ typedef long long __int64;
+#endif
+
+/* Fix for jlong on 64-bit x86 Solaris */
+#if defined(__x86_64)
+# ifdef _LP64
+# undef _LP64
+# endif
+#endif
+
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* Support for throwing Java exceptions */
+typedef enum {
+ SWIG_JavaOutOfMemoryError = 1,
+ SWIG_JavaIOException,
+ SWIG_JavaRuntimeException,
+ SWIG_JavaIndexOutOfBoundsException,
+ SWIG_JavaArithmeticException,
+ SWIG_JavaIllegalArgumentException,
+ SWIG_JavaNullPointerException,
+ SWIG_JavaDirectorPureVirtual,
+ SWIG_JavaUnknownError
+} SWIG_JavaExceptionCodes;
+
+typedef struct {
+ SWIG_JavaExceptionCodes code;
+ const char *java_exception;
+} SWIG_JavaExceptions_t;
+
+
+static void SWIGUNUSED SWIG_JavaThrowException(JNIEnv *jenv, SWIG_JavaExceptionCodes code, const char *msg) {
+ jclass excep;
+ static const SWIG_JavaExceptions_t java_exceptions[] = {
+ { SWIG_JavaOutOfMemoryError, "java/lang/OutOfMemoryError" },
+ { SWIG_JavaIOException, "java/io/IOException" },
+ { SWIG_JavaRuntimeException, "java/lang/RuntimeException" },
+ { SWIG_JavaIndexOutOfBoundsException, "java/lang/IndexOutOfBoundsException" },
+ { SWIG_JavaArithmeticException, "java/lang/ArithmeticException" },
+ { SWIG_JavaIllegalArgumentException, "java/lang/IllegalArgumentException" },
+ { SWIG_JavaNullPointerException, "java/lang/NullPointerException" },
+ { SWIG_JavaDirectorPureVirtual, "java/lang/RuntimeException" },
+ { SWIG_JavaUnknownError, "java/lang/UnknownError" },
+ { (SWIG_JavaExceptionCodes)0, "java/lang/UnknownError" }
+ };
+ const SWIG_JavaExceptions_t *except_ptr = java_exceptions;
+
+ while (except_ptr->code != code && except_ptr->code)
+ except_ptr++;
+
+ (*jenv)->ExceptionClear(jenv);
+ excep = (*jenv)->FindClass(jenv, except_ptr->java_exception);
+ if (excep)
+ (*jenv)->ThrowNew(jenv, excep, msg);
+}
+
+
+/* Contract support */
+
+#define SWIG_contract_assert(nullreturn, expr, msg) if (!(expr)) {SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, msg); return nullreturn; } else
+
+/* Errors in SWIG */
+#define SWIG_UnknownError -1
+#define SWIG_IOError -2
+#define SWIG_RuntimeError -3
+#define SWIG_IndexError -4
+#define SWIG_TypeError -5
+#define SWIG_DivisionByZero -6
+#define SWIG_OverflowError -7
+#define SWIG_SyntaxError -8
+#define SWIG_ValueError -9
+#define SWIG_SystemError -10
+#define SWIG_AttributeError -11
+#define SWIG_MemoryError -12
+#define SWIG_NullReferenceError -13
+
+
+
+
+SWIGINTERN void SWIG_JavaException(JNIEnv *jenv, int code, const char *msg) {
+ SWIG_JavaExceptionCodes exception_code = SWIG_JavaUnknownError;
+ switch(code) {
+ case SWIG_MemoryError:
+ exception_code = SWIG_JavaOutOfMemoryError;
+ break;
+ case SWIG_IOError:
+ exception_code = SWIG_JavaIOException;
+ break;
+ case SWIG_SystemError:
+ case SWIG_RuntimeError:
+ exception_code = SWIG_JavaRuntimeException;
+ break;
+ case SWIG_OverflowError:
+ case SWIG_IndexError:
+ exception_code = SWIG_JavaIndexOutOfBoundsException;
+ break;
+ case SWIG_DivisionByZero:
+ exception_code = SWIG_JavaArithmeticException;
+ break;
+ case SWIG_SyntaxError:
+ case SWIG_ValueError:
+ case SWIG_TypeError:
+ exception_code = SWIG_JavaIllegalArgumentException;
+ break;
+ case SWIG_UnknownError:
+ default:
+ exception_code = SWIG_JavaUnknownError;
+ break;
+ }
+ SWIG_JavaThrowException(jenv, exception_code, msg);
+}
+
+
+#if defined(SWIG_NOINCLUDE) || defined(SWIG_NOARRAYS)
+
+
+int SWIG_JavaArrayInSchar (JNIEnv *jenv, jbyte **jarr, signed char **carr, jbyteArray input);
+void SWIG_JavaArrayArgoutSchar (JNIEnv *jenv, jbyte *jarr, signed char *carr, jbyteArray input);
+jbyteArray SWIG_JavaArrayOutSchar (JNIEnv *jenv, signed char *result, jsize sz);
+
+
+int SWIG_JavaArrayInUchar (JNIEnv *jenv, jshort **jarr, unsigned char **carr, jshortArray input);
+void SWIG_JavaArrayArgoutUchar (JNIEnv *jenv, jshort *jarr, unsigned char *carr, jshortArray input);
+jshortArray SWIG_JavaArrayOutUchar (JNIEnv *jenv, unsigned char *result, jsize sz);
+
+
+int SWIG_JavaArrayInShort (JNIEnv *jenv, jshort **jarr, short **carr, jshortArray input);
+void SWIG_JavaArrayArgoutShort (JNIEnv *jenv, jshort *jarr, short *carr, jshortArray input);
+jshortArray SWIG_JavaArrayOutShort (JNIEnv *jenv, short *result, jsize sz);
+
+
+int SWIG_JavaArrayInUshort (JNIEnv *jenv, jint **jarr, unsigned short **carr, jintArray input);
+void SWIG_JavaArrayArgoutUshort (JNIEnv *jenv, jint *jarr, unsigned short *carr, jintArray input);
+jintArray SWIG_JavaArrayOutUshort (JNIEnv *jenv, unsigned short *result, jsize sz);
+
+
+int SWIG_JavaArrayInInt (JNIEnv *jenv, jint **jarr, int **carr, jintArray input);
+void SWIG_JavaArrayArgoutInt (JNIEnv *jenv, jint *jarr, int *carr, jintArray input);
+jintArray SWIG_JavaArrayOutInt (JNIEnv *jenv, int *result, jsize sz);
+
+
+int SWIG_JavaArrayInUint (JNIEnv *jenv, jlong **jarr, unsigned int **carr, jlongArray input);
+void SWIG_JavaArrayArgoutUint (JNIEnv *jenv, jlong *jarr, unsigned int *carr, jlongArray input);
+jlongArray SWIG_JavaArrayOutUint (JNIEnv *jenv, unsigned int *result, jsize sz);
+
+
+int SWIG_JavaArrayInLong (JNIEnv *jenv, jint **jarr, long **carr, jintArray input);
+void SWIG_JavaArrayArgoutLong (JNIEnv *jenv, jint *jarr, long *carr, jintArray input);
+jintArray SWIG_JavaArrayOutLong (JNIEnv *jenv, long *result, jsize sz);
+
+
+int SWIG_JavaArrayInUlong (JNIEnv *jenv, jlong **jarr, unsigned long **carr, jlongArray input);
+void SWIG_JavaArrayArgoutUlong (JNIEnv *jenv, jlong *jarr, unsigned long *carr, jlongArray input);
+jlongArray SWIG_JavaArrayOutUlong (JNIEnv *jenv, unsigned long *result, jsize sz);
+
+
+int SWIG_JavaArrayInLonglong (JNIEnv *jenv, jlong **jarr, jlong **carr, jlongArray input);
+void SWIG_JavaArrayArgoutLonglong (JNIEnv *jenv, jlong *jarr, jlong *carr, jlongArray input);
+jlongArray SWIG_JavaArrayOutLonglong (JNIEnv *jenv, jlong *result, jsize sz);
+
+
+int SWIG_JavaArrayInFloat (JNIEnv *jenv, jfloat **jarr, float **carr, jfloatArray input);
+void SWIG_JavaArrayArgoutFloat (JNIEnv *jenv, jfloat *jarr, float *carr, jfloatArray input);
+jfloatArray SWIG_JavaArrayOutFloat (JNIEnv *jenv, float *result, jsize sz);
+
+
+int SWIG_JavaArrayInDouble (JNIEnv *jenv, jdouble **jarr, double **carr, jdoubleArray input);
+void SWIG_JavaArrayArgoutDouble (JNIEnv *jenv, jdouble *jarr, double *carr, jdoubleArray input);
+jdoubleArray SWIG_JavaArrayOutDouble (JNIEnv *jenv, double *result, jsize sz);
+
+
+#else
+
+
+/* signed char[] support */
+int SWIG_JavaArrayInSchar (JNIEnv *jenv, jbyte **jarr, signed char **carr, jbyteArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetByteArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (signed char*) calloc(sz, sizeof(signed char));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (signed char)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutSchar (JNIEnv *jenv, jbyte *jarr, signed char *carr, jbyteArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jbyte)carr[i];
+ (*jenv)->ReleaseByteArrayElements(jenv, input, jarr, 0);
+}
+
+jbyteArray SWIG_JavaArrayOutSchar (JNIEnv *jenv, signed char *result, jsize sz) {
+ jbyte *arr;
+ int i;
+ jbyteArray jresult = (*jenv)->NewByteArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetByteArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jbyte)result[i];
+ (*jenv)->ReleaseByteArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* unsigned char[] support */
+int SWIG_JavaArrayInUchar (JNIEnv *jenv, jshort **jarr, unsigned char **carr, jshortArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetShortArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (unsigned char*) calloc(sz, sizeof(unsigned char));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (unsigned char)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutUchar (JNIEnv *jenv, jshort *jarr, unsigned char *carr, jshortArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jshort)carr[i];
+ (*jenv)->ReleaseShortArrayElements(jenv, input, jarr, 0);
+}
+
+jshortArray SWIG_JavaArrayOutUchar (JNIEnv *jenv, unsigned char *result, jsize sz) {
+ jshort *arr;
+ int i;
+ jshortArray jresult = (*jenv)->NewShortArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetShortArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jshort)result[i];
+ (*jenv)->ReleaseShortArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* short[] support */
+int SWIG_JavaArrayInShort (JNIEnv *jenv, jshort **jarr, short **carr, jshortArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetShortArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (short*) calloc(sz, sizeof(short));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (short)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutShort (JNIEnv *jenv, jshort *jarr, short *carr, jshortArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jshort)carr[i];
+ (*jenv)->ReleaseShortArrayElements(jenv, input, jarr, 0);
+}
+
+jshortArray SWIG_JavaArrayOutShort (JNIEnv *jenv, short *result, jsize sz) {
+ jshort *arr;
+ int i;
+ jshortArray jresult = (*jenv)->NewShortArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetShortArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jshort)result[i];
+ (*jenv)->ReleaseShortArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* unsigned short[] support */
+int SWIG_JavaArrayInUshort (JNIEnv *jenv, jint **jarr, unsigned short **carr, jintArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (unsigned short*) calloc(sz, sizeof(unsigned short));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (unsigned short)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutUshort (JNIEnv *jenv, jint *jarr, unsigned short *carr, jintArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jint)carr[i];
+ (*jenv)->ReleaseIntArrayElements(jenv, input, jarr, 0);
+}
+
+jintArray SWIG_JavaArrayOutUshort (JNIEnv *jenv, unsigned short *result, jsize sz) {
+ jint *arr;
+ int i;
+ jintArray jresult = (*jenv)->NewIntArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jint)result[i];
+ (*jenv)->ReleaseIntArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* int[] support */
+int SWIG_JavaArrayInInt (JNIEnv *jenv, jint **jarr, int **carr, jintArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (int*) calloc(sz, sizeof(int));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (int)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutInt (JNIEnv *jenv, jint *jarr, int *carr, jintArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jint)carr[i];
+ (*jenv)->ReleaseIntArrayElements(jenv, input, jarr, 0);
+}
+
+jintArray SWIG_JavaArrayOutInt (JNIEnv *jenv, int *result, jsize sz) {
+ jint *arr;
+ int i;
+ jintArray jresult = (*jenv)->NewIntArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jint)result[i];
+ (*jenv)->ReleaseIntArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* unsigned int[] support */
+int SWIG_JavaArrayInUint (JNIEnv *jenv, jlong **jarr, unsigned int **carr, jlongArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (unsigned int*) calloc(sz, sizeof(unsigned int));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (unsigned int)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutUint (JNIEnv *jenv, jlong *jarr, unsigned int *carr, jlongArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jlong)carr[i];
+ (*jenv)->ReleaseLongArrayElements(jenv, input, jarr, 0);
+}
+
+jlongArray SWIG_JavaArrayOutUint (JNIEnv *jenv, unsigned int *result, jsize sz) {
+ jlong *arr;
+ int i;
+ jlongArray jresult = (*jenv)->NewLongArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jlong)result[i];
+ (*jenv)->ReleaseLongArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* long[] support */
+int SWIG_JavaArrayInLong (JNIEnv *jenv, jint **jarr, long **carr, jintArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (long*) calloc(sz, sizeof(long));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (long)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutLong (JNIEnv *jenv, jint *jarr, long *carr, jintArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jint)carr[i];
+ (*jenv)->ReleaseIntArrayElements(jenv, input, jarr, 0);
+}
+
+jintArray SWIG_JavaArrayOutLong (JNIEnv *jenv, long *result, jsize sz) {
+ jint *arr;
+ int i;
+ jintArray jresult = (*jenv)->NewIntArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jint)result[i];
+ (*jenv)->ReleaseIntArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* unsigned long[] support */
+int SWIG_JavaArrayInUlong (JNIEnv *jenv, jlong **jarr, unsigned long **carr, jlongArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (unsigned long*) calloc(sz, sizeof(unsigned long));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (unsigned long)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutUlong (JNIEnv *jenv, jlong *jarr, unsigned long *carr, jlongArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jlong)carr[i];
+ (*jenv)->ReleaseLongArrayElements(jenv, input, jarr, 0);
+}
+
+jlongArray SWIG_JavaArrayOutUlong (JNIEnv *jenv, unsigned long *result, jsize sz) {
+ jlong *arr;
+ int i;
+ jlongArray jresult = (*jenv)->NewLongArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jlong)result[i];
+ (*jenv)->ReleaseLongArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* jlong[] support */
+int SWIG_JavaArrayInLonglong (JNIEnv *jenv, jlong **jarr, jlong **carr, jlongArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (jlong*) calloc(sz, sizeof(jlong));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (jlong)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutLonglong (JNIEnv *jenv, jlong *jarr, jlong *carr, jlongArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jlong)carr[i];
+ (*jenv)->ReleaseLongArrayElements(jenv, input, jarr, 0);
+}
+
+jlongArray SWIG_JavaArrayOutLonglong (JNIEnv *jenv, jlong *result, jsize sz) {
+ jlong *arr;
+ int i;
+ jlongArray jresult = (*jenv)->NewLongArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jlong)result[i];
+ (*jenv)->ReleaseLongArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* float[] support */
+int SWIG_JavaArrayInFloat (JNIEnv *jenv, jfloat **jarr, float **carr, jfloatArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetFloatArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (float*) calloc(sz, sizeof(float));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (float)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutFloat (JNIEnv *jenv, jfloat *jarr, float *carr, jfloatArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jfloat)carr[i];
+ (*jenv)->ReleaseFloatArrayElements(jenv, input, jarr, 0);
+}
+
+jfloatArray SWIG_JavaArrayOutFloat (JNIEnv *jenv, float *result, jsize sz) {
+ jfloat *arr;
+ int i;
+ jfloatArray jresult = (*jenv)->NewFloatArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetFloatArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jfloat)result[i];
+ (*jenv)->ReleaseFloatArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+/* double[] support */
+int SWIG_JavaArrayInDouble (JNIEnv *jenv, jdouble **jarr, double **carr, jdoubleArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetDoubleArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (double*) calloc(sz, sizeof(double));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (double)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutDouble (JNIEnv *jenv, jdouble *jarr, double *carr, jdoubleArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jdouble)carr[i];
+ (*jenv)->ReleaseDoubleArrayElements(jenv, input, jarr, 0);
+}
+
+jdoubleArray SWIG_JavaArrayOutDouble (JNIEnv *jenv, double *result, jsize sz) {
+ jdouble *arr;
+ int i;
+ jdoubleArray jresult = (*jenv)->NewDoubleArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetDoubleArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jdouble)result[i];
+ (*jenv)->ReleaseDoubleArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+#endif
+
+
+#include "webp/types.h"
+
+
+int SWIG_JavaArrayInUint8 (JNIEnv *jenv, jbyte **jarr, uint8_t **carr, jbyteArray input);
+void SWIG_JavaArrayArgoutUint8 (JNIEnv *jenv, jbyte *jarr, uint8_t *carr, jbyteArray input);
+jbyteArray SWIG_JavaArrayOutUint8 (JNIEnv *jenv, uint8_t *result, jsize sz);
+
+
+/* uint8_t[] support */
+int SWIG_JavaArrayInUint8 (JNIEnv *jenv, jbyte **jarr, uint8_t **carr, jbyteArray input) {
+ int i;
+ jsize sz;
+ if (!input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+ return 0;
+ }
+ sz = (*jenv)->GetArrayLength(jenv, input);
+ *jarr = (*jenv)->GetByteArrayElements(jenv, input, 0);
+ if (!*jarr)
+ return 0;
+ *carr = (uint8_t*) calloc(sz, sizeof(uint8_t));
+ if (!*carr) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+ return 0;
+ }
+ for (i=0; i<sz; i++)
+ (*carr)[i] = (uint8_t)(*jarr)[i];
+ return 1;
+}
+
+void SWIG_JavaArrayArgoutUint8 (JNIEnv *jenv, jbyte *jarr, uint8_t *carr, jbyteArray input) {
+ int i;
+ jsize sz = (*jenv)->GetArrayLength(jenv, input);
+ for (i=0; i<sz; i++)
+ jarr[i] = (jbyte)carr[i];
+ (*jenv)->ReleaseByteArrayElements(jenv, input, jarr, 0);
+}
+
+jbyteArray SWIG_JavaArrayOutUint8 (JNIEnv *jenv, uint8_t *result, jsize sz) {
+ jbyte *arr;
+ int i;
+ jbyteArray jresult = (*jenv)->NewByteArray(jenv, sz);
+ if (!jresult)
+ return NULL;
+ arr = (*jenv)->GetByteArrayElements(jenv, jresult, 0);
+ if (!arr)
+ return NULL;
+ for (i=0; i<sz; i++)
+ arr[i] = (jbyte)result[i];
+ (*jenv)->ReleaseByteArrayElements(jenv, jresult, arr, 0);
+ return jresult;
+}
+
+
+#include "webp/decode.h"
+#include "webp/encode.h"
+
+
+#define FillMeInAsSizeCannotBeDeterminedAutomatically \
+ (result ? (jint)ReturnedBufferSize(__FUNCTION__, arg3, arg4) : 0)
+
+
+static size_t ReturnedBufferSize(
+ const char* function, int* width, int* height) {
+ static const struct sizemap {
+ const char* function;
+ int size_multiplier;
+ } size_map[] = {
+#ifdef SWIGJAVA
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB", 3 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR", 3 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 },
+#endif
+#ifdef SWIGPYTHON
+ { "WebPDecodeRGB", 3 },
+ { "WebPDecodeRGBA", 4 },
+ { "WebPDecodeARGB", 4 },
+ { "WebPDecodeBGR", 3 },
+ { "WebPDecodeBGRA", 4 },
+ { "wrap_WebPEncodeRGB", 1 },
+ { "wrap_WebPEncodeBGR", 1 },
+ { "wrap_WebPEncodeRGBA", 1 },
+ { "wrap_WebPEncodeBGRA", 1 },
+ { "wrap_WebPEncodeLosslessRGB", 1 },
+ { "wrap_WebPEncodeLosslessBGR", 1 },
+ { "wrap_WebPEncodeLosslessRGBA", 1 },
+ { "wrap_WebPEncodeLosslessBGRA", 1 },
+#endif
+ { NULL, 0 }
+ };
+ const struct sizemap* p;
+ size_t size = 0;
+
+ for (p = size_map; p->function; ++p) {
+ if (!strcmp(function, p->function)) {
+ size = *width * *height * p->size_multiplier;
+ break;
+ }
+ }
+
+ return size;
+}
+
+
+typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb,
+ int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb,
+ int width, int height, int stride,
+ uint8_t** output);
+
+static uint8_t* EncodeLossy(const uint8_t* rgb,
+ int width, int height, int stride,
+ float quality_factor,
+ WebPEncodeFunction encfn,
+ int* output_size, int* unused) {
+ uint8_t* output = NULL;
+ const size_t image_size =
+ encfn(rgb, width, height, stride, quality_factor, &output);
+ // the values of following two will be interpreted by ReturnedBufferSize()
+ // as 'width' and 'height' in the size calculation.
+ *output_size = image_size;
+ *unused = 1;
+ return image_size ? output : NULL;
+}
+
+static uint8_t* EncodeLossless(const uint8_t* rgb,
+ int width, int height, int stride,
+ WebPEncodeLosslessFunction encfn,
+ int* output_size, int* unused) {
+ uint8_t* output = NULL;
+ const size_t image_size = encfn(rgb, width, height, stride, &output);
+ // the values of the following two will be interpreted by
+ // ReturnedBufferSize() as 'width' and 'height' in the size calculation.
+ *output_size = image_size;
+ *unused = 1;
+ return image_size ? output : NULL;
+}
+
+
+// Changes the return type of WebPEncode* to more closely match Decode*.
+// This also makes it easier to wrap the output buffer in a native type rather
+// than dealing with the return pointer.
+// The additional parameters are to allow reuse of ReturnedBufferSize(),
+// unused2 and output_size will be used in this case.
+#define LOSSY_WRAPPER(FUNC) \
+ static uint8_t* wrap_##FUNC( \
+ const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+ int width, int height, int stride, float quality_factor) { \
+ return EncodeLossy(rgb, width, height, stride, quality_factor, \
+ FUNC, output_size, unused2); \
+ } \
+
+LOSSY_WRAPPER(WebPEncodeRGB)
+LOSSY_WRAPPER(WebPEncodeBGR)
+LOSSY_WRAPPER(WebPEncodeRGBA)
+LOSSY_WRAPPER(WebPEncodeBGRA)
+
+#undef LOSSY_WRAPPER
+
+#define LOSSLESS_WRAPPER(FUNC) \
+ static uint8_t* wrap_##FUNC( \
+ const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+ int width, int height, int stride) { \
+ return EncodeLossless(rgb, width, height, stride, \
+ FUNC, output_size, unused2); \
+ } \
+
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+
+#undef LOSSLESS_WRAPPER
+
+
+
+/* Work around broken gcj jni.h */
+#ifdef __GCJ_JNI_H__
+# undef JNIEXPORT
+# define JNIEXPORT
+# undef JNICALL
+# define JNICALL
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetDecoderVersion(JNIEnv *jenv, jclass jcls) {
+ jint jresult = 0 ;
+ int result;
+
+ (void)jenv;
+ (void)jcls;
+ result = (int)WebPGetDecoderVersion();
+ jresult = (jint)result;
+ return jresult;
+}
+
+
+SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetInfo(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+ jint jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ jbyte *jarr1 ;
+ int temp3 ;
+ int temp4 ;
+ int result;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (size_t)jarg2;
+ {
+ if (!jarg3) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg3 = &temp3;
+ }
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4);
+ jresult = (jint)result;
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp3;
+ (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+ }
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ jbyte *jarr1 ;
+ int temp3 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (size_t)jarg2;
+ {
+ if (!jarg3) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg3 = &temp3;
+ }
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ result = (uint8_t *)WebPDecodeRGB((uint8_t const *)arg1,arg2,arg3,arg4);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp3;
+ (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+ }
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ jbyte *jarr1 ;
+ int temp3 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (size_t)jarg2;
+ {
+ if (!jarg3) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg3 = &temp3;
+ }
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ result = (uint8_t *)WebPDecodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp3;
+ (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+ }
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeARGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ jbyte *jarr1 ;
+ int temp3 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (size_t)jarg2;
+ {
+ if (!jarg3) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg3 = &temp3;
+ }
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ result = (uint8_t *)WebPDecodeARGB((uint8_t const *)arg1,arg2,arg3,arg4);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp3;
+ (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+ }
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ jbyte *jarr1 ;
+ int temp3 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (size_t)jarg2;
+ {
+ if (!jarg3) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg3 = &temp3;
+ }
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ result = (uint8_t *)WebPDecodeBGR((uint8_t const *)arg1,arg2,arg3,arg4);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp3;
+ (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+ }
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ jbyte *jarr1 ;
+ int temp3 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (size_t)jarg2;
+ {
+ if (!jarg3) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg3 = &temp3;
+ }
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ result = (uint8_t *)WebPDecodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp3;
+ (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+ }
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetEncoderVersion(JNIEnv *jenv, jclass jcls) {
+ jint jresult = 0 ;
+ int result;
+
+ (void)jenv;
+ (void)jcls;
+ result = (int)WebPGetEncoderVersion();
+ jresult = (jint)result;
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ arg8 = (float)jarg8;
+ result = (uint8_t *)wrap_WebPEncodeRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ arg8 = (float)jarg8;
+ result = (uint8_t *)wrap_WebPEncodeBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ arg8 = (float)jarg8;
+ result = (uint8_t *)wrap_WebPEncodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ arg8 = (float)jarg8;
+ result = (uint8_t *)wrap_WebPEncodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ result = (uint8_t *)wrap_WebPEncodeLosslessRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ result = (uint8_t *)wrap_WebPEncodeLosslessBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ result = (uint8_t *)wrap_WebPEncodeLosslessRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+ jbyteArray jresult = 0 ;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ jbyte *jarr1 ;
+ int temp4 ;
+ uint8_t *result = 0 ;
+
+ (void)jenv;
+ (void)jcls;
+ if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+ arg2 = (int *)&jarg2;
+ arg3 = (int *)&jarg3;
+ {
+ if (!jarg4) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return 0;
+ }
+ if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return 0;
+ }
+ arg4 = &temp4;
+ }
+ arg5 = (int)jarg5;
+ arg6 = (int)jarg6;
+ arg7 = (int)jarg7;
+ result = (uint8_t *)wrap_WebPEncodeLosslessBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+ SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+ {
+ jint jvalue = (jint)temp4;
+ (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+ }
+ free(arg1);
+
+
+
+ free(result);
+ return jresult;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/src/third_party/libwebp/swig/libwebp_python_wrap.c b/src/third_party/libwebp/swig/libwebp_python_wrap.c
new file mode 100644
index 0000000..b29f834
--- /dev/null
+++ b/src/third_party/libwebp/swig/libwebp_python_wrap.c
@@ -0,0 +1,5398 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.4
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+#define SWIGPYTHON
+#define SWIG_PYTHON_DIRECTOR_NO_VTABLE
+
+/* -----------------------------------------------------------------------------
+ * This section contains generic SWIG labels for method/variable
+ * declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+# define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+# define SWIGINLINE inline
+# else
+# define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+# elif defined(__ICC)
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+# pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+# define SWIGUNUSEDPARM(p)
+# else
+# define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# ifndef GCC_HASCLASSVISIBILITY
+# define GCC_HASCLASSVISIBILITY
+# endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# if defined(STATIC_LINKED)
+# define SWIGEXPORT
+# else
+# define SWIGEXPORT __declspec(dllexport)
+# endif
+# else
+# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+# define SWIGEXPORT __attribute__ ((visibility("default")))
+# else
+# define SWIGEXPORT
+# endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# define SWIGSTDCALL __stdcall
+# else
+# define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+
+
+/* Python.h has to appear first */
+#include <Python.h>
+
+/* -----------------------------------------------------------------------------
+ * swigrun.swg
+ *
+ * This file contains generic C API SWIG runtime support for pointer
+ * type checking.
+ * ----------------------------------------------------------------------------- */
+
+/* This should only be incremented when either the layout of swig_type_info changes,
+ or for whatever reason, the runtime changes incompatibly */
+#define SWIG_RUNTIME_VERSION "4"
+
+/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */
+#ifdef SWIG_TYPE_TABLE
+# define SWIG_QUOTE_STRING(x) #x
+# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x)
+# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE)
+#else
+# define SWIG_TYPE_TABLE_NAME
+#endif
+
+/*
+ You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for
+ creating a static or dynamic library from the SWIG runtime code.
+ In 99.9% of the cases, SWIG just needs to declare them as 'static'.
+
+ But only do this if strictly necessary, ie, if you have problems
+ with your compiler or suchlike.
+*/
+
+#ifndef SWIGRUNTIME
+# define SWIGRUNTIME SWIGINTERN
+#endif
+
+#ifndef SWIGRUNTIMEINLINE
+# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE
+#endif
+
+/* Generic buffer size */
+#ifndef SWIG_BUFFER_SIZE
+# define SWIG_BUFFER_SIZE 1024
+#endif
+
+/* Flags for pointer conversions */
+#define SWIG_POINTER_DISOWN 0x1
+#define SWIG_CAST_NEW_MEMORY 0x2
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_OWN 0x1
+
+
+/*
+ Flags/methods for returning states.
+
+ The SWIG conversion methods, as ConvertPtr, return an integer
+ that tells if the conversion was successful or not. And if not,
+ an error code can be returned (see swigerrors.swg for the codes).
+
+ Use the following macros/flags to set or process the returning
+ states.
+
+ In old versions of SWIG, code such as the following was usually written:
+
+ if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) {
+ // success code
+ } else {
+ //fail code
+ }
+
+ Now you can be more explicit:
+
+ int res = SWIG_ConvertPtr(obj,vptr,ty.flags);
+ if (SWIG_IsOK(res)) {
+ // success code
+ } else {
+ // fail code
+ }
+
+ which is the same really, but now you can also do
+
+ Type *ptr;
+ int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags);
+ if (SWIG_IsOK(res)) {
+ // success code
+ if (SWIG_IsNewObj(res) {
+ ...
+ delete *ptr;
+ } else {
+ ...
+ }
+ } else {
+ // fail code
+ }
+
+ I.e., now SWIG_ConvertPtr can return new objects and you can
+ identify the case and take care of the deallocation. Of course that
+ also requires SWIG_ConvertPtr to return new result values, such as
+
+ int SWIG_ConvertPtr(obj, ptr,...) {
+ if (<obj is ok>) {
+ if (<need new object>) {
+ *ptr = <ptr to new allocated object>;
+ return SWIG_NEWOBJ;
+ } else {
+ *ptr = <ptr to old object>;
+ return SWIG_OLDOBJ;
+ }
+ } else {
+ return SWIG_BADOBJ;
+ }
+ }
+
+ Of course, returning the plain '0(success)/-1(fail)' still works, but you can be
+ more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the
+ SWIG errors code.
+
+ Finally, if the SWIG_CASTRANK_MODE is enabled, the result code
+ allows to return the 'cast rank', for example, if you have this
+
+ int food(double)
+ int fooi(int);
+
+ and you call
+
+ food(1) // cast rank '1' (1 -> 1.0)
+ fooi(1) // cast rank '0'
+
+ just use the SWIG_AddCast()/SWIG_CheckState()
+*/
+
+#define SWIG_OK (0)
+#define SWIG_ERROR (-1)
+#define SWIG_IsOK(r) (r >= 0)
+#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError)
+
+/* The CastRankLimit says how many bits are used for the cast rank */
+#define SWIG_CASTRANKLIMIT (1 << 8)
+/* The NewMask denotes the object was created (using new/malloc) */
+#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1)
+/* The TmpMask is for in/out typemaps that use temporal objects */
+#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1)
+/* Simple returning values */
+#define SWIG_BADOBJ (SWIG_ERROR)
+#define SWIG_OLDOBJ (SWIG_OK)
+#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK)
+#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK)
+/* Check, add and del mask methods */
+#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r)
+#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r)
+#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK))
+#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r)
+#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r)
+#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK))
+
+/* Cast-Rank Mode */
+#if defined(SWIG_CASTRANK_MODE)
+# ifndef SWIG_TypeRank
+# define SWIG_TypeRank unsigned long
+# endif
+# ifndef SWIG_MAXCASTRANK /* Default cast allowed */
+# define SWIG_MAXCASTRANK (2)
+# endif
+# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1)
+# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK)
+SWIGINTERNINLINE int SWIG_AddCast(int r) {
+ return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r;
+}
+SWIGINTERNINLINE int SWIG_CheckState(int r) {
+ return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0;
+}
+#else /* no cast-rank mode */
+# define SWIG_AddCast
+# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0)
+#endif
+
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *(*swig_converter_func)(void *, int *);
+typedef struct swig_type_info *(*swig_dycast_func)(void **);
+
+/* Structure to store information on one type */
+typedef struct swig_type_info {
+ const char *name; /* mangled name of this type */
+ const char *str; /* human readable name of this type */
+ swig_dycast_func dcast; /* dynamic cast function down a hierarchy */
+ struct swig_cast_info *cast; /* linked list of types that can cast into this type */
+ void *clientdata; /* language specific type data */
+ int owndata; /* flag if the structure owns the clientdata */
+} swig_type_info;
+
+/* Structure to store a type and conversion function used for casting */
+typedef struct swig_cast_info {
+ swig_type_info *type; /* pointer to type that is equivalent to this type */
+ swig_converter_func converter; /* function to cast the void pointers */
+ struct swig_cast_info *next; /* pointer to next cast in linked list */
+ struct swig_cast_info *prev; /* pointer to the previous cast */
+} swig_cast_info;
+
+/* Structure used to store module information
+ * Each module generates one structure like this, and the runtime collects
+ * all of these structures and stores them in a circularly linked list.*/
+typedef struct swig_module_info {
+ swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */
+ size_t size; /* Number of types in this module */
+ struct swig_module_info *next; /* Pointer to next element in circularly linked list */
+ swig_type_info **type_initial; /* Array of initially generated type structures */
+ swig_cast_info **cast_initial; /* Array of initially generated casting structures */
+ void *clientdata; /* Language specific module data */
+} swig_module_info;
+
+/*
+ Compare two type names skipping the space characters, therefore
+ "char*" == "char *" and "Class<int>" == "Class<int >", etc.
+
+ Return 0 when the two name types are equivalent, as in
+ strncmp, but skipping ' '.
+*/
+SWIGRUNTIME int
+SWIG_TypeNameComp(const char *f1, const char *l1,
+ const char *f2, const char *l2) {
+ for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) {
+ while ((*f1 == ' ') && (f1 != l1)) ++f1;
+ while ((*f2 == ' ') && (f2 != l2)) ++f2;
+ if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1;
+ }
+ return (int)((l1 - f1) - (l2 - f2));
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if not equal, 1 if equal
+*/
+SWIGRUNTIME int
+SWIG_TypeEquiv(const char *nb, const char *tb) {
+ int equiv = 0;
+ const char* te = tb + strlen(tb);
+ const char* ne = nb;
+ while (!equiv && *ne) {
+ for (nb = ne; *ne; ++ne) {
+ if (*ne == '|') break;
+ }
+ equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0;
+ if (*ne) ++ne;
+ }
+ return equiv;
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if equal, -1 if nb < tb, 1 if nb > tb
+*/
+SWIGRUNTIME int
+SWIG_TypeCompare(const char *nb, const char *tb) {
+ int equiv = 0;
+ const char* te = tb + strlen(tb);
+ const char* ne = nb;
+ while (!equiv && *ne) {
+ for (nb = ne; *ne; ++ne) {
+ if (*ne == '|') break;
+ }
+ equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0;
+ if (*ne) ++ne;
+ }
+ return equiv;
+}
+
+
+/*
+ Check the typename
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheck(const char *c, swig_type_info *ty) {
+ if (ty) {
+ swig_cast_info *iter = ty->cast;
+ while (iter) {
+ if (strcmp(iter->type->name, c) == 0) {
+ if (iter == ty->cast)
+ return iter;
+ /* Move iter to the top of the linked list */
+ iter->prev->next = iter->next;
+ if (iter->next)
+ iter->next->prev = iter->prev;
+ iter->next = ty->cast;
+ iter->prev = 0;
+ if (ty->cast) ty->cast->prev = iter;
+ ty->cast = iter;
+ return iter;
+ }
+ iter = iter->next;
+ }
+ }
+ return 0;
+}
+
+/*
+ Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *ty) {
+ if (ty) {
+ swig_cast_info *iter = ty->cast;
+ while (iter) {
+ if (iter->type == from) {
+ if (iter == ty->cast)
+ return iter;
+ /* Move iter to the top of the linked list */
+ iter->prev->next = iter->next;
+ if (iter->next)
+ iter->next->prev = iter->prev;
+ iter->next = ty->cast;
+ iter->prev = 0;
+ if (ty->cast) ty->cast->prev = iter;
+ ty->cast = iter;
+ return iter;
+ }
+ iter = iter->next;
+ }
+ }
+ return 0;
+}
+
+/*
+ Cast a pointer up an inheritance hierarchy
+*/
+SWIGRUNTIMEINLINE void *
+SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) {
+ return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory);
+}
+
+/*
+ Dynamic pointer casting. Down an inheritance hierarchy
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) {
+ swig_type_info *lastty = ty;
+ if (!ty || !ty->dcast) return ty;
+ while (ty && (ty->dcast)) {
+ ty = (*ty->dcast)(ptr);
+ if (ty) lastty = ty;
+ }
+ return lastty;
+}
+
+/*
+ Return the name associated with this type
+*/
+SWIGRUNTIMEINLINE const char *
+SWIG_TypeName(const swig_type_info *ty) {
+ return ty->name;
+}
+
+/*
+ Return the pretty name associated with this type,
+ that is an unmangled type name in a form presentable to the user.
+*/
+SWIGRUNTIME const char *
+SWIG_TypePrettyName(const swig_type_info *type) {
+ /* The "str" field contains the equivalent pretty names of the
+ type, separated by vertical-bar characters. We choose
+ to print the last name, as it is often (?) the most
+ specific. */
+ if (!type) return NULL;
+ if (type->str != NULL) {
+ const char *last_name = type->str;
+ const char *s;
+ for (s = type->str; *s; s++)
+ if (*s == '|') last_name = s+1;
+ return last_name;
+ }
+ else
+ return type->name;
+}
+
+/*
+ Set the clientdata field for a type
+*/
+SWIGRUNTIME void
+SWIG_TypeClientData(swig_type_info *ti, void *clientdata) {
+ swig_cast_info *cast = ti->cast;
+ /* if (ti->clientdata == clientdata) return; */
+ ti->clientdata = clientdata;
+
+ while (cast) {
+ if (!cast->converter) {
+ swig_type_info *tc = cast->type;
+ if (!tc->clientdata) {
+ SWIG_TypeClientData(tc, clientdata);
+ }
+ }
+ cast = cast->next;
+ }
+}
+SWIGRUNTIME void
+SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) {
+ SWIG_TypeClientData(ti, clientdata);
+ ti->owndata = 1;
+}
+
+/*
+ Search for a swig_type_info structure only by mangled name
+ Search is a O(log #types)
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_MangledTypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ swig_module_info *iter = start;
+ do {
+ if (iter->size) {
+ register size_t l = 0;
+ register size_t r = iter->size - 1;
+ do {
+ /* since l+r >= 0, we can (>> 1) instead (/ 2) */
+ register size_t i = (l + r) >> 1;
+ const char *iname = iter->types[i]->name;
+ if (iname) {
+ register int compare = strcmp(name, iname);
+ if (compare == 0) {
+ return iter->types[i];
+ } else if (compare < 0) {
+ if (i) {
+ r = i - 1;
+ } else {
+ break;
+ }
+ } else if (compare > 0) {
+ l = i + 1;
+ }
+ } else {
+ break; /* should never happen */
+ }
+ } while (l <= r);
+ }
+ iter = iter->next;
+ } while (iter != end);
+ return 0;
+}
+
+/*
+ Search for a swig_type_info structure for either a mangled name or a human readable name.
+ It first searches the mangled names of the types, which is a O(log #types)
+ If a type is not found it then searches the human readable names, which is O(#types).
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ /* STEP 1: Search the name field using binary search */
+ swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name);
+ if (ret) {
+ return ret;
+ } else {
+ /* STEP 2: If the type hasn't been found, do a complete search
+ of the str field (the human readable name) */
+ swig_module_info *iter = start;
+ do {
+ register size_t i = 0;
+ for (; i < iter->size; ++i) {
+ if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name)))
+ return iter->types[i];
+ }
+ iter = iter->next;
+ } while (iter != end);
+ }
+
+ /* neither found a match */
+ return 0;
+}
+
+/*
+ Pack binary data into a string
+*/
+SWIGRUNTIME char *
+SWIG_PackData(char *c, void *ptr, size_t sz) {
+ static const char hex[17] = "0123456789abcdef";
+ register const unsigned char *u = (unsigned char *) ptr;
+ register const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ register unsigned char uu = *u;
+ *(c++) = hex[(uu & 0xf0) >> 4];
+ *(c++) = hex[uu & 0xf];
+ }
+ return c;
+}
+
+/*
+ Unpack binary data from a string
+*/
+SWIGRUNTIME const char *
+SWIG_UnpackData(const char *c, void *ptr, size_t sz) {
+ register unsigned char *u = (unsigned char *) ptr;
+ register const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ register char d = *(c++);
+ register unsigned char uu;
+ if ((d >= '0') && (d <= '9'))
+ uu = ((d - '0') << 4);
+ else if ((d >= 'a') && (d <= 'f'))
+ uu = ((d - ('a'-10)) << 4);
+ else
+ return (char *) 0;
+ d = *(c++);
+ if ((d >= '0') && (d <= '9'))
+ uu |= (d - '0');
+ else if ((d >= 'a') && (d <= 'f'))
+ uu |= (d - ('a'-10));
+ else
+ return (char *) 0;
+ *u = uu;
+ }
+ return c;
+}
+
+/*
+ Pack 'void *' into a string buffer.
+*/
+SWIGRUNTIME char *
+SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) {
+ char *r = buff;
+ if ((2*sizeof(void *) + 2) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,&ptr,sizeof(void *));
+ if (strlen(name) + 1 > (bsz - (r - buff))) return 0;
+ strcpy(r,name);
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ *ptr = (void *) 0;
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sizeof(void *));
+}
+
+SWIGRUNTIME char *
+SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) {
+ char *r = buff;
+ size_t lname = (name ? strlen(name) : 0);
+ if ((2*sz + 2 + lname) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,ptr,sz);
+ if (lname) {
+ strncpy(r,name,lname+1);
+ } else {
+ *r = 0;
+ }
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ memset(ptr,0,sz);
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sz);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Errors in SWIG */
+#define SWIG_UnknownError -1
+#define SWIG_IOError -2
+#define SWIG_RuntimeError -3
+#define SWIG_IndexError -4
+#define SWIG_TypeError -5
+#define SWIG_DivisionByZero -6
+#define SWIG_OverflowError -7
+#define SWIG_SyntaxError -8
+#define SWIG_ValueError -9
+#define SWIG_SystemError -10
+#define SWIG_AttributeError -11
+#define SWIG_MemoryError -12
+#define SWIG_NullReferenceError -13
+
+
+
+/* Compatibility macros for Python 3 */
+#if PY_VERSION_HEX >= 0x03000000
+
+#define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type)
+#define PyInt_Check(x) PyLong_Check(x)
+#define PyInt_AsLong(x) PyLong_AsLong(x)
+#define PyInt_FromLong(x) PyLong_FromLong(x)
+#define PyString_Check(name) PyBytes_Check(name)
+#define PyString_FromString(x) PyUnicode_FromString(x)
+#define PyString_Format(fmt, args) PyUnicode_Format(fmt, args)
+#define PyString_AsString(str) PyBytes_AsString(str)
+#define PyString_Size(str) PyBytes_Size(str)
+#define PyString_InternFromString(key) PyUnicode_InternFromString(key)
+#define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE
+#define PyString_AS_STRING(x) PyUnicode_AS_STRING(x)
+#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x)
+
+#endif
+
+#ifndef Py_TYPE
+# define Py_TYPE(op) ((op)->ob_type)
+#endif
+
+/* SWIG APIs for compatibility of both Python 2 & 3 */
+
+#if PY_VERSION_HEX >= 0x03000000
+# define SWIG_Python_str_FromFormat PyUnicode_FromFormat
+#else
+# define SWIG_Python_str_FromFormat PyString_FromFormat
+#endif
+
+
+/* Warning: This function will allocate a new string in Python 3,
+ * so please call SWIG_Python_str_DelForPy3(x) to free the space.
+ */
+SWIGINTERN char*
+SWIG_Python_str_AsChar(PyObject *str)
+{
+#if PY_VERSION_HEX >= 0x03000000
+ char *cstr;
+ char *newstr;
+ Py_ssize_t len;
+ str = PyUnicode_AsUTF8String(str);
+ PyBytes_AsStringAndSize(str, &cstr, &len);
+ newstr = (char *) malloc(len+1);
+ memcpy(newstr, cstr, len+1);
+ Py_XDECREF(str);
+ return newstr;
+#else
+ return PyString_AsString(str);
+#endif
+}
+
+#if PY_VERSION_HEX >= 0x03000000
+# define SWIG_Python_str_DelForPy3(x) free( (void*) (x) )
+#else
+# define SWIG_Python_str_DelForPy3(x)
+#endif
+
+
+SWIGINTERN PyObject*
+SWIG_Python_str_FromChar(const char *c)
+{
+#if PY_VERSION_HEX >= 0x03000000
+ return PyUnicode_FromString(c);
+#else
+ return PyString_FromString(c);
+#endif
+}
+
+/* Add PyOS_snprintf for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM)
+# define PyOS_snprintf _snprintf
+# else
+# define PyOS_snprintf snprintf
+# endif
+#endif
+
+/* A crude PyString_FromFormat implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+
+#ifndef SWIG_PYBUFFER_SIZE
+# define SWIG_PYBUFFER_SIZE 1024
+#endif
+
+static PyObject *
+PyString_FromFormat(const char *fmt, ...) {
+ va_list ap;
+ char buf[SWIG_PYBUFFER_SIZE * 2];
+ int res;
+ va_start(ap, fmt);
+ res = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf);
+}
+#endif
+
+/* Add PyObject_Del for old Pythons */
+#if PY_VERSION_HEX < 0x01060000
+# define PyObject_Del(op) PyMem_DEL((op))
+#endif
+#ifndef PyObject_DEL
+# define PyObject_DEL PyObject_Del
+#endif
+
+/* A crude PyExc_StopIteration exception for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# ifndef PyExc_StopIteration
+# define PyExc_StopIteration PyExc_RuntimeError
+# endif
+# ifndef PyObject_GenericGetAttr
+# define PyObject_GenericGetAttr 0
+# endif
+#endif
+
+/* Py_NotImplemented is defined in 2.1 and up. */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef Py_NotImplemented
+# define Py_NotImplemented PyExc_RuntimeError
+# endif
+#endif
+
+/* A crude PyString_AsStringAndSize implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef PyString_AsStringAndSize
+# define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;}
+# endif
+#endif
+
+/* PySequence_Size for old Pythons */
+#if PY_VERSION_HEX < 0x02000000
+# ifndef PySequence_Size
+# define PySequence_Size PySequence_Length
+# endif
+#endif
+
+/* PyBool_FromLong for old Pythons */
+#if PY_VERSION_HEX < 0x02030000
+static
+PyObject *PyBool_FromLong(long ok)
+{
+ PyObject *result = ok ? Py_True : Py_False;
+ Py_INCREF(result);
+ return result;
+}
+#endif
+
+/* Py_ssize_t for old Pythons */
+/* This code is as recommended by: */
+/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+# define PY_SSIZE_T_MAX INT_MAX
+# define PY_SSIZE_T_MIN INT_MIN
+typedef inquiry lenfunc;
+typedef intargfunc ssizeargfunc;
+typedef intintargfunc ssizessizeargfunc;
+typedef intobjargproc ssizeobjargproc;
+typedef intintobjargproc ssizessizeobjargproc;
+typedef getreadbufferproc readbufferproc;
+typedef getwritebufferproc writebufferproc;
+typedef getsegcountproc segcountproc;
+typedef getcharbufferproc charbufferproc;
+static long PyNumber_AsSsize_t (PyObject *x, void *SWIGUNUSEDPARM(exc))
+{
+ long result = 0;
+ PyObject *i = PyNumber_Int(x);
+ if (i) {
+ result = PyInt_AsLong(i);
+ Py_DECREF(i);
+ }
+ return result;
+}
+#endif
+
+#if PY_VERSION_HEX < 0x02040000
+#define Py_VISIT(op) \
+ do { \
+ if (op) { \
+ int vret = visit((op), arg); \
+ if (vret) \
+ return vret; \
+ } \
+ } while (0)
+#endif
+
+#if PY_VERSION_HEX < 0x02030000
+typedef struct {
+ PyTypeObject type;
+ PyNumberMethods as_number;
+ PyMappingMethods as_mapping;
+ PySequenceMethods as_sequence;
+ PyBufferProcs as_buffer;
+ PyObject *name, *slots;
+} PyHeapTypeObject;
+#endif
+
+#if PY_VERSION_HEX < 0x02030000
+typedef destructor freefunc;
+#endif
+
+#if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 6) || \
+ (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 0) || \
+ (PY_MAJOR_VERSION > 3))
+# define SWIGPY_USE_CAPSULE
+# define SWIGPY_CAPSULE_NAME ((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION ".type_pointer_capsule" SWIG_TYPE_TABLE_NAME)
+#endif
+
+#if PY_VERSION_HEX < 0x03020000
+#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type)
+#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name)
+#endif
+
+/* -----------------------------------------------------------------------------
+ * error manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIME PyObject*
+SWIG_Python_ErrorType(int code) {
+ PyObject* type = 0;
+ switch(code) {
+ case SWIG_MemoryError:
+ type = PyExc_MemoryError;
+ break;
+ case SWIG_IOError:
+ type = PyExc_IOError;
+ break;
+ case SWIG_RuntimeError:
+ type = PyExc_RuntimeError;
+ break;
+ case SWIG_IndexError:
+ type = PyExc_IndexError;
+ break;
+ case SWIG_TypeError:
+ type = PyExc_TypeError;
+ break;
+ case SWIG_DivisionByZero:
+ type = PyExc_ZeroDivisionError;
+ break;
+ case SWIG_OverflowError:
+ type = PyExc_OverflowError;
+ break;
+ case SWIG_SyntaxError:
+ type = PyExc_SyntaxError;
+ break;
+ case SWIG_ValueError:
+ type = PyExc_ValueError;
+ break;
+ case SWIG_SystemError:
+ type = PyExc_SystemError;
+ break;
+ case SWIG_AttributeError:
+ type = PyExc_AttributeError;
+ break;
+ default:
+ type = PyExc_RuntimeError;
+ }
+ return type;
+}
+
+
+SWIGRUNTIME void
+SWIG_Python_AddErrorMsg(const char* mesg)
+{
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+
+ if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ char *tmp;
+ PyObject *old_str = PyObject_Str(value);
+ PyErr_Clear();
+ Py_XINCREF(type);
+
+ PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg);
+ SWIG_Python_str_DelForPy3(tmp);
+ Py_DECREF(old_str);
+ Py_DECREF(value);
+ } else {
+ PyErr_SetString(PyExc_RuntimeError, mesg);
+ }
+}
+
+#if defined(SWIG_PYTHON_NO_THREADS)
+# if defined(SWIG_PYTHON_THREADS)
+# undef SWIG_PYTHON_THREADS
+# endif
+#endif
+#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */
+# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL)
+# if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */
+# define SWIG_PYTHON_USE_GIL
+# endif
+# endif
+# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */
+# ifndef SWIG_PYTHON_INITIALIZE_THREADS
+# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads()
+# endif
+# ifdef __cplusplus /* C++ code */
+ class SWIG_Python_Thread_Block {
+ bool status;
+ PyGILState_STATE state;
+ public:
+ void end() { if (status) { PyGILState_Release(state); status = false;} }
+ SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {}
+ ~SWIG_Python_Thread_Block() { end(); }
+ };
+ class SWIG_Python_Thread_Allow {
+ bool status;
+ PyThreadState *save;
+ public:
+ void end() { if (status) { PyEval_RestoreThread(save); status = false; }}
+ SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {}
+ ~SWIG_Python_Thread_Allow() { end(); }
+ };
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block
+# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end()
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow
+# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end()
+# else /* C code */
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure()
+# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread()
+# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow)
+# endif
+# else /* Old thread way, not implemented, user must provide it */
+# if !defined(SWIG_PYTHON_INITIALIZE_THREADS)
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK)
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_BLOCK)
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_ALLOW)
+# define SWIG_PYTHON_THREAD_END_ALLOW
+# endif
+# endif
+#else /* No thread support */
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# define SWIG_PYTHON_THREAD_END_ALLOW
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Python API portion that goes into the runtime
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Constant declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Constant Types */
+#define SWIG_PY_POINTER 4
+#define SWIG_PY_BINARY 5
+
+/* Constant information structure */
+typedef struct swig_const_info {
+ int type;
+ char *name;
+ long lvalue;
+ double dvalue;
+ void *pvalue;
+ swig_type_info **ptype;
+} swig_const_info;
+
+
+/* -----------------------------------------------------------------------------
+ * Wrapper of PyInstanceMethod_New() used in Python 3
+ * It is exported to the generated module, used for -fastproxy
+ * ----------------------------------------------------------------------------- */
+#if PY_VERSION_HEX >= 0x03000000
+SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func)
+{
+ return PyInstanceMethod_New(func);
+}
+#else
+SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(func))
+{
+ return NULL;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ * pyrun.swg
+ *
+ * This file contains the runtime support for Python modules
+ * and includes code for managing global variables and pointer
+ * type checking.
+ *
+ * ----------------------------------------------------------------------------- */
+
+/* Common SWIG API */
+
+/* for raw pointers */
+#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0)
+#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own)
+
+#ifdef SWIGPYTHON_BUILTIN
+#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags)
+#else
+#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags)
+#endif
+
+#define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags)
+
+#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty)
+#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src)
+#define swig_owntype int
+
+/* for raw packed data */
+#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+/* for class or struct pointers */
+#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags)
+
+/* for C or C++ function pointers */
+#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type)
+#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0)
+
+/* for C++ member pointers, ie, member methods */
+#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+
+/* Runtime API */
+
+#define SWIG_GetModule(clientdata) SWIG_Python_GetModule()
+#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer)
+#define SWIG_NewClientData(obj) SwigPyClientData_New(obj)
+
+#define SWIG_SetErrorObj SWIG_Python_SetErrorObj
+#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg
+#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code)
+#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg)
+#define SWIG_fail goto fail
+
+
+/* Runtime API implementation */
+
+/* Error manipulation */
+
+SWIGINTERN void
+SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetObject(errtype, obj);
+ Py_DECREF(obj);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+SWIGINTERN void
+SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetString(errtype, (char *) msg);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj)
+
+/* Set a constant value */
+
+#if defined(SWIGPYTHON_BUILTIN)
+
+SWIGINTERN void
+SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) {
+ PyObject *s = PyString_InternFromString(key);
+ PyList_Append(seq, s);
+ Py_DECREF(s);
+}
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) {
+ PyDict_SetItemString(d, (char *)name, obj);
+ Py_DECREF(obj);
+ if (public_interface)
+ SwigPyBuiltin_AddPublicSymbol(public_interface, name);
+}
+
+#else
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) {
+ PyDict_SetItemString(d, (char *)name, obj);
+ Py_DECREF(obj);
+}
+
+#endif
+
+/* Append a value to the result obj */
+
+SWIGINTERN PyObject*
+SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) {
+#if !defined(SWIG_PYTHON_OUTPUT_TUPLE)
+ if (!result) {
+ result = obj;
+ } else if (result == Py_None) {
+ Py_DECREF(result);
+ result = obj;
+ } else {
+ if (!PyList_Check(result)) {
+ PyObject *o2 = result;
+ result = PyList_New(1);
+ PyList_SetItem(result, 0, o2);
+ }
+ PyList_Append(result,obj);
+ Py_DECREF(obj);
+ }
+ return result;
+#else
+ PyObject* o2;
+ PyObject* o3;
+ if (!result) {
+ result = obj;
+ } else if (result == Py_None) {
+ Py_DECREF(result);
+ result = obj;
+ } else {
+ if (!PyTuple_Check(result)) {
+ o2 = result;
+ result = PyTuple_New(1);
+ PyTuple_SET_ITEM(result, 0, o2);
+ }
+ o3 = PyTuple_New(1);
+ PyTuple_SET_ITEM(o3, 0, obj);
+ o2 = result;
+ result = PySequence_Concat(o2, o3);
+ Py_DECREF(o2);
+ Py_DECREF(o3);
+ }
+ return result;
+#endif
+}
+
+/* Unpack the argument tuple */
+
+SWIGINTERN int
+SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs)
+{
+ if (!args) {
+ if (!min && !max) {
+ return 1;
+ } else {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none",
+ name, (min == max ? "" : "at least "), (int)min);
+ return 0;
+ }
+ }
+ if (!PyTuple_Check(args)) {
+ if (min <= 1 && max >= 1) {
+ register int i;
+ objs[0] = args;
+ for (i = 1; i < max; ++i) {
+ objs[i] = 0;
+ }
+ return 2;
+ }
+ PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple");
+ return 0;
+ } else {
+ register Py_ssize_t l = PyTuple_GET_SIZE(args);
+ if (l < min) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at least "), (int)min, (int)l);
+ return 0;
+ } else if (l > max) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at most "), (int)max, (int)l);
+ return 0;
+ } else {
+ register int i;
+ for (i = 0; i < l; ++i) {
+ objs[i] = PyTuple_GET_ITEM(args, i);
+ }
+ for (; l < max; ++l) {
+ objs[l] = 0;
+ }
+ return i + 1;
+ }
+ }
+}
+
+/* A functor is a function object with one single object argument */
+#if PY_VERSION_HEX >= 0x02020000
+#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL);
+#else
+#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj);
+#endif
+
+/*
+ Helper for static pointer initialization for both C and C++ code, for example
+ static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...);
+*/
+#ifdef __cplusplus
+#define SWIG_STATIC_POINTER(var) var
+#else
+#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Pointer declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1)
+#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN)
+
+#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1)
+
+#define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2)
+#define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* How to access Py_None */
+#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# ifndef SWIG_PYTHON_NO_BUILD_NONE
+# ifndef SWIG_PYTHON_BUILD_NONE
+# define SWIG_PYTHON_BUILD_NONE
+# endif
+# endif
+#endif
+
+#ifdef SWIG_PYTHON_BUILD_NONE
+# ifdef Py_None
+# undef Py_None
+# define Py_None SWIG_Py_None()
+# endif
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_Py_None(void)
+{
+ PyObject *none = Py_BuildValue((char*)"");
+ Py_DECREF(none);
+ return none;
+}
+SWIGRUNTIME PyObject *
+SWIG_Py_None(void)
+{
+ static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None();
+ return none;
+}
+#endif
+
+/* The python void return value */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Py_Void(void)
+{
+ PyObject *none = Py_None;
+ Py_INCREF(none);
+ return none;
+}
+
+/* SwigPyClientData */
+
+typedef struct {
+ PyObject *klass;
+ PyObject *newraw;
+ PyObject *newargs;
+ PyObject *destroy;
+ int delargs;
+ int implicitconv;
+ PyTypeObject *pytype;
+} SwigPyClientData;
+
+SWIGRUNTIMEINLINE int
+SWIG_Python_CheckImplicit(swig_type_info *ty)
+{
+ SwigPyClientData *data = (SwigPyClientData *)ty->clientdata;
+ return data ? data->implicitconv : 0;
+}
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_ExceptionType(swig_type_info *desc) {
+ SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0;
+ PyObject *klass = data ? data->klass : 0;
+ return (klass ? klass : PyExc_RuntimeError);
+}
+
+
+SWIGRUNTIME SwigPyClientData *
+SwigPyClientData_New(PyObject* obj)
+{
+ if (!obj) {
+ return 0;
+ } else {
+ SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData));
+ /* the klass element */
+ data->klass = obj;
+ Py_INCREF(data->klass);
+ /* the newraw method and newargs arguments used to create a new raw instance */
+ if (PyClass_Check(obj)) {
+ data->newraw = 0;
+ data->newargs = obj;
+ Py_INCREF(obj);
+ } else {
+#if (PY_VERSION_HEX < 0x02020000)
+ data->newraw = 0;
+#else
+ data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__");
+#endif
+ if (data->newraw) {
+ Py_INCREF(data->newraw);
+ data->newargs = PyTuple_New(1);
+ PyTuple_SetItem(data->newargs, 0, obj);
+ } else {
+ data->newargs = obj;
+ }
+ Py_INCREF(data->newargs);
+ }
+ /* the destroy method, aka as the C++ delete method */
+ data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__");
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ data->destroy = 0;
+ }
+ if (data->destroy) {
+ int flags;
+ Py_INCREF(data->destroy);
+ flags = PyCFunction_GET_FLAGS(data->destroy);
+#ifdef METH_O
+ data->delargs = !(flags & (METH_O));
+#else
+ data->delargs = 0;
+#endif
+ } else {
+ data->delargs = 0;
+ }
+ data->implicitconv = 0;
+ data->pytype = 0;
+ return data;
+ }
+}
+
+SWIGRUNTIME void
+SwigPyClientData_Del(SwigPyClientData *data) {
+ Py_XDECREF(data->newraw);
+ Py_XDECREF(data->newargs);
+ Py_XDECREF(data->destroy);
+}
+
+/* =============== SwigPyObject =====================*/
+
+typedef struct {
+ PyObject_HEAD
+ void *ptr;
+ swig_type_info *ty;
+ int own;
+ PyObject *next;
+#ifdef SWIGPYTHON_BUILTIN
+ PyObject *dict;
+#endif
+} SwigPyObject;
+
+SWIGRUNTIME PyObject *
+SwigPyObject_long(SwigPyObject *v)
+{
+ return PyLong_FromVoidPtr(v->ptr);
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_format(const char* fmt, SwigPyObject *v)
+{
+ PyObject *res = NULL;
+ PyObject *args = PyTuple_New(1);
+ if (args) {
+ if (PyTuple_SetItem(args, 0, SwigPyObject_long(v)) == 0) {
+ PyObject *ofmt = SWIG_Python_str_FromChar(fmt);
+ if (ofmt) {
+#if PY_VERSION_HEX >= 0x03000000
+ res = PyUnicode_Format(ofmt,args);
+#else
+ res = PyString_Format(ofmt,args);
+#endif
+ Py_DECREF(ofmt);
+ }
+ Py_DECREF(args);
+ }
+ }
+ return res;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_oct(SwigPyObject *v)
+{
+ return SwigPyObject_format("%o",v);
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_hex(SwigPyObject *v)
+{
+ return SwigPyObject_format("%x",v);
+}
+
+SWIGRUNTIME PyObject *
+#ifdef METH_NOARGS
+SwigPyObject_repr(SwigPyObject *v)
+#else
+SwigPyObject_repr(SwigPyObject *v, PyObject *args)
+#endif
+{
+ const char *name = SWIG_TypePrettyName(v->ty);
+ PyObject *repr = SWIG_Python_str_FromFormat("<Swig Object of type '%s' at %p>", name, (void *)v);
+ if (v->next) {
+# ifdef METH_NOARGS
+ PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next);
+# else
+ PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next, args);
+# endif
+# if PY_VERSION_HEX >= 0x03000000
+ PyObject *joined = PyUnicode_Concat(repr, nrep);
+ Py_DecRef(repr);
+ Py_DecRef(nrep);
+ repr = joined;
+# else
+ PyString_ConcatAndDel(&repr,nrep);
+# endif
+ }
+ return repr;
+}
+
+SWIGRUNTIME int
+SwigPyObject_print(SwigPyObject *v, FILE *fp, int SWIGUNUSEDPARM(flags))
+{
+ char *str;
+#ifdef METH_NOARGS
+ PyObject *repr = SwigPyObject_repr(v);
+#else
+ PyObject *repr = SwigPyObject_repr(v, NULL);
+#endif
+ if (repr) {
+ str = SWIG_Python_str_AsChar(repr);
+ fputs(str, fp);
+ SWIG_Python_str_DelForPy3(str);
+ Py_DECREF(repr);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_str(SwigPyObject *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ return SWIG_PackVoidPtr(result, v->ptr, v->ty->name, sizeof(result)) ?
+ SWIG_Python_str_FromChar(result) : 0;
+}
+
+SWIGRUNTIME int
+SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w)
+{
+ void *i = v->ptr;
+ void *j = w->ptr;
+ return (i < j) ? -1 : ((i > j) ? 1 : 0);
+}
+
+/* Added for Python 3.x, would it also be useful for Python 2.x? */
+SWIGRUNTIME PyObject*
+SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op)
+{
+ PyObject* res;
+ if( op != Py_EQ && op != Py_NE ) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0);
+ return res;
+}
+
+
+SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void);
+
+#ifdef SWIGPYTHON_BUILTIN
+static swig_type_info *SwigPyObject_stype = 0;
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_type(void) {
+ SwigPyClientData *cd;
+ assert(SwigPyObject_stype);
+ cd = (SwigPyClientData*) SwigPyObject_stype->clientdata;
+ assert(cd);
+ assert(cd->pytype);
+ return cd->pytype;
+}
+#else
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce();
+ return type;
+}
+#endif
+
+SWIGRUNTIMEINLINE int
+SwigPyObject_Check(PyObject *op) {
+#ifdef SWIGPYTHON_BUILTIN
+ PyTypeObject *target_tp = SwigPyObject_type();
+ if (PyType_IsSubtype(op->ob_type, target_tp))
+ return 1;
+ return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0);
+#else
+ return (Py_TYPE(op) == SwigPyObject_type())
+ || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0);
+#endif
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_New(void *ptr, swig_type_info *ty, int own);
+
+SWIGRUNTIME void
+SwigPyObject_dealloc(PyObject *v)
+{
+ SwigPyObject *sobj = (SwigPyObject *) v;
+ PyObject *next = sobj->next;
+ if (sobj->own == SWIG_POINTER_OWN) {
+ swig_type_info *ty = sobj->ty;
+ SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0;
+ PyObject *destroy = data ? data->destroy : 0;
+ if (destroy) {
+ /* destroy is always a VARARGS method */
+ PyObject *res;
+ if (data->delargs) {
+ /* we need to create a temporary object to carry the destroy operation */
+ PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0);
+ res = SWIG_Python_CallFunctor(destroy, tmp);
+ Py_DECREF(tmp);
+ } else {
+ PyCFunction meth = PyCFunction_GET_FUNCTION(destroy);
+ PyObject *mself = PyCFunction_GET_SELF(destroy);
+ res = ((*meth)(mself, v));
+ }
+ Py_XDECREF(res);
+ }
+#if !defined(SWIG_PYTHON_SILENT_MEMLEAK)
+ else {
+ const char *name = SWIG_TypePrettyName(ty);
+ printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown"));
+ }
+#endif
+ }
+ Py_XDECREF(next);
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyObject*
+SwigPyObject_append(PyObject* v, PyObject* next)
+{
+ SwigPyObject *sobj = (SwigPyObject *) v;
+#ifndef METH_O
+ PyObject *tmp = 0;
+ if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL;
+ next = tmp;
+#endif
+ if (!SwigPyObject_Check(next)) {
+ return NULL;
+ }
+ sobj->next = next;
+ Py_INCREF(next);
+ return SWIG_Py_Void();
+}
+
+SWIGRUNTIME PyObject*
+#ifdef METH_NOARGS
+SwigPyObject_next(PyObject* v)
+#else
+SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ SwigPyObject *sobj = (SwigPyObject *) v;
+ if (sobj->next) {
+ Py_INCREF(sobj->next);
+ return sobj->next;
+ } else {
+ return SWIG_Py_Void();
+ }
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+SwigPyObject_disown(PyObject *v)
+#else
+SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ SwigPyObject *sobj = (SwigPyObject *)v;
+ sobj->own = 0;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+SwigPyObject_acquire(PyObject *v)
+#else
+SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ SwigPyObject *sobj = (SwigPyObject *)v;
+ sobj->own = SWIG_POINTER_OWN;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+SwigPyObject_own(PyObject *v, PyObject *args)
+{
+ PyObject *val = 0;
+#if (PY_VERSION_HEX < 0x02020000)
+ if (!PyArg_ParseTuple(args,(char *)"|O:own",&val))
+#else
+ if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val))
+#endif
+ {
+ return NULL;
+ }
+ else
+ {
+ SwigPyObject *sobj = (SwigPyObject *)v;
+ PyObject *obj = PyBool_FromLong(sobj->own);
+ if (val) {
+#ifdef METH_NOARGS
+ if (PyObject_IsTrue(val)) {
+ SwigPyObject_acquire(v);
+ } else {
+ SwigPyObject_disown(v);
+ }
+#else
+ if (PyObject_IsTrue(val)) {
+ SwigPyObject_acquire(v,args);
+ } else {
+ SwigPyObject_disown(v,args);
+ }
+#endif
+ }
+ return obj;
+ }
+}
+
+#ifdef METH_O
+static PyMethodDef
+swigobject_methods[] = {
+ {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_NOARGS, (char *)"releases ownership of the pointer"},
+ {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_NOARGS, (char *)"aquires ownership of the pointer"},
+ {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"},
+ {(char *)"append", (PyCFunction)SwigPyObject_append, METH_O, (char *)"appends another 'this' object"},
+ {(char *)"next", (PyCFunction)SwigPyObject_next, METH_NOARGS, (char *)"returns the next 'this' object"},
+ {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_NOARGS, (char *)"returns object representation"},
+ {0, 0, 0, 0}
+};
+#else
+static PyMethodDef
+swigobject_methods[] = {
+ {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"},
+ {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_VARARGS, (char *)"aquires ownership of the pointer"},
+ {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"},
+ {(char *)"append", (PyCFunction)SwigPyObject_append, METH_VARARGS, (char *)"appends another 'this' object"},
+ {(char *)"next", (PyCFunction)SwigPyObject_next, METH_VARARGS, (char *)"returns the next 'this' object"},
+ {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_VARARGS, (char *)"returns object representation"},
+ {0, 0, 0, 0}
+};
+#endif
+
+#if PY_VERSION_HEX < 0x02020000
+SWIGINTERN PyObject *
+SwigPyObject_getattr(SwigPyObject *sobj,char *name)
+{
+ return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name);
+}
+#endif
+
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_TypeOnce(void) {
+ static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer";
+
+ static PyNumberMethods SwigPyObject_as_number = {
+ (binaryfunc)0, /*nb_add*/
+ (binaryfunc)0, /*nb_subtract*/
+ (binaryfunc)0, /*nb_multiply*/
+ /* nb_divide removed in Python 3 */
+#if PY_VERSION_HEX < 0x03000000
+ (binaryfunc)0, /*nb_divide*/
+#endif
+ (binaryfunc)0, /*nb_remainder*/
+ (binaryfunc)0, /*nb_divmod*/
+ (ternaryfunc)0,/*nb_power*/
+ (unaryfunc)0, /*nb_negative*/
+ (unaryfunc)0, /*nb_positive*/
+ (unaryfunc)0, /*nb_absolute*/
+ (inquiry)0, /*nb_nonzero*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+#if PY_VERSION_HEX < 0x03000000
+ 0, /*nb_coerce*/
+#endif
+ (unaryfunc)SwigPyObject_long, /*nb_int*/
+#if PY_VERSION_HEX < 0x03000000
+ (unaryfunc)SwigPyObject_long, /*nb_long*/
+#else
+ 0, /*nb_reserved*/
+#endif
+ (unaryfunc)0, /*nb_float*/
+#if PY_VERSION_HEX < 0x03000000
+ (unaryfunc)SwigPyObject_oct, /*nb_oct*/
+ (unaryfunc)SwigPyObject_hex, /*nb_hex*/
+#endif
+#if PY_VERSION_HEX >= 0x03000000 /* 3.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */
+#elif PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */
+#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */
+#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */
+ 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */
+#endif
+ };
+
+ static PyTypeObject swigpyobject_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+ /* PyObject header changed in Python 3 */
+#if PY_VERSION_HEX >= 0x03000000
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+#endif
+ (char *)"SwigPyObject", /* tp_name */
+ sizeof(SwigPyObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)SwigPyObject_dealloc, /* tp_dealloc */
+ (printfunc)SwigPyObject_print, /* tp_print */
+#if PY_VERSION_HEX < 0x02020000
+ (getattrfunc)SwigPyObject_getattr, /* tp_getattr */
+#else
+ (getattrfunc)0, /* tp_getattr */
+#endif
+ (setattrfunc)0, /* tp_setattr */
+#if PY_VERSION_HEX >= 0x03000000
+ 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */
+#else
+ (cmpfunc)SwigPyObject_compare, /* tp_compare */
+#endif
+ (reprfunc)SwigPyObject_repr, /* tp_repr */
+ &SwigPyObject_as_number, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)SwigPyObject_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigobject_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ swigobject_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#if PY_VERSION_HEX >= 0x02060000
+ 0, /* tp_version */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ swigpyobject_type = tmp;
+ type_init = 1;
+#if PY_VERSION_HEX < 0x02020000
+ swigpyobject_type.ob_type = &PyType_Type;
+#else
+ if (PyType_Ready(&swigpyobject_type) < 0)
+ return NULL;
+#endif
+ }
+ return &swigpyobject_type;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_New(void *ptr, swig_type_info *ty, int own)
+{
+ SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type());
+ if (sobj) {
+ sobj->ptr = ptr;
+ sobj->ty = ty;
+ sobj->own = own;
+ sobj->next = 0;
+ }
+ return (PyObject *)sobj;
+}
+
+/* -----------------------------------------------------------------------------
+ * Implements a simple Swig Packed type, and use it instead of string
+ * ----------------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ void *pack;
+ swig_type_info *ty;
+ size_t size;
+} SwigPyPacked;
+
+SWIGRUNTIME int
+SwigPyPacked_print(SwigPyPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags))
+{
+ char result[SWIG_BUFFER_SIZE];
+ fputs("<Swig Packed ", fp);
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+ fputs("at ", fp);
+ fputs(result, fp);
+ }
+ fputs(v->ty->name,fp);
+ fputs(">", fp);
+ return 0;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_repr(SwigPyPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+ return SWIG_Python_str_FromFormat("<Swig Packed at %s%s>", result, v->ty->name);
+ } else {
+ return SWIG_Python_str_FromFormat("<Swig Packed %s>", v->ty->name);
+ }
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_str(SwigPyPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){
+ return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name);
+ } else {
+ return SWIG_Python_str_FromChar(v->ty->name);
+ }
+}
+
+SWIGRUNTIME int
+SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w)
+{
+ size_t i = v->size;
+ size_t j = w->size;
+ int s = (i < j) ? -1 : ((i > j) ? 1 : 0);
+ return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size);
+}
+
+SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void);
+
+SWIGRUNTIME PyTypeObject*
+SwigPyPacked_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce();
+ return type;
+}
+
+SWIGRUNTIMEINLINE int
+SwigPyPacked_Check(PyObject *op) {
+ return ((op)->ob_type == SwigPyPacked_TypeOnce())
+ || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0);
+}
+
+SWIGRUNTIME void
+SwigPyPacked_dealloc(PyObject *v)
+{
+ if (SwigPyPacked_Check(v)) {
+ SwigPyPacked *sobj = (SwigPyPacked *) v;
+ free(sobj->pack);
+ }
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyTypeObject*
+SwigPyPacked_TypeOnce(void) {
+ static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer";
+ static PyTypeObject swigpypacked_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+ /* PyObject header changed in Python 3 */
+#if PY_VERSION_HEX>=0x03000000
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+#endif
+ (char *)"SwigPyPacked", /* tp_name */
+ sizeof(SwigPyPacked), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)SwigPyPacked_dealloc, /* tp_dealloc */
+ (printfunc)SwigPyPacked_print, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+#if PY_VERSION_HEX>=0x03000000
+ 0, /* tp_reserved in 3.0.1 */
+#else
+ (cmpfunc)SwigPyPacked_compare, /* tp_compare */
+#endif
+ (reprfunc)SwigPyPacked_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)SwigPyPacked_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigpacked_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#if PY_VERSION_HEX >= 0x02060000
+ 0, /* tp_version */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ swigpypacked_type = tmp;
+ type_init = 1;
+#if PY_VERSION_HEX < 0x02020000
+ swigpypacked_type.ob_type = &PyType_Type;
+#else
+ if (PyType_Ready(&swigpypacked_type) < 0)
+ return NULL;
+#endif
+ }
+ return &swigpypacked_type;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty)
+{
+ SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type());
+ if (sobj) {
+ void *pack = malloc(size);
+ if (pack) {
+ memcpy(pack, ptr, size);
+ sobj->pack = pack;
+ sobj->ty = ty;
+ sobj->size = size;
+ } else {
+ PyObject_DEL((PyObject *) sobj);
+ sobj = 0;
+ }
+ }
+ return (PyObject *) sobj;
+}
+
+SWIGRUNTIME swig_type_info *
+SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size)
+{
+ if (SwigPyPacked_Check(obj)) {
+ SwigPyPacked *sobj = (SwigPyPacked *)obj;
+ if (sobj->size != size) return 0;
+ memcpy(ptr, sobj->pack, size);
+ return sobj->ty;
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * pointers/data manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_This(void)
+{
+ return SWIG_Python_str_FromChar("this");
+}
+
+static PyObject *swig_this = NULL;
+
+SWIGRUNTIME PyObject *
+SWIG_This(void)
+{
+ if (swig_this == NULL)
+ swig_this = _SWIG_This();
+ return swig_this;
+}
+
+/* #define SWIG_PYTHON_SLOW_GETSET_THIS */
+
+/* TODO: I don't know how to implement the fast getset in Python 3 right now */
+#if PY_VERSION_HEX>=0x03000000
+#define SWIG_PYTHON_SLOW_GETSET_THIS
+#endif
+
+SWIGRUNTIME SwigPyObject *
+SWIG_Python_GetSwigThis(PyObject *pyobj)
+{
+ PyObject *obj;
+
+ if (SwigPyObject_Check(pyobj))
+ return (SwigPyObject *) pyobj;
+
+#ifdef SWIGPYTHON_BUILTIN
+ (void)obj;
+# ifdef PyWeakref_CheckProxy
+ if (PyWeakref_CheckProxy(pyobj)) {
+ pyobj = PyWeakref_GET_OBJECT(pyobj);
+ if (pyobj && SwigPyObject_Check(pyobj))
+ return (SwigPyObject*) pyobj;
+ }
+# endif
+ return NULL;
+#else
+
+ obj = 0;
+
+#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000))
+ if (PyInstance_Check(pyobj)) {
+ obj = _PyInstance_Lookup(pyobj, SWIG_This());
+ } else {
+ PyObject **dictptr = _PyObject_GetDictPtr(pyobj);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0;
+ } else {
+#ifdef PyWeakref_CheckProxy
+ if (PyWeakref_CheckProxy(pyobj)) {
+ PyObject *wobj = PyWeakref_GET_OBJECT(pyobj);
+ return wobj ? SWIG_Python_GetSwigThis(wobj) : 0;
+ }
+#endif
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+ }
+ }
+#else
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+#endif
+ if (obj && !SwigPyObject_Check(obj)) {
+ /* a PyObject is called 'this', try to get the 'real this'
+ SwigPyObject from it */
+ return SWIG_Python_GetSwigThis(obj);
+ }
+ return (SwigPyObject *)obj;
+#endif
+}
+
+/* Acquire a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_AcquirePtr(PyObject *obj, int own) {
+ if (own == SWIG_POINTER_OWN) {
+ SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj);
+ if (sobj) {
+ int oldown = sobj->own;
+ sobj->own = own;
+ return oldown;
+ }
+ }
+ return 0;
+}
+
+/* Convert a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) {
+ int res;
+ SwigPyObject *sobj;
+
+ if (!obj)
+ return SWIG_ERROR;
+ if (obj == Py_None) {
+ if (ptr)
+ *ptr = 0;
+ return SWIG_OK;
+ }
+
+ res = SWIG_ERROR;
+
+ sobj = SWIG_Python_GetSwigThis(obj);
+ if (own)
+ *own = 0;
+ while (sobj) {
+ void *vptr = sobj->ptr;
+ if (ty) {
+ swig_type_info *to = sobj->ty;
+ if (to == ty) {
+ /* no type cast needed */
+ if (ptr) *ptr = vptr;
+ break;
+ } else {
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) {
+ sobj = (SwigPyObject *)sobj->next;
+ } else {
+ if (ptr) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ if (newmemory == SWIG_CAST_NEW_MEMORY) {
+ assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */
+ if (own)
+ *own = *own | SWIG_CAST_NEW_MEMORY;
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ if (ptr) *ptr = vptr;
+ break;
+ }
+ }
+ if (sobj) {
+ if (own)
+ *own = *own | sobj->own;
+ if (flags & SWIG_POINTER_DISOWN) {
+ sobj->own = 0;
+ }
+ res = SWIG_OK;
+ } else {
+ if (flags & SWIG_POINTER_IMPLICIT_CONV) {
+ SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0;
+ if (data && !data->implicitconv) {
+ PyObject *klass = data->klass;
+ if (klass) {
+ PyObject *impconv;
+ data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/
+ impconv = SWIG_Python_CallFunctor(klass, obj);
+ data->implicitconv = 0;
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ impconv = 0;
+ }
+ if (impconv) {
+ SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv);
+ if (iobj) {
+ void *vptr;
+ res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0);
+ if (SWIG_IsOK(res)) {
+ if (ptr) {
+ *ptr = vptr;
+ /* transfer the ownership to 'ptr' */
+ iobj->own = 0;
+ res = SWIG_AddCast(res);
+ res = SWIG_AddNewMask(res);
+ } else {
+ res = SWIG_AddCast(res);
+ }
+ }
+ }
+ Py_DECREF(impconv);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+/* Convert a function ptr value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) {
+ if (!PyCFunction_Check(obj)) {
+ return SWIG_ConvertPtr(obj, ptr, ty, 0);
+ } else {
+ void *vptr = 0;
+
+ /* here we get the method pointer for callbacks */
+ const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc);
+ const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0;
+ if (desc)
+ desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0;
+ if (!desc)
+ return SWIG_ERROR;
+ if (ty) {
+ swig_cast_info *tc = SWIG_TypeCheck(desc,ty);
+ if (tc) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ assert(!newmemory); /* newmemory handling not yet implemented */
+ } else {
+ return SWIG_ERROR;
+ }
+ } else {
+ *ptr = vptr;
+ }
+ return SWIG_OK;
+ }
+}
+
+/* Convert a packed value value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) {
+ swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz);
+ if (!to) return SWIG_ERROR;
+ if (ty) {
+ if (to != ty) {
+ /* check type cast? */
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) return SWIG_ERROR;
+ }
+ }
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Create a new pointer object
+ * ----------------------------------------------------------------------------- */
+
+/*
+ Create a new instance object, without calling __init__, and set the
+ 'this' attribute.
+*/
+
+SWIGRUNTIME PyObject*
+SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this)
+{
+#if (PY_VERSION_HEX >= 0x02020000)
+ PyObject *inst = 0;
+ PyObject *newraw = data->newraw;
+ if (newraw) {
+ inst = PyObject_Call(newraw, data->newargs, NULL);
+ if (inst) {
+#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ }
+ }
+#else
+ PyObject *key = SWIG_This();
+ PyObject_SetAttr(inst, key, swig_this);
+#endif
+ }
+ } else {
+#if PY_VERSION_HEX >= 0x03000000
+ inst = PyBaseObject_Type.tp_new((PyTypeObject*) data->newargs, Py_None, Py_None);
+ PyObject_SetAttr(inst, SWIG_This(), swig_this);
+ Py_TYPE(inst)->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
+#else
+ PyObject *dict = PyDict_New();
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ inst = PyInstance_NewRaw(data->newargs, dict);
+ Py_DECREF(dict);
+#endif
+ }
+ return inst;
+#else
+#if (PY_VERSION_HEX >= 0x02010000)
+ PyObject *inst;
+ PyObject *dict = PyDict_New();
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ inst = PyInstance_NewRaw(data->newargs, dict);
+ Py_DECREF(dict);
+ return (PyObject *) inst;
+#else
+ PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type);
+ if (inst == NULL) {
+ return NULL;
+ }
+ inst->in_class = (PyClassObject *)data->newargs;
+ Py_INCREF(inst->in_class);
+ inst->in_dict = PyDict_New();
+ if (inst->in_dict == NULL) {
+ Py_DECREF(inst);
+ return NULL;
+ }
+#ifdef Py_TPFLAGS_HAVE_WEAKREFS
+ inst->in_weakreflist = NULL;
+#endif
+#ifdef Py_TPFLAGS_GC
+ PyObject_GC_Init(inst);
+#endif
+ PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this);
+ return (PyObject *) inst;
+#endif
+#endif
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this)
+{
+ PyObject *dict;
+#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ }
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ return;
+ }
+#endif
+ dict = PyObject_GetAttrString(inst, (char*)"__dict__");
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ Py_DECREF(dict);
+}
+
+
+SWIGINTERN PyObject *
+SWIG_Python_InitShadowInstance(PyObject *args) {
+ PyObject *obj[2];
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swiginit", 2, 2, obj)) {
+ return NULL;
+ } else {
+ SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]);
+ if (sthis) {
+ SwigPyObject_append((PyObject*) sthis, obj[1]);
+ } else {
+ SWIG_Python_SetSwigThis(obj[0], obj[1]);
+ }
+ return SWIG_Py_Void();
+ }
+}
+
+/* Create a new pointer object */
+
+SWIGRUNTIME PyObject *
+SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) {
+ SwigPyClientData *clientdata;
+ PyObject * robj;
+ int own;
+
+ if (!ptr)
+ return SWIG_Py_Void();
+
+ clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0;
+ own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0;
+ if (clientdata && clientdata->pytype) {
+ SwigPyObject *newobj;
+ if (flags & SWIG_BUILTIN_TP_INIT) {
+ newobj = (SwigPyObject*) self;
+ if (newobj->ptr) {
+ PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0);
+ while (newobj->next)
+ newobj = (SwigPyObject *) newobj->next;
+ newobj->next = next_self;
+ newobj = (SwigPyObject *)next_self;
+ }
+ } else {
+ newobj = PyObject_New(SwigPyObject, clientdata->pytype);
+ }
+ if (newobj) {
+ newobj->ptr = ptr;
+ newobj->ty = type;
+ newobj->own = own;
+ newobj->next = 0;
+#ifdef SWIGPYTHON_BUILTIN
+ newobj->dict = 0;
+#endif
+ return (PyObject*) newobj;
+ }
+ return SWIG_Py_Void();
+ }
+
+ assert(!(flags & SWIG_BUILTIN_TP_INIT));
+
+ robj = SwigPyObject_New(ptr, type, own);
+ if (clientdata && !(flags & SWIG_POINTER_NOSHADOW)) {
+ PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj);
+ if (inst) {
+ Py_DECREF(robj);
+ robj = inst;
+ }
+ }
+ return robj;
+}
+
+/* Create a new packed object */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) {
+ return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void();
+}
+
+/* -----------------------------------------------------------------------------*
+ * Get type list
+ * -----------------------------------------------------------------------------*/
+
+#ifdef SWIG_LINK_RUNTIME
+void *SWIG_ReturnGlobalTypeList(void *);
+#endif
+
+SWIGRUNTIME swig_module_info *
+SWIG_Python_GetModule(void) {
+ static void *type_pointer = (void *)0;
+ /* first check if module already created */
+ if (!type_pointer) {
+#ifdef SWIG_LINK_RUNTIME
+ type_pointer = SWIG_ReturnGlobalTypeList((void *)0);
+#else
+# ifdef SWIGPY_USE_CAPSULE
+ type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0);
+# else
+ type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION,
+ (char*)"type_pointer" SWIG_TYPE_TABLE_NAME);
+# endif
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ type_pointer = (void *)0;
+ }
+#endif
+ }
+ return (swig_module_info *) type_pointer;
+}
+
+#if PY_MAJOR_VERSION < 2
+/* PyModule_AddObject function was introduced in Python 2.0. The following function
+ is copied out of Python/modsupport.c in python version 2.3.4 */
+SWIGINTERN int
+PyModule_AddObject(PyObject *m, char *name, PyObject *o)
+{
+ PyObject *dict;
+ if (!PyModule_Check(m)) {
+ PyErr_SetString(PyExc_TypeError,
+ "PyModule_AddObject() needs module as first arg");
+ return SWIG_ERROR;
+ }
+ if (!o) {
+ PyErr_SetString(PyExc_TypeError,
+ "PyModule_AddObject() needs non-NULL value");
+ return SWIG_ERROR;
+ }
+
+ dict = PyModule_GetDict(m);
+ if (dict == NULL) {
+ /* Internal error -- modules must have a dict! */
+ PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
+ PyModule_GetName(m));
+ return SWIG_ERROR;
+ }
+ if (PyDict_SetItemString(dict, name, o))
+ return SWIG_ERROR;
+ Py_DECREF(o);
+ return SWIG_OK;
+}
+#endif
+
+SWIGRUNTIME void
+#ifdef SWIGPY_USE_CAPSULE
+SWIG_Python_DestroyModule(PyObject *obj)
+#else
+SWIG_Python_DestroyModule(void *vptr)
+#endif
+{
+#ifdef SWIGPY_USE_CAPSULE
+ swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME);
+#else
+ swig_module_info *swig_module = (swig_module_info *) vptr;
+#endif
+ swig_type_info **types = swig_module->types;
+ size_t i;
+ for (i =0; i < swig_module->size; ++i) {
+ swig_type_info *ty = types[i];
+ if (ty->owndata) {
+ SwigPyClientData *data = (SwigPyClientData *) ty->clientdata;
+ if (data) SwigPyClientData_Del(data);
+ }
+ }
+ Py_DECREF(SWIG_This());
+ swig_this = NULL;
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetModule(swig_module_info *swig_module) {
+#if PY_VERSION_HEX >= 0x03000000
+ /* Add a dummy module object into sys.modules */
+ PyObject *module = PyImport_AddModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION);
+#else
+ static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */
+ PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table);
+#endif
+#ifdef SWIGPY_USE_CAPSULE
+ PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule);
+ if (pointer && module) {
+ PyModule_AddObject(module, (char*)"type_pointer_capsule" SWIG_TYPE_TABLE_NAME, pointer);
+ } else {
+ Py_XDECREF(pointer);
+ }
+#else
+ PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule);
+ if (pointer && module) {
+ PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer);
+ } else {
+ Py_XDECREF(pointer);
+ }
+#endif
+}
+
+/* The python cached type query */
+SWIGRUNTIME PyObject *
+SWIG_Python_TypeCache(void) {
+ static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New();
+ return cache;
+}
+
+SWIGRUNTIME swig_type_info *
+SWIG_Python_TypeQuery(const char *type)
+{
+ PyObject *cache = SWIG_Python_TypeCache();
+ PyObject *key = SWIG_Python_str_FromChar(type);
+ PyObject *obj = PyDict_GetItem(cache, key);
+ swig_type_info *descriptor;
+ if (obj) {
+#ifdef SWIGPY_USE_CAPSULE
+ descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL);
+#else
+ descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj);
+#endif
+ } else {
+ swig_module_info *swig_module = SWIG_Python_GetModule();
+ descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type);
+ if (descriptor) {
+#ifdef SWIGPY_USE_CAPSULE
+ obj = PyCapsule_New((void*) descriptor, NULL, NULL);
+#else
+ obj = PyCObject_FromVoidPtr(descriptor, NULL);
+#endif
+ PyDict_SetItem(cache, key, obj);
+ Py_DECREF(obj);
+ }
+ }
+ Py_DECREF(key);
+ return descriptor;
+}
+
+/*
+ For backward compatibility only
+*/
+#define SWIG_POINTER_EXCEPTION 0
+#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg)
+#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags)
+
+SWIGRUNTIME int
+SWIG_Python_AddErrMesg(const char* mesg, int infront)
+{
+ if (PyErr_Occurred()) {
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+ PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ char *tmp;
+ PyObject *old_str = PyObject_Str(value);
+ Py_XINCREF(type);
+ PyErr_Clear();
+ if (infront) {
+ PyErr_Format(type, "%s %s", mesg, tmp = SWIG_Python_str_AsChar(old_str));
+ } else {
+ PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg);
+ }
+ SWIG_Python_str_DelForPy3(tmp);
+ Py_DECREF(old_str);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIME int
+SWIG_Python_ArgFail(int argnum)
+{
+ if (PyErr_Occurred()) {
+ /* add information about failing argument */
+ char mesg[256];
+ PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum);
+ return SWIG_Python_AddErrMesg(mesg, 1);
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIMEINLINE const char *
+SwigPyObject_GetDesc(PyObject *self)
+{
+ SwigPyObject *v = (SwigPyObject *)self;
+ swig_type_info *ty = v ? v->ty : 0;
+ return ty ? ty->str : (char*)"";
+}
+
+SWIGRUNTIME void
+SWIG_Python_TypeError(const char *type, PyObject *obj)
+{
+ if (type) {
+#if defined(SWIG_COBJECT_TYPES)
+ if (obj && SwigPyObject_Check(obj)) {
+ const char *otype = (const char *) SwigPyObject_GetDesc(obj);
+ if (otype) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received",
+ type, otype);
+ return;
+ }
+ } else
+#endif
+ {
+ const char *otype = (obj ? obj->ob_type->tp_name : 0);
+ if (otype) {
+ PyObject *str = PyObject_Str(obj);
+ const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0;
+ if (cstr) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received",
+ type, otype, cstr);
+ SWIG_Python_str_DelForPy3(cstr);
+ } else {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received",
+ type, otype);
+ }
+ Py_XDECREF(str);
+ return;
+ }
+ }
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected", type);
+ } else {
+ PyErr_Format(PyExc_TypeError, "unexpected type is received");
+ }
+}
+
+
+/* Convert a pointer value, signal an exception on a type mismatch */
+SWIGRUNTIME void *
+SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) {
+ void *result;
+ if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) {
+ PyErr_Clear();
+#if SWIG_POINTER_EXCEPTION
+ if (flags) {
+ SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj);
+ SWIG_Python_ArgFail(argnum);
+ }
+#endif
+ }
+ return result;
+}
+
+SWIGRUNTIME int
+SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) {
+ PyTypeObject *tp = obj->ob_type;
+ PyObject *descr;
+ PyObject *encoded_name;
+ descrsetfunc f;
+ int res;
+
+#ifdef Py_USING_UNICODE
+ if (PyString_Check(name)) {
+ name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL);
+ if (!name)
+ return -1;
+ } else if (!PyUnicode_Check(name))
+#else
+ if (!PyString_Check(name))
+#endif
+ {
+ PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name);
+ return -1;
+ } else {
+ Py_INCREF(name);
+ }
+
+ if (!tp->tp_dict) {
+ if (PyType_Ready(tp) < 0)
+ goto done;
+ }
+
+ res = -1;
+ descr = _PyType_Lookup(tp, name);
+ f = NULL;
+ if (descr != NULL)
+ f = descr->ob_type->tp_descr_set;
+ if (!f) {
+ if (PyString_Check(name)) {
+ encoded_name = name;
+ Py_INCREF(name);
+ } else {
+ encoded_name = PyUnicode_AsUTF8String(name);
+ }
+ PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name));
+ Py_DECREF(encoded_name);
+ } else {
+ res = f(descr, obj, value);
+ }
+
+ done:
+ Py_DECREF(name);
+ return res;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0)
+
+#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else
+
+
+
+ #define SWIG_exception(code, msg) do { SWIG_Error(code, msg); SWIG_fail;; } while(0)
+
+
+/* -------- TYPES TABLE (BEGIN) -------- */
+
+#define SWIGTYPE_p_char swig_types[0]
+#define SWIGTYPE_p_int swig_types[1]
+#define SWIGTYPE_p_uint8_t swig_types[2]
+static swig_type_info *swig_types[4];
+static swig_module_info swig_module = {swig_types, 3, 0, 0, 0, 0};
+#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)
+#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)
+
+/* -------- TYPES TABLE (END) -------- */
+
+#if (PY_VERSION_HEX <= 0x02000000)
+# if !defined(SWIG_PYTHON_CLASSIC)
+# error "This python version requires swig to be run with the '-classic' option"
+# endif
+#endif
+
+/*-----------------------------------------------
+ @(target):= _libwebp.so
+ ------------------------------------------------*/
+#if PY_VERSION_HEX >= 0x03000000
+# define SWIG_init PyInit__libwebp
+
+#else
+# define SWIG_init init_libwebp
+
+#endif
+#define SWIG_name "_libwebp"
+
+#define SWIGVERSION 0x020004
+#define SWIG_VERSION SWIGVERSION
+
+
+#define SWIG_as_voidptr(a) (void *)((const void *)(a))
+#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a))
+
+
+ #define SWIG_From_long PyInt_FromLong
+
+
+SWIGINTERNINLINE PyObject *
+SWIG_From_int (int value)
+{
+ return SWIG_From_long (value);
+}
+
+
+SWIGINTERN swig_type_info*
+SWIG_pchar_descriptor(void)
+{
+ static int init = 0;
+ static swig_type_info* info = 0;
+ if (!init) {
+ info = SWIG_TypeQuery("_p_char");
+ init = 1;
+ }
+ return info;
+}
+
+
+SWIGINTERN int
+SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc)
+{
+#if PY_VERSION_HEX>=0x03000000
+ if (PyUnicode_Check(obj))
+#else
+ if (PyString_Check(obj))
+#endif
+ {
+ char *cstr; Py_ssize_t len;
+#if PY_VERSION_HEX>=0x03000000
+ if (!alloc && cptr) {
+ /* We can't allow converting without allocation, since the internal
+ representation of string in Python 3 is UCS-2/UCS-4 but we require
+ a UTF-8 representation.
+ TODO(bhy) More detailed explanation */
+ return SWIG_RuntimeError;
+ }
+ obj = PyUnicode_AsUTF8String(obj);
+ PyBytes_AsStringAndSize(obj, &cstr, &len);
+ if(alloc) *alloc = SWIG_NEWOBJ;
+#else
+ PyString_AsStringAndSize(obj, &cstr, &len);
+#endif
+ if (cptr) {
+ if (alloc) {
+ /*
+ In python the user should not be able to modify the inner
+ string representation. To warranty that, if you define
+ SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string
+ buffer is always returned.
+
+ The default behavior is just to return the pointer value,
+ so, be careful.
+ */
+#if defined(SWIG_PYTHON_SAFE_CSTRINGS)
+ if (*alloc != SWIG_OLDOBJ)
+#else
+ if (*alloc == SWIG_NEWOBJ)
+#endif
+ {
+ *cptr = (char *)memcpy((char *)malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
+ *alloc = SWIG_NEWOBJ;
+ }
+ else {
+ *cptr = cstr;
+ *alloc = SWIG_OLDOBJ;
+ }
+ } else {
+ #if PY_VERSION_HEX>=0x03000000
+ assert(0); /* Should never reach here in Python 3 */
+ #endif
+ *cptr = SWIG_Python_str_AsChar(obj);
+ }
+ }
+ if (psize) *psize = len + 1;
+#if PY_VERSION_HEX>=0x03000000
+ Py_XDECREF(obj);
+#endif
+ return SWIG_OK;
+ } else {
+ swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
+ if (pchar_descriptor) {
+ void* vptr = 0;
+ if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) {
+ if (cptr) *cptr = (char *) vptr;
+ if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0;
+ if (alloc) *alloc = SWIG_OLDOBJ;
+ return SWIG_OK;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_double (PyObject *obj, double *val)
+{
+ int res = SWIG_TypeError;
+ if (PyFloat_Check(obj)) {
+ if (val) *val = PyFloat_AsDouble(obj);
+ return SWIG_OK;
+ } else if (PyInt_Check(obj)) {
+ if (val) *val = PyInt_AsLong(obj);
+ return SWIG_OK;
+ } else if (PyLong_Check(obj)) {
+ double v = PyLong_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ double d = PyFloat_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = d;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_AddCast(SWIG_OK));
+ } else {
+ PyErr_Clear();
+ }
+ }
+ }
+#endif
+ return res;
+}
+
+
+#include <float.h>
+
+
+#include <math.h>
+
+
+SWIGINTERNINLINE int
+SWIG_CanCastAsInteger(double *d, double min, double max) {
+ double x = *d;
+ if ((min <= x && x <= max)) {
+ double fx = floor(x);
+ double cx = ceil(x);
+ double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */
+ if ((errno == EDOM) || (errno == ERANGE)) {
+ errno = 0;
+ } else {
+ double summ, reps, diff;
+ if (rd < x) {
+ diff = x - rd;
+ } else if (rd > x) {
+ diff = rd - x;
+ } else {
+ return 1;
+ }
+ summ = rd + x;
+ reps = diff/summ;
+ if (reps < 8*DBL_EPSILON) {
+ *d = rd;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val)
+{
+ if (PyInt_Check(obj)) {
+ long v = PyInt_AsLong(obj);
+ if (v >= 0) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ return SWIG_OverflowError;
+ }
+ } else if (PyLong_Check(obj)) {
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) {
+ if (val) *val = (unsigned long)(d);
+ return res;
+ }
+ }
+ }
+#endif
+ return SWIG_TypeError;
+}
+
+
+SWIGINTERNINLINE int
+SWIG_AsVal_size_t (PyObject * obj, size_t *val)
+{
+ unsigned long v;
+ int res = SWIG_AsVal_unsigned_SS_long (obj, val ? &v : 0);
+ if (SWIG_IsOK(res) && val) *val = (size_t)(v);
+ return res;
+}
+
+
+#include "webp/decode.h"
+#include "webp/encode.h"
+
+
+static size_t ReturnedBufferSize(
+ const char* function, int* width, int* height) {
+ static const struct sizemap {
+ const char* function;
+ int size_multiplier;
+ } size_map[] = {
+#ifdef SWIGJAVA
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB", 3 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR", 3 },
+ { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 },
+ { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 },
+#endif
+#ifdef SWIGPYTHON
+ { "WebPDecodeRGB", 3 },
+ { "WebPDecodeRGBA", 4 },
+ { "WebPDecodeARGB", 4 },
+ { "WebPDecodeBGR", 3 },
+ { "WebPDecodeBGRA", 4 },
+ { "wrap_WebPEncodeRGB", 1 },
+ { "wrap_WebPEncodeBGR", 1 },
+ { "wrap_WebPEncodeRGBA", 1 },
+ { "wrap_WebPEncodeBGRA", 1 },
+ { "wrap_WebPEncodeLosslessRGB", 1 },
+ { "wrap_WebPEncodeLosslessBGR", 1 },
+ { "wrap_WebPEncodeLosslessRGBA", 1 },
+ { "wrap_WebPEncodeLosslessBGRA", 1 },
+#endif
+ { NULL, 0 }
+ };
+ const struct sizemap* p;
+ size_t size = 0;
+
+ for (p = size_map; p->function; ++p) {
+ if (!strcmp(function, p->function)) {
+ size = *width * *height * p->size_multiplier;
+ break;
+ }
+ }
+
+ return size;
+}
+
+
+typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb,
+ int width, int height, int stride,
+ float quality_factor, uint8_t** output);
+typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb,
+ int width, int height, int stride,
+ uint8_t** output);
+
+static uint8_t* EncodeLossy(const uint8_t* rgb,
+ int width, int height, int stride,
+ float quality_factor,
+ WebPEncodeFunction encfn,
+ int* output_size, int* unused) {
+ uint8_t* output = NULL;
+ const size_t image_size =
+ encfn(rgb, width, height, stride, quality_factor, &output);
+ // the values of following two will be interpreted by ReturnedBufferSize()
+ // as 'width' and 'height' in the size calculation.
+ *output_size = image_size;
+ *unused = 1;
+ return image_size ? output : NULL;
+}
+
+static uint8_t* EncodeLossless(const uint8_t* rgb,
+ int width, int height, int stride,
+ WebPEncodeLosslessFunction encfn,
+ int* output_size, int* unused) {
+ uint8_t* output = NULL;
+ const size_t image_size = encfn(rgb, width, height, stride, &output);
+ // the values of the following two will be interpreted by
+ // ReturnedBufferSize() as 'width' and 'height' in the size calculation.
+ *output_size = image_size;
+ *unused = 1;
+ return image_size ? output : NULL;
+}
+
+
+// Changes the return type of WebPEncode* to more closely match Decode*.
+// This also makes it easier to wrap the output buffer in a native type rather
+// than dealing with the return pointer.
+// The additional parameters are to allow reuse of ReturnedBufferSize(),
+// unused2 and output_size will be used in this case.
+#define LOSSY_WRAPPER(FUNC) \
+ static uint8_t* wrap_##FUNC( \
+ const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+ int width, int height, int stride, float quality_factor) { \
+ return EncodeLossy(rgb, width, height, stride, quality_factor, \
+ FUNC, output_size, unused2); \
+ } \
+
+LOSSY_WRAPPER(WebPEncodeRGB)
+LOSSY_WRAPPER(WebPEncodeBGR)
+LOSSY_WRAPPER(WebPEncodeRGBA)
+LOSSY_WRAPPER(WebPEncodeBGRA)
+
+#undef LOSSY_WRAPPER
+
+#define LOSSLESS_WRAPPER(FUNC) \
+ static uint8_t* wrap_##FUNC( \
+ const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+ int width, int height, int stride) { \
+ return EncodeLossless(rgb, width, height, stride, \
+ FUNC, output_size, unused2); \
+ } \
+
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+
+#undef LOSSLESS_WRAPPER
+
+
+
+#include <limits.h>
+#if !defined(SWIG_NO_LLONG_MAX)
+# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__)
+# define LLONG_MAX __LONG_LONG_MAX__
+# define LLONG_MIN (-LLONG_MAX - 1LL)
+# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
+# endif
+#endif
+
+
+SWIGINTERN int
+SWIG_AsVal_long (PyObject *obj, long* val)
+{
+ if (PyInt_Check(obj)) {
+ if (val) *val = PyInt_AsLong(obj);
+ return SWIG_OK;
+ } else if (PyLong_Check(obj)) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ long v = PyInt_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
+ if (val) *val = (long)(d);
+ return res;
+ }
+ }
+ }
+#endif
+ return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_int (PyObject * obj, int *val)
+{
+ long v;
+ int res = SWIG_AsVal_long (obj, &v);
+ if (SWIG_IsOK(res)) {
+ if ((v < INT_MIN || v > INT_MAX)) {
+ return SWIG_OverflowError;
+ } else {
+ if (val) *val = (int)(v);
+ }
+ }
+ return res;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_float (PyObject * obj, float *val)
+{
+ double v;
+ int res = SWIG_AsVal_double (obj, &v);
+ if (SWIG_IsOK(res)) {
+ if ((v < -FLT_MAX || v > FLT_MAX)) {
+ return SWIG_OverflowError;
+ } else {
+ if (val) *val = (float)(v);
+ }
+ }
+ return res;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+SWIGINTERN PyObject *_wrap_WebPGetDecoderVersion(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ int result;
+
+ if (!PyArg_ParseTuple(args,(char *)":WebPGetDecoderVersion")) SWIG_fail;
+ result = (int)WebPGetDecoderVersion();
+ resultobj = SWIG_From_int((int)(result));
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPGetInfo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int res1 ;
+ char *buf1 = 0 ;
+ size_t size1 = 0 ;
+ int alloc1 = 0 ;
+ int temp3 ;
+ int res3 = SWIG_TMPOBJ ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ PyObject * obj0 = 0 ;
+ int result;
+
+ arg3 = &temp3;
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"O:WebPGetInfo",&obj0)) SWIG_fail;
+ res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPGetInfo" "', argument " "1"" of type '" "uint8_t const *""'");
+ }
+ arg1 = (uint8_t *)(buf1);
+ arg2 = (size_t)(size1 - 1);
+ result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4);
+ resultobj = SWIG_From_int((int)(result));
+ if (SWIG_IsTmpObj(res3)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ return resultobj;
+fail:
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int res1 ;
+ char *buf1 = 0 ;
+ size_t size1 = 0 ;
+ int alloc1 = 0 ;
+ int temp3 ;
+ int res3 = SWIG_TMPOBJ ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ PyObject * obj0 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg3 = &temp3;
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeRGB",&obj0)) SWIG_fail;
+ res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeRGB" "', argument " "1"" of type '" "uint8_t const *""'");
+ }
+ arg1 = (uint8_t *)(buf1);
+ arg2 = (size_t)(size1 - 1);
+ result = (uint8_t *)WebPDecodeRGB((uint8_t const *)arg1,arg2,arg3,arg4);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeRGB", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res3)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ free(result);
+ return resultobj;
+fail:
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int res1 ;
+ char *buf1 = 0 ;
+ size_t size1 = 0 ;
+ int alloc1 = 0 ;
+ int temp3 ;
+ int res3 = SWIG_TMPOBJ ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ PyObject * obj0 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg3 = &temp3;
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeRGBA",&obj0)) SWIG_fail;
+ res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeRGBA" "', argument " "1"" of type '" "uint8_t const *""'");
+ }
+ arg1 = (uint8_t *)(buf1);
+ arg2 = (size_t)(size1 - 1);
+ result = (uint8_t *)WebPDecodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeRGBA", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res3)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ free(result);
+ return resultobj;
+fail:
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeARGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int res1 ;
+ char *buf1 = 0 ;
+ size_t size1 = 0 ;
+ int alloc1 = 0 ;
+ int temp3 ;
+ int res3 = SWIG_TMPOBJ ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ PyObject * obj0 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg3 = &temp3;
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeARGB",&obj0)) SWIG_fail;
+ res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeARGB" "', argument " "1"" of type '" "uint8_t const *""'");
+ }
+ arg1 = (uint8_t *)(buf1);
+ arg2 = (size_t)(size1 - 1);
+ result = (uint8_t *)WebPDecodeARGB((uint8_t const *)arg1,arg2,arg3,arg4);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeARGB", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res3)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ free(result);
+ return resultobj;
+fail:
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int res1 ;
+ char *buf1 = 0 ;
+ size_t size1 = 0 ;
+ int alloc1 = 0 ;
+ int temp3 ;
+ int res3 = SWIG_TMPOBJ ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ PyObject * obj0 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg3 = &temp3;
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeBGR",&obj0)) SWIG_fail;
+ res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeBGR" "', argument " "1"" of type '" "uint8_t const *""'");
+ }
+ arg1 = (uint8_t *)(buf1);
+ arg2 = (size_t)(size1 - 1);
+ result = (uint8_t *)WebPDecodeBGR((uint8_t const *)arg1,arg2,arg3,arg4);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeBGR", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res3)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ free(result);
+ return resultobj;
+fail:
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ size_t arg2 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int res1 ;
+ char *buf1 = 0 ;
+ size_t size1 = 0 ;
+ int alloc1 = 0 ;
+ int temp3 ;
+ int res3 = SWIG_TMPOBJ ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ PyObject * obj0 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg3 = &temp3;
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeBGRA",&obj0)) SWIG_fail;
+ res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeBGRA" "', argument " "1"" of type '" "uint8_t const *""'");
+ }
+ arg1 = (uint8_t *)(buf1);
+ arg2 = (size_t)(size1 - 1);
+ result = (uint8_t *)WebPDecodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeBGRA", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res3)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ free(result);
+ return resultobj;
+fail:
+ if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPGetEncoderVersion(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ int result;
+
+ if (!PyArg_ParseTuple(args,(char *)":WebPGetEncoderVersion")) SWIG_fail;
+ result = (int)WebPGetEncoderVersion();
+ resultobj = SWIG_From_int((int)(result));
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ float val8 ;
+ int ecode8 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ PyObject * obj6 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeRGB",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeRGB', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeRGB', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGB" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGB" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeRGB" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeRGB" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeRGB" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ ecode8 = SWIG_AsVal_float(obj6, &val8);
+ if (!SWIG_IsOK(ecode8)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeRGB" "', argument " "8"" of type '" "float""'");
+ }
+ arg8 = (float)(val8);
+ result = (uint8_t *)wrap_WebPEncodeRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeRGB", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ float val8 ;
+ int ecode8 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ PyObject * obj6 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeBGR",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeBGR', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeBGR', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGR" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGR" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeBGR" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeBGR" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeBGR" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ ecode8 = SWIG_AsVal_float(obj6, &val8);
+ if (!SWIG_IsOK(ecode8)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeBGR" "', argument " "8"" of type '" "float""'");
+ }
+ arg8 = (float)(val8);
+ result = (uint8_t *)wrap_WebPEncodeBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeBGR", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ float val8 ;
+ int ecode8 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ PyObject * obj6 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeRGBA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeRGBA', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeRGBA', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGBA" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGBA" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeRGBA" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeRGBA" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeRGBA" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ ecode8 = SWIG_AsVal_float(obj6, &val8);
+ if (!SWIG_IsOK(ecode8)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeRGBA" "', argument " "8"" of type '" "float""'");
+ }
+ arg8 = (float)(val8);
+ result = (uint8_t *)wrap_WebPEncodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeRGBA", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ float arg8 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ float val8 ;
+ int ecode8 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ PyObject * obj6 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeBGRA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeBGRA', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeBGRA', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGRA" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGRA" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeBGRA" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeBGRA" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeBGRA" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ ecode8 = SWIG_AsVal_float(obj6, &val8);
+ if (!SWIG_IsOK(ecode8)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeBGRA" "', argument " "8"" of type '" "float""'");
+ }
+ arg8 = (float)(val8);
+ result = (uint8_t *)wrap_WebPEncodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeBGRA", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessRGB",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeLosslessRGB', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeLosslessRGB', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ result = (uint8_t *)wrap_WebPEncodeLosslessRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessRGB", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessBGR",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeLosslessBGR', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeLosslessBGR', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ result = (uint8_t *)wrap_WebPEncodeLosslessBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessBGR", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessRGBA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeLosslessRGBA', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeLosslessRGBA', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ result = (uint8_t *)wrap_WebPEncodeLosslessRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessRGBA", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ uint8_t *arg1 = (uint8_t *) 0 ;
+ int *arg2 = (int *) 0 ;
+ int *arg3 = (int *) 0 ;
+ int *arg4 = (int *) 0 ;
+ int arg5 ;
+ int arg6 ;
+ int arg7 ;
+ Py_buffer rgb_buffer1 ;
+ int temp2 ;
+ int res2 = 0 ;
+ int temp3 ;
+ int res3 = 0 ;
+ int temp4 ;
+ int res4 = SWIG_TMPOBJ ;
+ int val5 ;
+ int ecode5 = 0 ;
+ int val6 ;
+ int ecode6 = 0 ;
+ int val7 ;
+ int ecode7 = 0 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ uint8_t *result = 0 ;
+
+ arg4 = &temp4;
+ if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessBGRA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+ {
+ // NB: with Python < 2.6 the old style buffer protocol may be used:
+ // Py_ssize_t unused;
+ // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+ if (!PyObject_CheckBuffer(obj0)) {
+ SWIG_exception_fail(SWIG_TypeError,
+ "in method 'wrap_WebPEncodeLosslessBGRA', argument 1"
+ " does not support the buffer interface");
+ }
+ if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+ SWIG_exception_fail(SWIG_RuntimeError,
+ "in method 'wrap_WebPEncodeLosslessBGRA', unable to get buffer view");
+ }
+ arg1 = (uint8_t *)rgb_buffer1.buf;
+ }
+ if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj1, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "2"" of type '" "int""'");
+ }
+ temp2 = (int)(val);
+ arg2 = &temp2;
+ res2 = SWIG_AddTmpMask(ecode);
+ }
+ if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+ int val;
+ int ecode = SWIG_AsVal_int(obj2, &val);
+ if (!SWIG_IsOK(ecode)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "3"" of type '" "int""'");
+ }
+ temp3 = (int)(val);
+ arg3 = &temp3;
+ res3 = SWIG_AddTmpMask(ecode);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "5"" of type '" "int""'");
+ }
+ arg5 = (int)(val5);
+ ecode6 = SWIG_AsVal_int(obj4, &val6);
+ if (!SWIG_IsOK(ecode6)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "6"" of type '" "int""'");
+ }
+ arg6 = (int)(val6);
+ ecode7 = SWIG_AsVal_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "7"" of type '" "int""'");
+ }
+ arg7 = (int)(val7);
+ result = (uint8_t *)wrap_WebPEncodeLosslessBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ {
+ resultobj = PyString_FromStringAndSize(
+ (const char*)result,
+ (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessBGRA", arg3, arg4));
+ }
+ if (SWIG_IsTmpObj(res4)) {
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN | 0 ) : 0 ;
+ resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+ }
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ free(result);
+ return resultobj;
+fail:
+ {
+ PyBuffer_Release(&rgb_buffer1);
+ }
+ if (SWIG_IsNewObj(res2)) free((char*)arg2);
+ if (SWIG_IsNewObj(res3)) free((char*)arg3);
+ return NULL;
+}
+
+
+static PyMethodDef SwigMethods[] = {
+ { (char *)"SWIG_PyInstanceMethod_New", (PyCFunction)SWIG_PyInstanceMethod_New, METH_O, NULL},
+ { (char *)"WebPGetDecoderVersion", _wrap_WebPGetDecoderVersion, METH_VARARGS, (char *)"WebPGetDecoderVersion() -> int"},
+ { (char *)"WebPGetInfo", _wrap_WebPGetInfo, METH_VARARGS, (char *)"WebPGetInfo(uint8_t data) -> (width, height)"},
+ { (char *)"WebPDecodeRGB", _wrap_WebPDecodeRGB, METH_VARARGS, (char *)"WebPDecodeRGB(uint8_t data) -> (rgb, width, height)"},
+ { (char *)"WebPDecodeRGBA", _wrap_WebPDecodeRGBA, METH_VARARGS, (char *)"WebPDecodeRGBA(uint8_t data) -> (rgb, width, height)"},
+ { (char *)"WebPDecodeARGB", _wrap_WebPDecodeARGB, METH_VARARGS, (char *)"WebPDecodeARGB(uint8_t data) -> (rgb, width, height)"},
+ { (char *)"WebPDecodeBGR", _wrap_WebPDecodeBGR, METH_VARARGS, (char *)"WebPDecodeBGR(uint8_t data) -> (rgb, width, height)"},
+ { (char *)"WebPDecodeBGRA", _wrap_WebPDecodeBGRA, METH_VARARGS, (char *)"WebPDecodeBGRA(uint8_t data) -> (rgb, width, height)"},
+ { (char *)"WebPGetEncoderVersion", _wrap_WebPGetEncoderVersion, METH_VARARGS, (char *)"WebPGetEncoderVersion() -> int"},
+ { (char *)"wrap_WebPEncodeRGB", _wrap_wrap_WebPEncodeRGB, METH_VARARGS, (char *)"private, do not call directly."},
+ { (char *)"wrap_WebPEncodeBGR", _wrap_wrap_WebPEncodeBGR, METH_VARARGS, (char *)"private, do not call directly."},
+ { (char *)"wrap_WebPEncodeRGBA", _wrap_wrap_WebPEncodeRGBA, METH_VARARGS, (char *)"private, do not call directly."},
+ { (char *)"wrap_WebPEncodeBGRA", _wrap_wrap_WebPEncodeBGRA, METH_VARARGS, (char *)"private, do not call directly."},
+ { (char *)"wrap_WebPEncodeLosslessRGB", _wrap_wrap_WebPEncodeLosslessRGB, METH_VARARGS, (char *)"private, do not call directly."},
+ { (char *)"wrap_WebPEncodeLosslessBGR", _wrap_wrap_WebPEncodeLosslessBGR, METH_VARARGS, (char *)"private, do not call directly."},
+ { (char *)"wrap_WebPEncodeLosslessRGBA", _wrap_wrap_WebPEncodeLosslessRGBA, METH_VARARGS, (char *)"private, do not call directly."},
+ { (char *)"wrap_WebPEncodeLosslessBGRA", _wrap_wrap_WebPEncodeLosslessBGRA, METH_VARARGS, (char *)"private, do not call directly."},
+ { NULL, NULL, 0, NULL }
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */
+
+static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_int = {"_p_int", "int *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_uint8_t = {"_p_uint8_t", "uint8_t *", 0, 0, (void*)0, 0};
+
+static swig_type_info *swig_type_initial[] = {
+ &_swigt__p_char,
+ &_swigt__p_int,
+ &_swigt__p_uint8_t,
+};
+
+static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_int[] = { {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_uint8_t[] = { {&_swigt__p_uint8_t, 0, 0, 0},{0, 0, 0, 0}};
+
+static swig_cast_info *swig_cast_initial[] = {
+ _swigc__p_char,
+ _swigc__p_int,
+ _swigc__p_uint8_t,
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */
+
+static swig_const_info swig_const_table[] = {
+{0, 0, 0, 0.0, 0, 0}};
+
+#ifdef __cplusplus
+}
+#endif
+/* -----------------------------------------------------------------------------
+ * Type initialization:
+ * This problem is tough by the requirement that no dynamic
+ * memory is used. Also, since swig_type_info structures store pointers to
+ * swig_cast_info structures and swig_cast_info structures store pointers back
+ * to swig_type_info structures, we need some lookup code at initialization.
+ * The idea is that swig generates all the structures that are needed.
+ * The runtime then collects these partially filled structures.
+ * The SWIG_InitializeModule function takes these initial arrays out of
+ * swig_module, and does all the lookup, filling in the swig_module.types
+ * array with the correct data and linking the correct swig_cast_info
+ * structures together.
+ *
+ * The generated swig_type_info structures are assigned staticly to an initial
+ * array. We just loop through that array, and handle each type individually.
+ * First we lookup if this type has been already loaded, and if so, use the
+ * loaded structure instead of the generated one. Then we have to fill in the
+ * cast linked list. The cast data is initially stored in something like a
+ * two-dimensional array. Each row corresponds to a type (there are the same
+ * number of rows as there are in the swig_type_initial array). Each entry in
+ * a column is one of the swig_cast_info structures for that type.
+ * The cast_initial array is actually an array of arrays, because each row has
+ * a variable number of columns. So to actually build the cast linked list,
+ * we find the array of casts associated with the type, and loop through it
+ * adding the casts to the list. The one last trick we need to do is making
+ * sure the type pointer in the swig_cast_info struct is correct.
+ *
+ * First off, we lookup the cast->type name to see if it is already loaded.
+ * There are three cases to handle:
+ * 1) If the cast->type has already been loaded AND the type we are adding
+ * casting info to has not been loaded (it is in this module), THEN we
+ * replace the cast->type pointer with the type pointer that has already
+ * been loaded.
+ * 2) If BOTH types (the one we are adding casting info to, and the
+ * cast->type) are loaded, THEN the cast info has already been loaded by
+ * the previous module so we just ignore it.
+ * 3) Finally, if cast->type has not already been loaded, then we add that
+ * swig_cast_info to the linked list (because the cast->type) pointer will
+ * be correct.
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* c-mode */
+#endif
+#endif
+
+#if 0
+#define SWIGRUNTIME_DEBUG
+#endif
+
+
+SWIGRUNTIME void
+SWIG_InitializeModule(void *clientdata) {
+ size_t i;
+ swig_module_info *module_head, *iter;
+ int found, init;
+
+ clientdata = clientdata;
+
+ /* check to see if the circular list has been setup, if not, set it up */
+ if (swig_module.next==0) {
+ /* Initialize the swig_module */
+ swig_module.type_initial = swig_type_initial;
+ swig_module.cast_initial = swig_cast_initial;
+ swig_module.next = &swig_module;
+ init = 1;
+ } else {
+ init = 0;
+ }
+
+ /* Try and load any already created modules */
+ module_head = SWIG_GetModule(clientdata);
+ if (!module_head) {
+ /* This is the first module loaded for this interpreter */
+ /* so set the swig module into the interpreter */
+ SWIG_SetModule(clientdata, &swig_module);
+ module_head = &swig_module;
+ } else {
+ /* the interpreter has loaded a SWIG module, but has it loaded this one? */
+ found=0;
+ iter=module_head;
+ do {
+ if (iter==&swig_module) {
+ found=1;
+ break;
+ }
+ iter=iter->next;
+ } while (iter!= module_head);
+
+ /* if the is found in the list, then all is done and we may leave */
+ if (found) return;
+ /* otherwise we must add out module into the list */
+ swig_module.next = module_head->next;
+ module_head->next = &swig_module;
+ }
+
+ /* When multiple interpeters are used, a module could have already been initialized in
+ a different interpreter, but not yet have a pointer in this interpreter.
+ In this case, we do not want to continue adding types... everything should be
+ set up already */
+ if (init == 0) return;
+
+ /* Now work on filling in swig_module.types */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: size %d\n", swig_module.size);
+#endif
+ for (i = 0; i < swig_module.size; ++i) {
+ swig_type_info *type = 0;
+ swig_type_info *ret;
+ swig_cast_info *cast;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+#endif
+
+ /* if there is another module already loaded */
+ if (swig_module.next != &swig_module) {
+ type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name);
+ }
+ if (type) {
+ /* Overwrite clientdata field */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found type %s\n", type->name);
+#endif
+ if (swig_module.type_initial[i]->clientdata) {
+ type->clientdata = swig_module.type_initial[i]->clientdata;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name);
+#endif
+ }
+ } else {
+ type = swig_module.type_initial[i];
+ }
+
+ /* Insert casting types */
+ cast = swig_module.cast_initial[i];
+ while (cast->type) {
+ /* Don't need to add information already in the list */
+ ret = 0;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: look cast %s\n", cast->type->name);
+#endif
+ if (swig_module.next != &swig_module) {
+ ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name);
+#endif
+ }
+ if (ret) {
+ if (type == swig_module.type_initial[i]) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: skip old type %s\n", ret->name);
+#endif
+ cast->type = ret;
+ ret = 0;
+ } else {
+ /* Check for casting already in the list */
+ swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name);
+#endif
+ if (!ocast) ret = 0;
+ }
+ }
+
+ if (!ret) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name);
+#endif
+ if (type->cast) {
+ type->cast->prev = cast;
+ cast->next = type->cast;
+ }
+ type->cast = cast;
+ }
+ cast++;
+ }
+ /* Set entry in modules->types array equal to the type */
+ swig_module.types[i] = type;
+ }
+ swig_module.types[i] = 0;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+ for (i = 0; i < swig_module.size; ++i) {
+ int j = 0;
+ swig_cast_info *cast = swig_module.cast_initial[i];
+ printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+ while (cast->type) {
+ printf("SWIG_InitializeModule: cast type %s\n", cast->type->name);
+ cast++;
+ ++j;
+ }
+ printf("---- Total casts: %d\n",j);
+ }
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+#endif
+}
+
+/* This function will propagate the clientdata field of type to
+* any new swig_type_info structures that have been added into the list
+* of equivalent types. It is like calling
+* SWIG_TypeClientData(type, clientdata) a second time.
+*/
+SWIGRUNTIME void
+SWIG_PropagateClientData(void) {
+ size_t i;
+ swig_cast_info *equiv;
+ static int init_run = 0;
+
+ if (init_run) return;
+ init_run = 1;
+
+ for (i = 0; i < swig_module.size; i++) {
+ if (swig_module.types[i]->clientdata) {
+ equiv = swig_module.types[i]->cast;
+ while (equiv) {
+ if (!equiv->converter) {
+ if (equiv->type && !equiv->type->clientdata)
+ SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata);
+ }
+ equiv = equiv->next;
+ }
+ }
+ }
+}
+
+#ifdef __cplusplus
+#if 0
+{
+ /* c-mode */
+#endif
+}
+#endif
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Python-specific SWIG API */
+#define SWIG_newvarlink() SWIG_Python_newvarlink()
+#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr)
+#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants)
+
+ /* -----------------------------------------------------------------------------
+ * global variable support code.
+ * ----------------------------------------------------------------------------- */
+
+ typedef struct swig_globalvar {
+ char *name; /* Name of global variable */
+ PyObject *(*get_attr)(void); /* Return the current value */
+ int (*set_attr)(PyObject *); /* Set the value */
+ struct swig_globalvar *next;
+ } swig_globalvar;
+
+ typedef struct swig_varlinkobject {
+ PyObject_HEAD
+ swig_globalvar *vars;
+ } swig_varlinkobject;
+
+ SWIGINTERN PyObject *
+ swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) {
+#if PY_VERSION_HEX >= 0x03000000
+ return PyUnicode_InternFromString("<Swig global variables>");
+#else
+ return PyString_FromString("<Swig global variables>");
+#endif
+ }
+
+ SWIGINTERN PyObject *
+ swig_varlink_str(swig_varlinkobject *v) {
+#if PY_VERSION_HEX >= 0x03000000
+ PyObject *str = PyUnicode_InternFromString("(");
+ PyObject *tail;
+ PyObject *joined;
+ swig_globalvar *var;
+ for (var = v->vars; var; var=var->next) {
+ tail = PyUnicode_FromString(var->name);
+ joined = PyUnicode_Concat(str, tail);
+ Py_DecRef(str);
+ Py_DecRef(tail);
+ str = joined;
+ if (var->next) {
+ tail = PyUnicode_InternFromString(", ");
+ joined = PyUnicode_Concat(str, tail);
+ Py_DecRef(str);
+ Py_DecRef(tail);
+ str = joined;
+ }
+ }
+ tail = PyUnicode_InternFromString(")");
+ joined = PyUnicode_Concat(str, tail);
+ Py_DecRef(str);
+ Py_DecRef(tail);
+ str = joined;
+#else
+ PyObject *str = PyString_FromString("(");
+ swig_globalvar *var;
+ for (var = v->vars; var; var=var->next) {
+ PyString_ConcatAndDel(&str,PyString_FromString(var->name));
+ if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", "));
+ }
+ PyString_ConcatAndDel(&str,PyString_FromString(")"));
+#endif
+ return str;
+ }
+
+ SWIGINTERN int
+ swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) {
+ char *tmp;
+ PyObject *str = swig_varlink_str(v);
+ fprintf(fp,"Swig global variables ");
+ fprintf(fp,"%s\n", tmp = SWIG_Python_str_AsChar(str));
+ SWIG_Python_str_DelForPy3(tmp);
+ Py_DECREF(str);
+ return 0;
+ }
+
+ SWIGINTERN void
+ swig_varlink_dealloc(swig_varlinkobject *v) {
+ swig_globalvar *var = v->vars;
+ while (var) {
+ swig_globalvar *n = var->next;
+ free(var->name);
+ free(var);
+ var = n;
+ }
+ }
+
+ SWIGINTERN PyObject *
+ swig_varlink_getattr(swig_varlinkobject *v, char *n) {
+ PyObject *res = NULL;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->get_attr)();
+ break;
+ }
+ var = var->next;
+ }
+ if (res == NULL && !PyErr_Occurred()) {
+ PyErr_SetString(PyExc_NameError,"Unknown C global variable");
+ }
+ return res;
+ }
+
+ SWIGINTERN int
+ swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) {
+ int res = 1;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->set_attr)(p);
+ break;
+ }
+ var = var->next;
+ }
+ if (res == 1 && !PyErr_Occurred()) {
+ PyErr_SetString(PyExc_NameError,"Unknown C global variable");
+ }
+ return res;
+ }
+
+ SWIGINTERN PyTypeObject*
+ swig_varlink_type(void) {
+ static char varlink__doc__[] = "Swig var link object";
+ static PyTypeObject varlink_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+ /* PyObject header changed in Python 3 */
+#if PY_VERSION_HEX >= 0x03000000
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+#endif
+ (char *)"swigvarlink", /* tp_name */
+ sizeof(swig_varlinkobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) swig_varlink_dealloc, /* tp_dealloc */
+ (printfunc) swig_varlink_print, /* tp_print */
+ (getattrfunc) swig_varlink_getattr, /* tp_getattr */
+ (setattrfunc) swig_varlink_setattr, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) swig_varlink_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc) swig_varlink_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ 0, /* tp_flags */
+ varlink__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#if PY_VERSION_HEX >= 0x02060000
+ 0, /* tp_version */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ varlink_type = tmp;
+ type_init = 1;
+#if PY_VERSION_HEX < 0x02020000
+ varlink_type.ob_type = &PyType_Type;
+#else
+ if (PyType_Ready(&varlink_type) < 0)
+ return NULL;
+#endif
+ }
+ return &varlink_type;
+ }
+
+ /* Create a variable linking object for use later */
+ SWIGINTERN PyObject *
+ SWIG_Python_newvarlink(void) {
+ swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type());
+ if (result) {
+ result->vars = 0;
+ }
+ return ((PyObject*) result);
+ }
+
+ SWIGINTERN void
+ SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) {
+ swig_varlinkobject *v = (swig_varlinkobject *) p;
+ swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar));
+ if (gv) {
+ size_t size = strlen(name)+1;
+ gv->name = (char *)malloc(size);
+ if (gv->name) {
+ strncpy(gv->name,name,size);
+ gv->get_attr = get_attr;
+ gv->set_attr = set_attr;
+ gv->next = v->vars;
+ }
+ }
+ v->vars = gv;
+ }
+
+ SWIGINTERN PyObject *
+ SWIG_globals(void) {
+ static PyObject *_SWIG_globals = 0;
+ if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink();
+ return _SWIG_globals;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * constants/methods manipulation
+ * ----------------------------------------------------------------------------- */
+
+ /* Install Constants */
+ SWIGINTERN void
+ SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) {
+ PyObject *obj = 0;
+ size_t i;
+ for (i = 0; constants[i].type; ++i) {
+ switch(constants[i].type) {
+ case SWIG_PY_POINTER:
+ obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0);
+ break;
+ case SWIG_PY_BINARY:
+ obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype));
+ break;
+ default:
+ obj = 0;
+ break;
+ }
+ if (obj) {
+ PyDict_SetItemString(d, constants[i].name, obj);
+ Py_DECREF(obj);
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------------------*/
+ /* Fix SwigMethods to carry the callback ptrs when needed */
+ /* -----------------------------------------------------------------------------*/
+
+ SWIGINTERN void
+ SWIG_Python_FixMethods(PyMethodDef *methods,
+ swig_const_info *const_table,
+ swig_type_info **types,
+ swig_type_info **types_initial) {
+ size_t i;
+ for (i = 0; methods[i].ml_name; ++i) {
+ const char *c = methods[i].ml_doc;
+ if (c && (c = strstr(c, "swig_ptr: "))) {
+ int j;
+ swig_const_info *ci = 0;
+ const char *name = c + 10;
+ for (j = 0; const_table[j].type; ++j) {
+ if (strncmp(const_table[j].name, name,
+ strlen(const_table[j].name)) == 0) {
+ ci = &(const_table[j]);
+ break;
+ }
+ }
+ if (ci) {
+ void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0;
+ if (ptr) {
+ size_t shift = (ci->ptype) - types;
+ swig_type_info *ty = types_initial[shift];
+ size_t ldoc = (c - methods[i].ml_doc);
+ size_t lptr = strlen(ty->name)+2*sizeof(void*)+2;
+ char *ndoc = (char*)malloc(ldoc + lptr + 10);
+ if (ndoc) {
+ char *buff = ndoc;
+ strncpy(buff, methods[i].ml_doc, ldoc);
+ buff += ldoc;
+ strncpy(buff, "swig_ptr: ", 10);
+ buff += 10;
+ SWIG_PackVoidPtr(buff, ptr, ty->name, lptr);
+ methods[i].ml_doc = ndoc;
+ }
+ }
+ }
+ }
+ }
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+/* -----------------------------------------------------------------------------*
+ * Partial Init method
+ * -----------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+
+SWIGEXPORT
+#if PY_VERSION_HEX >= 0x03000000
+PyObject*
+#else
+void
+#endif
+SWIG_init(void) {
+ PyObject *m, *d, *md;
+#if PY_VERSION_HEX >= 0x03000000
+ static struct PyModuleDef SWIG_module = {
+# if PY_VERSION_HEX >= 0x03020000
+ PyModuleDef_HEAD_INIT,
+# else
+ {
+ PyObject_HEAD_INIT(NULL)
+ NULL, /* m_init */
+ 0, /* m_index */
+ NULL, /* m_copy */
+ },
+# endif
+ (char *) SWIG_name,
+ NULL,
+ -1,
+ SwigMethods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+#endif
+
+#if defined(SWIGPYTHON_BUILTIN)
+ static SwigPyClientData SwigPyObject_clientdata = {
+ 0, 0, 0, 0, 0, 0, 0
+ };
+ static PyGetSetDef this_getset_def = {
+ (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL
+ };
+ static SwigPyGetSet thisown_getset_closure = {
+ (PyCFunction) SwigPyObject_own,
+ (PyCFunction) SwigPyObject_own
+ };
+ static PyGetSetDef thisown_getset_def = {
+ (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure
+ };
+ PyObject *metatype_args;
+ PyTypeObject *builtin_pytype;
+ int builtin_base_count;
+ swig_type_info *builtin_basetype;
+ PyObject *tuple;
+ PyGetSetDescrObject *static_getset;
+ PyTypeObject *metatype;
+ SwigPyClientData *cd;
+ PyObject *public_interface, *public_symbol;
+ PyObject *this_descr;
+ PyObject *thisown_descr;
+ int i;
+
+ (void)builtin_pytype;
+ (void)builtin_base_count;
+ (void)builtin_basetype;
+ (void)tuple;
+ (void)static_getset;
+
+ /* metatype is used to implement static member variables. */
+ metatype_args = Py_BuildValue("(s(O){})", "SwigPyObjectType", &PyType_Type);
+ assert(metatype_args);
+ metatype = (PyTypeObject *) PyType_Type.tp_call((PyObject *) &PyType_Type, metatype_args, NULL);
+ assert(metatype);
+ Py_DECREF(metatype_args);
+ metatype->tp_setattro = (setattrofunc) &SwigPyObjectType_setattro;
+ assert(PyType_Ready(metatype) >= 0);
+#endif
+
+ /* Fix SwigMethods to carry the callback ptrs when needed */
+ SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial);
+
+#if PY_VERSION_HEX >= 0x03000000
+ m = PyModule_Create(&SWIG_module);
+#else
+ m = Py_InitModule((char *) SWIG_name, SwigMethods);
+#endif
+ md = d = PyModule_GetDict(m);
+
+ SWIG_InitializeModule(0);
+
+#ifdef SWIGPYTHON_BUILTIN
+ SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject");
+ assert(SwigPyObject_stype);
+ cd = (SwigPyClientData*) SwigPyObject_stype->clientdata;
+ if (!cd) {
+ SwigPyObject_stype->clientdata = &SwigPyObject_clientdata;
+ SwigPyObject_clientdata.pytype = SwigPyObject_TypeOnce();
+ } else if (SwigPyObject_TypeOnce()->tp_basicsize != cd->pytype->tp_basicsize) {
+ PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules.");
+# if PY_VERSION_HEX >= 0x03000000
+ return NULL;
+# else
+ return;
+# endif
+ }
+
+ /* All objects have a 'this' attribute */
+ this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def);
+ (void)this_descr;
+
+ /* All objects have a 'thisown' attribute */
+ thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def);
+ (void)thisown_descr;
+
+ public_interface = PyList_New(0);
+ public_symbol = 0;
+ (void)public_symbol;
+
+ PyDict_SetItemString(md, "__all__", public_interface);
+ Py_DECREF(public_interface);
+ for (i = 0; SwigMethods[i].ml_name != NULL; ++i)
+ SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name);
+ for (i = 0; swig_const_table[i].name != 0; ++i)
+ SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name);
+#endif
+
+ SWIG_InstallConstants(d,swig_const_table);
+
+#if PY_VERSION_HEX >= 0x03000000
+ return m;
+#else
+ return;
+#endif
+}
+
diff --git a/src/third_party/libwebp/swig/setup.py b/src/third_party/libwebp/swig/setup.py
new file mode 100644
index 0000000..3a3bfe1
--- /dev/null
+++ b/src/third_party/libwebp/swig/setup.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+"""distutils script for libwebp python module."""
+
+from distutils.core import setup
+from distutils.extension import Extension
+import os
+import shutil
+import tempfile
+
+tmpdir = tempfile.mkdtemp()
+package = "com.google.webp"
+package_path = os.path.join(tmpdir, *package.split("."))
+os.makedirs(package_path)
+
+# Create __init_.py files along the package path.
+initpy_path = tmpdir
+for d in package.split("."):
+ initpy_path = os.path.join(initpy_path, d)
+ open(os.path.join(initpy_path, "__init__.py"), "w").close()
+
+shutil.copy2("libwebp.py", package_path)
+setup(name="libwebp",
+ version="0.0",
+ description="libwebp python wrapper",
+ long_description="Provides access to 'simple' libwebp decode interface",
+ license="BSD",
+ url="http://developers.google.com/speed/webp",
+ ext_package=package,
+ ext_modules=[Extension("_libwebp",
+ ["libwebp_python_wrap.c"],
+ libraries=["webp"],
+ ),
+ ],
+ package_dir={"": tmpdir},
+ packages=["com", "com.google", "com.google.webp"],
+ py_modules=[package + ".libwebp"],
+ )
+
+shutil.rmtree(tmpdir)
diff --git a/src/third_party/libwebp/utils/bit_reader.c b/src/third_party/libwebp/utils/bit_reader.c
deleted file mode 100644
index ad6dee0..0000000
--- a/src/third_party/libwebp/utils/bit_reader.c
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Boolean decoder
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "./bit_reader.h"
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#ifndef USE_RIGHT_JUSTIFY
-#define MK(X) (((range_t)(X) << (BITS)) | (MASK))
-#else
-#define MK(X) ((range_t)(X))
-#endif
-
-//------------------------------------------------------------------------------
-// VP8BitReader
-
-void VP8InitBitReader(VP8BitReader* const br,
- const uint8_t* const start, const uint8_t* const end) {
- SB_DCHECK(br != NULL);
- SB_DCHECK(start != NULL);
- SB_DCHECK(start <= end);
- br->range_ = MK(255 - 1);
- br->buf_ = start;
- br->buf_end_ = end;
- br->value_ = 0;
- br->bits_ = -8; // to load the very first 8bits
- br->eof_ = 0;
-}
-
-const uint8_t kVP8Log2Range[128] = {
- 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0
-};
-
-// range = (range << kVP8Log2Range[range]) + trailing 1's
-const range_t kVP8NewRange[128] = {
- MK(127), MK(127), MK(191), MK(127), MK(159), MK(191), MK(223), MK(127),
- MK(143), MK(159), MK(175), MK(191), MK(207), MK(223), MK(239), MK(127),
- MK(135), MK(143), MK(151), MK(159), MK(167), MK(175), MK(183), MK(191),
- MK(199), MK(207), MK(215), MK(223), MK(231), MK(239), MK(247), MK(127),
- MK(131), MK(135), MK(139), MK(143), MK(147), MK(151), MK(155), MK(159),
- MK(163), MK(167), MK(171), MK(175), MK(179), MK(183), MK(187), MK(191),
- MK(195), MK(199), MK(203), MK(207), MK(211), MK(215), MK(219), MK(223),
- MK(227), MK(231), MK(235), MK(239), MK(243), MK(247), MK(251), MK(127),
- MK(129), MK(131), MK(133), MK(135), MK(137), MK(139), MK(141), MK(143),
- MK(145), MK(147), MK(149), MK(151), MK(153), MK(155), MK(157), MK(159),
- MK(161), MK(163), MK(165), MK(167), MK(169), MK(171), MK(173), MK(175),
- MK(177), MK(179), MK(181), MK(183), MK(185), MK(187), MK(189), MK(191),
- MK(193), MK(195), MK(197), MK(199), MK(201), MK(203), MK(205), MK(207),
- MK(209), MK(211), MK(213), MK(215), MK(217), MK(219), MK(221), MK(223),
- MK(225), MK(227), MK(229), MK(231), MK(233), MK(235), MK(237), MK(239),
- MK(241), MK(243), MK(245), MK(247), MK(249), MK(251), MK(253), MK(127)
-};
-
-#undef MK
-
-void VP8LoadFinalBytes(VP8BitReader* const br) {
- SB_DCHECK(br != NULL && br->buf_ != NULL);
- // Only read 8bits at a time
- if (br->buf_ < br->buf_end_) {
-#ifndef USE_RIGHT_JUSTIFY
- br->value_ |= (bit_t)(*br->buf_++) << ((BITS) - 8 - br->bits_);
-#else
- br->value_ = (bit_t)(*br->buf_++) | (br->value_ << 8);
-#endif
- br->bits_ += 8;
- } else if (!br->eof_) {
-#ifdef USE_RIGHT_JUSTIFY
- // These are not strictly needed, but it makes the behaviour
- // consistent for both USE_RIGHT_JUSTIFY and !USE_RIGHT_JUSTIFY.
- br->value_ <<= 8;
- br->bits_ += 8;
-#endif
- br->eof_ = 1;
- }
-}
-
-//------------------------------------------------------------------------------
-// Higher-level calls
-
-uint32_t VP8GetValue(VP8BitReader* const br, int bits) {
- uint32_t v = 0;
- while (bits-- > 0) {
- v |= VP8GetBit(br, 0x80) << bits;
- }
- return v;
-}
-
-int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) {
- const int value = VP8GetValue(br, bits);
- return VP8Get(br) ? -value : value;
-}
-
-//------------------------------------------------------------------------------
-// VP8LBitReader
-
-#define MAX_NUM_BIT_READ 25
-
-#define LBITS 64 // Number of bits prefetched.
-#define WBITS 32 // Minimum number of bytes needed after VP8LFillBitWindow.
-#define LOG8_WBITS 4 // Number of bytes needed to store WBITS bits.
-
-static const uint32_t kBitMask[MAX_NUM_BIT_READ] = {
- 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767,
- 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215
-};
-
-void VP8LInitBitReader(VP8LBitReader* const br,
- const uint8_t* const start,
- size_t length) {
- size_t i;
- SB_DCHECK(br != NULL);
- SB_DCHECK(start != NULL);
- SB_DCHECK(length < 0xfffffff8u); // can't happen with a RIFF chunk.
-
- br->buf_ = start;
- br->len_ = length;
- br->val_ = 0;
- br->pos_ = 0;
- br->bit_pos_ = 0;
- br->eos_ = 0;
- br->error_ = 0;
- for (i = 0; i < sizeof(br->val_) && i < br->len_; ++i) {
- br->val_ |= ((vp8l_val_t)br->buf_[br->pos_]) << (8 * i);
- ++br->pos_;
- }
-}
-
-void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
- const uint8_t* const buf, size_t len) {
- SB_DCHECK(br != NULL);
- SB_DCHECK(buf != NULL);
- SB_DCHECK(len < 0xfffffff8u); // can't happen with a RIFF chunk.
- br->eos_ = (br->pos_ >= len);
- br->buf_ = buf;
- br->len_ = len;
-}
-
-// If not at EOS, reload up to LBITS byte-by-byte
-static void ShiftBytes(VP8LBitReader* const br) {
- while (br->bit_pos_ >= 8 && br->pos_ < br->len_) {
- br->val_ >>= 8;
- br->val_ |= ((vp8l_val_t)br->buf_[br->pos_]) << (LBITS - 8);
- ++br->pos_;
- br->bit_pos_ -= 8;
- }
-}
-
-void VP8LFillBitWindow(VP8LBitReader* const br) {
- if (br->bit_pos_ >= WBITS) {
-#if (defined(__x86_64__) || defined(_M_X64))
- if (br->pos_ + sizeof(br->val_) < br->len_) {
- br->val_ >>= WBITS;
- br->bit_pos_ -= WBITS;
- // The expression below needs a little-endian arch to work correctly.
- // This gives a large speedup for decoding speed.
- br->val_ |= *(const vp8l_val_t*)(br->buf_ + br->pos_) << (LBITS - WBITS);
- br->pos_ += LOG8_WBITS;
- return;
- }
-#endif
- ShiftBytes(br); // Slow path.
- if (br->pos_ == br->len_ && br->bit_pos_ == LBITS) {
- br->eos_ = 1;
- }
- }
-}
-
-uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) {
- SB_DCHECK(n_bits >= 0);
- // Flag an error if end_of_stream or n_bits is more than allowed limit.
- if (!br->eos_ && n_bits < MAX_NUM_BIT_READ) {
- const uint32_t val =
- (uint32_t)(br->val_ >> br->bit_pos_) & kBitMask[n_bits];
- const int new_bits = br->bit_pos_ + n_bits;
- br->bit_pos_ = new_bits;
- // If this read is going to cross the read buffer, set the eos flag.
- if (br->pos_ == br->len_) {
- if (new_bits >= LBITS) {
- br->eos_ = 1;
- }
- }
- ShiftBytes(br);
- return val;
- } else {
- br->error_ = 1;
- return 0;
- }
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/bit_reader.h b/src/third_party/libwebp/utils/bit_reader.h
deleted file mode 100644
index 5168b5b..0000000
--- a/src/third_party/libwebp/utils/bit_reader.h
+++ /dev/null
@@ -1,341 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Boolean decoder
-//
-// Author: Skal (pascal.massimino@gmail.com)
-// Vikas Arora (vikaas.arora@gmail.com)
-
-#ifndef WEBP_UTILS_BIT_READER_H_
-#define WEBP_UTILS_BIT_READER_H_
-
-#if defined(STARBOARD)
-#include "starboard/byte_swap.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#ifdef _MSC_VER
-#include <stdlib.h> // _byteswap_ulong
-#endif
-#include <string.h> // For memcpy
-#endif // defined(STARBOARD)
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// The Boolean decoder needs to maintain infinite precision on the value_ field.
-// However, since range_ is only 8bit, we only need an active window of 8 bits
-// for value_. Left bits (MSB) gets zeroed and shifted away when value_ falls
-// below 128, range_ is updated, and fresh bits read from the bitstream are
-// brought in as LSB.
-// To avoid reading the fresh bits one by one (slow), we cache a few of them
-// ahead (actually, we cache BITS of them ahead. See below). There's two
-// strategies regarding how to shift these looked-ahead fresh bits into the
-// 8bit window of value_: either we shift them in, while keeping the position of
-// the window fixed. Or we slide the window to the right while keeping the cache
-// bits at a fixed, right-justified, position.
-//
-// Example, for BITS=16: here is the content of value_ for both strategies:
-//
-// !USE_RIGHT_JUSTIFY || USE_RIGHT_JUSTIFY
-// ||
-// <- 8b -><- 8b -><- BITS bits -> || <- 8b+3b -><- 8b -><- 13 bits ->
-// [unused][value_][cached bits][0] || [unused...][value_][cached bits]
-// [........00vvvvvvBBBBBBBBBBBBB000]LSB || [...........00vvvvvvBBBBBBBBBBBBB]
-// ||
-// After calling VP8Shift(), where we need to shift away two zeros:
-// [........vvvvvvvvBBBBBBBBBBB00000]LSB || [.............vvvvvvvvBBBBBBBBBBB]
-// ||
-// Just before we need to call VP8LoadNewBytes(), the situation is:
-// [........vvvvvv000000000000000000]LSB || [..........................vvvvvv]
-// ||
-// And just after calling VP8LoadNewBytes():
-// [........vvvvvvvvBBBBBBBBBBBBBBBB]LSB || [........vvvvvvvvBBBBBBBBBBBBBBBB]
-//
-// -> we're back to height active 'value_' bits (marked 'v') and BITS cached
-// bits (marked 'B')
-//
-// The right-justify strategy tends to use less shifts and is often faster.
-
-//------------------------------------------------------------------------------
-// BITS can be any multiple of 8 from 8 to 56 (inclusive).
-// Pick values that fit natural register size.
-
-#if !defined(WEBP_REFERENCE_IMPLEMENTATION)
-
-#define USE_RIGHT_JUSTIFY
-
-#if defined(__i386__) || defined(_M_IX86) // x86 32bit
-#define BITS 16
-#elif defined(__x86_64__) || defined(_M_X64) // x86 64bit
-#define BITS 56
-#elif defined(__arm__) || defined(_M_ARM) // ARM
-#define BITS 24
-#else // reasonable default
-#define BITS 24
-#endif
-
-#else // reference choices
-
-#define USE_RIGHT_JUSTIFY
-#define BITS 8
-
-#endif
-
-//------------------------------------------------------------------------------
-// Derived types and constants
-
-// bit_t = natural register type
-// lbit_t = natural type for memory I/O
-
-#if (BITS > 32)
-typedef uint64_t bit_t;
-typedef uint64_t lbit_t;
-#elif (BITS == 32)
-typedef uint64_t bit_t;
-typedef uint32_t lbit_t;
-#elif (BITS == 24)
-typedef uint32_t bit_t;
-typedef uint32_t lbit_t;
-#elif (BITS == 16)
-typedef uint32_t bit_t;
-typedef uint16_t lbit_t;
-#else
-typedef uint32_t bit_t;
-typedef uint8_t lbit_t;
-#endif
-
-#ifndef USE_RIGHT_JUSTIFY
-typedef bit_t range_t; // type for storing range_
-#define MASK ((((bit_t)1) << (BITS)) - 1)
-#else
-typedef uint32_t range_t; // range_ only uses 8bits here. No need for bit_t.
-#endif
-
-//------------------------------------------------------------------------------
-// Bitreader
-
-typedef struct VP8BitReader VP8BitReader;
-struct VP8BitReader {
- const uint8_t* buf_; // next byte to be read
- const uint8_t* buf_end_; // end of read buffer
- int eof_; // true if input is exhausted
-
- // boolean decoder
- range_t range_; // current range minus 1. In [127, 254] interval.
- bit_t value_; // current value
- int bits_; // number of valid bits left
-};
-
-// Initialize the bit reader and the boolean decoder.
-void VP8InitBitReader(VP8BitReader* const br,
- const uint8_t* const start, const uint8_t* const end);
-
-// return the next value made of 'num_bits' bits
-uint32_t VP8GetValue(VP8BitReader* const br, int num_bits);
-static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) {
- return VP8GetValue(br, 1);
-}
-
-// return the next value with sign-extension.
-int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits);
-
-// Read a bit with proba 'prob'. Speed-critical function!
-extern const uint8_t kVP8Log2Range[128];
-extern const range_t kVP8NewRange[128];
-
-void VP8LoadFinalBytes(VP8BitReader* const br); // special case for the tail
-
-static WEBP_INLINE void VP8LoadNewBytes(VP8BitReader* const br) {
- SB_DCHECK(br != NULL && br->buf_ != NULL);
- // Read 'BITS' bits at a time if possible.
- if (br->buf_ + sizeof(lbit_t) <= br->buf_end_) {
- // convert memory type to register type (with some zero'ing!)
- bit_t bits;
- lbit_t in_bits = *(lbit_t*)br->buf_;
- br->buf_ += (BITS) >> 3;
-#if !defined(__BIG_ENDIAN__)
-#if (BITS > 32)
-// gcc 4.3 has builtin functions for swap32/swap64
-#if defined(STARBOARD)
- bits = SbByteSwapU64(in_bits);
-#elif defined(__GNUC__) && \
- (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
- bits = (bit_t)__builtin_bswap64(in_bits);
-#elif defined(_MSC_VER)
- bits = (bit_t)_byteswap_uint64(in_bits);
-#elif defined(__x86_64__)
- __asm__ volatile("bswapq %0" : "=r"(bits) : "0"(in_bits));
-#else // generic code for swapping 64-bit values (suggested by bdb@)
- bits = (bit_t)in_bits;
- bits = ((bits & 0xffffffff00000000ull) >> 32) |
- ((bits & 0x00000000ffffffffull) << 32);
- bits = ((bits & 0xffff0000ffff0000ull) >> 16) |
- ((bits & 0x0000ffff0000ffffull) << 16);
- bits = ((bits & 0xff00ff00ff00ff00ull) >> 8) |
- ((bits & 0x00ff00ff00ff00ffull) << 8);
-#endif
- bits >>= 64 - BITS;
-#elif (BITS >= 24)
-#if defined(STARBOARD)
- bits = SbByteSwapU32(in_bits);
-#elif defined(__i386__) || defined(__x86_64__)
- __asm__ volatile("bswap %k0" : "=r"(in_bits) : "0"(in_bits));
- bits = (bit_t)in_bits; // 24b/32b -> 32b/64b zero-extension
-#elif defined(_MSC_VER)
- bits = (bit_t)_byteswap_ulong(in_bits);
-#else
- bits = (bit_t)(in_bits >> 24) | ((in_bits >> 8) & 0xff00)
- | ((in_bits << 8) & 0xff0000) | (in_bits << 24);
-#endif // x86
- bits >>= (32 - BITS);
-#elif (BITS == 16)
- // gcc will recognize a 'rorw $8, ...' here:
- bits = (bit_t)(in_bits >> 8) | ((in_bits & 0xff) << 8);
-#else // BITS == 8
- bits = (bit_t)in_bits;
-#endif
-#else // BIG_ENDIAN
- bits = (bit_t)in_bits;
- if (BITS != 8 * sizeof(bit_t)) bits >>= (8 * sizeof(bit_t) - BITS);
-#endif
-#ifndef USE_RIGHT_JUSTIFY
- br->value_ |= bits << (-br->bits_);
-#else
- br->value_ = bits | (br->value_ << (BITS));
-#endif
- br->bits_ += (BITS);
- } else {
- VP8LoadFinalBytes(br); // no need to be inlined
- }
-}
-
-static WEBP_INLINE int VP8BitUpdate(VP8BitReader* const br, range_t split) {
- if (br->bits_ < 0) { // Make sure we have a least BITS bits in 'value_'
- VP8LoadNewBytes(br);
- }
-#ifndef USE_RIGHT_JUSTIFY
- split |= (MASK);
- if (br->value_ > split) {
- br->range_ -= split + 1;
- br->value_ -= split + 1;
- return 1;
- } else {
- br->range_ = split;
- return 0;
- }
-#else
- {
- const int pos = br->bits_;
- const range_t value = (range_t)(br->value_ >> pos);
- if (value > split) {
- br->range_ -= split + 1;
- br->value_ -= (bit_t)(split + 1) << pos;
- return 1;
- } else {
- br->range_ = split;
- return 0;
- }
- }
-#endif
-}
-
-static WEBP_INLINE void VP8Shift(VP8BitReader* const br) {
-#ifndef USE_RIGHT_JUSTIFY
- // range_ is in [0..127] interval here.
- const bit_t idx = br->range_ >> (BITS);
- const int shift = kVP8Log2Range[idx];
- br->range_ = kVP8NewRange[idx];
- br->value_ <<= shift;
- br->bits_ -= shift;
-#else
- const int shift = kVP8Log2Range[br->range_];
- SB_DCHECK(br->range_ < (range_t)128);
- br->range_ = kVP8NewRange[br->range_];
- br->bits_ -= shift;
-#endif
-}
-static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) {
-#ifndef USE_RIGHT_JUSTIFY
- // It's important to avoid generating a 64bit x 64bit multiply here.
- // We just need an 8b x 8b after all.
- const range_t split =
- (range_t)((uint32_t)(br->range_ >> (BITS)) * prob) << ((BITS) - 8);
- const int bit = VP8BitUpdate(br, split);
- if (br->range_ <= (((range_t)0x7e << (BITS)) | (MASK))) {
- VP8Shift(br);
- }
- return bit;
-#else
- const range_t split = (br->range_ * prob) >> 8;
- const int bit = VP8BitUpdate(br, split);
- if (br->range_ <= (range_t)0x7e) {
- VP8Shift(br);
- }
- return bit;
-#endif
-}
-
-static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) {
- const range_t split = (br->range_ >> 1);
- const int bit = VP8BitUpdate(br, split);
- VP8Shift(br);
- return bit ? -v : v;
-}
-
-
-// -----------------------------------------------------------------------------
-// Bitreader for lossless format
-
-typedef uint64_t vp8l_val_t; // right now, this bit-reader can only use 64bit.
-
-typedef struct {
- vp8l_val_t val_; // pre-fetched bits
- const uint8_t* buf_; // input byte buffer
- size_t len_; // buffer length
- size_t pos_; // byte position in buf_
- int bit_pos_; // current bit-reading position in val_
- int eos_; // bitstream is finished
- int error_; // an error occurred (buffer overflow attempt...)
-} VP8LBitReader;
-
-void VP8LInitBitReader(VP8LBitReader* const br,
- const uint8_t* const start,
- size_t length);
-
-// Sets a new data buffer.
-void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
- const uint8_t* const buffer, size_t length);
-
-// Reads the specified number of bits from Read Buffer.
-// Flags an error in case end_of_stream or n_bits is more than allowed limit.
-// Flags eos if this read attempt is going to cross the read buffer.
-uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits);
-
-// Return the prefetched bits, so they can be looked up.
-static WEBP_INLINE uint32_t VP8LPrefetchBits(VP8LBitReader* const br) {
- return (uint32_t)(br->val_ >> br->bit_pos_);
-}
-
-// Discard 'num_bits' bits from the cache.
-static WEBP_INLINE void VP8LDiscardBits(VP8LBitReader* const br, int num_bits) {
- br->bit_pos_ += num_bits;
-}
-
-// Advances the Read buffer by 4 bytes to make room for reading next 32 bits.
-void VP8LFillBitWindow(VP8LBitReader* const br);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_BIT_READER_H_ */
diff --git a/src/third_party/libwebp/utils/bit_writer.c b/src/third_party/libwebp/utils/bit_writer.c
deleted file mode 100644
index 8ee1cd1..0000000
--- a/src/third_party/libwebp/utils/bit_writer.c
+++ /dev/null
@@ -1,292 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Bit writing and boolean coder
-//
-// Author: Skal (pascal.massimino@gmail.com)
-// Vikas Arora (vikaas.arora@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <string.h> // for memcpy()
-#include <stdlib.h>
-#endif
-
-#include "./bit_writer.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// VP8BitWriter
-
-static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) {
- uint8_t* new_buf;
- size_t new_size;
- const uint64_t needed_size_64b = (uint64_t)bw->pos_ + extra_size;
- const size_t needed_size = (size_t)needed_size_64b;
- if (needed_size_64b != needed_size) {
- bw->error_ = 1;
- return 0;
- }
- if (needed_size <= bw->max_pos_) return 1;
- // If the following line wraps over 32bit, the test just after will catch it.
- new_size = 2 * bw->max_pos_;
- if (new_size < needed_size) new_size = needed_size;
- if (new_size < 1024) new_size = 1024;
- new_buf = (uint8_t*)SbMemoryAllocate(new_size);
- if (new_buf == NULL) {
- bw->error_ = 1;
- return 0;
- }
- SbMemoryCopy(new_buf, bw->buf_, bw->pos_);
- SbMemoryDeallocate(bw->buf_);
- bw->buf_ = new_buf;
- bw->max_pos_ = new_size;
- return 1;
-}
-
-static void kFlush(VP8BitWriter* const bw) {
- const int s = 8 + bw->nb_bits_;
- const int32_t bits = bw->value_ >> s;
- SB_DCHECK(bw->nb_bits_ >= 0);
- bw->value_ -= bits << s;
- bw->nb_bits_ -= 8;
- if ((bits & 0xff) != 0xff) {
- size_t pos = bw->pos_;
- if (!BitWriterResize(bw, bw->run_ + 1)) {
- return;
- }
- if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's
- if (pos > 0) bw->buf_[pos - 1]++;
- }
- if (bw->run_ > 0) {
- const int value = (bits & 0x100) ? 0x00 : 0xff;
- for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value;
- }
- bw->buf_[pos++] = bits;
- bw->pos_ = pos;
- } else {
- bw->run_++; // delay writing of bytes 0xff, pending eventual carry.
- }
-}
-
-//------------------------------------------------------------------------------
-// renormalization
-
-static const uint8_t kNorm[128] = { // renorm_sizes[i] = 8 - log2(i)
- 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 0
-};
-
-// range = ((range + 1) << kVP8Log2Range[range]) - 1
-static const uint8_t kNewRange[128] = {
- 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239,
- 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239,
- 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179,
- 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239,
- 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149,
- 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179,
- 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209,
- 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239,
- 241, 243, 245, 247, 249, 251, 253, 127
-};
-
-int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) {
- const int split = (bw->range_ * prob) >> 8;
- if (bit) {
- bw->value_ += split + 1;
- bw->range_ -= split + 1;
- } else {
- bw->range_ = split;
- }
- if (bw->range_ < 127) { // emit 'shift' bits out and renormalize
- const int shift = kNorm[bw->range_];
- bw->range_ = kNewRange[bw->range_];
- bw->value_ <<= shift;
- bw->nb_bits_ += shift;
- if (bw->nb_bits_ > 0) kFlush(bw);
- }
- return bit;
-}
-
-int VP8PutBitUniform(VP8BitWriter* const bw, int bit) {
- const int split = bw->range_ >> 1;
- if (bit) {
- bw->value_ += split + 1;
- bw->range_ -= split + 1;
- } else {
- bw->range_ = split;
- }
- if (bw->range_ < 127) {
- bw->range_ = kNewRange[bw->range_];
- bw->value_ <<= 1;
- bw->nb_bits_ += 1;
- if (bw->nb_bits_ > 0) kFlush(bw);
- }
- return bit;
-}
-
-void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits) {
- int mask;
- for (mask = 1 << (nb_bits - 1); mask; mask >>= 1)
- VP8PutBitUniform(bw, value & mask);
-}
-
-void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits) {
- if (!VP8PutBitUniform(bw, value != 0))
- return;
- if (value < 0) {
- VP8PutValue(bw, ((-value) << 1) | 1, nb_bits + 1);
- } else {
- VP8PutValue(bw, value << 1, nb_bits + 1);
- }
-}
-
-//------------------------------------------------------------------------------
-
-int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size) {
- bw->range_ = 255 - 1;
- bw->value_ = 0;
- bw->run_ = 0;
- bw->nb_bits_ = -8;
- bw->pos_ = 0;
- bw->max_pos_ = 0;
- bw->error_ = 0;
- bw->buf_ = NULL;
- return (expected_size > 0) ? BitWriterResize(bw, expected_size) : 1;
-}
-
-uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) {
- VP8PutValue(bw, 0, 9 - bw->nb_bits_);
- bw->nb_bits_ = 0; // pad with zeroes
- kFlush(bw);
- return bw->buf_;
-}
-
-int VP8BitWriterAppend(VP8BitWriter* const bw,
- const uint8_t* data, size_t size) {
- SB_DCHECK(data);
- if (bw->nb_bits_ != -8) return 0; // kFlush() must have been called
- if (!BitWriterResize(bw, size)) return 0;
- SbMemoryCopy(bw->buf_ + bw->pos_, data, size);
- bw->pos_ += size;
- return 1;
-}
-
-void VP8BitWriterWipeOut(VP8BitWriter* const bw) {
- if (bw) {
- SbMemoryDeallocate(bw->buf_);
- SbMemorySet(bw, 0, sizeof(*bw));
- }
-}
-
-//------------------------------------------------------------------------------
-// VP8LBitWriter
-
-// Returns 1 on success.
-static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) {
- uint8_t* allocated_buf;
- size_t allocated_size;
- const size_t current_size = VP8LBitWriterNumBytes(bw);
- const uint64_t size_required_64b = (uint64_t)current_size + extra_size;
- const size_t size_required = (size_t)size_required_64b;
- if (size_required != size_required_64b) {
- bw->error_ = 1;
- return 0;
- }
- if (bw->max_bytes_ > 0 && size_required <= bw->max_bytes_) return 1;
- allocated_size = (3 * bw->max_bytes_) >> 1;
- if (allocated_size < size_required) allocated_size = size_required;
- // make allocated size multiple of 1k
- allocated_size = (((allocated_size >> 10) + 1) << 10);
- allocated_buf = (uint8_t*)SbMemoryAllocate(allocated_size);
- if (allocated_buf == NULL) {
- bw->error_ = 1;
- return 0;
- }
- SbMemoryCopy(allocated_buf, bw->buf_, current_size);
- SbMemoryDeallocate(bw->buf_);
- bw->buf_ = allocated_buf;
- bw->max_bytes_ = allocated_size;
- SbMemorySet(allocated_buf + current_size, 0, allocated_size - current_size);
- return 1;
-}
-
-int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) {
- SbMemorySet(bw, 0, sizeof(*bw));
- return VP8LBitWriterResize(bw, expected_size);
-}
-
-void VP8LBitWriterDestroy(VP8LBitWriter* const bw) {
- if (bw != NULL) {
- SbMemoryDeallocate(bw->buf_);
- SbMemorySet(bw, 0, sizeof(*bw));
- }
-}
-
-void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) {
- if (n_bits < 1) return;
-#if !defined(__BIG_ENDIAN__)
- // Technically, this branch of the code can write up to 25 bits at a time,
- // but in prefix encoding, the maximum number of bits written is 18 at a time.
- {
- uint8_t* const p = &bw->buf_[bw->bit_pos_ >> 3];
- uint32_t v = *(const uint32_t*)p;
- v |= bits << (bw->bit_pos_ & 7);
- *(uint32_t*)p = v;
- bw->bit_pos_ += n_bits;
- }
-#else // BIG_ENDIAN
- {
- uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
- const int bits_reserved_in_first_byte = bw->bit_pos_ & 7;
- const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte;
- // implicit & 0xff is assumed for uint8_t arithmetics
- *p++ |= bits << bits_reserved_in_first_byte;
- bits >>= 8 - bits_reserved_in_first_byte;
- if (bits_left_to_write >= 1) {
- *p++ = bits;
- bits >>= 8;
- if (bits_left_to_write >= 9) {
- *p++ = bits;
- bits >>= 8;
- }
- }
- SB_DCHECK(n_bits <= 25);
- *p = bits;
- bw->bit_pos_ += n_bits;
- }
-#endif
- if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) {
- const uint64_t extra_size = 32768ULL + bw->max_bytes_;
- if (extra_size != (size_t)extra_size ||
- !VP8LBitWriterResize(bw, (size_t)extra_size)) {
- bw->bit_pos_ = 0;
- bw->error_ = 1;
- }
- }
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/bit_writer.h b/src/third_party/libwebp/utils/bit_writer.h
deleted file mode 100644
index cbb095c..0000000
--- a/src/third_party/libwebp/utils/bit_writer.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Bit writing and boolean coder
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_UTILS_BIT_WRITER_H_
-#define WEBP_UTILS_BIT_WRITER_H_
-
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Bit-writing
-
-typedef struct VP8BitWriter VP8BitWriter;
-struct VP8BitWriter {
- int32_t range_; // range-1
- int32_t value_;
- int run_; // number of outstanding bits
- int nb_bits_; // number of pending bits
- uint8_t* buf_; // internal buffer. Re-allocated regularly. Not owned.
- size_t pos_;
- size_t max_pos_;
- int error_; // true in case of error
-};
-
-// Initialize the object. Allocates some initial memory based on expected_size.
-int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size);
-// Finalize the bitstream coding. Returns a pointer to the internal buffer.
-uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw);
-// Release any pending memory and zeroes the object. Not a mandatory call.
-// Only useful in case of error, when the internal buffer hasn't been grabbed!
-void VP8BitWriterWipeOut(VP8BitWriter* const bw);
-
-int VP8PutBit(VP8BitWriter* const bw, int bit, int prob);
-int VP8PutBitUniform(VP8BitWriter* const bw, int bit);
-void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits);
-void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits);
-
-// Appends some bytes to the internal buffer. Data is copied.
-int VP8BitWriterAppend(VP8BitWriter* const bw,
- const uint8_t* data, size_t size);
-
-// return approximate write position (in bits)
-static WEBP_INLINE uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) {
- return (uint64_t)(bw->pos_ + bw->run_) * 8 + 8 + bw->nb_bits_;
-}
-
-// Returns a pointer to the internal buffer.
-static WEBP_INLINE uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) {
- return bw->buf_;
-}
-// Returns the size of the internal buffer.
-static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) {
- return bw->pos_;
-}
-
-//------------------------------------------------------------------------------
-// VP8LBitWriter
-// TODO(vikasa): VP8LBitWriter is copied as-is from lossless code. There's scope
-// of re-using VP8BitWriter. Will evaluate once basic lossless encoder is
-// implemented.
-
-typedef struct {
- uint8_t* buf_;
- size_t bit_pos_;
- size_t max_bytes_;
-
- // After all bits are written, the caller must observe the state of
- // error_. A value of 1 indicates that a memory allocation failure
- // has happened during bit writing. A value of 0 indicates successful
- // writing of bits.
- int error_;
-} VP8LBitWriter;
-
-static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) {
- return (bw->bit_pos_ + 7) >> 3;
-}
-
-static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) {
- return bw->buf_;
-}
-
-// Returns 0 in case of memory allocation error.
-int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size);
-
-void VP8LBitWriterDestroy(VP8LBitWriter* const bw);
-
-// This function writes bits into bytes in increasing addresses, and within
-// a byte least-significant-bit first.
-//
-// The function can write up to 16 bits in one go with WriteBits
-// Example: let's assume that 3 bits (Rs below) have been written already:
-//
-// BYTE-0 BYTE+1 BYTE+2
-//
-// 0000 0RRR 0000 0000 0000 0000
-//
-// Now, we could write 5 or less bits in MSB by just sifting by 3
-// and OR'ing to BYTE-0.
-//
-// For n bits, we take the last 5 bytes, OR that with high bits in BYTE-0,
-// and locate the rest in BYTE+1 and BYTE+2.
-//
-// VP8LBitWriter's error_ flag is set in case of memory allocation error.
-void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_BIT_WRITER_H_ */
diff --git a/src/third_party/libwebp/utils/color_cache.c b/src/third_party/libwebp/utils/color_cache.c
deleted file mode 100644
index a9a0d2a..0000000
--- a/src/third_party/libwebp/utils/color_cache.c
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Color Cache for WebP Lossless
-//
-// Author: Jyrki Alakuijala (jyrki@google.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#endif
-
-#include "./color_cache.h"
-#include "../utils/utils.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// VP8LColorCache.
-
-int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
- const int hash_size = 1 << hash_bits;
- SB_DCHECK(cc != NULL);
- SB_DCHECK(hash_bits > 0);
- cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size,
- sizeof(*cc->colors_));
- if (cc->colors_ == NULL) return 0;
- cc->hash_shift_ = 32 - hash_bits;
- return 1;
-}
-
-void VP8LColorCacheClear(VP8LColorCache* const cc) {
- if (cc != NULL) {
- SbMemoryDeallocate(cc->colors_);
- cc->colors_ = NULL;
- }
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-}
-#endif
diff --git a/src/third_party/libwebp/utils/color_cache.h b/src/third_party/libwebp/utils/color_cache.h
deleted file mode 100644
index 4876bd4..0000000
--- a/src/third_party/libwebp/utils/color_cache.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Color Cache for WebP Lossless
-//
-// Authors: Jyrki Alakuijala (jyrki@google.com)
-// Urvang Joshi (urvang@google.com)
-
-#ifndef WEBP_UTILS_COLOR_CACHE_H_
-#define WEBP_UTILS_COLOR_CACHE_H_
-
-#include "../webp/types.h"
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// Main color cache struct.
-typedef struct {
- uint32_t *colors_; // color entries
- int hash_shift_; // Hash shift: 32 - hash_bits.
-} VP8LColorCache;
-
-static const uint32_t kHashMul = 0x1e35a7bd;
-
-static WEBP_INLINE uint32_t VP8LColorCacheLookup(
- const VP8LColorCache* const cc, uint32_t key) {
- SB_DCHECK(key <= (~0U >> cc->hash_shift_));
- return cc->colors_[key];
-}
-
-static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc,
- uint32_t argb) {
- const uint32_t key = (kHashMul * argb) >> cc->hash_shift_;
- cc->colors_[key] = argb;
-}
-
-static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc,
- uint32_t argb) {
- return (kHashMul * argb) >> cc->hash_shift_;
-}
-
-static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc,
- uint32_t argb) {
- const uint32_t key = (kHashMul * argb) >> cc->hash_shift_;
- return cc->colors_[key] == argb;
-}
-
-//------------------------------------------------------------------------------
-
-// Initializes the color cache with 'hash_bits' bits for the keys.
-// Returns false in case of memory error.
-int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits);
-
-// Delete the memory associated to color cache.
-void VP8LColorCacheClear(VP8LColorCache* const color_cache);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-}
-#endif
-
-#endif // WEBP_UTILS_COLOR_CACHE_H_
diff --git a/src/third_party/libwebp/utils/filters.c b/src/third_party/libwebp/utils/filters.c
deleted file mode 100644
index 000c6cd..0000000
--- a/src/third_party/libwebp/utils/filters.c
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Spatial prediction using various filters
-//
-// Author: Urvang (urvang@google.com)
-
-#include "./filters.h"
-#if defined(STARBOARD)
-#include "starboard/client_porting/poem/stdlib_poem.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Helpful macro.
-
-# define SANITY_CHECK(in, out) \
- SB_DCHECK(in != NULL); \
- SB_DCHECK(out != NULL); \
- SB_DCHECK(width > 0); \
- SB_DCHECK(height > 0); \
- SB_DCHECK(stride >= width);
-
-static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred,
- uint8_t* dst, int length, int inverse) {
- int i;
- if (inverse) {
- for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i];
- } else {
- for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i];
- }
-}
-
-//------------------------------------------------------------------------------
-// Horizontal filter.
-
-static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in,
- int width, int height, int stride,
- int inverse, uint8_t* out) {
- int h;
- const uint8_t* preds = (inverse ? out : in);
- SANITY_CHECK(in, out);
-
- // Filter line-by-line.
- for (h = 0; h < height; ++h) {
- // Leftmost pixel is predicted from above (except for topmost scanline).
- if (h == 0) {
- out[0] = in[0];
- } else {
- PredictLine(in, preds - stride, out, 1, inverse);
- }
- PredictLine(in + 1, preds, out + 1, width - 1, inverse);
- preds += stride;
- in += stride;
- out += stride;
- }
-}
-
-static void HorizontalFilter(const uint8_t* data, int width, int height,
- int stride, uint8_t* filtered_data) {
- DoHorizontalFilter(data, width, height, stride, 0, filtered_data);
-}
-
-static void HorizontalUnfilter(int width, int height, int stride,
- uint8_t* data) {
- DoHorizontalFilter(data, width, height, stride, 1, data);
-}
-
-//------------------------------------------------------------------------------
-// Vertical filter.
-
-static WEBP_INLINE void DoVerticalFilter(const uint8_t* in,
- int width, int height, int stride,
- int inverse, uint8_t* out) {
- int h;
- const uint8_t* preds = (inverse ? out : in);
- SANITY_CHECK(in, out);
-
- // Very first top-left pixel is copied.
- out[0] = in[0];
- // Rest of top scan-line is left-predicted.
- PredictLine(in + 1, preds, out + 1, width - 1, inverse);
-
- // Filter line-by-line.
- for (h = 1; h < height; ++h) {
- in += stride;
- out += stride;
- PredictLine(in, preds, out, width, inverse);
- preds += stride;
- }
-}
-
-static void VerticalFilter(const uint8_t* data, int width, int height,
- int stride, uint8_t* filtered_data) {
- DoVerticalFilter(data, width, height, stride, 0, filtered_data);
-}
-
-static void VerticalUnfilter(int width, int height, int stride, uint8_t* data) {
- DoVerticalFilter(data, width, height, stride, 1, data);
-}
-
-//------------------------------------------------------------------------------
-// Gradient filter.
-
-static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
- const int g = a + b - c;
- return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
-}
-
-static WEBP_INLINE
-void DoGradientFilter(const uint8_t* in, int width, int height,
- int stride, int inverse, uint8_t* out) {
- const uint8_t* preds = (inverse ? out : in);
- int h;
- SANITY_CHECK(in, out);
-
- // left prediction for top scan-line
- out[0] = in[0];
- PredictLine(in + 1, preds, out + 1, width - 1, inverse);
-
- // Filter line-by-line.
- for (h = 1; h < height; ++h) {
- int w;
- preds += stride;
- in += stride;
- out += stride;
- // leftmost pixel: predict from above.
- PredictLine(in, preds - stride, out, 1, inverse);
- for (w = 1; w < width; ++w) {
- const int pred = GradientPredictor(preds[w - 1],
- preds[w - stride],
- preds[w - stride - 1]);
- out[w] = in[w] + (inverse ? pred : -pred);
- }
- }
-}
-
-static void GradientFilter(const uint8_t* data, int width, int height,
- int stride, uint8_t* filtered_data) {
- DoGradientFilter(data, width, height, stride, 0, filtered_data);
-}
-
-static void GradientUnfilter(int width, int height, int stride, uint8_t* data) {
- DoGradientFilter(data, width, height, stride, 1, data);
-}
-
-#undef SANITY_CHECK
-
-// -----------------------------------------------------------------------------
-// Quick estimate of a potentially interesting filter mode to try.
-
-#define SMAX 16
-#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX)
-
-WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
- int width, int height, int stride) {
- int i, j;
- int bins[WEBP_FILTER_LAST][SMAX];
- SbMemorySet(bins, 0, sizeof(bins));
-
- // We only sample every other pixels. That's enough.
- for (j = 2; j < height - 1; j += 2) {
- const uint8_t* const p = data + j * stride;
- int mean = p[0];
- for (i = 2; i < width - 1; i += 2) {
- const int diff0 = SDIFF(p[i], mean);
- const int diff1 = SDIFF(p[i], p[i - 1]);
- const int diff2 = SDIFF(p[i], p[i - width]);
- const int grad_pred =
- GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]);
- const int diff3 = SDIFF(p[i], grad_pred);
- bins[WEBP_FILTER_NONE][diff0] = 1;
- bins[WEBP_FILTER_HORIZONTAL][diff1] = 1;
- bins[WEBP_FILTER_VERTICAL][diff2] = 1;
- bins[WEBP_FILTER_GRADIENT][diff3] = 1;
- mean = (3 * mean + p[i] + 2) >> 2;
- }
- }
- {
- WEBP_FILTER_TYPE filter, best_filter = WEBP_FILTER_NONE;
- int best_score = 0x7fffffff;
- for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) {
- int score = 0;
- for (i = 0; i < SMAX; ++i) {
- if (bins[filter][i] > 0) {
- score += i;
- }
- }
- if (score < best_score) {
- best_score = score;
- best_filter = filter;
- }
- }
- return best_filter;
- }
-}
-
-#undef SMAX
-#undef SDIFF
-
-//------------------------------------------------------------------------------
-
-const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
- NULL, // WEBP_FILTER_NONE
- HorizontalFilter, // WEBP_FILTER_HORIZONTAL
- VerticalFilter, // WEBP_FILTER_VERTICAL
- GradientFilter // WEBP_FILTER_GRADIENT
-};
-
-const WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
- NULL, // WEBP_FILTER_NONE
- HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
- VerticalUnfilter, // WEBP_FILTER_VERTICAL
- GradientUnfilter // WEBP_FILTER_GRADIENT
-};
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/filters.h b/src/third_party/libwebp/utils/filters.h
deleted file mode 100644
index 1f5fa16..0000000
--- a/src/third_party/libwebp/utils/filters.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Spatial prediction using various filters
-//
-// Author: Urvang (urvang@google.com)
-
-#ifndef WEBP_UTILS_FILTERS_H_
-#define WEBP_UTILS_FILTERS_H_
-
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// Filters.
-typedef enum {
- WEBP_FILTER_NONE = 0,
- WEBP_FILTER_HORIZONTAL,
- WEBP_FILTER_VERTICAL,
- WEBP_FILTER_GRADIENT,
- WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
- WEBP_FILTER_BEST,
- WEBP_FILTER_FAST
-} WEBP_FILTER_TYPE;
-
-typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
- int stride, uint8_t* out);
-typedef void (*WebPUnfilterFunc)(int width, int height, int stride,
- uint8_t* data);
-
-// Filter the given data using the given predictor.
-// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
-// in raster order.
-// 'stride' is number of bytes per scan line (with possible padding).
-// 'out' should be pre-allocated.
-extern const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
-
-// In-place reconstruct the original data from the given filtered data.
-extern const WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
-
-// Fast estimate of a potentially good filter.
-extern WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
- int width, int height, int stride);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_FILTERS_H_ */
diff --git a/src/third_party/libwebp/utils/huffman.c b/src/third_party/libwebp/utils/huffman.c
deleted file mode 100644
index f6b883f..0000000
--- a/src/third_party/libwebp/utils/huffman.c
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Utilities for building and looking up Huffman trees.
-//
-// Author: Urvang Joshi (urvang@google.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#endif
-
-#include "./huffman.h"
-#include "../utils/utils.h"
-#include "../webp/format_constants.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define NON_EXISTENT_SYMBOL (-1)
-
-static void TreeNodeInit(HuffmanTreeNode* const node) {
- node->children_ = -1; // means: 'unassigned so far'
-}
-
-static int NodeIsEmpty(const HuffmanTreeNode* const node) {
- return (node->children_ < 0);
-}
-
-static int IsFull(const HuffmanTree* const tree) {
- return (tree->num_nodes_ == tree->max_nodes_);
-}
-
-static void AssignChildren(HuffmanTree* const tree,
- HuffmanTreeNode* const node) {
- HuffmanTreeNode* const children = tree->root_ + tree->num_nodes_;
- node->children_ = (int)(children - node);
- SB_DCHECK(children - node == (int)(children - node));
- tree->num_nodes_ += 2;
- TreeNodeInit(children + 0);
- TreeNodeInit(children + 1);
-}
-
-static int TreeInit(HuffmanTree* const tree, int num_leaves) {
- SB_DCHECK(tree != NULL);
- if (num_leaves == 0) return 0;
- // We allocate maximum possible nodes in the tree at once.
- // Note that a Huffman tree is a full binary tree; and in a full binary tree
- // with L leaves, the total number of nodes N = 2 * L - 1.
- tree->max_nodes_ = 2 * num_leaves - 1;
- tree->root_ = (HuffmanTreeNode*)WebPSafeMalloc((uint64_t)tree->max_nodes_,
- sizeof(*tree->root_));
- if (tree->root_ == NULL) return 0;
- TreeNodeInit(tree->root_); // Initialize root.
- tree->num_nodes_ = 1;
- return 1;
-}
-
-void HuffmanTreeRelease(HuffmanTree* const tree) {
- if (tree != NULL) {
- SbMemoryDeallocate(tree->root_);
- tree->root_ = NULL;
- tree->max_nodes_ = 0;
- tree->num_nodes_ = 0;
- }
-}
-
-int HuffmanCodeLengthsToCodes(const int* const code_lengths,
- int code_lengths_size, int* const huff_codes) {
- int symbol;
- int code_len;
- int code_length_hist[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 };
- int curr_code;
- int next_codes[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 };
- int max_code_length = 0;
-
- SB_DCHECK(code_lengths != NULL);
- SB_DCHECK(code_lengths_size > 0);
- SB_DCHECK(huff_codes != NULL);
-
- // Calculate max code length.
- for (symbol = 0; symbol < code_lengths_size; ++symbol) {
- if (code_lengths[symbol] > max_code_length) {
- max_code_length = code_lengths[symbol];
- }
- }
- if (max_code_length > MAX_ALLOWED_CODE_LENGTH) return 0;
-
- // Calculate code length histogram.
- for (symbol = 0; symbol < code_lengths_size; ++symbol) {
- ++code_length_hist[code_lengths[symbol]];
- }
- code_length_hist[0] = 0;
-
- // Calculate the initial values of 'next_codes' for each code length.
- // next_codes[code_len] denotes the code to be assigned to the next symbol
- // of code length 'code_len'.
- curr_code = 0;
- next_codes[0] = -1; // Unused, as code length = 0 implies code doesn't exist.
- for (code_len = 1; code_len <= max_code_length; ++code_len) {
- curr_code = (curr_code + code_length_hist[code_len - 1]) << 1;
- next_codes[code_len] = curr_code;
- }
-
- // Get symbols.
- for (symbol = 0; symbol < code_lengths_size; ++symbol) {
- if (code_lengths[symbol] > 0) {
- huff_codes[symbol] = next_codes[code_lengths[symbol]]++;
- } else {
- huff_codes[symbol] = NON_EXISTENT_SYMBOL;
- }
- }
- return 1;
-}
-
-static int TreeAddSymbol(HuffmanTree* const tree,
- int symbol, int code, int code_length) {
- HuffmanTreeNode* node = tree->root_;
- const HuffmanTreeNode* const max_node = tree->root_ + tree->max_nodes_;
- while (code_length-- > 0) {
- if (node >= max_node) {
- return 0;
- }
- if (NodeIsEmpty(node)) {
- if (IsFull(tree)) return 0; // error: too many symbols.
- AssignChildren(tree, node);
- } else if (HuffmanTreeNodeIsLeaf(node)) {
- return 0; // leaf is already occupied.
- }
- node += node->children_ + ((code >> code_length) & 1);
- }
- if (NodeIsEmpty(node)) {
- node->children_ = 0; // turn newly created node into a leaf.
- } else if (!HuffmanTreeNodeIsLeaf(node)) {
- return 0; // trying to assign a symbol to already used code.
- }
- node->symbol_ = symbol; // Add symbol in this node.
- return 1;
-}
-
-int HuffmanTreeBuildImplicit(HuffmanTree* const tree,
- const int* const code_lengths,
- int code_lengths_size) {
- int symbol;
- int num_symbols = 0;
- int root_symbol = 0;
-
- SB_DCHECK(tree != NULL);
- SB_DCHECK(code_lengths != NULL);
-
- // Find out number of symbols and the root symbol.
- for (symbol = 0; symbol < code_lengths_size; ++symbol) {
- if (code_lengths[symbol] > 0) {
- // Note: code length = 0 indicates non-existent symbol.
- ++num_symbols;
- root_symbol = symbol;
- }
- }
-
- // Initialize the tree. Will fail for num_symbols = 0
- if (!TreeInit(tree, num_symbols)) return 0;
-
- // Build tree.
- if (num_symbols == 1) { // Trivial case.
- const int max_symbol = code_lengths_size;
- if (root_symbol < 0 || root_symbol >= max_symbol) {
- HuffmanTreeRelease(tree);
- return 0;
- }
- return TreeAddSymbol(tree, root_symbol, 0, 0);
- } else { // Normal case.
- int ok = 0;
-
- // Get Huffman codes from the code lengths.
- int* const codes =
- (int*)WebPSafeMalloc((uint64_t)code_lengths_size, sizeof(*codes));
- if (codes == NULL) goto End;
-
- if (!HuffmanCodeLengthsToCodes(code_lengths, code_lengths_size, codes)) {
- goto End;
- }
-
- // Add symbols one-by-one.
- for (symbol = 0; symbol < code_lengths_size; ++symbol) {
- if (code_lengths[symbol] > 0) {
- if (!TreeAddSymbol(tree, symbol, codes[symbol], code_lengths[symbol])) {
- goto End;
- }
- }
- }
- ok = 1;
- End:
- SbMemoryDeallocate(codes);
- ok = ok && IsFull(tree);
- if (!ok) HuffmanTreeRelease(tree);
- return ok;
- }
-}
-
-int HuffmanTreeBuildExplicit(HuffmanTree* const tree,
- const int* const code_lengths,
- const int* const codes,
- const int* const symbols, int max_symbol,
- int num_symbols) {
- int ok = 0;
- int i;
-
- SB_DCHECK(tree != NULL);
- SB_DCHECK(code_lengths != NULL);
- SB_DCHECK(codes != NULL);
- SB_DCHECK(symbols != NULL);
-
- // Initialize the tree. Will fail if num_symbols = 0.
- if (!TreeInit(tree, num_symbols)) return 0;
-
- // Add symbols one-by-one.
- for (i = 0; i < num_symbols; ++i) {
- if (codes[i] != NON_EXISTENT_SYMBOL) {
- if (symbols[i] < 0 || symbols[i] >= max_symbol) {
- goto End;
- }
- if (!TreeAddSymbol(tree, symbols[i], codes[i], code_lengths[i])) {
- goto End;
- }
- }
- }
- ok = 1;
- End:
- ok = ok && IsFull(tree);
- if (!ok) HuffmanTreeRelease(tree);
- return ok;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/huffman.h b/src/third_party/libwebp/utils/huffman.h
deleted file mode 100644
index 5d59b7d..0000000
--- a/src/third_party/libwebp/utils/huffman.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Utilities for building and looking up Huffman trees.
-//
-// Author: Urvang Joshi (urvang@google.com)
-
-#ifndef WEBP_UTILS_HUFFMAN_H_
-#define WEBP_UTILS_HUFFMAN_H_
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#else
-#include <assert.h>
-#endif
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// A node of a Huffman tree.
-typedef struct {
- int symbol_;
- int children_; // delta offset to both children (contiguous) or 0 if leaf.
-} HuffmanTreeNode;
-
-// Huffman Tree.
-typedef struct HuffmanTree HuffmanTree;
-struct HuffmanTree {
- HuffmanTreeNode* root_; // all the nodes, starting at root.
- int max_nodes_; // max number of nodes
- int num_nodes_; // number of currently occupied nodes
-};
-
-// Returns true if the given node is a leaf of the Huffman tree.
-static WEBP_INLINE int HuffmanTreeNodeIsLeaf(
- const HuffmanTreeNode* const node) {
- return (node->children_ == 0);
-}
-
-// Go down one level. Most critical function. 'right_child' must be 0 or 1.
-static WEBP_INLINE const HuffmanTreeNode* HuffmanTreeNextNode(
- const HuffmanTreeNode* node, int right_child) {
- return node + node->children_ + right_child;
-}
-
-// Releases the nodes of the Huffman tree.
-// Note: It does NOT free 'tree' itself.
-void HuffmanTreeRelease(HuffmanTree* const tree);
-
-// Builds Huffman tree assuming code lengths are implicitly in symbol order.
-// Returns false in case of error (invalid tree or memory error).
-int HuffmanTreeBuildImplicit(HuffmanTree* const tree,
- const int* const code_lengths,
- int code_lengths_size);
-
-// Build a Huffman tree with explicitly given lists of code lengths, codes
-// and symbols. Verifies that all symbols added are smaller than max_symbol.
-// Returns false in case of an invalid symbol, invalid tree or memory error.
-int HuffmanTreeBuildExplicit(HuffmanTree* const tree,
- const int* const code_lengths,
- const int* const codes,
- const int* const symbols, int max_symbol,
- int num_symbols);
-
-// Utility: converts Huffman code lengths to corresponding Huffman codes.
-// 'huff_codes' should be pre-allocated.
-// Returns false in case of error (memory allocation, invalid codes).
-int HuffmanCodeLengthsToCodes(const int* const code_lengths,
- int code_lengths_size, int* const huff_codes);
-
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif // WEBP_UTILS_HUFFMAN_H_
diff --git a/src/third_party/libwebp/utils/huffman_encode.c b/src/third_party/libwebp/utils/huffman_encode.c
deleted file mode 100644
index b66dd41..0000000
--- a/src/third_party/libwebp/utils/huffman_encode.c
+++ /dev/null
@@ -1,446 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Author: Jyrki Alakuijala (jyrki@google.com)
-//
-// Entropy encoding (Huffman) for webp lossless.
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#endif
-
-#include "./huffman_encode.h"
-#include "../utils/utils.h"
-#include "../webp/format_constants.h"
-
-// -----------------------------------------------------------------------------
-// Util function to optimize the symbol map for RLE coding
-
-// Heuristics for selecting the stride ranges to collapse.
-static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) {
- return abs(a - b) < 4;
-}
-
-// Change the population counts in a way that the consequent
-// Hufmann tree compression, especially its RLE-part, give smaller output.
-static int OptimizeHuffmanForRle(int length, int* const counts) {
- uint8_t* good_for_rle;
- // 1) Let's make the Huffman code more compatible with rle encoding.
- int i;
- for (; length >= 0; --length) {
- if (length == 0) {
- return 1; // All zeros.
- }
- if (counts[length - 1] != 0) {
- // Now counts[0..length - 1] does not have trailing zeros.
- break;
- }
- }
- // 2) Let's mark all population counts that already can be encoded
- // with an rle code.
- good_for_rle = (uint8_t*)SbMemoryCalloc(length, 1);
- if (good_for_rle == NULL) {
- return 0;
- }
- {
- // Let's not spoil any of the existing good rle codes.
- // Mark any seq of 0's that is longer as 5 as a good_for_rle.
- // Mark any seq of non-0's that is longer as 7 as a good_for_rle.
- int symbol = counts[0];
- int stride = 0;
- for (i = 0; i < length + 1; ++i) {
- if (i == length || counts[i] != symbol) {
- if ((symbol == 0 && stride >= 5) ||
- (symbol != 0 && stride >= 7)) {
- int k;
- for (k = 0; k < stride; ++k) {
- good_for_rle[i - k - 1] = 1;
- }
- }
- stride = 1;
- if (i != length) {
- symbol = counts[i];
- }
- } else {
- ++stride;
- }
- }
- }
- // 3) Let's replace those population counts that lead to more rle codes.
- {
- int stride = 0;
- int limit = counts[0];
- int sum = 0;
- for (i = 0; i < length + 1; ++i) {
- if (i == length || good_for_rle[i] ||
- (i != 0 && good_for_rle[i - 1]) ||
- !ValuesShouldBeCollapsedToStrideAverage(counts[i], limit)) {
- if (stride >= 4 || (stride >= 3 && sum == 0)) {
- int k;
- // The stride must end, collapse what we have, if we have enough (4).
- int count = (sum + stride / 2) / stride;
- if (count < 1) {
- count = 1;
- }
- if (sum == 0) {
- // Don't make an all zeros stride to be upgraded to ones.
- count = 0;
- }
- for (k = 0; k < stride; ++k) {
- // We don't want to change value at counts[i],
- // that is already belonging to the next stride. Thus - 1.
- counts[i - k - 1] = count;
- }
- }
- stride = 0;
- sum = 0;
- if (i < length - 3) {
- // All interesting strides have a count of at least 4,
- // at least when non-zeros.
- limit = (counts[i] + counts[i + 1] +
- counts[i + 2] + counts[i + 3] + 2) / 4;
- } else if (i < length) {
- limit = counts[i];
- } else {
- limit = 0;
- }
- }
- ++stride;
- if (i != length) {
- sum += counts[i];
- if (stride >= 4) {
- limit = (sum + stride / 2) / stride;
- }
- }
- }
- }
- SbMemoryDeallocate(good_for_rle);
- return 1;
-}
-
-typedef struct {
- int total_count_;
- int value_;
- int pool_index_left_;
- int pool_index_right_;
-} HuffmanTree;
-
-// A comparer function for two Huffman trees: sorts first by 'total count'
-// (more comes first), and then by 'value' (more comes first).
-static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) {
- const HuffmanTree* const t1 = (const HuffmanTree*)ptr1;
- const HuffmanTree* const t2 = (const HuffmanTree*)ptr2;
- if (t1->total_count_ > t2->total_count_) {
- return -1;
- } else if (t1->total_count_ < t2->total_count_) {
- return 1;
- } else {
- SB_DCHECK(t1->value_ != t2->value_);
- return (t1->value_ < t2->value_) ? -1 : 1;
- }
-}
-
-static void SetBitDepths(const HuffmanTree* const tree,
- const HuffmanTree* const pool,
- uint8_t* const bit_depths, int level) {
- if (tree->pool_index_left_ >= 0) {
- SetBitDepths(&pool[tree->pool_index_left_], pool, bit_depths, level + 1);
- SetBitDepths(&pool[tree->pool_index_right_], pool, bit_depths, level + 1);
- } else {
- bit_depths[tree->value_] = level;
- }
-}
-
-// Create an optimal Huffman tree.
-//
-// (data,length): population counts.
-// tree_limit: maximum bit depth (inclusive) of the codes.
-// bit_depths[]: how many bits are used for the symbol.
-//
-// Returns 0 when an error has occurred.
-//
-// The catch here is that the tree cannot be arbitrarily deep
-//
-// count_limit is the value that is to be faked as the minimum value
-// and this minimum value is raised until the tree matches the
-// maximum length requirement.
-//
-// This algorithm is not of excellent performance for very long data blocks,
-// especially when population counts are longer than 2**tree_limit, but
-// we are not planning to use this with extremely long blocks.
-//
-// See http://en.wikipedia.org/wiki/Huffman_coding
-static int GenerateOptimalTree(const int* const histogram, int histogram_size,
- int tree_depth_limit,
- uint8_t* const bit_depths) {
- int count_min;
- HuffmanTree* tree_pool;
- HuffmanTree* tree;
- int tree_size_orig = 0;
- int i;
-
- for (i = 0; i < histogram_size; ++i) {
- if (histogram[i] != 0) {
- ++tree_size_orig;
- }
- }
-
- if (tree_size_orig == 0) { // pretty optimal already!
- return 1;
- }
-
- // 3 * tree_size is enough to cover all the nodes representing a
- // population and all the inserted nodes combining two existing nodes.
- // The tree pool needs 2 * (tree_size_orig - 1) entities, and the
- // tree needs exactly tree_size_orig entities.
- tree = (HuffmanTree*)WebPSafeMalloc(3ULL * tree_size_orig, sizeof(*tree));
- if (tree == NULL) return 0;
- tree_pool = tree + tree_size_orig;
-
- // For block sizes with less than 64k symbols we never need to do a
- // second iteration of this loop.
- // If we actually start running inside this loop a lot, we would perhaps
- // be better off with the Katajainen algorithm.
- SB_DCHECK(tree_size_orig <= (1 << (tree_depth_limit - 1)));
- for (count_min = 1; ; count_min *= 2) {
- int tree_size = tree_size_orig;
- // We need to pack the Huffman tree in tree_depth_limit bits.
- // So, we try by faking histogram entries to be at least 'count_min'.
- int idx = 0;
- int j;
- for (j = 0; j < histogram_size; ++j) {
- if (histogram[j] != 0) {
- const int count =
- (histogram[j] < count_min) ? count_min : histogram[j];
- tree[idx].total_count_ = count;
- tree[idx].value_ = j;
- tree[idx].pool_index_left_ = -1;
- tree[idx].pool_index_right_ = -1;
- ++idx;
- }
- }
-
- // Build the Huffman tree.
- SbSystemSort(tree, tree_size, sizeof(*tree), CompareHuffmanTrees);
-
- if (tree_size > 1) { // Normal case.
- int tree_pool_size = 0;
- while (tree_size > 1) { // Finish when we have only one root.
- int count;
- tree_pool[tree_pool_size++] = tree[tree_size - 1];
- tree_pool[tree_pool_size++] = tree[tree_size - 2];
- count = tree_pool[tree_pool_size - 1].total_count_ +
- tree_pool[tree_pool_size - 2].total_count_;
- tree_size -= 2;
- {
- // Search for the insertion point.
- int k;
- for (k = 0; k < tree_size; ++k) {
- if (tree[k].total_count_ <= count) {
- break;
- }
- }
- SbMemoryMove(tree + (k + 1), tree + k, (tree_size - k) * sizeof(*tree));
- tree[k].total_count_ = count;
- tree[k].value_ = -1;
-
- tree[k].pool_index_left_ = tree_pool_size - 1;
- tree[k].pool_index_right_ = tree_pool_size - 2;
- tree_size = tree_size + 1;
- }
- }
- SetBitDepths(&tree[0], tree_pool, bit_depths, 0);
- } else if (tree_size == 1) { // Trivial case: only one element.
- bit_depths[tree[0].value_] = 1;
- }
-
- {
- // Test if this Huffman tree satisfies our 'tree_depth_limit' criteria.
- int max_depth = bit_depths[0];
- for (j = 1; j < histogram_size; ++j) {
- if (max_depth < bit_depths[j]) {
- max_depth = bit_depths[j];
- }
- }
- if (max_depth <= tree_depth_limit) {
- break;
- }
- }
- }
- SbMemoryDeallocate(tree);
- return 1;
-}
-
-// -----------------------------------------------------------------------------
-// Coding of the Huffman tree values
-
-static HuffmanTreeToken* CodeRepeatedValues(int repetitions,
- HuffmanTreeToken* tokens,
- int value, int prev_value) {
- SB_DCHECK(value <= MAX_ALLOWED_CODE_LENGTH);
- if (value != prev_value) {
- tokens->code = value;
- tokens->extra_bits = 0;
- ++tokens;
- --repetitions;
- }
- while (repetitions >= 1) {
- if (repetitions < 3) {
- int i;
- for (i = 0; i < repetitions; ++i) {
- tokens->code = value;
- tokens->extra_bits = 0;
- ++tokens;
- }
- break;
- } else if (repetitions < 7) {
- tokens->code = 16;
- tokens->extra_bits = repetitions - 3;
- ++tokens;
- break;
- } else {
- tokens->code = 16;
- tokens->extra_bits = 3;
- ++tokens;
- repetitions -= 6;
- }
- }
- return tokens;
-}
-
-static HuffmanTreeToken* CodeRepeatedZeros(int repetitions,
- HuffmanTreeToken* tokens) {
- while (repetitions >= 1) {
- if (repetitions < 3) {
- int i;
- for (i = 0; i < repetitions; ++i) {
- tokens->code = 0; // 0-value
- tokens->extra_bits = 0;
- ++tokens;
- }
- break;
- } else if (repetitions < 11) {
- tokens->code = 17;
- tokens->extra_bits = repetitions - 3;
- ++tokens;
- break;
- } else if (repetitions < 139) {
- tokens->code = 18;
- tokens->extra_bits = repetitions - 11;
- ++tokens;
- break;
- } else {
- tokens->code = 18;
- tokens->extra_bits = 0x7f; // 138 repeated 0s
- ++tokens;
- repetitions -= 138;
- }
- }
- return tokens;
-}
-
-int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
- HuffmanTreeToken* tokens, int max_tokens) {
- HuffmanTreeToken* const starting_token = tokens;
- HuffmanTreeToken* const ending_token = tokens + max_tokens;
- const int depth_size = tree->num_symbols;
- int prev_value = 8; // 8 is the initial value for rle.
- int i = 0;
- SB_DCHECK(tokens != NULL);
- while (i < depth_size) {
- const int value = tree->code_lengths[i];
- int k = i + 1;
- int runs;
- while (k < depth_size && tree->code_lengths[k] == value) ++k;
- runs = k - i;
- if (value == 0) {
- tokens = CodeRepeatedZeros(runs, tokens);
- } else {
- tokens = CodeRepeatedValues(runs, tokens, value, prev_value);
- prev_value = value;
- }
- i += runs;
- SB_DCHECK(tokens <= ending_token);
- }
- (void)ending_token; // suppress 'unused variable' warning
- return (int)(tokens - starting_token);
-}
-
-// -----------------------------------------------------------------------------
-
-// Pre-reversed 4-bit values.
-static const uint8_t kReversedBits[16] = {
- 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
- 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
-};
-
-static uint32_t ReverseBits(int num_bits, uint32_t bits) {
- uint32_t retval = 0;
- int i = 0;
- while (i < num_bits) {
- i += 4;
- retval |= kReversedBits[bits & 0xf] << (MAX_ALLOWED_CODE_LENGTH + 1 - i);
- bits >>= 4;
- }
- retval >>= (MAX_ALLOWED_CODE_LENGTH + 1 - num_bits);
- return retval;
-}
-
-// Get the actual bit values for a tree of bit depths.
-static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) {
- // 0 bit-depth means that the symbol does not exist.
- int i;
- int len;
- uint32_t next_code[MAX_ALLOWED_CODE_LENGTH + 1];
- int depth_count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 };
-
- SB_DCHECK(tree != NULL);
- len = tree->num_symbols;
- for (i = 0; i < len; ++i) {
- const int code_length = tree->code_lengths[i];
- SB_DCHECK(code_length <= MAX_ALLOWED_CODE_LENGTH);
- ++depth_count[code_length];
- }
- depth_count[0] = 0; // ignore unused symbol
- next_code[0] = 0;
- {
- uint32_t code = 0;
- for (i = 1; i <= MAX_ALLOWED_CODE_LENGTH; ++i) {
- code = (code + depth_count[i - 1]) << 1;
- next_code[i] = code;
- }
- }
- for (i = 0; i < len; ++i) {
- const int code_length = tree->code_lengths[i];
- tree->codes[i] = ReverseBits(code_length, next_code[code_length]++);
- }
-}
-
-// -----------------------------------------------------------------------------
-// Main entry point
-
-int VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
- HuffmanTreeCode* const tree) {
- const int num_symbols = tree->num_symbols;
- if (!OptimizeHuffmanForRle(num_symbols, histogram)) {
- return 0;
- }
- if (!GenerateOptimalTree(histogram, num_symbols,
- tree_depth_limit, tree->code_lengths)) {
- return 0;
- }
- // Create the actual bit codes for the bit lengths.
- ConvertBitDepthsToSymbols(tree);
- return 1;
-}
diff --git a/src/third_party/libwebp/utils/huffman_encode.h b/src/third_party/libwebp/utils/huffman_encode.h
deleted file mode 100644
index 0b81f47..0000000
--- a/src/third_party/libwebp/utils/huffman_encode.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Author: Jyrki Alakuijala (jyrki@google.com)
-//
-// Entropy encoding (Huffman) for webp lossless
-
-#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_
-#define WEBP_UTILS_HUFFMAN_ENCODE_H_
-
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// Struct for holding the tree header in coded form.
-typedef struct {
- uint8_t code; // value (0..15) or escape code (16,17,18)
- uint8_t extra_bits; // extra bits for escape codes
-} HuffmanTreeToken;
-
-// Struct to represent the tree codes (depth and bits array).
-typedef struct {
- int num_symbols; // Number of symbols.
- uint8_t* code_lengths; // Code lengths of the symbols.
- uint16_t* codes; // Symbol Codes.
-} HuffmanTreeCode;
-
-// Turn the Huffman tree into a token sequence.
-// Returns the number of tokens used.
-int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
- HuffmanTreeToken* tokens, int max_tokens);
-
-// Create an optimized tree, and tokenize it.
-int VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
- HuffmanTreeCode* const tree);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-}
-#endif
-
-#endif // WEBP_UTILS_HUFFMAN_ENCODE_H_
diff --git a/src/third_party/libwebp/utils/quant_levels.c b/src/third_party/libwebp/utils/quant_levels.c
deleted file mode 100644
index 2fb610b..0000000
--- a/src/third_party/libwebp/utils/quant_levels.c
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Quantize levels for specified number of quantization-levels ([2, 256]).
-// Min and max values are preserved (usual 0 and 255 for alpha plane).
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#else
-#include <assert.h>
-#endif
-
-#include "./quant_levels.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define NUM_SYMBOLS 256
-
-#define MAX_ITER 6 // Maximum number of convergence steps.
-#define ERROR_THRESHOLD 1e-4 // MSE stopping criterion.
-
-// -----------------------------------------------------------------------------
-// Quantize levels.
-
-int QuantizeLevels(uint8_t* const data, int width, int height,
- int num_levels, uint64_t* const sse) {
- int freq[NUM_SYMBOLS] = { 0 };
- int q_level[NUM_SYMBOLS] = { 0 };
- double inv_q_level[NUM_SYMBOLS] = { 0 };
- int min_s = 255, max_s = 0;
- const size_t data_size = height * width;
- int i, num_levels_in, iter;
- double last_err = 1.e38, err = 0.;
- const double err_threshold = ERROR_THRESHOLD * data_size;
-
- if (data == NULL) {
- return 0;
- }
-
- if (width <= 0 || height <= 0) {
- return 0;
- }
-
- if (num_levels < 2 || num_levels > 256) {
- return 0;
- }
-
- {
- size_t n;
- num_levels_in = 0;
- for (n = 0; n < data_size; ++n) {
- num_levels_in += (freq[data[n]] == 0);
- if (min_s > data[n]) min_s = data[n];
- if (max_s < data[n]) max_s = data[n];
- ++freq[data[n]];
- }
- }
-
- if (num_levels_in <= num_levels) goto End; // nothing to do!
-
- // Start with uniformly spread centroids.
- for (i = 0; i < num_levels; ++i) {
- inv_q_level[i] = min_s + (double)(max_s - min_s) * i / (num_levels - 1);
- }
-
- // Fixed values. Won't be changed.
- q_level[min_s] = 0;
- q_level[max_s] = num_levels - 1;
- SB_DCHECK(inv_q_level[0] == min_s);
- SB_DCHECK(inv_q_level[num_levels - 1] == max_s);
-
- // k-Means iterations.
- for (iter = 0; iter < MAX_ITER; ++iter) {
- double q_sum[NUM_SYMBOLS] = { 0 };
- double q_count[NUM_SYMBOLS] = { 0 };
- int s, slot = 0;
-
- // Assign classes to representatives.
- for (s = min_s; s <= max_s; ++s) {
- // Keep track of the nearest neighbour 'slot'
- while (slot < num_levels - 1 &&
- 2 * s > inv_q_level[slot] + inv_q_level[slot + 1]) {
- ++slot;
- }
- if (freq[s] > 0) {
- q_sum[slot] += s * freq[s];
- q_count[slot] += freq[s];
- }
- q_level[s] = slot;
- }
-
- // Assign new representatives to classes.
- if (num_levels > 2) {
- for (slot = 1; slot < num_levels - 1; ++slot) {
- const double count = q_count[slot];
- if (count > 0.) {
- inv_q_level[slot] = q_sum[slot] / count;
- }
- }
- }
-
- // Compute convergence error.
- err = 0.;
- for (s = min_s; s <= max_s; ++s) {
- const double error = s - inv_q_level[q_level[s]];
- err += freq[s] * error * error;
- }
-
- // Check for convergence: we stop as soon as the error is no
- // longer improving.
- if (last_err - err < err_threshold) break;
- last_err = err;
- }
-
- // Remap the alpha plane to quantized values.
- {
- // double->int rounding operation can be costly, so we do it
- // once for all before remapping. We also perform the data[] -> slot
- // mapping, while at it (avoid one indirection in the final loop).
- uint8_t map[NUM_SYMBOLS];
- int s;
- size_t n;
- for (s = min_s; s <= max_s; ++s) {
- const int slot = q_level[s];
- map[s] = (uint8_t)(inv_q_level[slot] + .5);
- }
- // Final pass.
- for (n = 0; n < data_size; ++n) {
- data[n] = map[data[n]];
- }
- }
- End:
- // Store sum of squared error if needed.
- if (sse != NULL) *sse = (uint64_t)err;
-
- return 1;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/quant_levels.h b/src/third_party/libwebp/utils/quant_levels.h
deleted file mode 100644
index 2d90828..0000000
--- a/src/third_party/libwebp/utils/quant_levels.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Alpha plane quantization utility
-//
-// Author: Vikas Arora (vikasa@google.com)
-
-#ifndef WEBP_UTILS_QUANT_LEVELS_H_
-#define WEBP_UTILS_QUANT_LEVELS_H_
-
-#include <stdlib.h>
-
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// Replace the input 'data' of size 'width'x'height' with 'num-levels'
-// quantized values. If not NULL, 'sse' will contain the sum of squared error.
-// Valid range for 'num_levels' is [2, 256].
-// Returns false in case of error (data is NULL, or parameters are invalid).
-int QuantizeLevels(uint8_t* const data, int width, int height, int num_levels,
- uint64_t* const sse);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_QUANT_LEVELS_H_ */
diff --git a/src/third_party/libwebp/utils/quant_levels_dec.c b/src/third_party/libwebp/utils/quant_levels_dec.c
deleted file mode 100644
index d93594b..0000000
--- a/src/third_party/libwebp/utils/quant_levels_dec.c
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2013 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// TODO(skal): implement gradient smoothing.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include "./quant_levels_dec.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-int DequantizeLevels(uint8_t* const data, int width, int height) {
- if (data == NULL || width <= 0 || height <= 0) return 0;
- (void)data;
- (void)width;
- (void)height;
- return 1;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/quant_levels_dec.h b/src/third_party/libwebp/utils/quant_levels_dec.h
deleted file mode 100644
index 5891067..0000000
--- a/src/third_party/libwebp/utils/quant_levels_dec.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2013 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Alpha plane de-quantization utility
-//
-// Author: Vikas Arora (vikasa@google.com)
-
-#ifndef WEBP_UTILS_QUANT_LEVELS_DEC_H_
-#define WEBP_UTILS_QUANT_LEVELS_DEC_H_
-
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// Apply post-processing to input 'data' of size 'width'x'height' assuming
-// that the source was quantized to a reduced number of levels.
-// Returns false in case of error (data is NULL, invalid parameters, ...).
-int DequantizeLevels(uint8_t* const data, int width, int height);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_QUANT_LEVELS_DEC_H_ */
diff --git a/src/third_party/libwebp/utils/rescaler.c b/src/third_party/libwebp/utils/rescaler.c
deleted file mode 100644
index e5ddc29..0000000
--- a/src/third_party/libwebp/utils/rescaler.c
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Rescaling functions
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#include <assert.h>
-#include <stdlib.h>
-#include "./rescaler.h"
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define RFIX 30
-#define MULT_FIX(x, y) (((int64_t)(x) * (y) + (1 << (RFIX - 1))) >> RFIX)
-
-void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
- uint8_t* const dst, int dst_width, int dst_height,
- int dst_stride, int num_channels, int x_add, int x_sub,
- int y_add, int y_sub, int32_t* const work) {
- wrk->x_expand = (src_width < dst_width);
- wrk->src_width = src_width;
- wrk->src_height = src_height;
- wrk->dst_width = dst_width;
- wrk->dst_height = dst_height;
- wrk->dst = dst;
- wrk->dst_stride = dst_stride;
- wrk->num_channels = num_channels;
- // for 'x_expand', we use bilinear interpolation
- wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add - x_sub;
- wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub;
- wrk->y_accum = y_add;
- wrk->y_add = y_add;
- wrk->y_sub = y_sub;
- wrk->fx_scale = (1 << RFIX) / x_sub;
- wrk->fy_scale = (1 << RFIX) / y_sub;
- wrk->fxy_scale = wrk->x_expand ?
- ((int64_t)dst_height << RFIX) / (x_sub * src_height) :
- ((int64_t)dst_height << RFIX) / (x_add * src_height);
- wrk->irow = work;
- wrk->frow = work + num_channels * dst_width;
-}
-
-void WebPRescalerImportRow(WebPRescaler* const wrk,
- const uint8_t* const src, int channel) {
- const int x_stride = wrk->num_channels;
- const int x_out_max = wrk->dst_width * wrk->num_channels;
- int x_in = channel;
- int x_out;
- int accum = 0;
- if (!wrk->x_expand) {
- int sum = 0;
- for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
- accum += wrk->x_add;
- for (; accum > 0; accum -= wrk->x_sub) {
- sum += src[x_in];
- x_in += x_stride;
- }
- { // Emit next horizontal pixel.
- const int32_t base = src[x_in];
- const int32_t frac = base * (-accum);
- x_in += x_stride;
- wrk->frow[x_out] = (sum + base) * wrk->x_sub - frac;
- // fresh fractional start for next pixel
- sum = (int)MULT_FIX(frac, wrk->fx_scale);
- }
- }
- } else { // simple bilinear interpolation
- int left = src[channel], right = src[channel];
- for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
- if (accum < 0) {
- left = right;
- x_in += x_stride;
- right = src[x_in];
- accum += wrk->x_add;
- }
- wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
- accum -= wrk->x_sub;
- }
- }
- // Accumulate the new row's contribution
- for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
- wrk->irow[x_out] += wrk->frow[x_out];
- }
-}
-
-uint8_t* WebPRescalerExportRow(WebPRescaler* const wrk) {
- if (wrk->y_accum <= 0) {
- int x_out;
- uint8_t* const dst = wrk->dst;
- int32_t* const irow = wrk->irow;
- const int32_t* const frow = wrk->frow;
- const int yscale = wrk->fy_scale * (-wrk->y_accum);
- const int x_out_max = wrk->dst_width * wrk->num_channels;
-
- for (x_out = 0; x_out < x_out_max; ++x_out) {
- const int frac = (int)MULT_FIX(frow[x_out], yscale);
- const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
- dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
- irow[x_out] = frac; // new fractional start
- }
- wrk->y_accum += wrk->y_add;
- wrk->dst += wrk->dst_stride;
- return dst;
- } else {
- return NULL;
- }
-}
-
-#undef MULT_FIX
-#undef RFIX
-
-//------------------------------------------------------------------------------
-// all-in-one calls
-
-int WebPRescalerImport(WebPRescaler* const wrk, int num_lines,
- const uint8_t* src, int src_stride) {
- int total_imported = 0;
- while (total_imported < num_lines && wrk->y_accum > 0) {
- int channel;
- for (channel = 0; channel < wrk->num_channels; ++channel) {
- WebPRescalerImportRow(wrk, src, channel);
- }
- src += src_stride;
- ++total_imported;
- wrk->y_accum -= wrk->y_sub;
- }
- return total_imported;
-}
-
-int WebPRescalerExport(WebPRescaler* const rescaler) {
- int total_exported = 0;
- while (WebPRescalerHasPendingOutput(rescaler)) {
- WebPRescalerExportRow(rescaler);
- ++total_exported;
- }
- return total_exported;
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/rescaler.h b/src/third_party/libwebp/utils/rescaler.h
deleted file mode 100644
index aedce46..0000000
--- a/src/third_party/libwebp/utils/rescaler.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Rescaling functions
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_UTILS_RESCALER_H_
-#define WEBP_UTILS_RESCALER_H_
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#include "../webp/types.h"
-
-// Structure used for on-the-fly rescaling
-typedef struct {
- int x_expand; // true if we're expanding in the x direction
- int num_channels; // bytes to jump between pixels
- int fy_scale, fx_scale; // fixed-point scaling factor
- int64_t fxy_scale; // ''
- // we need hpel-precise add/sub increments, for the downsampled U/V planes.
- int y_accum; // vertical accumulator
- int y_add, y_sub; // vertical increments (add ~= src, sub ~= dst)
- int x_add, x_sub; // horizontal increments (add ~= src, sub ~= dst)
- int src_width, src_height; // source dimensions
- int dst_width, dst_height; // destination dimensions
- uint8_t* dst;
- int dst_stride;
- int32_t* irow, *frow; // work buffer
-} WebPRescaler;
-
-// Initialize a rescaler given scratch area 'work' and dimensions of src & dst.
-void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
- uint8_t* const dst,
- int dst_width, int dst_height, int dst_stride,
- int num_channels,
- int x_add, int x_sub,
- int y_add, int y_sub,
- int32_t* const work);
-
-// Import a row of data and save its contribution in the rescaler.
-// 'channel' denotes the channel number to be imported.
-void WebPRescalerImportRow(WebPRescaler* const rescaler,
- const uint8_t* const src, int channel);
-
-// Import multiple rows over all channels, until at least one row is ready to
-// be exported. Returns the actual number of lines that were imported.
-int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows,
- const uint8_t* src, int src_stride);
-
-// Return true if there is pending output rows ready.
-static WEBP_INLINE
-int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) {
- return (rescaler->y_accum <= 0);
-}
-
-// Export one row from rescaler. Returns the pointer where output was written,
-// or NULL if no row was pending.
-uint8_t* WebPRescalerExportRow(WebPRescaler* const wrk);
-
-// Export as many rows as possible. Return the numbers of rows written.
-int WebPRescalerExport(WebPRescaler* const wrk);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_RESCALER_H_ */
diff --git a/src/third_party/libwebp/utils/thread.c b/src/third_party/libwebp/utils/thread.c
deleted file mode 100644
index 55403a8..0000000
--- a/src/third_party/libwebp/utils/thread.c
+++ /dev/null
@@ -1,250 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Multi-threaded worker
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <string.h> // for SbMemorySet()
-#endif
-#include "./thread.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#ifdef WEBP_USE_THREAD
-
-#if defined(_WIN32)
-
-//------------------------------------------------------------------------------
-// simplistic pthread emulation layer
-
-#include <process.h>
-
-// _beginthreadex requires __stdcall
-#define THREADFN unsigned int __stdcall
-#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val)
-
-static int pthread_create(pthread_t* const thread, const void* attr,
- unsigned int (__stdcall *start)(void*), void* arg) {
- (void)attr;
- *thread = (pthread_t)_beginthreadex(NULL, /* void *security */
- 0, /* unsigned stack_size */
- start,
- arg,
- 0, /* unsigned initflag */
- NULL); /* unsigned *thrdaddr */
- if (*thread == NULL) return 1;
- SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL);
- return 0;
-}
-
-static int pthread_join(pthread_t thread, void** value_ptr) {
- (void)value_ptr;
- return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 ||
- CloseHandle(thread) == 0);
-}
-
-// Mutex
-static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) {
- (void)mutexattr;
- InitializeCriticalSection(mutex);
- return 0;
-}
-
-static int pthread_mutex_lock(pthread_mutex_t* const mutex) {
- EnterCriticalSection(mutex);
- return 0;
-}
-
-static int pthread_mutex_unlock(pthread_mutex_t* const mutex) {
- LeaveCriticalSection(mutex);
- return 0;
-}
-
-static int pthread_mutex_destroy(pthread_mutex_t* const mutex) {
- DeleteCriticalSection(mutex);
- return 0;
-}
-
-// Condition
-static int pthread_cond_destroy(pthread_cond_t* const condition) {
- int ok = 1;
- ok &= (CloseHandle(condition->waiting_sem_) != 0);
- ok &= (CloseHandle(condition->received_sem_) != 0);
- ok &= (CloseHandle(condition->signal_event_) != 0);
- return !ok;
-}
-
-static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) {
- (void)cond_attr;
- condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
- condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
- condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (condition->waiting_sem_ == NULL ||
- condition->received_sem_ == NULL ||
- condition->signal_event_ == NULL) {
- pthread_cond_destroy(condition);
- return 1;
- }
- return 0;
-}
-
-static int pthread_cond_signal(pthread_cond_t* const condition) {
- int ok = 1;
- if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) {
- // a thread is waiting in pthread_cond_wait: allow it to be notified
- ok = SetEvent(condition->signal_event_);
- // wait until the event is consumed so the signaler cannot consume
- // the event via its own pthread_cond_wait.
- ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) !=
- WAIT_OBJECT_0);
- }
- return !ok;
-}
-
-static int pthread_cond_wait(pthread_cond_t* const condition,
- pthread_mutex_t* const mutex) {
- int ok;
- // note that there is a consumer available so the signal isn't dropped in
- // pthread_cond_signal
- if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL))
- return 1;
- // now unlock the mutex so pthread_cond_signal may be issued
- pthread_mutex_unlock(mutex);
- ok = (WaitForSingleObject(condition->signal_event_, INFINITE) ==
- WAIT_OBJECT_0);
- ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL);
- pthread_mutex_lock(mutex);
- return !ok;
-}
-
-#else // _WIN32
-# define THREADFN void*
-# define THREAD_RETURN(val) val
-#endif
-
-//------------------------------------------------------------------------------
-
-static THREADFN WebPWorkerThreadLoop(void *ptr) { // thread loop
- WebPWorker* const worker = (WebPWorker*)ptr;
- int done = 0;
- while (!done) {
- pthread_mutex_lock(&worker->mutex_);
- while (worker->status_ == OK) { // wait in idling mode
- pthread_cond_wait(&worker->condition_, &worker->mutex_);
- }
- if (worker->status_ == WORK) {
- if (worker->hook) {
- worker->had_error |= !worker->hook(worker->data1, worker->data2);
- }
- worker->status_ = OK;
- } else if (worker->status_ == NOT_OK) { // finish the worker
- done = 1;
- }
- // signal to the main thread that we're done (for Sync())
- pthread_cond_signal(&worker->condition_);
- pthread_mutex_unlock(&worker->mutex_);
- }
- return THREAD_RETURN(NULL); // Thread is finished
-}
-
-// main thread state control
-static void WebPWorkerChangeState(WebPWorker* const worker,
- WebPWorkerStatus new_status) {
- // no-op when attempting to change state on a thread that didn't come up
- if (worker->status_ < OK) return;
-
- pthread_mutex_lock(&worker->mutex_);
- // wait for the worker to finish
- while (worker->status_ != OK) {
- pthread_cond_wait(&worker->condition_, &worker->mutex_);
- }
- // assign new status and release the working thread if needed
- if (new_status != OK) {
- worker->status_ = new_status;
- pthread_cond_signal(&worker->condition_);
- }
- pthread_mutex_unlock(&worker->mutex_);
-}
-
-#endif
-
-//------------------------------------------------------------------------------
-
-void WebPWorkerInit(WebPWorker* const worker) {
- SbMemorySet(worker, 0, sizeof(*worker));
- worker->status_ = NOT_OK;
-}
-
-int WebPWorkerSync(WebPWorker* const worker) {
-#ifdef WEBP_USE_THREAD
- WebPWorkerChangeState(worker, OK);
-#endif
- SB_DCHECK(worker->status_ <= OK);
- return !worker->had_error;
-}
-
-int WebPWorkerReset(WebPWorker* const worker) {
- int ok = 1;
- worker->had_error = 0;
- if (worker->status_ < OK) {
-#ifdef WEBP_USE_THREAD
- if (pthread_mutex_init(&worker->mutex_, NULL) ||
- pthread_cond_init(&worker->condition_, NULL)) {
- return 0;
- }
- pthread_mutex_lock(&worker->mutex_);
- ok = !pthread_create(&worker->thread_, NULL, WebPWorkerThreadLoop, worker);
- if (ok) worker->status_ = OK;
- pthread_mutex_unlock(&worker->mutex_);
-#else
- worker->status_ = OK;
-#endif
- } else if (worker->status_ > OK) {
- ok = WebPWorkerSync(worker);
- }
- SB_DCHECK(!ok || (worker->status_ == OK));
- return ok;
-}
-
-void WebPWorkerLaunch(WebPWorker* const worker) {
-#ifdef WEBP_USE_THREAD
- WebPWorkerChangeState(worker, WORK);
-#else
- if (worker->hook)
- worker->had_error |= !worker->hook(worker->data1, worker->data2);
-#endif
-}
-
-void WebPWorkerEnd(WebPWorker* const worker) {
- if (worker->status_ >= OK) {
-#ifdef WEBP_USE_THREAD
- WebPWorkerChangeState(worker, NOT_OK);
- pthread_join(worker->thread_, NULL);
- pthread_mutex_destroy(&worker->mutex_);
- pthread_cond_destroy(&worker->condition_);
-#else
- worker->status_ = NOT_OK;
-#endif
- }
- SB_DCHECK(worker->status_ == NOT_OK);
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/thread.h b/src/third_party/libwebp/utils/thread.h
deleted file mode 100644
index 13a61a4..0000000
--- a/src/third_party/libwebp/utils/thread.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Multi-threaded worker
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_UTILS_THREAD_H_
-#define WEBP_UTILS_THREAD_H_
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#if WEBP_USE_THREAD
-
-#if defined(_WIN32)
-
-#include <windows.h>
-typedef HANDLE pthread_t;
-typedef CRITICAL_SECTION pthread_mutex_t;
-typedef struct {
- HANDLE waiting_sem_;
- HANDLE received_sem_;
- HANDLE signal_event_;
-} pthread_cond_t;
-
-#else
-
-#include <pthread.h>
-
-#endif /* _WIN32 */
-#endif /* WEBP_USE_THREAD */
-
-// State of the worker thread object
-typedef enum {
- NOT_OK = 0, // object is unusable
- OK, // ready to work
- WORK // busy finishing the current task
-} WebPWorkerStatus;
-
-// Function to be called by the worker thread. Takes two opaque pointers as
-// arguments (data1 and data2), and should return false in case of error.
-typedef int (*WebPWorkerHook)(void*, void*);
-
-// Synchronize object used to launch job in the worker thread
-typedef struct {
-#if WEBP_USE_THREAD
- pthread_mutex_t mutex_;
- pthread_cond_t condition_;
- pthread_t thread_;
-#endif
- WebPWorkerStatus status_;
- WebPWorkerHook hook; // hook to call
- void* data1; // first argument passed to 'hook'
- void* data2; // second argument passed to 'hook'
- int had_error; // return value of the last call to 'hook'
-} WebPWorker;
-
-// Must be called first, before any other method.
-void WebPWorkerInit(WebPWorker* const worker);
-// Must be called to initialize the object and spawn the thread. Re-entrant.
-// Will potentially launch the thread. Returns false in case of error.
-int WebPWorkerReset(WebPWorker* const worker);
-// Makes sure the previous work is finished. Returns true if worker->had_error
-// was not set and no error condition was triggered by the working thread.
-int WebPWorkerSync(WebPWorker* const worker);
-// Triggers the thread to call hook() with data1 and data2 argument. These
-// hook/data1/data2 can be changed at any time before calling this function,
-// but not be changed afterward until the next call to WebPWorkerSync().
-void WebPWorkerLaunch(WebPWorker* const worker);
-// Kill the thread and terminate the object. To use the object again, one
-// must call WebPWorkerReset() again.
-void WebPWorkerEnd(WebPWorker* const worker);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_THREAD_H_ */
diff --git a/src/third_party/libwebp/utils/utils.c b/src/third_party/libwebp/utils/utils.c
deleted file mode 100644
index 69697c3..0000000
--- a/src/third_party/libwebp/utils/utils.c
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Misc. common utility functions
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h>
-#endif
-#include "./utils.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Checked memory allocation
-
-// Returns 0 in case of overflow of nmemb * size.
-static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
- const uint64_t total_size = nmemb * size;
- if (nmemb == 0) return 1;
- if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
- if (total_size != (size_t)total_size) return 0;
- return 1;
-}
-
-void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
- if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
- SB_DCHECK(nmemb * size > 0);
- return SbMemoryAllocate((size_t)(nmemb * size));
-}
-
-void* WebPSafeCalloc(uint64_t nmemb, size_t size) {
- if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
- SB_DCHECK(nmemb * size > 0);
- return SbMemoryCalloc((size_t)nmemb, size);
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
diff --git a/src/third_party/libwebp/utils/utils.h b/src/third_party/libwebp/utils/utils.h
deleted file mode 100644
index e65c339..0000000
--- a/src/third_party/libwebp/utils/utils.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Misc. common utility functions
-//
-// Authors: Skal (pascal.massimino@gmail.com)
-// Urvang (urvang@google.com)
-
-#ifndef WEBP_UTILS_UTILS_H_
-#define WEBP_UTILS_UTILS_H_
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#else
-#include <assert.h>
-#endif
-
-#include "../webp/types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-//------------------------------------------------------------------------------
-// Memory allocation
-
-// This is the maximum memory amount that libwebp will ever try to allocate.
-#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 40)
-
-// size-checking safe malloc/calloc: verify that the requested size is not too
-// large, or return NULL. You don't need to call these for constructs like
-// malloc(sizeof(foo)), but only if there's picture-dependent size involved
-// somewhere (like: malloc(num_pixels * sizeof(*something))). That's why this
-// safe malloc() borrows the signature from malloc(), pointing at the dangerous
-// underlying multiply involved.
-void* WebPSafeMalloc(uint64_t nmemb, size_t size);
-// Note that WebPSafeCalloc() expects the second argument type to be 'size_t'
-// in order to favor the "SbMemoryCalloc(num_foo, sizeof(foo))" pattern.
-void* WebPSafeCalloc(uint64_t nmemb, size_t size);
-
-//------------------------------------------------------------------------------
-// Reading/writing data.
-
-// Read 16, 24 or 32 bits stored in little-endian order.
-static WEBP_INLINE int GetLE16(const uint8_t* const data) {
- return (int)(data[0] << 0) | (data[1] << 8);
-}
-
-static WEBP_INLINE int GetLE24(const uint8_t* const data) {
- return GetLE16(data) | (data[2] << 16);
-}
-
-static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) {
- return (uint32_t)GetLE16(data) | (GetLE16(data + 2) << 16);
-}
-
-// Store 16, 24 or 32 bits in little-endian order.
-static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
- SB_DCHECK(val < (1 << 16));
- data[0] = (val >> 0);
- data[1] = (val >> 8);
-}
-
-static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
- SB_DCHECK(val < (1 << 24));
- PutLE16(data, val & 0xffff);
- data[2] = (val >> 16);
-}
-
-static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
- PutLE16(data, (int)(val & 0xffff));
- PutLE16(data + 2, (int)(val >> 16));
-}
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_UTILS_UTILS_H_ */
diff --git a/src/third_party/libwebp/webp/decode.h b/src/third_party/libwebp/webp/decode.h
deleted file mode 100644
index 1d98b8c..0000000
--- a/src/third_party/libwebp/webp/decode.h
+++ /dev/null
@@ -1,490 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Main decoding functions for WebP images.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_WEBP_DECODE_H_
-#define WEBP_WEBP_DECODE_H_
-
-#include "./types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define WEBP_DECODER_ABI_VERSION 0x0201 // MAJOR(8b) + MINOR(8b)
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum VP8StatusCode VP8StatusCode;
-// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE;
-typedef struct WebPRGBABuffer WebPRGBABuffer;
-typedef struct WebPYUVABuffer WebPYUVABuffer;
-typedef struct WebPDecBuffer WebPDecBuffer;
-typedef struct WebPIDecoder WebPIDecoder;
-typedef struct WebPBitstreamFeatures WebPBitstreamFeatures;
-typedef struct WebPDecoderOptions WebPDecoderOptions;
-typedef struct WebPDecoderConfig WebPDecoderConfig;
-
-// Return the decoder's version number, packed in hexadecimal using 8bits for
-// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN(int) WebPGetDecoderVersion(void);
-
-// Retrieve basic header information: width, height.
-// This function will also validate the header and return 0 in
-// case of formatting error.
-// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
-WEBP_EXTERN(int) WebPGetInfo(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
-// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
-// with the dimensions in *width and *height. The ordering of samples in
-// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
-// The returned pointer should be deleted calling free().
-// Returns NULL in case of error.
-WEBP_EXTERN(uint8_t*) WebPDecodeRGBA(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
-// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.
-WEBP_EXTERN(uint8_t*) WebPDecodeARGB(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
-// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.
-WEBP_EXTERN(uint8_t*) WebPDecodeBGRA(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
-// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.
-// If the bitstream contains transparency, it is ignored.
-WEBP_EXTERN(uint8_t*) WebPDecodeRGB(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
-// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.
-WEBP_EXTERN(uint8_t*) WebPDecodeBGR(const uint8_t* data, size_t data_size,
- int* width, int* height);
-
-
-// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
-// returned is the Y samples buffer. Upon return, *u and *v will point to
-// the U and V chroma data. These U and V buffers need NOT be freed()'d,
-// unlike the returned Y luma one. The dimension of the U and V planes
-// are both (*width + 1) / 2 and (*height + 1)/ 2.
-// Upon return, the Y buffer has a stride returned as '*stride', while U and V
-// have a common stride returned as '*uv_stride'.
-// Return NULL in case of error.
-// (*) Also named Y'CbCr. See: http://en.wikipedia.org/wiki/YCbCr
-WEBP_EXTERN(uint8_t*) WebPDecodeYUV(const uint8_t* data, size_t data_size,
- int* width, int* height,
- uint8_t** u, uint8_t** v,
- int* stride, int* uv_stride);
-
-// These five functions are variants of the above ones, that decode the image
-// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
-// available in this buffer is indicated by 'output_buffer_size'. If this
-// storage is not sufficient (or an error occurred), NULL is returned.
-// Otherwise, output_buffer is returned, for convenience.
-// The parameter 'output_stride' specifies the distance (in bytes)
-// between scanlines. Hence, output_buffer_size is expected to be at least
-// output_stride x picture-height.
-WEBP_EXTERN(uint8_t*) WebPDecodeRGBAInto(
- const uint8_t* data, size_t data_size,
- uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN(uint8_t*) WebPDecodeARGBInto(
- const uint8_t* data, size_t data_size,
- uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN(uint8_t*) WebPDecodeBGRAInto(
- const uint8_t* data, size_t data_size,
- uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-
-// RGB and BGR variants. Here too the transparency information, if present,
-// will be dropped and ignored.
-WEBP_EXTERN(uint8_t*) WebPDecodeRGBInto(
- const uint8_t* data, size_t data_size,
- uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN(uint8_t*) WebPDecodeBGRInto(
- const uint8_t* data, size_t data_size,
- uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-
-// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly
-// into pre-allocated luma/chroma plane buffers. This function requires the
-// strides to be passed: one for the luma plane and one for each of the
-// chroma ones. The size of each plane buffer is passed as 'luma_size',
-// 'u_size' and 'v_size' respectively.
-// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred
-// during decoding (or because some buffers were found to be too small).
-WEBP_EXTERN(uint8_t*) WebPDecodeYUVInto(
- const uint8_t* data, size_t data_size,
- uint8_t* luma, size_t luma_size, int luma_stride,
- uint8_t* u, size_t u_size, int u_stride,
- uint8_t* v, size_t v_size, int v_stride);
-
-//------------------------------------------------------------------------------
-// Output colorspaces and buffer
-
-// Colorspaces
-// Note: the naming describes the byte-ordering of packed samples in memory.
-// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,...
-// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels.
-// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order:
-// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ...
-// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ...
-// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for
-// these two modes:
-// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ...
-// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ...
-
-typedef enum WEBP_CSP_MODE {
- MODE_RGB = 0, MODE_RGBA = 1,
- MODE_BGR = 2, MODE_BGRA = 3,
- MODE_ARGB = 4, MODE_RGBA_4444 = 5,
- MODE_RGB_565 = 6,
- // RGB-premultiplied transparent modes (alpha value is preserved)
- MODE_rgbA = 7,
- MODE_bgrA = 8,
- MODE_Argb = 9,
- MODE_rgbA_4444 = 10,
- // YUV modes must come after RGB ones.
- MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0
- MODE_LAST = 13
-} WEBP_CSP_MODE;
-
-// Some useful macros:
-static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) {
- return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb ||
- mode == MODE_rgbA_4444);
-}
-
-static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) {
- return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
- mode == MODE_RGBA_4444 || mode == MODE_YUVA ||
- WebPIsPremultipliedMode(mode));
-}
-
-static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) {
- return (mode < MODE_YUV);
-}
-
-//------------------------------------------------------------------------------
-// WebPDecBuffer: Generic structure for describing the output sample buffer.
-
-struct WebPRGBABuffer { // view as RGBA
- uint8_t* rgba; // pointer to RGBA samples
- int stride; // stride in bytes from one scanline to the next.
- size_t size; // total size of the *rgba buffer.
-};
-
-struct WebPYUVABuffer { // view as YUVA
- uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples
- int y_stride; // luma stride
- int u_stride, v_stride; // chroma strides
- int a_stride; // alpha stride
- size_t y_size; // luma plane size
- size_t u_size, v_size; // chroma planes size
- size_t a_size; // alpha-plane size
-};
-
-// Output buffer
-struct WebPDecBuffer {
- WEBP_CSP_MODE colorspace; // Colorspace.
- int width, height; // Dimensions.
- int is_external_memory; // If true, 'internal_memory' pointer is not used.
- union {
- WebPRGBABuffer RGBA;
- WebPYUVABuffer YUVA;
- } u; // Nameless union of buffer parameters.
- uint32_t pad[4]; // padding for later use
-
- uint8_t* private_memory; // Internally allocated memory (only when
- // is_external_memory is false). Should not be used
- // externally, but accessed via the buffer union.
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(int) WebPInitDecBufferInternal(WebPDecBuffer*, int);
-
-// Initialize the structure as empty. Must be called before any other use.
-// Returns false in case of version mismatch
-static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
- return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);
-}
-
-// Free any memory associated with the buffer. Must always be called last.
-// Note: doesn't free the 'buffer' structure itself.
-WEBP_EXTERN(void) WebPFreeDecBuffer(WebPDecBuffer* buffer);
-
-//------------------------------------------------------------------------------
-// Enumeration of the status codes
-
-typedef enum VP8StatusCode {
- VP8_STATUS_OK = 0,
- VP8_STATUS_OUT_OF_MEMORY,
- VP8_STATUS_INVALID_PARAM,
- VP8_STATUS_BITSTREAM_ERROR,
- VP8_STATUS_UNSUPPORTED_FEATURE,
- VP8_STATUS_SUSPENDED,
- VP8_STATUS_USER_ABORT,
- VP8_STATUS_NOT_ENOUGH_DATA
-} VP8StatusCode;
-
-//------------------------------------------------------------------------------
-// Incremental decoding
-//
-// This API allows streamlined decoding of partial data.
-// Picture can be incrementally decoded as data become available thanks to the
-// WebPIDecoder object. This object can be left in a SUSPENDED state if the
-// picture is only partially decoded, pending additional input.
-// Code example:
-//
-// WebPInitDecBuffer(&buffer);
-// buffer.colorspace = mode;
-// ...
-// WebPIDecoder* idec = WebPINewDecoder(&buffer);
-// while (has_more_data) {
-// // ... (get additional data)
-// status = WebPIAppend(idec, new_data, new_data_size);
-// if (status != VP8_STATUS_SUSPENDED ||
-// break;
-// }
-//
-// // The above call decodes the current available buffer.
-// // Part of the image can now be refreshed by calling to
-// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
-// }
-// WebPIDelete(idec);
-
-// Creates a new incremental decoder with the supplied buffer parameter.
-// This output_buffer can be passed NULL, in which case a default output buffer
-// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer'
-// is kept, which means that the lifespan of 'output_buffer' must be larger than
-// that of the returned WebPIDecoder object.
-// The supplied 'output_buffer' content MUST NOT be changed between calls to
-// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is
-// set to 1. In such a case, it is allowed to modify the pointers, size and
-// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain
-// within valid bounds.
-// All other fields of WebPDecBuffer MUST remain constant between calls.
-// Returns NULL if the allocation failed.
-WEBP_EXTERN(WebPIDecoder*) WebPINewDecoder(WebPDecBuffer* output_buffer);
-
-// This function allocates and initializes an incremental-decoder object, which
-// will output the RGB/A samples specified by 'csp' into a preallocated
-// buffer 'output_buffer'. The size of this buffer is at least
-// 'output_buffer_size' and the stride (distance in bytes between two scanlines)
-// is specified by 'output_stride'.
-// Additionally, output_buffer can be passed NULL in which case the output
-// buffer will be allocated automatically when the decoding starts. The
-// colorspace 'csp' is taken into account for allocating this buffer. All other
-// parameters are ignored.
-// Returns NULL if the allocation failed, or if some parameters are invalid.
-WEBP_EXTERN(WebPIDecoder*) WebPINewRGB(
- WEBP_CSP_MODE csp,
- uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-
-// This function allocates and initializes an incremental-decoder object, which
-// will output the raw luma/chroma samples into a preallocated planes if
-// supplied. The luma plane is specified by its pointer 'luma', its size
-// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane
-// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v
-// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer
-// can be pass NULL in case one is not interested in the transparency plane.
-// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied.
-// In this case, the output buffer will be automatically allocated (using
-// MODE_YUVA) when decoding starts. All parameters are then ignored.
-// Returns NULL if the allocation failed or if a parameter is invalid.
-WEBP_EXTERN(WebPIDecoder*) WebPINewYUVA(
- uint8_t* luma, size_t luma_size, int luma_stride,
- uint8_t* u, size_t u_size, int u_stride,
- uint8_t* v, size_t v_size, int v_stride,
- uint8_t* a, size_t a_size, int a_stride);
-
-// Deprecated version of the above, without the alpha plane.
-// Kept for backward compatibility.
-WEBP_EXTERN(WebPIDecoder*) WebPINewYUV(
- uint8_t* luma, size_t luma_size, int luma_stride,
- uint8_t* u, size_t u_size, int u_stride,
- uint8_t* v, size_t v_size, int v_stride);
-
-// Deletes the WebPIDecoder object and associated memory. Must always be called
-// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded.
-WEBP_EXTERN(void) WebPIDelete(WebPIDecoder* idec);
-
-// Copies and decodes the next available data. Returns VP8_STATUS_OK when
-// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more
-// data is expected. Returns error in other cases.
-WEBP_EXTERN(VP8StatusCode) WebPIAppend(
- WebPIDecoder* idec, const uint8_t* data, size_t data_size);
-
-// A variant of the above function to be used when data buffer contains
-// partial data from the beginning. In this case data buffer is not copied
-// to the internal memory.
-// Note that the value of the 'data' pointer can change between calls to
-// WebPIUpdate, for instance when the data buffer is resized to fit larger data.
-WEBP_EXTERN(VP8StatusCode) WebPIUpdate(
- WebPIDecoder* idec, const uint8_t* data, size_t data_size);
-
-// Returns the RGB/A image decoded so far. Returns NULL if output params
-// are not initialized yet. The RGB/A output type corresponds to the colorspace
-// specified during call to WebPINewDecoder() or WebPINewRGB().
-// *last_y is the index of last decoded row in raster scan order. Some pointers
-// (*last_y, *width etc.) can be NULL if corresponding information is not
-// needed.
-WEBP_EXTERN(uint8_t*) WebPIDecGetRGB(
- const WebPIDecoder* idec, int* last_y,
- int* width, int* height, int* stride);
-
-// Same as above function to get a YUVA image. Returns pointer to the luma
-// plane or NULL in case of error. If there is no alpha information
-// the alpha pointer '*a' will be returned NULL.
-WEBP_EXTERN(uint8_t*) WebPIDecGetYUVA(
- const WebPIDecoder* idec, int* last_y,
- uint8_t** u, uint8_t** v, uint8_t** a,
- int* width, int* height, int* stride, int* uv_stride, int* a_stride);
-
-// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
-// alpha information (if present). Kept for backward compatibility.
-static WEBP_INLINE uint8_t* WebPIDecGetYUV(
- const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
- int* width, int* height, int* stride, int* uv_stride) {
- return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
- stride, uv_stride, NULL);
-}
-
-// Generic call to retrieve information about the displayable area.
-// If non NULL, the left/right/width/height pointers are filled with the visible
-// rectangular area so far.
-// Returns NULL in case the incremental decoder object is in an invalid state.
-// Otherwise returns the pointer to the internal representation. This structure
-// is read-only, tied to WebPIDecoder's lifespan and should not be modified.
-WEBP_EXTERN(const WebPDecBuffer*) WebPIDecodedArea(
- const WebPIDecoder* idec, int* left, int* top, int* width, int* height);
-
-//------------------------------------------------------------------------------
-// Advanced decoding parametrization
-//
-// Code sample for using the advanced decoding API
-/*
- // 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', if needed
- config.no_fancy_upsampling = 1;
- config.output.colorspace = MODE_BGRA;
- // etc.
-
- // Note that you can also make config.output point to an externally
- // supplied memory buffer, provided it's big enough to store the decoded
- // picture. Otherwise, config.output will just be used to allocate memory
- // and store the decoded picture.
-
- // D) Decode!
- CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
-
- // E) Decoded image is now in config.output (and config.output.u.RGBA)
-
- // F) 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);
-*/
-
-// Features gathered from the bitstream
-struct WebPBitstreamFeatures {
- int width; // Width in pixels, as read from the bitstream.
- int height; // Height in pixels, as read from the bitstream.
- int has_alpha; // True if the bitstream contains an alpha channel.
- int has_animation; // True if the bitstream is an animation.
-
- // Unused for now:
- int bitstream_version; // should be 0 for now. TODO(later)
- int no_incremental_decoding; // if true, using incremental decoding is not
- // recommended.
- int rotate; // TODO(later)
- int uv_sampling; // should be 0 for now. TODO(later)
- uint32_t pad[2]; // padding for later use
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(VP8StatusCode) WebPGetFeaturesInternal(
- const uint8_t*, size_t, WebPBitstreamFeatures*, int);
-
-// Retrieve features from the bitstream. The *features structure is filled
-// with information gathered from the bitstream.
-// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
-// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
-// features from headers. Returns error in other cases.
-static WEBP_INLINE VP8StatusCode WebPGetFeatures(
- const uint8_t* data, size_t data_size,
- WebPBitstreamFeatures* features) {
- return WebPGetFeaturesInternal(data, data_size, features,
- WEBP_DECODER_ABI_VERSION);
-}
-
-// Decoding options
-struct WebPDecoderOptions {
- int bypass_filtering; // if true, skip the in-loop filtering
- int no_fancy_upsampling; // if true, use faster pointwise upsampler
- int use_cropping; // if true, cropping is applied _first_
- int crop_left, crop_top; // top-left position for cropping.
- // Will be snapped to even values.
- int crop_width, crop_height; // dimension of the cropping area
- int use_scaling; // if true, scaling is applied _afterward_
- int scaled_width, scaled_height; // final resolution
- int use_threads; // if true, use multi-threaded decoding
-
- // Unused for now:
- int force_rotation; // forced rotation (to be applied _last_)
- int no_enhancement; // if true, discard enhancement layer
- uint32_t pad[6]; // padding for later use
-};
-
-// Main object storing the configuration for advanced decoding.
-struct WebPDecoderConfig {
- WebPBitstreamFeatures input; // Immutable bitstream features (optional)
- WebPDecBuffer output; // Output buffer (can point to external mem)
- WebPDecoderOptions options; // Decoding options
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(int) WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
-
-// Initialize the configuration as empty. This function must always be
-// called first, unless WebPGetFeatures() is to be called.
-// Returns false in case of mismatched version.
-static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
- return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION);
-}
-
-// Instantiate a new incremental decoder object with the requested
-// configuration. The bitstream can be passed using 'data' and 'data_size'
-// parameter, in which case the features will be parsed and stored into
-// config->input. Otherwise, 'data' can be NULL and no parsing will occur.
-// Note that 'config' can be NULL too, in which case a default configuration
-// is used.
-// The return WebPIDecoder object must always be deleted calling WebPIDelete().
-// Returns NULL in case of error (and config->status will then reflect
-// the error condition).
-WEBP_EXTERN(WebPIDecoder*) WebPIDecode(const uint8_t* data, size_t data_size,
- WebPDecoderConfig* config);
-
-// Non-incremental version. This version decodes the full data at once, taking
-// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
-// if the decoding was successful).
-WEBP_EXTERN(VP8StatusCode) WebPDecode(const uint8_t* data, size_t data_size,
- WebPDecoderConfig* config);
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_WEBP_DECODE_H_ */
diff --git a/src/third_party/libwebp/webp/demux.h b/src/third_party/libwebp/webp/demux.h
deleted file mode 100644
index c7cd5d6..0000000
--- a/src/third_party/libwebp/webp/demux.h
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Demux API.
-// Enables extraction of image and extended format data from WebP files.
-
-// 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);
-
-#ifndef WEBP_WEBP_DEMUX_H_
-#define WEBP_WEBP_DEMUX_H_
-
-#include "./mux_types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define WEBP_DEMUX_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b)
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPDemuxState WebPDemuxState;
-// typedef enum WebPFormatFeature WebPFormatFeature;
-typedef struct WebPDemuxer WebPDemuxer;
-typedef struct WebPIterator WebPIterator;
-typedef struct WebPChunkIterator WebPChunkIterator;
-
-//------------------------------------------------------------------------------
-
-// Returns the version number of the demux library, packed in hexadecimal using
-// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN(int) WebPGetDemuxVersion(void);
-
-//------------------------------------------------------------------------------
-// Life of a Demux object
-
-typedef enum WebPDemuxState {
- WEBP_DEMUX_PARSING_HEADER, // Not enough data to parse full header.
- WEBP_DEMUX_PARSED_HEADER, // Header parsing complete, data may be available.
- WEBP_DEMUX_DONE // Entire file has been parsed.
-} WebPDemuxState;
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(WebPDemuxer*) WebPDemuxInternal(
- const WebPData*, int, WebPDemuxState*, int);
-
-// Parses the full WebP file given by 'data'.
-// Returns a WebPDemuxer object on successful parse, NULL otherwise.
-static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
- return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION);
-}
-
-// Parses the possibly incomplete WebP file given by 'data'.
-// If 'state' is non-NULL it will be set to indicate the status of the demuxer.
-// Returns a WebPDemuxer object on successful parse, NULL otherwise.
-static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
- const WebPData* data, WebPDemuxState* state) {
- return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION);
-}
-
-// Frees memory associated with 'dmux'.
-WEBP_EXTERN(void) WebPDemuxDelete(WebPDemuxer* dmux);
-
-//------------------------------------------------------------------------------
-// Data/information extraction.
-
-typedef enum WebPFormatFeature {
- WEBP_FF_FORMAT_FLAGS, // Extended format flags present in the 'VP8X' chunk.
- WEBP_FF_CANVAS_WIDTH,
- WEBP_FF_CANVAS_HEIGHT,
- WEBP_FF_LOOP_COUNT,
- WEBP_FF_BACKGROUND_COLOR,
- WEBP_FF_FRAME_COUNT // Number of frames present in the demux object.
- // In case of a partial demux, this is the number of
- // frames seen so far, with the last frame possibly
- // being partial.
-} WebPFormatFeature;
-
-// Get the 'feature' value from the 'dmux'.
-// NOTE: values are only valid if WebPDemux() was used or WebPDemuxPartial()
-// returned a state > WEBP_DEMUX_PARSING_HEADER.
-WEBP_EXTERN(uint32_t) WebPDemuxGetI(
- const WebPDemuxer* dmux, WebPFormatFeature feature);
-
-//------------------------------------------------------------------------------
-// Frame iteration.
-
-struct WebPIterator {
- int frame_num;
- int num_frames; // equivalent to WEBP_FF_FRAME_COUNT.
- int fragment_num;
- int num_fragments;
- int x_offset, y_offset; // offset relative to the canvas.
- int width, height; // dimensions of this frame or fragment.
- int duration; // display duration in milliseconds.
- WebPMuxAnimDispose dispose_method; // dispose method for the frame.
- int complete; // true if 'fragment' contains a full frame. partial images
- // may still be decoded with the WebP incremental decoder.
- WebPData fragment; // The frame or fragment given by 'frame_num' and
- // 'fragment_num'.
-
- uint32_t pad[4]; // padding for later use.
- void* private_; // for internal use only.
-};
-
-// Retrieves frame 'frame_number' from 'dmux'.
-// 'iter->fragment' points to the first fragment on return from this function.
-// Individual fragments may be extracted using WebPDemuxSetFragment().
-// Setting 'frame_number' equal to 0 will return the last frame of the image.
-// Returns false if 'dmux' is NULL or frame 'frame_number' is not present.
-// Call WebPDemuxReleaseIterator() when use of the iterator is complete.
-// NOTE: 'dmux' must persist for the lifetime of 'iter'.
-WEBP_EXTERN(int) WebPDemuxGetFrame(
- const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
-
-// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or
-// previous ('iter->frame_num' - 1) frame. These functions do not loop.
-// Returns true on success, false otherwise.
-WEBP_EXTERN(int) WebPDemuxNextFrame(WebPIterator* iter);
-WEBP_EXTERN(int) WebPDemuxPrevFrame(WebPIterator* iter);
-
-// Sets 'iter->fragment' to reflect fragment number 'fragment_num'.
-// Returns true if fragment 'fragment_num' is present, false otherwise.
-WEBP_EXTERN(int) WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num);
-
-// Releases any memory associated with 'iter'.
-// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same
-// iter. Also, must be called before destroying the associated WebPDemuxer with
-// WebPDemuxDelete().
-WEBP_EXTERN(void) WebPDemuxReleaseIterator(WebPIterator* iter);
-
-//------------------------------------------------------------------------------
-// Chunk iteration.
-
-struct WebPChunkIterator {
- // The current and total number of chunks with the fourcc given to
- // WebPDemuxGetChunk().
- int chunk_num;
- int num_chunks;
- WebPData chunk; // The payload of the chunk.
-
- uint32_t pad[6]; // padding for later use
- void* private_;
-};
-
-// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from
-// 'dmux'.
-// 'fourcc' is a character array containing the fourcc of the chunk to return,
-// e.g., "ICCP", "XMP ", "EXIF", etc.
-// Setting 'chunk_number' equal to 0 will return the last chunk in a set.
-// Returns true if the chunk is found, false otherwise. Image related chunk
-// payloads are accessed through WebPDemuxGetFrame() and related functions.
-// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete.
-// NOTE: 'dmux' must persist for the lifetime of the iterator.
-WEBP_EXTERN(int) WebPDemuxGetChunk(const WebPDemuxer* dmux,
- const char fourcc[4], int chunk_number,
- WebPChunkIterator* iter);
-
-// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous
-// ('iter->chunk_num' - 1) chunk. These functions do not loop.
-// Returns true on success, false otherwise.
-WEBP_EXTERN(int) WebPDemuxNextChunk(WebPChunkIterator* iter);
-WEBP_EXTERN(int) WebPDemuxPrevChunk(WebPChunkIterator* iter);
-
-// Releases any memory associated with 'iter'.
-// Must be called before destroying the associated WebPDemuxer with
-// WebPDemuxDelete().
-WEBP_EXTERN(void) WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_WEBP_DEMUX_H_ */
diff --git a/src/third_party/libwebp/webp/encode.h b/src/third_party/libwebp/webp/encode.h
deleted file mode 100644
index 47d9d91..0000000
--- a/src/third_party/libwebp/webp/encode.h
+++ /dev/null
@@ -1,485 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// WebP encoder: main interface
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_WEBP_ENCODE_H_
-#define WEBP_WEBP_ENCODE_H_
-
-#include "./types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define WEBP_ENCODER_ABI_VERSION 0x0201 // MAJOR(8b) + MINOR(8b)
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPImageHint WebPImageHint;
-// typedef enum WebPEncCSP WebPEncCSP;
-// typedef enum WebPPreset WebPPreset;
-// typedef enum WebPEncodingError WebPEncodingError;
-typedef struct WebPConfig WebPConfig;
-typedef struct WebPPicture WebPPicture; // main structure for I/O
-typedef struct WebPAuxStats WebPAuxStats;
-typedef struct WebPMemoryWriter WebPMemoryWriter;
-
-// Return the encoder's version number, packed in hexadecimal using 8bits for
-// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN(int) WebPGetEncoderVersion(void);
-
-//------------------------------------------------------------------------------
-// One-stop-shop call! No questions asked:
-
-// Returns the size of the compressed data (pointed to by *output), or 0 if
-// an error occurred. The compressed data must be released by the caller
-// using the call 'SbMemoryDeallocate(*output)'.
-// These functions compress using the lossy format, and the quality_factor
-// can go from 0 (smaller output, lower quality) to 100 (best quality,
-// larger output).
-WEBP_EXTERN(size_t) WebPEncodeRGB(const uint8_t* rgb,
- int width, int height, int stride,
- float quality_factor, uint8_t** output);
-WEBP_EXTERN(size_t) WebPEncodeBGR(const uint8_t* bgr,
- int width, int height, int stride,
- float quality_factor, uint8_t** output);
-WEBP_EXTERN(size_t) WebPEncodeRGBA(const uint8_t* rgba,
- int width, int height, int stride,
- float quality_factor, uint8_t** output);
-WEBP_EXTERN(size_t) WebPEncodeBGRA(const uint8_t* bgra,
- int width, int height, int stride,
- float quality_factor, uint8_t** output);
-
-// These functions are the equivalent of the above, but compressing in a
-// lossless manner. Files are usually larger than lossy format, but will
-// not suffer any compression loss.
-WEBP_EXTERN(size_t) WebPEncodeLosslessRGB(const uint8_t* rgb,
- int width, int height, int stride,
- uint8_t** output);
-WEBP_EXTERN(size_t) WebPEncodeLosslessBGR(const uint8_t* bgr,
- int width, int height, int stride,
- uint8_t** output);
-WEBP_EXTERN(size_t) WebPEncodeLosslessRGBA(const uint8_t* rgba,
- int width, int height, int stride,
- uint8_t** output);
-WEBP_EXTERN(size_t) WebPEncodeLosslessBGRA(const uint8_t* bgra,
- int width, int height, int stride,
- uint8_t** output);
-
-//------------------------------------------------------------------------------
-// Coding parameters
-
-// Image characteristics hint for the underlying encoder.
-typedef enum WebPImageHint {
- WEBP_HINT_DEFAULT = 0, // default preset.
- WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
- WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting
- WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc).
- WEBP_HINT_LAST
-} WebPImageHint;
-
-// Compression parameters.
-struct WebPConfig {
- int lossless; // Lossless encoding (0=lossy(default), 1=lossless).
- float quality; // between 0 (smallest file) and 100 (biggest)
- int method; // quality/speed trade-off (0=fast, 6=slower-better)
-
- WebPImageHint image_hint; // Hint for image type (lossless only for now).
-
- // Parameters related to lossy compression only:
- int target_size; // if non-zero, set the desired target size in bytes.
- // Takes precedence over the 'compression' parameter.
- float target_PSNR; // if non-zero, specifies the minimal distortion to
- // try to achieve. Takes precedence over target_size.
- int segments; // maximum number of segments to use, in [1..4]
- int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum.
- int filter_strength; // range: [0 = off .. 100 = strongest]
- int filter_sharpness; // range: [0 = off .. 7 = least sharp]
- int filter_type; // filtering type: 0 = simple, 1 = strong (only used
- // if filter_strength > 0 or autofilter > 0)
- int autofilter; // Auto adjust filter's strength [0 = off, 1 = on]
- int alpha_compression; // Algorithm for encoding the alpha plane (0 = none,
- // 1 = compressed with WebP lossless). Default is 1.
- int alpha_filtering; // Predictive filtering method for alpha plane.
- // 0: none, 1: fast, 2: best. Default if 1.
- int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
- // Default is 100.
- int pass; // number of entropy-analysis passes (in [1..10]).
-
- int show_compressed; // if true, export the compressed picture back.
- // In-loop filtering is not applied.
- int preprocessing; // preprocessing filter (0=none, 1=segment-smooth)
- int partitions; // log2(number of token partitions) in [0..3]. Default
- // is set to 0 for easier progressive decoding.
- int partition_limit; // quality degradation allowed to fit the 512k limit
- // on prediction modes coding (0: no degradation,
- // 100: maximum possible degradation).
- int emulate_jpeg_size; // If true, compression parameters will be remapped
- // to better match the expected output size from
- // JPEG compression. Generally, the output size will
- // be similar but the degradation will be lower.
- int thread_level; // If non-zero, try and use multi-threaded encoding.
- int low_memory; // If set, reduce memory usage (but increase CPU use).
-
- uint32_t pad[5]; // padding for later use
-};
-
-// Enumerate some predefined settings for WebPConfig, depending on the type
-// of source picture. These presets are used when calling WebPConfigPreset().
-typedef enum WebPPreset {
- WEBP_PRESET_DEFAULT = 0, // default preset.
- WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot
- WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting
- WEBP_PRESET_DRAWING, // hand or line drawing, with high-contrast details
- WEBP_PRESET_ICON, // small-sized colorful images
- WEBP_PRESET_TEXT // text-like
-} WebPPreset;
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(int) WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int);
-
-// Should always be called, to initialize a fresh WebPConfig structure before
-// modification. Returns false in case of version mismatch. WebPConfigInit()
-// must have succeeded before using the 'config' object.
-// Note that the default values are lossless=0 and quality=75.
-static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
- return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f,
- WEBP_ENCODER_ABI_VERSION);
-}
-
-// This function will initialize the configuration according to a predefined
-// set of parameters (referred to by 'preset') and a given quality factor.
-// This function can be called as a replacement to WebPConfigInit(). Will
-// return false in case of error.
-static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
- WebPPreset preset, float quality) {
- return WebPConfigInitInternal(config, preset, quality,
- WEBP_ENCODER_ABI_VERSION);
-}
-
-// Returns true if 'config' is non-NULL and all configuration parameters are
-// within their valid ranges.
-WEBP_EXTERN(int) WebPValidateConfig(const WebPConfig* config);
-
-//------------------------------------------------------------------------------
-// Input / Output
-// Structure for storing auxiliary statistics (mostly for lossy encoding).
-
-struct WebPAuxStats {
- int coded_size; // final size
-
- float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha
- int block_count[3]; // number of intra4/intra16/skipped macroblocks
- int header_bytes[2]; // approximate number of bytes spent for header
- // and mode-partition #0
- int residual_bytes[3][4]; // approximate number of bytes spent for
- // DC/AC/uv coefficients for each (0..3) segments.
- int segment_size[4]; // number of macroblocks in each segments
- int segment_quant[4]; // quantizer values for each segments
- int segment_level[4]; // filtering strength for each segments [0..63]
-
- int alpha_data_size; // size of the transparency data
- int layer_data_size; // size of the enhancement layer data
-
- // lossless encoder statistics
- uint32_t lossless_features; // bit0:predictor bit1:cross-color transform
- // bit2:subtract-green bit3:color indexing
- int histogram_bits; // number of precision bits of histogram
- int transform_bits; // precision bits for transform
- int cache_bits; // number of bits for color cache lookup
- int palette_size; // number of color in palette, if used
- int lossless_size; // final lossless size
-
- uint32_t pad[4]; // padding for later use
-};
-
-// Signature for output function. Should return true if writing was successful.
-// data/data_size is the segment of data to write, and 'picture' is for
-// reference (and so one can make use of picture->custom_ptr).
-typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size,
- const WebPPicture* picture);
-
-// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using
-// the following WebPMemoryWriter object (to be set as a custom_ptr).
-struct WebPMemoryWriter {
- uint8_t* mem; // final buffer (of size 'max_size', larger than 'size').
- size_t size; // final size
- size_t max_size; // total capacity
- uint32_t pad[1]; // padding for later use
-};
-
-// The following must be called first before any use.
-WEBP_EXTERN(void) WebPMemoryWriterInit(WebPMemoryWriter* writer);
-
-// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon
-// completion, writer.mem and writer.size will hold the coded data.
-// writer.mem must be freed using the call 'SbMemoryDeallocate(writer.mem)'.
-WEBP_EXTERN(int) WebPMemoryWrite(const uint8_t* data, size_t data_size,
- const WebPPicture* picture);
-
-// Progress hook, called from time to time to report progress. It can return
-// false to request an abort of the encoding process, or true otherwise if
-// everything is OK.
-typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture);
-
-// Color spaces.
-typedef enum WebPEncCSP {
- // chroma sampling
- WEBP_YUV420 = 0, // 4:2:0
- WEBP_YUV422 = 1, // 4:2:2
- WEBP_YUV444 = 2, // 4:4:4
- WEBP_YUV400 = 3, // grayscale
- WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors
- // alpha channel variants
- WEBP_YUV420A = 4,
- WEBP_YUV422A = 5,
- WEBP_YUV444A = 6,
- WEBP_YUV400A = 7, // grayscale + alpha
- WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present
-} WebPEncCSP;
-
-// Encoding error conditions.
-typedef enum WebPEncodingError {
- VP8_ENC_OK = 0,
- VP8_ENC_ERROR_OUT_OF_MEMORY, // memory error allocating objects
- VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, // memory error while flushing bits
- VP8_ENC_ERROR_NULL_PARAMETER, // a pointer parameter is NULL
- VP8_ENC_ERROR_INVALID_CONFIGURATION, // configuration is invalid
- VP8_ENC_ERROR_BAD_DIMENSION, // picture has invalid width/height
- VP8_ENC_ERROR_PARTITION0_OVERFLOW, // partition is bigger than 512k
- VP8_ENC_ERROR_PARTITION_OVERFLOW, // partition is bigger than 16M
- VP8_ENC_ERROR_BAD_WRITE, // error while flushing bytes
- VP8_ENC_ERROR_FILE_TOO_BIG, // file is bigger than 4G
- VP8_ENC_ERROR_USER_ABORT, // abort request by user
- VP8_ENC_ERROR_LAST // list terminator. always last.
-} WebPEncodingError;
-
-// maximum width/height allowed (inclusive), in pixels
-#define WEBP_MAX_DIMENSION 16383
-
-// Main exchange structure (input samples, output bytes, statistics)
-struct WebPPicture {
- // INPUT
- //////////////
- // Main flag for encoder selecting between ARGB or YUV input.
- // It is recommended to use ARGB input (*argb, argb_stride) for lossless
- // compression, and YUV input (*y, *u, *v, etc.) for lossy compression
- // since these are the respective native colorspace for these formats.
- int use_argb;
-
- // YUV input (mostly used for input to lossy compression)
- WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr).
- int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION)
- uint8_t *y, *u, *v; // pointers to luma/chroma planes.
- int y_stride, uv_stride; // luma/chroma strides.
- uint8_t* a; // pointer to the alpha plane
- int a_stride; // stride of the alpha plane
- uint32_t pad1[2]; // padding for later use
-
- // ARGB input (mostly used for input to lossless compression)
- uint32_t* argb; // Pointer to argb (32 bit) plane.
- int argb_stride; // This is stride in pixels units, not bytes.
- uint32_t pad2[3]; // padding for later use
-
- // OUTPUT
- ///////////////
- // Byte-emission hook, to store compressed bytes as they are ready.
- WebPWriterFunction writer; // can be NULL
- void* custom_ptr; // can be used by the writer.
-
- // map for extra information (only for lossy compression mode)
- int extra_info_type; // 1: intra type, 2: segment, 3: quant
- // 4: intra-16 prediction mode,
- // 5: chroma prediction mode,
- // 6: bit cost, 7: distortion
- uint8_t* extra_info; // if not NULL, points to an array of size
- // ((width + 15) / 16) * ((height + 15) / 16) that
- // will be filled with a macroblock map, depending
- // on extra_info_type.
-
- // STATS AND REPORTS
- ///////////////////////////
- // Pointer to side statistics (updated only if not NULL)
- WebPAuxStats* stats;
-
- // Error code for the latest error encountered during encoding
- WebPEncodingError error_code;
-
- // If not NULL, report progress during encoding.
- WebPProgressHook progress_hook;
-
- void* user_data; // this field is free to be set to any value and
- // used during callbacks (like progress-report e.g.).
-
- uint32_t pad3[3]; // padding for later use
-
- // Unused for now: original samples (for non-YUV420 modes)
- uint8_t *u0, *v0;
- int uv0_stride;
-
- uint32_t pad4[7]; // padding for later use
-
- // PRIVATE FIELDS
- ////////////////////
- void* memory_; // row chunk of memory for yuva planes
- void* memory_argb_; // and for argb too.
- void* pad5[2]; // padding for later use
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(int) WebPPictureInitInternal(WebPPicture*, int);
-
-// Should always be called, to initialize the structure. Returns false in case
-// of version mismatch. WebPPictureInit() must have succeeded before using the
-// 'picture' object.
-// Note that, by default, use_argb is false and colorspace is WEBP_YUV420.
-static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
- return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION);
-}
-
-//------------------------------------------------------------------------------
-// WebPPicture utils
-
-// Convenience allocation / deallocation based on picture->width/height:
-// Allocate y/u/v buffers as per colorspace/width/height specification.
-// Note! This function will free the previous buffer if needed.
-// Returns false in case of memory error.
-WEBP_EXTERN(int) WebPPictureAlloc(WebPPicture* picture);
-
-// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*().
-// Note that this function does _not_ free the memory used by the 'picture'
-// object itself.
-// Besides memory (which is reclaimed) all other fields of 'picture' are
-// preserved.
-WEBP_EXTERN(void) WebPPictureFree(WebPPicture* picture);
-
-// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, *dst
-// will fully own the copied pixels (this is not a view). The 'dst' picture need
-// not be initialized as its content is overwritten.
-// Returns false in case of memory allocation error.
-WEBP_EXTERN(int) WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
-
-// Compute PSNR, SSIM or LSIM distortion metric between two pictures.
-// Result is in dB, stores in result[] in the Y/U/V/Alpha/All order.
-// Returns false in case of error (src and ref don't have same dimension, ...)
-// Warning: this function is rather CPU-intensive.
-WEBP_EXTERN(int) WebPPictureDistortion(
- const WebPPicture* src, const WebPPicture* ref,
- int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
- float result[5]);
-
-// self-crops a picture to the rectangle defined by top/left/width/height.
-// Returns false in case of memory allocation error, or if the rectangle is
-// outside of the source picture.
-// The rectangle for the view is defined by the top-left corner pixel
-// coordinates (left, top) as well as its width and height. This rectangle
-// must be fully be comprised inside the 'src' source picture. If the source
-// picture uses the YUV420 colorspace, the top and left coordinates will be
-// snapped to even values.
-WEBP_EXTERN(int) WebPPictureCrop(WebPPicture* picture,
- int left, int top, int width, int height);
-
-// Extracts a view from 'src' picture into 'dst'. The rectangle for the view
-// is defined by the top-left corner pixel coordinates (left, top) as well
-// as its width and height. This rectangle must be fully be comprised inside
-// the 'src' source picture. If the source picture uses the YUV420 colorspace,
-// the top and left coordinates will be snapped to even values.
-// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed
-// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so,
-// the original dimension will be lost). Picture 'dst' need not be initialized
-// with WebPPictureInit() if it is different from 'src', since its content will
-// be overwritten.
-// Returns false in case of memory allocation error or invalid parameters.
-WEBP_EXTERN(int) WebPPictureView(const WebPPicture* src,
- int left, int top, int width, int height,
- WebPPicture* dst);
-
-// Returns true if the 'picture' is actually a view and therefore does
-// not own the memory for pixels.
-WEBP_EXTERN(int) WebPPictureIsView(const WebPPicture* picture);
-
-// Rescale a picture to new dimension width x height.
-// Now gamma correction is applied.
-// Returns false in case of error (invalid parameter or insufficient memory).
-WEBP_EXTERN(int) WebPPictureRescale(WebPPicture* pic, int width, int height);
-
-// Colorspace conversion function to import RGB samples.
-// Previous buffer will be free'd, if any.
-// *rgb buffer should have a size of at least height * rgb_stride.
-// Returns false in case of memory error.
-WEBP_EXTERN(int) WebPPictureImportRGB(
- WebPPicture* picture, const uint8_t* rgb, int rgb_stride);
-// Same, but for RGBA buffer.
-WEBP_EXTERN(int) WebPPictureImportRGBA(
- WebPPicture* picture, const uint8_t* rgba, int rgba_stride);
-// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format
-// input buffer ignoring the alpha channel. Avoids needing to copy the data
-// to a temporary 24-bit RGB buffer to import the RGB only.
-WEBP_EXTERN(int) WebPPictureImportRGBX(
- WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride);
-
-// Variants of the above, but taking BGR(A|X) input.
-WEBP_EXTERN(int) WebPPictureImportBGR(
- WebPPicture* picture, const uint8_t* bgr, int bgr_stride);
-WEBP_EXTERN(int) WebPPictureImportBGRA(
- WebPPicture* picture, const uint8_t* bgra, int bgra_stride);
-WEBP_EXTERN(int) WebPPictureImportBGRX(
- WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride);
-
-// Converts picture->argb data to the YUVA format specified by 'colorspace'.
-// Upon return, picture->use_argb is set to false. The presence of real
-// non-opaque transparent values is detected, and 'colorspace' will be
-// adjusted accordingly. Note that this method is lossy.
-// Returns false in case of error.
-WEBP_EXTERN(int) WebPPictureARGBToYUVA(WebPPicture* picture,
- WebPEncCSP colorspace);
-
-// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
-// The input format must be YUV_420 or YUV_420A.
-// Note that the use of this method is discouraged if one has access to the
-// raw ARGB samples, since using YUV420 is comparatively lossy. Also, the
-// conversion from YUV420 to ARGB incurs a small loss too.
-// Returns false in case of error.
-WEBP_EXTERN(int) WebPPictureYUVAToARGB(WebPPicture* picture);
-
-// Helper function: given a width x height plane of YUV(A) samples
-// (with stride 'stride'), clean-up the YUV samples under fully transparent
-// area, to help compressibility (no guarantee, though).
-WEBP_EXTERN(void) WebPCleanupTransparentArea(WebPPicture* picture);
-
-// Scan the picture 'picture' for the presence of non fully opaque alpha values.
-// Returns true in such case. Otherwise returns false (indicating that the
-// alpha plane can be ignored altogether e.g.).
-WEBP_EXTERN(int) WebPPictureHasTransparency(const WebPPicture* picture);
-
-//------------------------------------------------------------------------------
-// Main call
-
-// Main encoding call, after config and picture have been initialized.
-// 'picture' must be less than 16384x16384 in dimension (cf WEBP_MAX_DIMENSION),
-// and the 'config' object must be a valid one.
-// Returns false in case of error, true otherwise.
-// In case of error, picture->error_code is updated accordingly.
-// 'picture' can hold the source samples in both YUV(A) or ARGB input, depending
-// on the value of 'picture->use_argb'. It is highly recommended to use
-// the former for lossy encoding, and the latter for lossless encoding
-// (when config.lossless is true). Automatic conversion from one format to
-// another is provided but they both incur some loss.
-WEBP_EXTERN(int) WebPEncode(const WebPConfig* config, WebPPicture* picture);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_WEBP_ENCODE_H_ */
diff --git a/src/third_party/libwebp/webp/format_constants.h b/src/third_party/libwebp/webp/format_constants.h
deleted file mode 100644
index 4c04b50..0000000
--- a/src/third_party/libwebp/webp/format_constants.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Internal header for constants related to WebP file format.
-//
-// Author: Urvang (urvang@google.com)
-
-#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_
-#define WEBP_WEBP_FORMAT_CONSTANTS_H_
-
-// Create fourcc of the chunk from the chunk tag characters.
-#define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24)
-
-// VP8 related constants.
-#define VP8_SIGNATURE 0x9d012a // Signature in VP8 data.
-#define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition
-#define VP8_MAX_PARTITION_SIZE (1 << 24) // max size for token partition
-#define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data.
-
-// VP8L related constants.
-#define VP8L_SIGNATURE_SIZE 1 // VP8L signature size.
-#define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte.
-#define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store
- // width and height.
-#define VP8L_VERSION_BITS 3 // 3 bits reserved for version.
-#define VP8L_VERSION 0 // version 0
-#define VP8L_FRAME_HEADER_SIZE 5 // Size of the VP8L frame header.
-
-#define MAX_PALETTE_SIZE 256
-#define MAX_CACHE_BITS 11
-#define HUFFMAN_CODES_PER_META_CODE 5
-#define ARGB_BLACK 0xff000000
-
-#define DEFAULT_CODE_LENGTH 8
-#define MAX_ALLOWED_CODE_LENGTH 15
-
-#define NUM_LITERAL_CODES 256
-#define NUM_LENGTH_CODES 24
-#define NUM_DISTANCE_CODES 40
-#define CODE_LENGTH_CODES 19
-
-#define MIN_HUFFMAN_BITS 2 // min number of Huffman bits
-#define MAX_HUFFMAN_BITS 9 // max number of Huffman bits
-
-#define TRANSFORM_PRESENT 1 // The bit to be written when next data
- // to be read is a transform.
-#define NUM_TRANSFORMS 4 // Maximum number of allowed transform
- // in a bitstream.
-typedef enum {
- PREDICTOR_TRANSFORM = 0,
- CROSS_COLOR_TRANSFORM = 1,
- SUBTRACT_GREEN = 2,
- COLOR_INDEXING_TRANSFORM = 3
-} VP8LImageTransformType;
-
-// Alpha related constants.
-#define ALPHA_HEADER_LEN 1
-#define ALPHA_NO_COMPRESSION 0
-#define ALPHA_LOSSLESS_COMPRESSION 1
-#define ALPHA_PREPROCESSED_LEVELS 1
-
-// Mux related constants.
-#define TAG_SIZE 4 // Size of a chunk tag (e.g. "VP8L").
-#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size.
-#define CHUNK_HEADER_SIZE 8 // Size of a chunk header.
-#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP").
-#define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk.
-#define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk.
-#define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk.
-#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk.
-
-#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height.
-#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height.
-#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count
-#define MAX_DURATION (1 << 24) // maximum duration
-#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/fragment x/y offset
-
-// Maximum chunk payload is such that adding the header and padding won't
-// overflow a uint32_t.
-#define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1)
-
-#endif /* WEBP_WEBP_FORMAT_CONSTANTS_H_ */
diff --git a/src/third_party/libwebp/webp/mux.h b/src/third_party/libwebp/webp/mux.h
deleted file mode 100644
index b8c7dc6..0000000
--- a/src/third_party/libwebp/webp/mux.h
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// RIFF container manipulation for WEBP images.
-//
-// Authors: Urvang (urvang@google.com)
-// Vikas (vikasa@google.com)
-
-// This API allows manipulation of WebP container images containing features
-// like color profile, metadata, animation and fragmented images.
-//
-// Code Example#1: Creating a MUX with image data, color profile and XMP
-// metadata.
-//
-// int copy_data = 0;
-// WebPMux* mux = WebPMuxNew();
-// // ... (Prepare image data).
-// WebPMuxSetImage(mux, &image, copy_data);
-// // ... (Prepare ICCP color 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);
-//
-// Code Example#2: 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_data).
-// WebPMuxDelete(mux);
-// free(data);
-
-#ifndef WEBP_WEBP_MUX_H_
-#define WEBP_WEBP_MUX_H_
-
-#include "./mux_types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-#define WEBP_MUX_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b)
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPMuxError WebPMuxError;
-// typedef enum WebPChunkId WebPChunkId;
-typedef struct WebPMux WebPMux; // main opaque object.
-typedef struct WebPMuxFrameInfo WebPMuxFrameInfo;
-typedef struct WebPMuxAnimParams WebPMuxAnimParams;
-
-// Error codes
-typedef enum WebPMuxError {
- WEBP_MUX_OK = 1,
- WEBP_MUX_NOT_FOUND = 0,
- WEBP_MUX_INVALID_ARGUMENT = -1,
- WEBP_MUX_BAD_DATA = -2,
- WEBP_MUX_MEMORY_ERROR = -3,
- WEBP_MUX_NOT_ENOUGH_DATA = -4
-} WebPMuxError;
-
-// IDs for different types of chunks.
-typedef enum WebPChunkId {
- WEBP_CHUNK_VP8X, // VP8X
- WEBP_CHUNK_ICCP, // ICCP
- WEBP_CHUNK_ANIM, // ANIM
- WEBP_CHUNK_ANMF, // ANMF
- WEBP_CHUNK_FRGM, // FRGM
- WEBP_CHUNK_ALPHA, // ALPH
- WEBP_CHUNK_IMAGE, // VP8/VP8L
- WEBP_CHUNK_EXIF, // EXIF
- WEBP_CHUNK_XMP, // XMP
- WEBP_CHUNK_UNKNOWN, // Other chunks.
- WEBP_CHUNK_NIL
-} WebPChunkId;
-
-//------------------------------------------------------------------------------
-
-// Returns the version number of the mux library, packed in hexadecimal using
-// 8bits or each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN(int) WebPGetMuxVersion(void);
-
-//------------------------------------------------------------------------------
-// Life of a Mux object
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(WebPMux*) WebPNewInternal(int);
-
-// Creates an empty mux object.
-// Returns:
-// A pointer to the newly created empty mux object.
-static WEBP_INLINE WebPMux* WebPMuxNew(void) {
- return WebPNewInternal(WEBP_MUX_ABI_VERSION);
-}
-
-// Deletes the mux object.
-// Parameters:
-// mux - (in/out) object to be deleted
-WEBP_EXTERN(void) WebPMuxDelete(WebPMux* mux);
-
-//------------------------------------------------------------------------------
-// Mux creation.
-
-// Internal, version-checked, entry point
-WEBP_EXTERN(WebPMux*) WebPMuxCreateInternal(const WebPData*, int, int);
-
-// Creates a mux object from raw data given in WebP RIFF format.
-// Parameters:
-// bitstream - (in) the bitstream data in WebP RIFF format
-// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// and value 0 indicates data will NOT be copied.
-// Returns:
-// A pointer to the mux object created from given data - on success.
-// NULL - In case of invalid data or memory error.
-static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
- int copy_data) {
- return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION);
-}
-
-//------------------------------------------------------------------------------
-// Non-image chunks.
-
-// Note: Only non-image related chunks should be managed through chunk APIs.
-// (Image related chunks are: "ANMF", "FRGM", "VP8 ", "VP8L" and "ALPH").
-// To add, get and delete images, use APIs WebPMuxSetImage(),
-// WebPMuxPushFrame(), WebPMuxGetFrame() and WebPMuxDeleteFrame().
-
-// Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object.
-// Any existing chunk(s) with the same id will be removed.
-// Parameters:
-// mux - (in/out) object to which the chunk is to be added
-// fourcc - (in) a character array containing the fourcc of the given chunk;
-// e.g., "ICCP", "XMP ", "EXIF" etc.
-// chunk_data - (in) the chunk data to be added
-// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// and value 0 indicates data will NOT be copied.
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
-// or if fourcc corresponds to an image chunk.
-// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxSetChunk(
- WebPMux* mux, const char fourcc[4], const WebPData* chunk_data,
- int copy_data);
-
-// Gets a reference to the data of the chunk with id 'fourcc' in the mux object.
-// The caller should NOT free the returned data.
-// Parameters:
-// mux - (in) object from which the chunk data is to be fetched
-// fourcc - (in) a character array containing the fourcc of the chunk;
-// e.g., "ICCP", "XMP ", "EXIF" etc.
-// chunk_data - (out) returned chunk data
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if either mux, fourcc or chunk_data is NULL
-// or if fourcc corresponds to an image chunk.
-// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given id.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxGetChunk(
- const WebPMux* mux, const char fourcc[4], WebPData* chunk_data);
-
-// Deletes the chunk with the given 'fourcc' from the mux object.
-// Parameters:
-// mux - (in/out) object from which the chunk is to be deleted
-// fourcc - (in) a character array containing the fourcc of the chunk;
-// e.g., "ICCP", "XMP ", "EXIF" etc.
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if mux or fourcc is NULL
-// or if fourcc corresponds to an image chunk.
-// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given fourcc.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxDeleteChunk(
- WebPMux* mux, const char fourcc[4]);
-
-//------------------------------------------------------------------------------
-// Images.
-
-// Encapsulates data about a single frame/fragment.
-struct WebPMuxFrameInfo {
- WebPData bitstream; // image data: can either be a raw VP8/VP8L bitstream
- // or a single-image WebP file.
- int x_offset; // x-offset of the frame.
- int y_offset; // y-offset of the frame.
- int duration; // duration of the frame (in milliseconds).
-
- WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF,
- // WEBP_CHUNK_FRGM or WEBP_CHUNK_IMAGE
- WebPMuxAnimDispose dispose_method; // Disposal method for the frame.
- uint32_t pad[2]; // padding for later use
-};
-
-// Sets the (non-animated and non-fragmented) image in the mux object.
-// Note: Any existing images (including frames/fragments) will be removed.
-// Parameters:
-// mux - (in/out) object in which the image is to be set
-// bitstream - (in) can either be a raw VP8/VP8L bitstream or a single-image
-// WebP file (non-animated and non-fragmented)
-// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// and value 0 indicates data will NOT be copied.
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL.
-// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxSetImage(
- WebPMux* mux, const WebPData* bitstream, int copy_data);
-
-// Adds a frame at the end of the mux object.
-// Notes: (1) frame.id should be one of WEBP_CHUNK_ANMF or WEBP_CHUNK_FRGM
-// (2) For setting a non-animated non-fragmented image, use
-// WebPMuxSetImage() instead.
-// (3) Type of frame being pushed must be same as the frames in mux.
-// (4) As WebP only supports even offsets, any odd offset will be snapped
-// to an even location using: offset &= ~1
-// Parameters:
-// mux - (in/out) object to which the frame is to be added
-// frame - (in) frame data.
-// copy_data - (in) value 1 indicates given data WILL be copied to the mux
-// and value 0 indicates data will NOT be copied.
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL
-// or if content of 'frame' is invalid.
-// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame(
- WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data);
-
-// Gets the nth frame from the mux object.
-// The content of 'frame->bitstream' is allocated using malloc(), and NOT
-// owned by the 'mux' object. It MUST be deallocated by the caller by calling
-// WebPDataClear().
-// nth=0 has a special meaning - last position.
-// Parameters:
-// mux - (in) object from which the info is to be fetched
-// nth - (in) index of the frame in the mux object
-// frame - (out) data of the returned frame
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL.
-// WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object.
-// WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame(
- const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame);
-
-// Deletes a frame from the mux object.
-// nth=0 has a special meaning - last position.
-// Parameters:
-// mux - (in/out) object from which a frame is to be deleted
-// nth - (in) The position from which the frame is to be deleted
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL.
-// WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object
-// before deletion.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth);
-
-//------------------------------------------------------------------------------
-// Animation.
-
-// Animation parameters.
-struct WebPMuxAnimParams {
- uint32_t bgcolor; // Background color of the canvas stored (in MSB order) as:
- // Bits 00 to 07: Alpha.
- // Bits 08 to 15: Red.
- // Bits 16 to 23: Green.
- // Bits 24 to 31: Blue.
- int loop_count; // Number of times to repeat the animation [0 = infinite].
-};
-
-// Sets the animation parameters in the mux object. Any existing ANIM chunks
-// will be removed.
-// Parameters:
-// mux - (in/out) object in which ANIM chunk is to be set/added
-// params - (in) animation parameters.
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if either mux or params is NULL
-// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxSetAnimationParams(
- WebPMux* mux, const WebPMuxAnimParams* params);
-
-// Gets the animation parameters from the mux object.
-// Parameters:
-// mux - (in) object from which the animation parameters to be fetched
-// params - (out) animation parameters extracted from the ANIM chunk
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if either of mux or params is NULL
-// WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxGetAnimationParams(
- const WebPMux* mux, WebPMuxAnimParams* params);
-
-//------------------------------------------------------------------------------
-// Misc Utilities.
-
-// Gets the feature flags from the mux object.
-// Parameters:
-// mux - (in) object from which the features are to be fetched
-// flags - (out) the flags specifying which features are present in the
-// mux object. This will be an OR of various flag values.
-// Enum 'WebPFeatureFlags' can be used to test individual flag values.
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL
-// WEBP_MUX_NOT_FOUND - if VP8X chunk is not present in mux object.
-// WEBP_MUX_BAD_DATA - if VP8X chunk in mux is invalid.
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxGetFeatures(const WebPMux* mux,
- uint32_t* flags);
-
-// Gets number of chunks having tag value tag in the mux object.
-// Parameters:
-// mux - (in) object from which the info is to be fetched
-// id - (in) chunk id specifying the type of chunk
-// num_elements - (out) number of chunks with the given chunk id
-// Returns:
-// WEBP_MUX_INVALID_ARGUMENT - if either mux, or num_elements is NULL
-// WEBP_MUX_OK - on success.
-WEBP_EXTERN(WebPMuxError) WebPMuxNumChunks(const WebPMux* mux,
- WebPChunkId id, int* num_elements);
-
-// Assembles all chunks in WebP RIFF format and returns in 'assembled_data'.
-// This function also validates the mux object.
-// Note: The content of 'assembled_data' will be ignored and overwritten.
-// Also, the content of 'assembled_data' is allocated using malloc(), and NOT
-// owned by the 'mux' object. It MUST be deallocated by the caller by calling
-// WebPDataClear().
-// Parameters:
-// mux - (in/out) object whose chunks are to be assembled
-// assembled_data - (out) assembled WebP data
-// Returns:
-// WEBP_MUX_BAD_DATA - if mux object is invalid.
-// WEBP_MUX_INVALID_ARGUMENT - if either mux, output_data or output_size is
-// NULL.
-// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-// WEBP_MUX_OK - on success
-WEBP_EXTERN(WebPMuxError) WebPMuxAssemble(WebPMux* mux,
- WebPData* assembled_data);
-
-//------------------------------------------------------------------------------
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_WEBP_MUX_H_ */
diff --git a/src/third_party/libwebp/webp/mux_types.h b/src/third_party/libwebp/webp/mux_types.h
deleted file mode 100644
index 7c7abac..0000000
--- a/src/third_party/libwebp/webp/mux_types.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Data-types common to the mux and demux libraries.
-//
-// Author: Urvang (urvang@google.com)
-
-#ifndef WEBP_WEBP_MUX_TYPES_H_
-#define WEBP_WEBP_MUX_TYPES_H_
-
-#if defined(STARBOARD)
-#include "starboard/log.h"
-#include "starboard/memory.h"
-#else
-#include <stdlib.h> // free()
-#include <string.h> // memset()
-#endif
-#include "./types.h"
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPFeatureFlags WebPFeatureFlags;
-// typedef enum WebPMuxAnimDispose WebPMuxAnimDispose;
-typedef struct WebPData WebPData;
-
-// VP8X Feature Flags.
-typedef enum WebPFeatureFlags {
- FRAGMENTS_FLAG = 0x00000001,
- ANIMATION_FLAG = 0x00000002,
- XMP_FLAG = 0x00000004,
- EXIF_FLAG = 0x00000008,
- ALPHA_FLAG = 0x00000010,
- ICCP_FLAG = 0x00000020
-} WebPFeatureFlags;
-
-// Dispose method (animation only). Indicates how the area used by the current
-// frame is to be treated before rendering the next frame on the canvas.
-typedef enum WebPMuxAnimDispose {
- WEBP_MUX_DISPOSE_NONE, // Do not dispose.
- WEBP_MUX_DISPOSE_BACKGROUND // Dispose to background color.
-} WebPMuxAnimDispose;
-
-// Data type used to describe 'raw' data, e.g., chunk data
-// (ICC profile, metadata) and WebP compressed image data.
-struct WebPData {
- const uint8_t* bytes;
- size_t size;
-};
-
-// Initializes the contents of the 'webp_data' object with default values.
-static WEBP_INLINE void WebPDataInit(WebPData* webp_data) {
- if (webp_data != NULL) {
- SbMemorySet(webp_data, 0, sizeof(*webp_data));
- }
-}
-
-// Clears the contents of the 'webp_data' object by calling free(). Does not
-// deallocate the object itself.
-static WEBP_INLINE void WebPDataClear(WebPData* webp_data) {
- if (webp_data != NULL) {
- SbMemoryDeallocate((void*)webp_data->bytes);
- WebPDataInit(webp_data);
- }
-}
-
-// Allocates necessary storage for 'dst' and copies the contents of 'src'.
-// Returns true on success.
-static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) {
- if (src == NULL || dst == NULL) return 0;
- WebPDataInit(dst);
- if (src->bytes != NULL && src->size != 0) {
- dst->bytes = (uint8_t*)SbMemoryAllocate(src->size);
- if (dst->bytes == NULL) return 0;
- SbMemoryCopy((void*)dst->bytes, src->bytes, src->size);
- dst->size = src->size;
- }
- return 1;
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-} // extern "C"
-#endif
-
-#endif /* WEBP_WEBP_MUX_TYPES_H_ */
diff --git a/src/third_party/libwebp/webp/types.h b/src/third_party/libwebp/webp/types.h
deleted file mode 100644
index 4572063..0000000
--- a/src/third_party/libwebp/webp/types.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Common types
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_WEBP_TYPES_H_
-#define WEBP_WEBP_TYPES_H_
-
-#if defined(STARBOARD)
-#include "starboard/types.h"
-#define WEBP_INLINE SB_C_INLINE
-#else
-#include <stddef.h> // for size_t
-
-#ifndef _MSC_VER
-#include <inttypes.h>
-#ifdef __STRICT_ANSI__
-#define WEBP_INLINE
-#else /* __STRICT_ANSI__ */
-#define WEBP_INLINE inline
-#endif
-#else
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef signed short int16_t;
-typedef unsigned short uint16_t;
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-typedef unsigned long long int uint64_t;
-typedef long long int int64_t;
-#define WEBP_INLINE __forceinline
-#endif /* _MSC_VER */
-#endif /* defined(STARBOARD) */
-
-#ifndef WEBP_EXTERN
-// This explicitly marks library functions and allows for changing the
-// signature for e.g., Windows DLL builds.
-#define WEBP_EXTERN(type) extern type
-#endif /* WEBP_EXTERN */
-
-// Macro to check ABI compatibility (same major revision number)
-#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8))
-
-#endif /* WEBP_WEBP_TYPES_H_ */
diff --git a/src/third_party/libwebp/webp_js/index.html b/src/third_party/libwebp/webp_js/index.html
new file mode 100644
index 0000000..10873a9
--- /dev/null
+++ b/src/third_party/libwebp/webp_js/index.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <title>simple Javascript WebP decoding demo</title>
+ <script type="text/javascript">
+ var Module = {
+ noInitialRun : true
+ };
+ </script>
+ <script type="text/javascript" src="./webp.js"></script>
+ <script type="text/javascript">
+
+'use strict';
+
+// main wrapper for the function decoding a WebP into a canvas object
+var WebpToCanvas;
+
+function init() {
+ WebpToCanvas = Module.cwrap('WebpToSDL', 'number', ['array', 'number']);
+}
+window.onload = init;
+
+function decode(webp_data, canvas_id) {
+ // get the canvas to decode into
+ var canvas = document.getElementById(canvas_id);
+ if (canvas == null) return;
+ // clear previous picture (if any)
+ Module.canvas = canvas;
+ canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
+ // decode and measure timing
+ var start = new Date();
+ var ret = WebpToCanvas(webp_data, webp_data.length);
+ var end = new Date();
+ var speed_result = document.getElementById('timing');
+ // display timing result
+ if (speed_result != null) {
+ var decode_time = end - start;
+ speed_result.innerHTML = '<p>decoding time: ' + decode_time +' ms.</p>';
+ }
+}
+
+function loadfile(filename, canvas_id) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', filename);
+ xhr.responseType = 'arraybuffer';
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4 && xhr.status == 200) {
+ var webp_data = new Uint8Array(xhr.response);
+ decode(webp_data, canvas_id);
+ }
+ };
+ xhr.send();
+}
+ </script>
+</head>
+
+<body>
+ <p>
+ <strong>WebP in JavaScript demo</strong> -
+ </p>
+ <p>
+ WebP decoder in JavaScript, using libwebp compiled with
+ <a href="https://github.com/kripken/emscripten/wiki">Emscripten</a>.
+ </p>
+ <p id="image_buttons">
+ <input type="button" value="test image!" name="./test_webp_js.webp"
+ onclick="loadfile(this.name, 'output_canvas')">
+ </p>
+ <p id="timing">Timing: N/A</p>
+ <canvas id="output_canvas">Your browser does not support canvas</canvas>
+
+</body>
+</html>
diff --git a/src/third_party/libwebp/webp_js/index_wasm.html b/src/third_party/libwebp/webp_js/index_wasm.html
new file mode 100644
index 0000000..b77c22c
--- /dev/null
+++ b/src/third_party/libwebp/webp_js/index_wasm.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <title>simple Javascript WebP decoding demo, using Web-Assembly (WASM)</title>
+ <script type="text/javascript">
+ var Module = {
+ noInitialRun : true
+ };
+ </script>
+ <script type="text/javascript">
+
+'use strict';
+
+// main wrapper for the function decoding a WebP into a canvas object
+var WebpToCanvas;
+
+function init() {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'webp_wasm.wasm', true);
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function() {
+ Module.wasmBinary = xhr.response;
+ var script = document.createElement('script');
+ script.src = "webp_wasm.js";
+ document.body.appendChild(script);
+ };
+ xhr.send(null);
+}
+window.onload = init;
+
+function decode(webp_data, canvas_id) {
+ var result;
+ if (Module["asm"] != undefined) {
+ // wrapper for the function decoding a WebP into a canvas object
+ WebpToCanvas = Module.cwrap('WebpToSDL', 'number', ['array', 'number']);
+ // get the canvas to decode into
+ var canvas = document.getElementById(canvas_id);
+ if (canvas == null) return;
+ // clear previous picture (if any)
+ Module.canvas = canvas;
+ canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
+ // decode and measure timing
+ var start = new Date();
+ var ret = WebpToCanvas(webp_data, webp_data.length);
+ var end = new Date();
+ var decode_time = end - start;
+ result = 'decoding time: ' + decode_time +' ms.';
+ } else {
+ result = "WASM module not finished loading! Please retry";
+ }
+ // display timing result
+ var speed_result = document.getElementById('timing');
+ if (speed_result != null) {
+ speed_result.innerHTML = '<p>'+ result + '</p>';
+ }
+}
+
+function loadfile(filename, canvas_id) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', filename);
+ xhr.responseType = 'arraybuffer';
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4 && xhr.status == 200) {
+ var webp_data = new Uint8Array(xhr.response);
+ decode(webp_data, canvas_id);
+ }
+ };
+ xhr.send();
+}
+ </script>
+</head>
+
+<body>
+ <p>
+ <strong>WebP demo using Web-Assembly</strong> -
+ </p>
+ <p>
+ WASM version of the WebP decoder, using libwebp compiled with
+ <a href="https://github.com/kripken/emscripten/wiki">Emscripten</a>.
+ </p>
+ <p id="image_buttons">
+ <input type="button" value="test image!"
+ onclick="loadfile('./test_webp_wasm.webp', 'output_canvas')">
+ </p>
+ <p id="timing">Timing: N/A</p>
+ <canvas id="output_canvas">Your browser does not support canvas</canvas>
+</body>
+</html>
diff --git a/src/third_party/libwebp/webp_js/test_webp_js.webp b/src/third_party/libwebp/webp_js/test_webp_js.webp
new file mode 100644
index 0000000..f798f55
--- /dev/null
+++ b/src/third_party/libwebp/webp_js/test_webp_js.webp
Binary files differ
diff --git a/src/third_party/libwebp/webp_js/test_webp_wasm.webp b/src/third_party/libwebp/webp_js/test_webp_wasm.webp
new file mode 100644
index 0000000..f798f55
--- /dev/null
+++ b/src/third_party/libwebp/webp_js/test_webp_wasm.webp
Binary files differ
diff --git a/src/third_party/libxml/libxml.gyp b/src/third_party/libxml/libxml.gyp
index 86fe4a4..5f14e2b 100644
--- a/src/third_party/libxml/libxml.gyp
+++ b/src/third_party/libxml/libxml.gyp
@@ -300,6 +300,9 @@
# trio_is_special_quantity and trio_is_negative are only
# used with certain preprocessor defines set.
'-Wno-unused-function',
+ # xpath.c xmlXPathNodeCollectAndTest compares 'xmlElementType'
+ # and 'xmlXPathTypeVal'.
+ '-Wno-enum-compare',
],
}],
],
diff --git a/src/third_party/mozjs-45/mfbt/mfbt_Compression.cpp b/src/third_party/mozjs-45/mfbt/mfbt_Compression.cpp
index c114c6c..223fb23 100644
--- a/src/third_party/mozjs-45/mfbt/mfbt_Compression.cpp
+++ b/src/third_party/mozjs-45/mfbt/mfbt_Compression.cpp
@@ -14,6 +14,15 @@
using namespace mozilla::Compression;
+// Because we wrap lz4.c in an anonymous namespace, all of its #includes
+// go in the anonymous namespace too. This would create conflicting
+// declarations for intrinsic functions that are internally defined
+// at top-level. Including intrin.h now prevents it from being included
+// later within the anonymous namespace.
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
namespace {
#include "lz4.c"
diff --git a/src/third_party/protobuf/src/google/protobuf/map.h b/src/third_party/protobuf/src/google/protobuf/map.h
index 6f1a71e..d871b58 100644
--- a/src/third_party/protobuf/src/google/protobuf/map.h
+++ b/src/third_party/protobuf/src/google/protobuf/map.h
@@ -1311,7 +1311,7 @@
GOOGLE_DCHECK(n >= kMinTableSize);
GOOGLE_DCHECK_EQ(n & (n - 1), 0);
void** result = Alloc<void*>(n);
- memset(result, 0, n * sizeof(result[0]));
+ ::memset(result, 0, n * sizeof(result[0]));
return result;
}
diff --git a/src/third_party/protobuf/src/google/protobuf/stubs/strutil.cc b/src/third_party/protobuf/src/google/protobuf/stubs/strutil.cc
index 688fabd..7cd1a19 100644
--- a/src/third_party/protobuf/src/google/protobuf/stubs/strutil.cc
+++ b/src/third_party/protobuf/src/google/protobuf/stubs/strutil.cc
@@ -29,12 +29,39 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// from google3/strings/strutil.cc
-#include "starboard/client_porting/poem/stdlib_poem.h"
-#include "starboard/client_porting/poem/stdio_poem.h"
-#include "starboard/client_porting/poem/string_poem.h"
#ifndef STARBOARD
+
#include <stdio.h>
+
+// We need to be able to build this library using the host toolchain for some
+// platforms, and Starboard is not available there. So we define these
+// "reverse poems" to move past this issue for host builds. For why we don't
+// just use poems, see the comment in the #else clause.
+#define SbMemoryCopy memcpy
+#define SbMemoryMove memmove
+#define SbStringGetLength strlen
+#define SbStringCopyUnsafe strcpy
+#define PoemFindCharacterInString strchr
+#define SbStringFormatF snprintf
+#define SbStringFormatUnsafeF sprintf
+#define SbStringParseUnsignedInteger strtoul
+#define SbStringParseSignedInteger strtol
+#define SbStringParseDouble strtod
+
+#else // STARBOARD
+
+// We avoid using poems here because a subsequent #include of math.h may
+// result, on some platforms, of the indirect inclusion of stdlib.h, which
+// will then conflict with our poem includes.
+#define POEM_NO_EMULATION
+// For access to PoemFindCharacterInString() as a replacement for strchr().
+#include "starboard/client_porting/poem/string_poem.h"
+#undef POEM_NO_EMULATION
+
+#include "starboard/memory.h"
+#include "starboard/string.h"
+
#endif // STARBOARD
#include <errno.h>
@@ -278,7 +305,7 @@
string* result) {
GOOGLE_CHECK(result != NULL);
result->clear();
- int delim_length = strlen(delim);
+ int delim_length = SbStringGetLength(delim);
// Precompute resulting length so we can reserve() memory in one shot.
int length = 0;
@@ -517,7 +544,7 @@
(last_hex_escape && isxdigit(*src)))) {
if (dest_len - used < 4) // need space for 4 letter escape
return -1;
- sprintf(dest + used, (use_hex ? "\\x%02x" : "\\%03o"),
+ SbStringFormatUnsafeF(dest + used, (use_hex ? "\\x%02x" : "\\%03o"),
static_cast<uint8>(*src));
is_hex_escape = use_hex;
used += 4;
@@ -645,7 +672,7 @@
int32 strto32_adaptor(const char *nptr, char **endptr, int base) {
const int saved_errno = errno;
errno = 0;
- const long result = strtol(nptr, endptr, base);
+ const long result = SbStringParseSignedInteger(nptr, endptr, base);
if (errno == ERANGE && result == LONG_MIN) {
return kint32min;
} else if (errno == ERANGE && result == LONG_MAX) {
@@ -665,7 +692,7 @@
uint32 strtou32_adaptor(const char *nptr, char **endptr, int base) {
const int saved_errno = errno;
errno = 0;
- const unsigned long result = strtoul(nptr, endptr, base);
+ const unsigned long result = SbStringParseUnsignedInteger(nptr, endptr, base);
if (errno == ERANGE && result == ULONG_MAX) {
return kuint32max;
} else if (errno == 0 && result > kuint32max) {
@@ -1227,7 +1254,7 @@
void DelocalizeRadix(char* buffer) {
// Fast check: if the buffer has a normal decimal point, assume no
// translation is needed.
- if (strchr(buffer, '.') != NULL) return;
+ if (PoemFindCharacterInString(buffer, '.') != NULL) return;
// Find the first unknown character.
while (IsValidFloatChar(*buffer)) ++buffer;
@@ -1247,7 +1274,7 @@
// extra bytes.
char* target = buffer;
do { ++buffer; } while (!IsValidFloatChar(*buffer) && *buffer != '\0');
- memmove(target, buffer, strlen(buffer) + 1);
+ SbMemoryMove(target, buffer, SbStringGetLength(buffer) + 1);
}
}
@@ -1259,20 +1286,20 @@
GOOGLE_COMPILE_ASSERT(DBL_DIG < 20, DBL_DIG_is_too_big);
if (value == numeric_limits<double>::infinity()) {
- strcpy(buffer, "inf");
+ SbStringCopyUnsafe(buffer, "inf");
return buffer;
} else if (value == -numeric_limits<double>::infinity()) {
- strcpy(buffer, "-inf");
+ SbStringCopyUnsafe(buffer, "-inf");
return buffer;
} else if (MathLimits<double>::IsNaN(value)) {
- strcpy(buffer, "nan");
+ SbStringCopyUnsafe(buffer, "nan");
return buffer;
}
int snprintf_result =
- snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value);
+ SbStringFormatF(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value);
- // The snprintf should never overflow because the buffer is significantly
+ // The SbStringFormatF should never overflow because the buffer is significantly
// larger than the precision we asked for.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
@@ -1282,10 +1309,10 @@
// of a double. This long double may have extra bits that make it compare
// unequal to "value" even though it would be exactly equal if it were
// truncated to a double.
- volatile double parsed_value = strtod(buffer, NULL);
+ volatile double parsed_value = SbStringParseDouble(buffer, NULL);
if (parsed_value != value) {
int snprintf_result =
- snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value);
+ SbStringFormatF(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value);
// Should never overflow; see above.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize);
@@ -1334,7 +1361,7 @@
char* endptr;
errno = 0; // errno only gets set on errors
#if defined(_WIN32) || defined (__hpux) || defined(STARBOARD) // has no strtof()
- *value = strtod(str, &endptr);
+ *value = SbStringParseDouble(str, &endptr);
#else
*value = strtof(str, &endptr);
#endif
@@ -1343,7 +1370,7 @@
bool safe_strtod(const char* str, double* value) {
char* endptr;
- *value = strtod(str, &endptr);
+ *value = SbStringParseDouble(str, &endptr);
if (endptr != str) {
while (ascii_isspace(*endptr)) ++endptr;
}
@@ -1377,27 +1404,27 @@
GOOGLE_COMPILE_ASSERT(FLT_DIG < 10, FLT_DIG_is_too_big);
if (value == numeric_limits<double>::infinity()) {
- strcpy(buffer, "inf");
+ SbStringCopyUnsafe(buffer, "inf");
return buffer;
} else if (value == -numeric_limits<double>::infinity()) {
- strcpy(buffer, "-inf");
+ SbStringCopyUnsafe(buffer, "-inf");
return buffer;
} else if (MathLimits<float>::IsNaN(value)) {
- strcpy(buffer, "nan");
+ SbStringCopyUnsafe(buffer, "nan");
return buffer;
}
int snprintf_result =
- snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value);
+ SbStringFormatF(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value);
- // The snprintf should never overflow because the buffer is significantly
- // larger than the precision we asked for.
+ // The SbStringFormatF should never overflow because the buffer is
+ // significantly larger than the precision we asked for.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
float parsed_value;
if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) {
int snprintf_result =
- snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value);
+ SbStringFormatF(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value);
// Should never overflow; see above.
GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize);
@@ -1437,35 +1464,35 @@
// of a mix of raw C strings, C++ strings, and integer values.
// ----------------------------------------------------------------------
-// Append is merely a version of memcpy that returns the address of the byte
+// Append is merely a version of SbMemoryCopy that returns the address of the byte
// after the area just overwritten. It comes in multiple flavors to minimize
// call overhead.
static char *Append1(char *out, const AlphaNum &x) {
- memcpy(out, x.data(), x.size());
+ SbMemoryCopy(out, x.data(), x.size());
return out + x.size();
}
static char *Append2(char *out, const AlphaNum &x1, const AlphaNum &x2) {
- memcpy(out, x1.data(), x1.size());
+ SbMemoryCopy(out, x1.data(), x1.size());
out += x1.size();
- memcpy(out, x2.data(), x2.size());
+ SbMemoryCopy(out, x2.data(), x2.size());
return out + x2.size();
}
static char *Append4(char *out,
const AlphaNum &x1, const AlphaNum &x2,
const AlphaNum &x3, const AlphaNum &x4) {
- memcpy(out, x1.data(), x1.size());
+ SbMemoryCopy(out, x1.data(), x1.size());
out += x1.size();
- memcpy(out, x2.data(), x2.size());
+ SbMemoryCopy(out, x2.data(), x2.size());
out += x2.size();
- memcpy(out, x3.data(), x3.size());
+ SbMemoryCopy(out, x3.data(), x3.size());
out += x3.size();
- memcpy(out, x4.data(), x4.size());
+ SbMemoryCopy(out, x4.data(), x4.size());
return out + x4.size();
}
@@ -2271,7 +2298,7 @@
len = 4;
}
tmp = ghtonl(tmp);
- memcpy(output, reinterpret_cast<const char*>(&tmp) + sizeof(tmp) - len, len);
+ SbMemoryCopy(output, reinterpret_cast<const char*>(&tmp) + sizeof(tmp) - len, len);
return len;
}
diff --git a/src/third_party/skia/include/core/SkCanvas.h b/src/third_party/skia/include/core/SkCanvas.h
index 8fbca71..afd5e96 100644
--- a/src/third_party/skia/include/core/SkCanvas.h
+++ b/src/third_party/skia/include/core/SkCanvas.h
@@ -180,6 +180,15 @@
*/
virtual GrContext* getGrContext();
+#if defined(COBALT)
+ /**
+ * Return the framebuffer object identifier for the render target, if
+ * applicable.
+ * NOTE: This will cause a flush, so use sparingly.
+ */
+ intptr_t getRenderTargetHandle() const;
+#endif
+
///////////////////////////////////////////////////////////////////////////
/**
diff --git a/src/third_party/skia/src/core/SkCanvas.cpp b/src/third_party/skia/src/core/SkCanvas.cpp
index dab5be4..9302d6b 100644
--- a/src/third_party/skia/src/core/SkCanvas.cpp
+++ b/src/third_party/skia/src/core/SkCanvas.cpp
@@ -1679,6 +1679,19 @@
return device ? device->context() : nullptr;
}
+#if defined(COBALT)
+intptr_t SkCanvas::getRenderTargetHandle() const {
+ if (fSurfaceBase) {
+ GrBackendObject handle;
+ if (fSurfaceBase->getRenderTargetHandle(&handle,
+ SkSurface::kFlushRead_BackendHandleAccess)) {
+ return handle;
+ }
+ }
+ return 0;
+}
+#endif
+
void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
const SkPaint& paint) {
TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
diff --git a/src/tools/gyp/pylib/gyp/generator/ninja.py b/src/tools/gyp/pylib/gyp/generator/ninja.py
index 41fd685..9dcf99a 100755
--- a/src/tools/gyp/pylib/gyp/generator/ninja.py
+++ b/src/tools/gyp/pylib/gyp/generator/ninja.py
@@ -77,8 +77,9 @@
is_linux = platform.system() == 'Linux'
is_windows = platform.system() == 'Windows'
-microsoft_flavors = ['win', 'win-win32', 'win-win32-lib', 'xb1', 'xb1-future',
- 'xb1-youtubetv']
+microsoft_flavors = [
+ 'win', 'win-win32', 'win-win32-lib', 'xb1', 'xb1-future', 'xb1-youtubetv'
+]
sony_flavors = ['ps3', 'ps4', 'ps4-vr']
windows_host_flavors = microsoft_flavors + sony_flavors
@@ -119,7 +120,7 @@
flags = config.get(keyword, [])
if toolset == 'host':
flags = config.get('{0}_host'.format(keyword), flags)
- return flags
+ return [flag.replace('$', '$$') for flag in flags]
def StripPrefix(arg, prefix):
@@ -222,6 +223,13 @@
def UsesToc(self, flavor):
"""Return true if the target should produce a restat rule based on a TOC
file."""
+ try:
+ # Do not use TOC files for abstract toolchain.
+ toolchain = GetTargetToolchain(flavor)
+ return False
+ except NotImplementedError:
+ # Follow the logic for the legacy toolchain.
+ pass
# For bundles, the .TOC should be produced for the binary, not for
# FinalOutput(). But the naive approach would put the TOC file into the
# bundle, so don't do this for bundles for now.
@@ -673,8 +681,8 @@
all_outputs = []
for action in actions:
# First write out a rule for the action.
- name = '%s_%s' % (action['action_name'],
- hashlib.md5(self.qualified_target).hexdigest())
+ name = '%s_%s' % (action['action_name'], hashlib.md5(
+ self.qualified_target).hexdigest())
description = self.GenerateDescription('ACTION',
action.get('message', None), name)
is_cygwin = self.IsCygwinRule(action)
@@ -703,15 +711,14 @@
all_outputs = []
for rule in rules:
# First write out a rule for the rule action.
- name = '%s_%s' % (rule['rule_name'],
- hashlib.md5(self.qualified_target).hexdigest())
+ name = '%s_%s' % (rule['rule_name'], hashlib.md5(
+ self.qualified_target).hexdigest())
# Skip a rule with no action and no inputs.
if 'action' not in rule and not rule.get('rule_sources', []):
continue
args = rule['action']
description = self.GenerateDescription(
- 'RULE',
- rule.get('message', None),
+ 'RULE', rule.get('message', None),
('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name)
is_cygwin = self.IsCygwinRule(rule)
rule_name, args = self.WriteNewNinjaRule(
@@ -936,9 +943,8 @@
objcxx_compiler = FindFirstInstanceOf(abstract.ObjectiveCxxCompiler,
toolchain)
if objcxx_compiler:
- objcxx_compiler_flags = objcxx_compiler.GetFlags(defines, include_dirs,
- cflags + cflags_cc +
- cflags_mm)
+ objcxx_compiler_flags = objcxx_compiler.GetFlags(
+ defines, include_dirs, cflags + cflags_cc + cflags_mm)
self.ninja.variable(
'{0}_flags'.format(GetNinjaRuleName(objcxx_compiler, self.toolset)),
shell.Join(objcxx_compiler_flags))
@@ -1036,9 +1042,9 @@
defines = config.get('defines', []) + extra_defines
if GetToolchainOrNone(self.flavor):
- self.WriteVariableList('defines', [
- GetToolchainOrNone(self.flavor).Define(d) for d in defines
- ])
+ self.WriteVariableList(
+ 'defines',
+ [GetToolchainOrNone(self.flavor).Define(d) for d in defines])
else:
self.WriteVariableList('defines',
[Define(d, self.flavor) for d in defines])
@@ -1184,7 +1190,7 @@
toolchain = GetHostToolchain(self.flavor)
shell = GetShell(self.flavor)
-
+ extra_bindings = []
target_type = spec['type']
if target_type == 'executable':
executable_linker = FindFirstInstanceOf(abstract.ExecutableLinker,
@@ -1208,9 +1214,35 @@
executable_linker_flags = executable_linker.GetFlags(ldflags)
self.ninja.variable('{0}_flags'.format(rule_name),
shell.Join(executable_linker_flags))
+ elif target_type == 'shared_library':
+ shared_library_linker = FindFirstInstanceOf(
+ abstract.SharedLibraryLinker, toolchain)
+ assert shared_library_linker, (
+ 'Toolchain must provide shared library linker '
+ 'for {0} platform.').format(self.toolset)
+
+ rule_name = GetNinjaRuleName(shared_library_linker, self.toolset)
+
+ # TODO: This code emulates legacy toolchain behavior. We need to migrate
+ # to single-responsibility, toolchain-independent GYP keywords as
+ # per abstract toolchain design doc.
+ libraries_keyword = 'libraries{0}'.format('_host' if self.toolset ==
+ 'host' else '')
+ libraries = spec.get(libraries_keyword, []) + config.get(
+ libraries_keyword, [])
+ ldflags = gyp.common.uniquer(
+ map(self.ExpandSpecial,
+ GetConfigFlags(config, self.toolset, 'ldflags') + libraries))
+
+ shared_library_linker_flags = shared_library_linker.GetFlags(ldflags)
+ self.ninja.variable('{0}_flags'.format(rule_name),
+ shell.Join(shared_library_linker_flags))
+ output = self.ComputeOutput(spec)
+ extra_bindings.append(('soname', os.path.split(output)[1]))
+
else:
raise Exception('Target type {0} is not supported for target {1}.'
- .format(target_type, spec['target_name']))
+ .format(target_type, spec['target_name']))
order_only_deps = set()
@@ -1246,7 +1278,11 @@
self.target.binary = output
self.ninja.build(
- output, rule_name, link_deps, order_only=list(order_only_deps))
+ output,
+ rule_name,
+ link_deps,
+ order_only=list(order_only_deps),
+ variables=extra_bindings)
except NotImplementedError:
# Fall back to the legacy toolchain.
@@ -1306,8 +1342,8 @@
output = self.ComputeMacBundleBinaryOutput()
else:
output = self.ComputeOutput(spec)
- extra_bindings.append(('postbuilds', self.GetPostbuildCommand(
- spec, output, output)))
+ extra_bindings.append(('postbuilds',
+ self.GetPostbuildCommand(spec, output, output)))
if self.flavor == 'mac':
ldflags = self.xcode_settings.GetLdflags(
@@ -1521,8 +1557,8 @@
abs_build_dir = self.abs_build_dir
return gyp.xcode_emulation.GetSortedXcodeEnv(
self.xcode_settings, abs_build_dir,
- os.path.join(abs_build_dir,
- self.build_to_base), self.config_name, additional_settings)
+ os.path.join(abs_build_dir, self.build_to_base), self.config_name,
+ additional_settings)
def GetSortedXcodePostbuildEnv(self):
"""Returns the variables Xcode would set for postbuild steps."""
@@ -1559,9 +1595,8 @@
return ''
# Postbuilds expect to be run in the gyp file's directory, so insert an
# implicit postbuild to cd to there.
- postbuilds.insert(0,
- gyp.common.EncodePOSIXShellList(
- ['cd', self.build_to_base]))
+ postbuilds.insert(
+ 0, gyp.common.EncodePOSIXShellList(['cd', self.build_to_base]))
env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
# G will be non-null if any postbuild fails. Run all postbuilds in a
# subshell.
@@ -1870,8 +1905,8 @@
"""Called by __init__ to initialize generator values based on params."""
user_config = params.get('generator_flags', {}).get('config', None)
toplevel = params['options'].toplevel_dir
- qualified_out_dir = os.path.normpath(os.path.join(
- toplevel, ComputeOutputDir(params), user_config, 'gypfiles'))
+ qualified_out_dir = os.path.normpath(
+ os.path.join(toplevel, ComputeOutputDir(params), user_config, 'gypfiles'))
global generator_filelist_paths
generator_filelist_paths = {
@@ -1952,9 +1987,8 @@
def MaybeWriteExtraFlagsVariable(ninja, tool, toolset, shell):
if tool.GetExtraFlags():
- ninja.variable(
- '{0}_extra_flags'.format(GetNinjaRuleName(tool, toolset)),
- shell.Join(tool.GetExtraFlags()))
+ ninja.variable('{0}_extra_flags'.format(GetNinjaRuleName(tool, toolset)),
+ shell.Join(tool.GetExtraFlags()))
def MaybeWritePool(ninja, tool, toolset):
@@ -2402,9 +2436,9 @@
else:
assert flavor in microsoft_flavors
# XB1 doesn't need a manifest.
- link_command = ('%s gyp-win-tool link-wrapper $arch '
- '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
- (python_exec))
+ link_command = (
+ '%s gyp-win-tool link-wrapper $arch '
+ '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' % (python_exec))
master_ninja.rule(
'link',
@@ -2560,9 +2594,9 @@
rspfile='$out.rsp',
rspfile_content='$in_newline $libflags_host')
- link_command = ('%s gyp-win-tool link-wrapper $arch '
- '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' %
- (python_exec))
+ link_command = (
+ '%s gyp-win-tool link-wrapper $arch '
+ '$ld /nologo /OUT:$out /PDB:$out.pdb @$out.rsp' % (python_exec))
master_ninja.rule(
'link_host',
@@ -2693,9 +2727,9 @@
master_ninja.newline()
master_ninja.comment('Short names for targets.')
for short_name in target_short_names:
- master_ninja.build(short_name, 'phony', [
- x.FinalOutput() for x in target_short_names[short_name]
- ])
+ master_ninja.build(
+ short_name, 'phony',
+ [x.FinalOutput() for x in target_short_names[short_name]])
if all_outputs:
master_ninja.newline()
diff --git a/src/v8/src/base/atomicops.h b/src/v8/src/base/atomicops.h
index c4c28f7..c160b33 100644
--- a/src/v8/src/base/atomicops.h
+++ b/src/v8/src/base/atomicops.h
@@ -36,9 +36,24 @@
#include "src/base/base-export.h"
#include "src/base/build_config.h"
+#if defined(V8_OS_STARBOARD)
+#include "starboard/atomic.h"
+#endif
+
+#if SB_API_VERSION < SB_INTRODUCE_ATOMIC8_VERSION
+#error Your version of Starboard must support SbAtomic8 in order to use V8.
+#endif
+
namespace v8 {
namespace base {
+#ifdef V8_OS_STARBOARD
+typedef SbAtomic8 Atomic8;
+typedef SbAtomic32 Atomic32;
+#ifdef V8_HOST_ARCH_64_BIT
+typedef SbAtomic64 Atomic64;
+#endif
+#else
typedef char Atomic8;
typedef int32_t Atomic32;
#if defined(V8_HOST_ARCH_64_BIT)
@@ -50,10 +65,15 @@
typedef intptr_t Atomic64;
#endif // defined(__ILP32__)
#endif // defined(V8_HOST_ARCH_64_BIT)
+#endif // V8_OS_STARBOARD
// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
// Atomic64 routines below, depending on your architecture.
+#if defined(V8_OS_STARBOARD)
+typedef SbAtomicPtr AtomicWord;
+#else
typedef intptr_t AtomicWord;
+#endif
// Atomically execute:
// result = *ptr;
diff --git a/src/v8/src/base/atomicops_internals_portable.h b/src/v8/src/base/atomicops_internals_portable.h
index 0779bfb..28ddf40 100644
--- a/src/v8/src/base/atomicops_internals_portable.h
+++ b/src/v8/src/base/atomicops_internals_portable.h
@@ -31,6 +31,10 @@
#include <atomic>
+#if defined(V8_OS_STARBOARD)
+#include "starboard/atomic.h"
+#endif
+
#include "src/base/build_config.h"
#include "src/base/macros.h"
@@ -41,135 +45,231 @@
// atomicops.h.
inline void SeqCst_MemoryFence() {
+#if defined(V8_OS_STARBOARD)
+ SbAtomicMemoryBarrier();
+#else
#if defined(__GLIBCXX__)
// Work around libstdc++ bug 51038 where atomic_thread_fence was declared but
// not defined, leading to the linker complaining about undefined references.
__atomic_thread_fence(std::memory_order_seq_cst);
#else
std::atomic_thread_fence(std::memory_order_seq_cst);
-#endif
+#endif // defined(__GLIBCXX__)
+#endif // defined(V8_OS_STARBOARD)
}
inline Atomic32 Relaxed_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value);
+#else
__atomic_compare_exchange_n(ptr, &old_value, new_value, false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
return old_value;
+#endif
}
inline Atomic32 Relaxed_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_Exchange(ptr, new_value);
+#else
return __atomic_exchange_n(ptr, new_value, __ATOMIC_RELAXED);
+#endif
}
inline Atomic32 Relaxed_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_Increment(ptr, increment);
+#else
return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_RELAXED);
+#endif
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicBarrier_Increment(ptr, increment);
+#else
return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_SEQ_CST);
+#endif
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicAcquire_CompareAndSwap(ptr, old_value, new_value);
+#else
__atomic_compare_exchange_n(ptr, &old_value, new_value, false,
__ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
return old_value;
+#endif
}
inline Atomic8 Release_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value,
Atomic8 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicRelease_CompareAndSwap8(ptr, old_value, new_value);
+#else
bool result = __atomic_compare_exchange_n(ptr, &old_value, new_value, false,
__ATOMIC_RELEASE, __ATOMIC_RELAXED);
USE(result); // Make gcc compiler happy.
return old_value;
+#endif
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicRelease_CompareAndSwap(ptr, old_value, new_value);
+#else
__atomic_compare_exchange_n(ptr, &old_value, new_value, false,
__ATOMIC_RELEASE, __ATOMIC_RELAXED);
return old_value;
+#endif
}
inline void Relaxed_Store(volatile Atomic8* ptr, Atomic8 value) {
+#if defined(V8_OS_STARBOARD)
+ SbAtomicNoBarrier_Store8(ptr, value);
+#else
__atomic_store_n(ptr, value, __ATOMIC_RELAXED);
+#endif
}
inline void Relaxed_Store(volatile Atomic32* ptr, Atomic32 value) {
+#if defined(V8_OS_STARBOARD)
+ SbAtomicNoBarrier_Store(ptr, value);
+#else
__atomic_store_n(ptr, value, __ATOMIC_RELAXED);
+#endif
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+#if defined(V8_OS_STARBOARD)
+ SbAtomicRelease_Store(ptr, value);
+#else
__atomic_store_n(ptr, value, __ATOMIC_RELEASE);
+#endif
}
inline Atomic8 Relaxed_Load(volatile const Atomic8* ptr) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_Load8(ptr);
+#else
return __atomic_load_n(ptr, __ATOMIC_RELAXED);
+#endif
}
inline Atomic32 Relaxed_Load(volatile const Atomic32* ptr) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_Load(ptr);
+#else
return __atomic_load_n(ptr, __ATOMIC_RELAXED);
+#endif
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicAcquire_Load(ptr);
+#else
return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
+#endif
}
#if defined(V8_HOST_ARCH_64_BIT)
inline Atomic64 Relaxed_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value);
+#else
__atomic_compare_exchange_n(ptr, &old_value, new_value, false,
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
return old_value;
+#endif
}
inline Atomic64 Relaxed_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_Exchange64(ptr, new_value);
+#else
return __atomic_exchange_n(ptr, new_value, __ATOMIC_RELAXED);
+#endif
}
inline Atomic64 Relaxed_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_Increment64(ptr, increment);
+#else
return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_RELAXED);
+#endif
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicBarrier_Increment64(ptr, increment);
+#else
return increment + __atomic_fetch_add(ptr, increment, __ATOMIC_SEQ_CST);
+#endif
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicAcquire_CompareAndSwap64(ptr, old_value, new_value);
+#else
__atomic_compare_exchange_n(ptr, &old_value, new_value, false,
__ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
return old_value;
+#endif
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicRelease_CompareAndSwap64(ptr, old_value, new_value);
+#else
__atomic_compare_exchange_n(ptr, &old_value, new_value, false,
__ATOMIC_RELEASE, __ATOMIC_RELAXED);
return old_value;
+#endif
}
inline void Relaxed_Store(volatile Atomic64* ptr, Atomic64 value) {
+#if defined(V8_OS_STARBOARD)
+ SbAtomicNoBarrier_Store64(ptr, value);
+#else
__atomic_store_n(ptr, value, __ATOMIC_RELAXED);
+#endif
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+#if defined(V8_OS_STARBOARD)
+ SbAtomicRelease_Store64(ptr, value);
+#else
__atomic_store_n(ptr, value, __ATOMIC_RELEASE);
+#endif
}
inline Atomic64 Relaxed_Load(volatile const Atomic64* ptr) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicNoBarrier_Load64(ptr);
+#else
return __atomic_load_n(ptr, __ATOMIC_RELAXED);
+#endif
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+#if defined(V8_OS_STARBOARD)
+ return SbAtomicAcquire_Load64(ptr);
+#else
return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
+#endif
}
#endif // defined(V8_HOST_ARCH_64_BIT)
diff --git a/src/v8/src/base/platform/platform-starboard.cc b/src/v8/src/base/platform/platform-starboard.cc
index 564d85d..d3f16a0 100644
--- a/src/v8/src/base/platform/platform-starboard.cc
+++ b/src/v8/src/base/platform/platform-starboard.cc
@@ -132,13 +132,19 @@
void* OS::GetRandomMmapAddr() { return nullptr; }
void* Allocate(void* address, size_t size, OS::MemoryPermission access) {
- // Starboard has no concept of changing permissions after allocating memory,
- // so we have to allocate everything with rwx permissions and then no-op on
- // changes. TODO: Actually use |access| once Starboard supports changing
- // permissions after allocation.
- SB_UNREFERENCED_PARAMETER(access);
- SbMemoryMapFlags sb_flags =
- SbMemoryMapFlags(kSbMemoryMapProtectReadWrite | kSbMemoryMapProtectExec);
+ SbMemoryMapFlags sb_flags;
+ switch (access) {
+ case OS::MemoryPermission::kNoAccess:
+ sb_flags = SbMemoryMapFlags(0);
+ break;
+ case OS::MemoryPermission::kReadWrite:
+ sb_flags = SbMemoryMapFlags(kSbMemoryMapProtectReadWrite);
+ break;
+ default:
+ SB_LOG(ERROR) << "The requested memory allocation access is not"
+ " implemented for Starboard: " << static_cast<int>(access);
+ return nullptr;
+ }
void* result = SbMemoryMap(size, sb_flags, "v8::Base::Allocate");
if (result == SB_MEMORY_MAP_FAILED) {
return nullptr;
@@ -192,7 +198,23 @@
// static
bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
- return true;
+ SbMemoryMapFlags new_protection;
+ switch (access) {
+ case OS::MemoryPermission::kNoAccess:
+ new_protection = SbMemoryMapFlags(0);
+ break;
+ case OS::MemoryPermission::kReadWrite:
+ new_protection = SbMemoryMapFlags(kSbMemoryMapProtectReadWrite);
+ break;
+ case OS::MemoryPermission::kReadExecute:
+ new_protection = SbMemoryMapFlags(kSbMemoryMapProtectRead |
+ kSbMemoryMapProtectExec);
+ break;
+ default:
+ // All other types are not supported by Starboard.
+ return false;
+ }
+ return SbMemoryProtect(address, size, new_protection);
}
// static
@@ -433,4 +455,4 @@
void OS::SignalCodeMovingGC() { SB_NOTIMPLEMENTED(); }
} // namespace base
-} // namespace v8
\ No newline at end of file
+} // namespace v8
diff --git a/src/v8/src/base/platform/time.cc b/src/v8/src/base/platform/time.cc
index 1e758d2..67dae0b 100644
--- a/src/v8/src/base/platform/time.cc
+++ b/src/v8/src/base/platform/time.cc
@@ -420,7 +420,7 @@
#elif V8_OS_STARBOARD
Time Time::Now() {
- return Time(SbTimeGetNow());
+ return Time(SbTimeToPosix(SbTimeGetNow()));
}
Time Time::NowFromSystemTime() {
@@ -785,4 +785,4 @@
#endif // V8_OS_WIN
} // namespace base
-} // namespace v8
\ No newline at end of file
+} // namespace v8
diff --git a/src/v8/src/d8.gyp b/src/v8/src/d8.gyp
index 707a4e0..085d59c 100644
--- a/src/v8/src/d8.gyp
+++ b/src/v8/src/d8.gyp
@@ -53,6 +53,7 @@
'v8.gyp:v8',
'v8.gyp:v8_libbase',
'v8.gyp:v8_libplatform',
+ '<(DEPTH)/starboard/starboard.gyp:starboard',
],
# Generated source files need this explicitly:
'include_dirs+': [
diff --git a/src/v8/src/flag-definitions.h b/src/v8/src/flag-definitions.h
index dbf8a63..71e33ac 100644
--- a/src/v8/src/flag-definitions.h
+++ b/src/v8/src/flag-definitions.h
@@ -652,7 +652,10 @@
#endif // defined(COBALT)
DEFINE_BOOL(parallel_scavenge, true, "parallel scavenge")
DEFINE_BOOL(trace_parallel_scavenge, false, "trace parallel scavenge")
-DEFINE_BOOL(write_protect_code_memory, false, "write protect code memory")
+#if defined(COBALT)
+// Starboard disallow rwx memory access.
+DEFINE_BOOL(write_protect_code_memory, true, "write protect code memory")
+#endif
#ifdef V8_CONCURRENT_MARKING
#define V8_CONCURRENT_MARKING_BOOL true
#else