| // 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/loader/image/webp_image_decoder.h" |
| |
| #include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| #include "cobalt/loader/image/animated_webp_image.h" |
| #include "starboard/memory.h" |
| |
| namespace cobalt { |
| namespace loader { |
| namespace image { |
| |
| WEBPImageDecoder::WEBPImageDecoder( |
| render_tree::ResourceProvider* resource_provider) |
| : ImageDataDecoder(resource_provider), |
| internal_decoder_(NULL), |
| has_animation_(false) { |
| TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::WEBPImageDecoder()"); |
| // Initialize the configuration as empty. |
| WebPInitDecoderConfig(&config_); |
| // Skip the in-loop filtering. |
| config_.options.bypass_filtering = 1; |
| // Use faster pointwise upsampler. |
| 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() { |
| TRACE_EVENT0("cobalt::loader::image", |
| "WEBPImageDecoder::~WEBPImageDecoder()"); |
| DeleteInternalDecoder(); |
| } |
| |
| uint8_t* WEBPImageDecoder::GetOriginalMemory() { |
| return config_.output.u.RGBA.rgba; |
| } |
| |
| size_t WEBPImageDecoder::DecodeChunkInternal(const uint8* data, |
| size_t input_byte) { |
| TRACE_EVENT0("cobalt::loader::image", |
| "WEBPImageDecoder::DecodeChunkInternal()"); |
| if (state() == kWaitingForHeader) { |
| if (!ReadHeader(data, input_byte)) { |
| return 0; |
| } |
| |
| if (!config_.input.has_animation) { |
| if (!AllocateImageData( |
| math::Size(config_.input.width, config_.input.height), |
| !!config_.input.has_alpha)) { |
| return 0; |
| } |
| if (!CreateInternalDecoder(!!config_.input.has_alpha)) { |
| return 0; |
| } |
| } else { |
| has_animation_ = true; |
| animated_webp_image_ = new AnimatedWebPImage( |
| math::Size(config_.input.width, config_.input.height), |
| !!config_.input.has_alpha, resource_provider()); |
| } |
| set_state(kReadLines); |
| } |
| |
| if (state() == kReadLines) { |
| if (!config_.input.has_animation) { |
| // 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. |
| VP8StatusCode status = WebPIAppend(internal_decoder_, data, input_byte); |
| if (status == VP8_STATUS_OK) { |
| DCHECK(image_data()); |
| DCHECK(config_.output.u.RGBA.rgba); |
| SbMemoryCopy(image_data()->GetMemory(), config_.output.u.RGBA.rgba, |
| config_.output.u.RGBA.size); |
| set_state(kDone); |
| } else if (status != VP8_STATUS_SUSPENDED) { |
| DLOG(ERROR) << "WebPIAppend error, status code: " << status; |
| DeleteInternalDecoder(); |
| set_state(kError); |
| } |
| } else { |
| animated_webp_image_->AppendChunk(data, input_byte); |
| } |
| } |
| |
| return input_byte; |
| } |
| |
| void WEBPImageDecoder::FinishInternal() { |
| if (config_.input.has_animation) { |
| set_state(kDone); |
| } |
| } |
| |
| bool WEBPImageDecoder::ReadHeader(const uint8* data, size_t size) { |
| TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::ReadHeader()"); |
| // 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. |
| VP8StatusCode status = WebPGetFeatures(data, size, &config_.input); |
| if (status == VP8_STATUS_OK) { |
| return true; |
| } else if (status == VP8_STATUS_NOT_ENOUGH_DATA) { |
| // Data is not enough for decoding the header. |
| return false; |
| } else { |
| DLOG(ERROR) << "WebPGetFeatures error, status code: " << status; |
| set_state(kError); |
| return false; |
| } |
| } |
| |
| bool WEBPImageDecoder::CreateInternalDecoder(bool has_alpha) { |
| TRACE_EVENT0("cobalt::loader::image", |
| "WEBPImageDecoder::CreateInternalDecoder()"); |
| config_.output.colorspace = has_alpha ? MODE_rgbA : MODE_RGBA; |
| config_.output.u.RGBA.stride = image_data()->GetDescriptor().pitch_in_bytes; |
| config_.output.u.RGBA.size = |
| static_cast<size_t>(config_.output.u.RGBA.stride * |
| image_data()->GetDescriptor().size.height()); |
| // We don't use image buffer as the decoding buffer because libwebp will read |
| // from it while we assume that our image buffer is write only. |
| config_.output.is_external_memory = 0; |
| |
| // Instantiate a new incremental decoder object with the requested |
| // configuration. |
| internal_decoder_ = WebPIDecode(NULL, 0, &config_); |
| |
| if (internal_decoder_ == NULL) { |
| DLOG(WARNING) << "Create internal WEBP decoder failed."; |
| set_state(kError); |
| return false; |
| } |
| return true; |
| } |
| |
| void WEBPImageDecoder::DeleteInternalDecoder() { |
| TRACE_EVENT0("cobalt::loader::image", |
| "WEBPImageDecoder::DeleteInternalDecoder()"); |
| if (internal_decoder_) { |
| // Deletes the WebPIDecoder object and associated memory. Must always be |
| // called if WebPIDecode succeeded. |
| WebPIDelete(internal_decoder_); |
| internal_decoder_ = NULL; |
| WebPFreeDecBuffer(&config_.output); |
| } |
| } |
| |
| } // namespace image |
| } // namespace loader |
| } // namespace cobalt |