1 // Copyright 2016 The Draco Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 #include "draco/compression/attributes/sequential_integer_attribute_encoder.h"
16 
17 #include "draco/compression/attributes/prediction_schemes/prediction_scheme_encoder_factory.h"
18 #include "draco/compression/attributes/prediction_schemes/prediction_scheme_wrap_encoding_transform.h"
19 #include "draco/compression/entropy/symbol_encoding.h"
20 #include "draco/core/bit_utils.h"
21 
22 namespace draco {
23 
SequentialIntegerAttributeEncoder()24 SequentialIntegerAttributeEncoder::SequentialIntegerAttributeEncoder() {}
25 
Init(PointCloudEncoder * encoder,int attribute_id)26 bool SequentialIntegerAttributeEncoder::Init(PointCloudEncoder *encoder,
27                                              int attribute_id) {
28   if (!SequentialAttributeEncoder::Init(encoder, attribute_id)) {
29     return false;
30   }
31   if (GetUniqueId() == SEQUENTIAL_ATTRIBUTE_ENCODER_INTEGER) {
32     // When encoding integers, this encoder currently works only for integer
33     // attributes up to 32 bits.
34     switch (attribute()->data_type()) {
35       case DT_INT8:
36       case DT_UINT8:
37       case DT_INT16:
38       case DT_UINT16:
39       case DT_INT32:
40       case DT_UINT32:
41         break;
42       default:
43         return false;
44     }
45   }
46   // Init prediction scheme.
47   const PredictionSchemeMethod prediction_scheme_method =
48       GetPredictionMethodFromOptions(attribute_id, *encoder->options());
49 
50   prediction_scheme_ = CreateIntPredictionScheme(prediction_scheme_method);
51 
52   if (prediction_scheme_ && !InitPredictionScheme(prediction_scheme_.get())) {
53     prediction_scheme_ = nullptr;
54   }
55 
56   return true;
57 }
58 
TransformAttributeToPortableFormat(const std::vector<PointIndex> & point_ids)59 bool SequentialIntegerAttributeEncoder::TransformAttributeToPortableFormat(
60     const std::vector<PointIndex> &point_ids) {
61   if (encoder()) {
62     if (!PrepareValues(point_ids, encoder()->point_cloud()->num_points())) {
63       return false;
64     }
65   } else {
66     if (!PrepareValues(point_ids, 0)) {
67       return false;
68     }
69   }
70 
71   // Update point to attribute mapping with the portable attribute if the
72   // attribute is a parent attribute (for now, we can skip it otherwise).
73   if (is_parent_encoder()) {
74     // First create map between original attribute value indices and new ones
75     // (determined by the encoding order).
76     const PointAttribute *const orig_att = attribute();
77     PointAttribute *const portable_att = portable_attribute();
78     IndexTypeVector<AttributeValueIndex, AttributeValueIndex>
79         value_to_value_map(orig_att->size());
80     for (int i = 0; i < point_ids.size(); ++i) {
81       value_to_value_map[orig_att->mapped_index(point_ids[i])] =
82           AttributeValueIndex(i);
83     }
84     if (portable_att->is_mapping_identity()) {
85       portable_att->SetExplicitMapping(encoder()->point_cloud()->num_points());
86     }
87     // Go over all points of the original attribute and update the mapping in
88     // the portable attribute.
89     for (PointIndex i(0); i < encoder()->point_cloud()->num_points(); ++i) {
90       portable_att->SetPointMapEntry(
91           i, value_to_value_map[orig_att->mapped_index(i)]);
92     }
93   }
94   return true;
95 }
96 
97 std::unique_ptr<PredictionSchemeTypedEncoderInterface<int32_t>>
CreateIntPredictionScheme(PredictionSchemeMethod method)98 SequentialIntegerAttributeEncoder::CreateIntPredictionScheme(
99     PredictionSchemeMethod method) {
100   return CreatePredictionSchemeForEncoder<
101       int32_t, PredictionSchemeWrapEncodingTransform<int32_t>>(
102       method, attribute_id(), encoder());
103 }
104 
EncodeValues(const std::vector<PointIndex> & point_ids,EncoderBuffer * out_buffer)105 bool SequentialIntegerAttributeEncoder::EncodeValues(
106     const std::vector<PointIndex> &point_ids, EncoderBuffer *out_buffer) {
107   // Initialize general quantization data.
108   const PointAttribute *const attrib = attribute();
109   if (attrib->size() == 0) {
110     return true;
111   }
112 
113   int8_t prediction_scheme_method = PREDICTION_NONE;
114   if (prediction_scheme_) {
115     if (!SetPredictionSchemeParentAttributes(prediction_scheme_.get())) {
116       return false;
117     }
118     prediction_scheme_method =
119         static_cast<int8_t>(prediction_scheme_->GetPredictionMethod());
120   }
121   out_buffer->Encode(prediction_scheme_method);
122   if (prediction_scheme_) {
123     out_buffer->Encode(
124         static_cast<int8_t>(prediction_scheme_->GetTransformType()));
125   }
126 
127   const int num_components = portable_attribute()->num_components();
128   const int num_values =
129       static_cast<int>(num_components * portable_attribute()->size());
130   const int32_t *const portable_attribute_data = GetPortableAttributeData();
131 
132   // We need to keep the portable data intact, but several encoding steps can
133   // result in changes of this data, e.g., by applying prediction schemes that
134   // change the data in place. To preserve the portable data we store and
135   // process all encoded data in a separate array.
136   std::vector<int32_t> encoded_data(num_values);
137 
138   // All integer values are initialized. Process them using the prediction
139   // scheme if we have one.
140   if (prediction_scheme_) {
141     prediction_scheme_->ComputeCorrectionValues(
142         portable_attribute_data, &encoded_data[0], num_values, num_components,
143         point_ids.data());
144   }
145 
146   if (prediction_scheme_ == nullptr ||
147       !prediction_scheme_->AreCorrectionsPositive()) {
148     const int32_t *const input =
149         prediction_scheme_ ? encoded_data.data() : portable_attribute_data;
150     ConvertSignedIntsToSymbols(input, num_values,
151                                reinterpret_cast<uint32_t *>(&encoded_data[0]));
152   }
153 
154   if (encoder() == nullptr || encoder()->options()->GetGlobalBool(
155                                   "use_built_in_attribute_compression", true)) {
156     out_buffer->Encode(static_cast<uint8_t>(1));
157     Options symbol_encoding_options;
158     if (encoder() != nullptr) {
159       SetSymbolEncodingCompressionLevel(&symbol_encoding_options,
160                                         10 - encoder()->options()->GetSpeed());
161     }
162     if (!EncodeSymbols(reinterpret_cast<uint32_t *>(encoded_data.data()),
163                        static_cast<int>(point_ids.size()) * num_components,
164                        num_components, &symbol_encoding_options, out_buffer)) {
165       return false;
166     }
167   } else {
168     // No compression. Just store the raw integer values, using the number of
169     // bytes as needed.
170 
171     // To compute the maximum bit-length, first OR all values.
172     uint32_t masked_value = 0;
173     for (uint32_t i = 0; i < static_cast<uint32_t>(num_values); ++i) {
174       masked_value |= encoded_data[i];
175     }
176     // Compute the msb of the ORed value.
177     int value_msb_pos = 0;
178     if (masked_value != 0) {
179       value_msb_pos = MostSignificantBit(masked_value);
180     }
181     const int num_bytes = 1 + value_msb_pos / 8;
182 
183     out_buffer->Encode(static_cast<uint8_t>(0));
184     out_buffer->Encode(static_cast<uint8_t>(num_bytes));
185 
186     if (num_bytes == DataTypeLength(DT_INT32)) {
187       out_buffer->Encode(encoded_data.data(), sizeof(int32_t) * num_values);
188     } else {
189       for (uint32_t i = 0; i < static_cast<uint32_t>(num_values); ++i) {
190         out_buffer->Encode(encoded_data.data() + i, num_bytes);
191       }
192     }
193   }
194   if (prediction_scheme_) {
195     prediction_scheme_->EncodePredictionData(out_buffer);
196   }
197   return true;
198 }
199 
PrepareValues(const std::vector<PointIndex> & point_ids,int num_points)200 bool SequentialIntegerAttributeEncoder::PrepareValues(
201     const std::vector<PointIndex> &point_ids, int num_points) {
202   // Convert all values to int32_t format.
203   const PointAttribute *const attrib = attribute();
204   const int num_components = attrib->num_components();
205   const int num_entries = static_cast<int>(point_ids.size());
206   PreparePortableAttribute(num_entries, num_components, num_points);
207   int32_t dst_index = 0;
208   int32_t *const portable_attribute_data = GetPortableAttributeData();
209   for (PointIndex pi : point_ids) {
210     const AttributeValueIndex att_id = attrib->mapped_index(pi);
211     if (!attrib->ConvertValue<int32_t>(att_id,
212                                        portable_attribute_data + dst_index)) {
213       return false;
214     }
215     dst_index += num_components;
216   }
217   return true;
218 }
219 
PreparePortableAttribute(int num_entries,int num_components,int num_points)220 void SequentialIntegerAttributeEncoder::PreparePortableAttribute(
221     int num_entries, int num_components, int num_points) {
222   GeometryAttribute va;
223   va.Init(attribute()->attribute_type(), nullptr, num_components, DT_INT32,
224           false, num_components * DataTypeLength(DT_INT32), 0);
225   std::unique_ptr<PointAttribute> port_att(new PointAttribute(va));
226   port_att->Reset(num_entries);
227   SetPortableAttribute(std::move(port_att));
228   if (num_points) {
229     portable_attribute()->SetExplicitMapping(num_points);
230   }
231 }
232 
233 }  // namespace draco
234