aboutsummaryrefslogtreecommitdiff
path: root/third_party/image_io/src/jpeg/jpeg_segment_builder.cc
blob: aafb5253bd8e6e183bb7771cccdbf6d6a0c04505 (plain)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "image_io/jpeg/jpeg_segment_builder.h"

#include "image_io/jpeg/jpeg_marker.h"

namespace photos_editing_formats {
namespace image_io {

using std::string;

// The strings needed to build the xml data associated with XMP data. See
// https://wwwimages2.adobe.com/content/dam/acom/en/devnet/xmp/pdfs/
//   XMP%20SDK%20Release%20cc-2016-08/XMPSpecificationPart1.pdf
const char kXmpMetaPrefix[] = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">";
const char kXmpMetaSuffix[] = "</x:xmpmeta>";
const char kRdfPrefix[] =
    "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
    "xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">";
const char kRdfSuffix[] = "</rdf:RDF>";
const char kRdfDescriptionPrefix[] = "<rdf:Description rdf:about=\"\"";
const char kRdfDescriptionSuffix[] = "/>";

bool JpegSegmentBuilder::SetPayloadSize(ByteBuffer* byte_buffer) {
  std::uint16_t size = byte_buffer->GetSize();
  if (size == byte_buffer->GetSize() && size >= 4) {
    return byte_buffer->SetBigEndianValue(2, size - 2);
  }
  return false;
}

string JpegSegmentBuilder::GetByteDataValues() const {
  string values;
  for (const auto& byte_datum : byte_data_) {
    if (!byte_datum.IsValid()) {
      return "";
    }
    values += byte_datum.GetValue();
    if (byte_datum.GetType() == ByteData::kAscii0) {
      values.append(1, 0);
    }
  }
  return values;
}

void JpegSegmentBuilder::AddMarkerAndSize(Byte marker_type, size_t size) {
  JpegMarker marker(marker_type);
  string hex_string = marker.GetHexString("FF");
  if (marker.HasVariablePayloadSize()) {
    hex_string += ByteData::Byte2Hex((size >> 8) & 0xFF);
    hex_string += ByteData::Byte2Hex(size & 0xFF);
  }
  byte_data_.emplace_back(ByteData::kHex, hex_string);
}

size_t JpegSegmentBuilder::AddMarkerAndSizePlaceholder(Byte marker_type) {
  JpegMarker marker(marker_type);
  string hex_string = marker.GetHexString("FF");
  if (marker.HasVariablePayloadSize()) {
    hex_string += "0000";
  }
  byte_data_.emplace_back(ByteData::kHex, hex_string);
  return byte_data_.size() - 1;
}

bool JpegSegmentBuilder::ReplaceSizePlaceholder(size_t index, size_t size) {
  if (index >= byte_data_.size() || size < 2 || size > 0xFFFF) {
    return false;
  }
  const ByteData& byte_datum = byte_data_[index];
  if (byte_datum.GetType() != ByteData::kHex) {
    return false;
  }
  string value = byte_datum.GetValue();
  if (value.length() < 4) {
    return false;
  }
  Byte flag, type;
  if (!ByteData::Hex2Byte(value[0], value[1], &flag) ||
      !ByteData::Hex2Byte(value[2], value[3], &type)) {
    return false;
  }
  JpegMarker marker(type);
  if (flag != JpegMarker::kStart || !marker.IsValid() ||
      !marker.HasVariablePayloadSize()) {
    return false;
  }
  value.replace(2, 2, ByteData::Byte2Hex((size >> 8) & 0xFF));
  value.replace(4, 2, ByteData::Byte2Hex(size & 0xFF));
  byte_data_[index] = ByteData(ByteData::kHex, value);
  return true;
}

void JpegSegmentBuilder::AddExtendedXmpHeader(const std::string& xmp_guid) {
  string guid_value(xmp_guid);
  guid_value.resize(kXmpGuidSize, '0');
  byte_data_.emplace_back(ByteData::kAscii0, kXmpExtendedId);
  byte_data_.emplace_back(ByteData::kAscii, guid_value);
  byte_data_.emplace_back(ByteData::kAscii, string(8, '0'));
}

void JpegSegmentBuilder::AddXmpMetaPrefix() {
  byte_data_.emplace_back(ByteData::kAscii, kXmpMetaPrefix);
}

void JpegSegmentBuilder::AddXmpMetaSuffix() {
  byte_data_.emplace_back(ByteData::kAscii, kXmpMetaSuffix);
}

void JpegSegmentBuilder::AddRdfPrefix() {
  byte_data_.emplace_back(ByteData::kAscii, kRdfPrefix);
}

void JpegSegmentBuilder::AddRdfSuffix() {
  byte_data_.emplace_back(ByteData::kAscii, kRdfSuffix);
}

void JpegSegmentBuilder::AddRdfDescriptionPrefix() {
  byte_data_.emplace_back(ByteData::kAscii, kRdfDescriptionPrefix);
}

void JpegSegmentBuilder::AddRdfDescriptionSuffix() {
  byte_data_.emplace_back(ByteData::kAscii, kRdfDescriptionSuffix);
}

void JpegSegmentBuilder::AddXmpPropertyPrefix(
    const std::string& property_name) {
  string property_name_equals_quote = property_name + "=\"";
  byte_data_.emplace_back(ByteData::kAscii, property_name_equals_quote);
}

void JpegSegmentBuilder::AddXmpPropertySuffix() {
  byte_data_.emplace_back(ByteData::kAscii, "\"");
}

void JpegSegmentBuilder::AddXmpPropertyNameAndValue(
    const std::string& property_name, const std::string& property_value) {
  AddXmpPropertyPrefix(property_name);
  byte_data_.emplace_back(ByteData::kAscii, property_value);
  AddXmpPropertySuffix();
}

void JpegSegmentBuilder::AddApp1XmpMarkerAndXmpExtendedHeader(
    const std::string& xmp_guid) {
  AddMarkerAndSizePlaceholder(JpegMarker::kAPP1);
  AddExtendedXmpHeader(xmp_guid);
}

void JpegSegmentBuilder::AddXmpAndRdfPrefixes() {
  AddXmpMetaPrefix();
  AddRdfPrefix();
  AddRdfDescriptionPrefix();
}

void JpegSegmentBuilder::AddXmpAndRdfSuffixes() {
  AddRdfDescriptionSuffix();
  AddRdfSuffix();
  AddXmpMetaSuffix();
}

}  // namespace image_io
}  // namespace photos_editing_formats