| /* |
| * Copyright 2014 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Author: Victor Chudnovsky |
| |
| #include "pagespeed/kernel/image/frame_interface_optimizer.h" |
| |
| #include "pagespeed/kernel/base/message_handler.h" |
| #include "pagespeed/kernel/base/string.h" |
| |
| namespace pagespeed { |
| |
| namespace image_compression { |
| |
| // Takes ownership of reader. |
| MultipleFramePaddingReader::MultipleFramePaddingReader( |
| MultipleFrameReader* reader) : |
| MultipleFrameReader(reader->message_handler()), |
| impl_(reader) { |
| } |
| |
| MultipleFramePaddingReader::~MultipleFramePaddingReader() {} |
| |
| ScanlineStatus MultipleFramePaddingReader::Reset() { |
| return impl_->Reset(); |
| } |
| |
| ScanlineStatus MultipleFramePaddingReader::Initialize() { |
| ScanlineStatus status = impl_->Initialize(image_buffer_, buffer_length_); |
| if (status.Success()) { |
| status = impl_->GetImageSpec(&image_spec_); |
| } |
| return status; |
| } |
| |
| bool MultipleFramePaddingReader::HasMoreFrames() const { |
| return impl_->HasMoreFrames(); |
| } |
| |
| bool MultipleFramePaddingReader::HasMoreScanlines() const { |
| return current_scanline_idx_ < padded_frame_spec_.height; |
| } |
| |
| ScanlineStatus MultipleFramePaddingReader::PrepareNextFrame() { |
| frame_needs_no_padding_ = false; |
| frame_is_full_height_ = false; |
| frame_is_full_width_ = false; |
| |
| // If image_spec_.use_bg_color == false, then we pad the frame with |
| // the transparent color defined in kTransparent. |
| static const PixelRgbaChannels kTransparent = {0, 0, 0, kAlphaTransparent}; |
| |
| ScanlineStatus status; |
| if (impl_->PrepareNextFrame(&status) && |
| impl_->GetFrameSpec(&impl_frame_spec_, &status)) { |
| // Bounds-check the FrameSpec. |
| impl_frame_spec_.left = image_spec_.TruncateXIndex(impl_frame_spec_.left); |
| impl_frame_spec_.width = |
| image_spec_.TruncateXIndex(impl_frame_spec_.left + |
| impl_frame_spec_.width) - |
| impl_frame_spec_.left; |
| |
| padded_frame_spec_ = impl_frame_spec_; |
| padded_frame_spec_.width = image_spec_.width; |
| padded_frame_spec_.height = image_spec_.height; |
| padded_frame_spec_.top = 0; |
| padded_frame_spec_.left = 0; |
| |
| bytes_per_pixel_ = GetBytesPerPixel(padded_frame_spec_.pixel_format); |
| size_px scanline_num_bytes = padded_frame_spec_.width * bytes_per_pixel_; |
| |
| current_scanline_.reset(new uint8_t[scanline_num_bytes]); |
| scanline_template_.reset(new uint8_t[scanline_num_bytes]); |
| |
| uint8_t* template_ptr_end = scanline_template_.get() + scanline_num_bytes; |
| const void* bg_color = (image_spec_.use_bg_color ? |
| image_spec_.bg_color : kTransparent); |
| for (uint8_t* template_ptr = scanline_template_.get(); |
| template_ptr < template_ptr_end; |
| template_ptr += bytes_per_pixel_) { |
| memcpy(template_ptr, bg_color, bytes_per_pixel_); |
| } |
| |
| current_scanline_idx_ = 0; |
| // These are guaranteed to be in range because impl_frame_spec_ |
| // was itself bounds-checked above. |
| size_px foreground_scanline_start_idx = impl_frame_spec_.left; |
| size_px foreground_scanline_end_idx = (impl_frame_spec_.left + |
| impl_frame_spec_.width); |
| foreground_scanline_start_byte_ = |
| (current_scanline_.get() + |
| bytes_per_pixel_ * foreground_scanline_start_idx); |
| |
| frame_is_full_width_ = ((foreground_scanline_start_idx == 0) && |
| (foreground_scanline_end_idx == image_spec_.width)); |
| frame_is_full_height_ = ((impl_frame_spec_.top == 0) && |
| (impl_frame_spec_.height == image_spec_.height)); |
| frame_needs_no_padding_ = frame_is_full_width_ && frame_is_full_height_; |
| |
| // Set the background color for all the scanlines to follow. Note |
| // that since the foreground is rectangular, the same foreground |
| // pixels will get overwritten in each scanline, while the |
| // background pixels remain untouched. |
| memcpy(current_scanline_.get(), |
| scanline_template_.get(), |
| scanline_num_bytes); |
| } |
| return status; |
| } |
| |
| ScanlineStatus MultipleFramePaddingReader::ReadNextScanline( |
| const void** out_scanline_bytes) { |
| if (frame_needs_no_padding_) { |
| // Short-circuit any additional computations. |
| ++current_scanline_idx_; |
| return impl_->ReadNextScanline(out_scanline_bytes); |
| } |
| |
| if (!HasMoreScanlines()) { |
| return PS_LOGGED_STATUS(PS_LOG_DFATAL, message_handler(), |
| SCANLINE_STATUS_INVOCATION_ERROR, |
| FRAME_PADDING_READER, |
| "no more scanlines in the current frame"); |
| } |
| |
| const void* impl_scanline = NULL; |
| ScanlineStatus status; |
| |
| if (frame_is_full_height_ || |
| ((current_scanline_idx_ >= impl_frame_spec_.top) && |
| (current_scanline_idx_ < (impl_frame_spec_.top + |
| impl_frame_spec_.height)))) { |
| // This scanline contains foreground pixels. |
| |
| // If a full-width row, we can short-circuit the remaining |
| // computations. |
| if (frame_is_full_width_) { |
| ++current_scanline_idx_; |
| return impl_->ReadNextScanline(out_scanline_bytes); |
| } |
| |
| // Read the foreground row for use below. |
| if (!impl_->ReadNextScanline(&impl_scanline, &status)) { |
| return status; |
| } |
| } |
| |
| if (impl_scanline == NULL) { |
| // This scanline contains only background pixels. |
| *out_scanline_bytes = scanline_template_.get(); |
| } else { |
| // Overwrite the foreground pixels appropriately. Note that the |
| // background pixels were already set in PrepareNextFrame. |
| memcpy(foreground_scanline_start_byte_, |
| impl_scanline, |
| bytes_per_pixel_ * impl_frame_spec_.width); |
| *out_scanline_bytes = current_scanline_.get(); |
| } |
| |
| ++current_scanline_idx_; |
| return status; |
| } |
| |
| ScanlineStatus MultipleFramePaddingReader::GetFrameSpec( |
| FrameSpec* frame_spec) const { |
| *frame_spec = padded_frame_spec_; |
| return ScanlineStatus(SCANLINE_STATUS_SUCCESS); |
| } |
| |
| ScanlineStatus MultipleFramePaddingReader::GetImageSpec( |
| ImageSpec* image_spec) const { |
| ScanlineStatus status = impl_->GetImageSpec(image_spec); |
| if (status.Success() && !image_spec->Equals(image_spec_)) { |
| return ScanlineStatus(SCANLINE_STATUS_INTERNAL_ERROR, |
| FRAME_PADDING_READER, |
| "ImageSpec changed during image processing"); |
| } |
| return status; |
| } |
| |
| MessageHandler* MultipleFramePaddingReader::message_handler() const { |
| return impl_->message_handler(); |
| } |
| |
| ScanlineStatus MultipleFramePaddingReader::set_quirks_mode( |
| QuirksMode quirks_mode) { |
| return impl_->set_quirks_mode(quirks_mode); |
| } |
| |
| QuirksMode MultipleFramePaddingReader::quirks_mode() const { |
| return impl_->quirks_mode(); |
| } |
| |
| |
| } // namespace image_compression |
| |
| } // namespace pagespeed |