| #include "image_io/jpeg/jpeg_scanner.h" |
| |
| #include <sstream> |
| |
| #include "image_io/base/message_handler.h" |
| #include "image_io/jpeg/jpeg_segment.h" |
| |
| namespace photos_editing_formats { |
| namespace image_io { |
| |
| using std::stringstream; |
| |
| /// The minimum size for the DataSegments requested from the DataSource. Using |
| /// this value will guarentee that a JpegSegment will occupy at most two |
| /// DataSegments. |
| const size_t kMinBufferDataRequestSize = 0x10000; |
| |
| void JpegScanner::Run(DataSource* data_source, |
| JpegSegmentProcessor* segment_processor) { |
| if (data_source_) { |
| // The Run() function is already active. |
| return; |
| } |
| data_source_ = data_source; |
| segment_processor_ = segment_processor; |
| current_location_ = 0; |
| done_ = false; |
| has_error_ = false; |
| data_source_->Reset(); |
| current_segment_ = data_source_->GetDataSegment(current_location_, |
| kMinBufferDataRequestSize); |
| segment_processor_->Start(this); |
| FindAndProcessSegments(); |
| segment_processor_->Finish(this); |
| data_source_ = nullptr; |
| segment_processor_ = nullptr; |
| current_segment_.reset(); |
| next_segment_.reset(); |
| } |
| |
| void JpegScanner::FindAndProcessSegments() { |
| while (!IsDone() && !HasError()) { |
| size_t begin_segment_location = |
| current_segment_->Find(current_location_, JpegMarker::kStart); |
| if (begin_segment_location == current_segment_->GetEnd()) { |
| GetNextSegment(); |
| if (next_segment_) { |
| current_location_ = |
| std::max(current_location_, next_segment_->GetBegin()); |
| current_segment_ = next_segment_; |
| next_segment_.reset(); |
| continue; |
| } |
| SetDone(); |
| break; |
| } |
| size_t payload_size = 0; |
| JpegMarker marker( |
| GetByte(begin_segment_location + JpegMarker::kTypeOffset)); |
| if (marker.IsValid() && !HasError()) { |
| payload_size = GetPayloadSize(marker, begin_segment_location); |
| if (marker.IsValid() && interesting_marker_flags_[marker.GetType()]) { |
| size_t end_segment_location = |
| begin_segment_location + JpegMarker::kLength + payload_size; |
| GetByte(end_segment_location - 1); |
| if (!HasError()) { |
| JpegSegment segment(begin_segment_location, end_segment_location, |
| current_segment_.get(), next_segment_.get()); |
| segment_processor_->Process(this, segment); |
| } |
| } |
| } |
| current_location_ = |
| begin_segment_location + JpegMarker::kLength + payload_size; |
| } |
| } |
| |
| size_t JpegScanner::GetPayloadSize(const JpegMarker& marker, |
| size_t begin_location) { |
| if (marker.HasVariablePayloadSize()) { |
| return (GetByte(begin_location + JpegMarker::kLength) << 8) | |
| GetByte(begin_location + JpegMarker::kLength + 1); |
| } else { |
| return 0; |
| } |
| } |
| |
| ValidatedByte JpegScanner::GetValidatedByte(size_t location) { |
| if (current_segment_->Contains(location)) { |
| return current_segment_->GetValidatedByte(location); |
| } |
| GetNextSegment(); |
| if (next_segment_ && next_segment_->Contains(location)) { |
| return next_segment_->GetValidatedByte(location); |
| } |
| if (message_handler_) { |
| stringstream sstream; |
| sstream << location; |
| message_handler_->ReportMessage(Message::kPrematureEndOfDataError, |
| sstream.str()); |
| } |
| return InvalidByte(); |
| } |
| |
| Byte JpegScanner::GetByte(size_t location) { |
| ValidatedByte validated_byte = GetValidatedByte(location); |
| if (validated_byte.is_valid) { |
| return validated_byte.value; |
| } |
| has_error_ = true; |
| return 0; |
| } |
| |
| void JpegScanner::GetNextSegment() { |
| if (!next_segment_ && current_segment_) { |
| next_segment_ = data_source_->GetDataSegment(current_segment_->GetEnd(), |
| kMinBufferDataRequestSize); |
| } |
| } |
| |
| } // namespace image_io |
| } // namespace photos_editing_formats |