summaryrefslogtreecommitdiff
path: root/src/jpeg/jpeg_scanner.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/jpeg/jpeg_scanner.cc')
-rw-r--r--src/jpeg/jpeg_scanner.cc120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/jpeg/jpeg_scanner.cc b/src/jpeg/jpeg_scanner.cc
new file mode 100644
index 0000000..85426b2
--- /dev/null
+++ b/src/jpeg/jpeg_scanner.cc
@@ -0,0 +1,120 @@
+#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);
+ }
+ stringstream sstream;
+ sstream << location;
+ MessageHandler::Get()->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