Shaka Packager SDK
dvb_image.cc
1 // Copyright 2020 Google LLC. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include <packager/media/formats/dvb/dvb_image.h>
8 
9 #include <algorithm>
10 #include <cstring>
11 #include <tuple>
12 
13 #include <absl/log/check.h>
14 #include <absl/log/log.h>
15 
16 namespace shaka {
17 namespace media {
18 
19 namespace {
20 
21 // See ETSI EN 300 743 Section 9.1.
22 constexpr const uint8_t k4To2ReductionMap[] = {
23  0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
24  0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
25 };
26 
27 // The only time when A==0 is when it is transparent. This means we can use
28 // other values internally for special values.
29 constexpr const RgbaColor kNoColor{145, 92, 47, 0};
30 
31 // DVB uses transparency, but libpng uses alpha, so we need to reverse the T
32 // value so we can pass the value to libpng.
33 #define COLOR(r, g, b, t) \
34  RgbaColor { \
35  static_cast<uint8_t>(255 * (r) / 100), \
36  static_cast<uint8_t>(255 * (g) / 100), \
37  static_cast<uint8_t>(255 * (b) / 100), \
38  static_cast<uint8_t>(255 * (100 - t) / 100) \
39  }
40 // Default color maps see ETSI EN 300 743 Section 10.
41 constexpr const RgbaColor k2BitDefaultColors[] = {
42  COLOR(0, 0, 0, 100), // 0 = 0b00
43  COLOR(100, 100, 100, 0), // 1 = 0b01
44  COLOR(0, 0, 0, 0), // 2 = 0b10
45  COLOR(50, 50, 50, 0), // 3 = 0b11
46 };
47 // Default color maps see ETSI EN 300 743 Section 10.
48 constexpr const RgbaColor k4BitDefaultColors[] = {
49  COLOR(0, 0, 0, 100), // 0 = 0b0000
50  COLOR(100, 0, 0, 0), // 1 = 0b0001
51  COLOR(0, 100, 0, 0), // 2 = 0b0010
52  COLOR(100, 100, 0, 0), // 3 = 0b0011
53  COLOR(0, 0, 100, 0), // 4 = 0b0100
54  COLOR(100, 0, 100, 0), // 5 = 0b0101
55  COLOR(0, 100, 100, 0), // 6 = 0b0110
56  COLOR(100, 100, 100, 0), // 7 = 0b0111
57 
58  COLOR(0, 0, 0, 0), // 8 = 0b1000
59  COLOR(50, 0, 0, 0), // 9 = 0b1001
60  COLOR(0, 50, 0, 0), // 10 = 0b1010
61  COLOR(50, 50, 0, 0), // 11 = 0b1011
62  COLOR(0, 0, 50, 0), // 12 = 0b1100
63  COLOR(50, 0, 50, 0), // 13 = 0b1101
64  COLOR(0, 50, 50, 0), // 14 = 0b1110
65  COLOR(50, 50, 50, 0), // 15 = 0b1111
66 };
67 
68 #define GET_BIT(n) ((entry_id >> (8 - (n))) & 0x1)
69 // Default color maps see ETSI EN 300 743 Section 10.
70 RgbaColor Get8BitDefaultColor(uint8_t entry_id) {
71  uint8_t r, g, b, t;
72  if (entry_id == 0) {
73  return COLOR(0, 0, 0, 100);
74  } else if ((entry_id & 0xf8) == 0) {
75  r = 100 * GET_BIT(8);
76  g = 100 * GET_BIT(7);
77  b = 100 * GET_BIT(6);
78  t = 75;
79  } else if (!GET_BIT(1)) {
80  r = (33 * GET_BIT(8)) + (67 * GET_BIT(4));
81  g = (33 * GET_BIT(7)) + (67 * GET_BIT(3));
82  b = (33 * GET_BIT(6)) + (67 * GET_BIT(2));
83  t = GET_BIT(5) ? 50 : 0;
84  } else {
85  r = (17 * GET_BIT(8)) + (33 * GET_BIT(4)) + (GET_BIT(5) ? 0 : 50);
86  g = (17 * GET_BIT(7)) + (33 * GET_BIT(3)) + (GET_BIT(5) ? 0 : 50);
87  b = (17 * GET_BIT(6)) + (33 * GET_BIT(2)) + (GET_BIT(5) ? 0 : 50);
88  t = 0;
89  }
90  return COLOR(r, g, b, t);
91 }
92 #undef GET_BIT
93 #undef COLOR
94 
95 } // namespace
96 
97 DvbImageColorSpace::DvbImageColorSpace() {
98  for (auto& item : color_map_2_)
99  item = kNoColor;
100  for (auto& item : color_map_4_)
101  item = kNoColor;
102  for (auto& item : color_map_8_)
103  item = kNoColor;
104 }
105 
106 DvbImageColorSpace::~DvbImageColorSpace() {}
107 
108 RgbaColor DvbImageColorSpace::GetColor(BitDepth bit_depth,
109  uint8_t entry_id) const {
110  auto color = GetColorRaw(bit_depth, entry_id);
111  if (color != kNoColor)
112  return color;
113 
114  // If we don't have the exact bit-depth, try mapping to another bit-depth.
115  // See ETSI EN 300 743 Section 9.
116  RgbaColor default_color, alt1, alt2;
117  switch (bit_depth) {
118  case BitDepth::k2Bit:
119  DCHECK_LT(entry_id, 4u);
120  alt1 = GetColorRaw(BitDepth::k4Bit, bit_depth_2_to_4_[entry_id]);
121  alt2 = GetColorRaw(BitDepth::k8Bit, bit_depth_2_to_8_[entry_id]);
122  default_color = k2BitDefaultColors[entry_id];
123  break;
124  case BitDepth::k4Bit:
125  DCHECK_LT(entry_id, 16u);
126  alt1 = GetColorRaw(BitDepth::k8Bit, bit_depth_4_to_8_[entry_id]);
127  alt2 = GetColorRaw(BitDepth::k2Bit, k4To2ReductionMap[entry_id]);
128  default_color = k4BitDefaultColors[entry_id];
129  break;
130  case BitDepth::k8Bit:
131  // 8-to-4-bit reduction is just take the low bits.
132  alt1 = GetColorRaw(BitDepth::k4Bit, entry_id & 0xf);
133  alt2 = GetColorRaw(BitDepth::k2Bit, k4To2ReductionMap[entry_id & 0xf]);
134  default_color = Get8BitDefaultColor(entry_id);
135  break;
136  default:
137  // Windows can't detect that all enums are handled and doesn't like
138  // NOTIMPLEMENTED.
139  return kNoColor;
140  }
141 
142  if (alt1 != kNoColor)
143  return alt1;
144  if (alt2 != kNoColor)
145  return alt2;
146  return default_color;
147 }
148 
149 void DvbImageColorSpace::SetColor(BitDepth bit_depth,
150  uint8_t entry_id,
151  RgbaColor color) {
152  DCHECK(color != kNoColor);
153  switch (bit_depth) {
154  case BitDepth::k2Bit:
155  DCHECK_LT(entry_id, 4u);
156  color_map_2_[entry_id] = color;
157  break;
158  case BitDepth::k4Bit:
159  DCHECK_LT(entry_id, 16u);
160  color_map_4_[entry_id] = color;
161  break;
162  case BitDepth::k8Bit:
163  color_map_8_[entry_id] = color;
164  break;
165  }
166 }
167 
168 void DvbImageColorSpace::Set2To4BitDepthMap(const uint8_t* map) {
169  memcpy(bit_depth_2_to_4_, map, sizeof(bit_depth_2_to_4_));
170 }
171 
172 void DvbImageColorSpace::Set2To8BitDepthMap(const uint8_t* map) {
173  memcpy(bit_depth_2_to_8_, map, sizeof(bit_depth_2_to_8_));
174 }
175 
176 void DvbImageColorSpace::Set4To8BitDepthMap(const uint8_t* map) {
177  memcpy(bit_depth_4_to_8_, map, sizeof(bit_depth_4_to_8_));
178 }
179 
180 RgbaColor DvbImageColorSpace::GetColorRaw(BitDepth bit_depth,
181  uint8_t entry_id) const {
182  switch (bit_depth) {
183  case BitDepth::k2Bit:
184  return color_map_2_[entry_id];
185  case BitDepth::k4Bit:
186  return color_map_4_[entry_id];
187  case BitDepth::k8Bit:
188  return color_map_8_[entry_id];
189  }
190  // Not reached, but Windows doesn't like NOTIMPLEMENTED.
191  return kNoColor;
192 }
193 
194 DvbImageBuilder::DvbImageBuilder(const DvbImageColorSpace* color_space,
195  const RgbaColor& default_color,
196  uint16_t max_width,
197  uint16_t max_height)
198  : pixels_(new RgbaColor[max_width * max_height]),
199  color_space_(color_space),
200  top_pos_{0, 0},
201  bottom_pos_{0, 1}, // Skip top row for bottom row.
202  max_width_(max_width),
203  max_height_(max_height),
204  width_(0) {
205  for (size_t i = 0; i < static_cast<size_t>(max_width) * max_height; i++)
206  pixels_[i] = default_color;
207 }
208 
209 DvbImageBuilder::~DvbImageBuilder() {}
210 
211 bool DvbImageBuilder::AddPixel(BitDepth bit_depth,
212  uint8_t byte_code,
213  bool is_top_rows) {
214  auto& pos = is_top_rows ? top_pos_ : bottom_pos_;
215  if (pos.x >= max_width_ || pos.y >= max_height_) {
216  LOG(ERROR) << "DVB-sub image cannot fit in region/window";
217  return false;
218  }
219 
220  pixels_[pos.y * max_width_ + pos.x++] =
221  color_space_->GetColor(bit_depth, byte_code);
222  if (pos.x > width_)
223  width_ = pos.x;
224  return true;
225 }
226 
227 void DvbImageBuilder::NewRow(bool is_top_rows) {
228  auto& pos = is_top_rows ? top_pos_ : bottom_pos_;
229  pos.x = 0;
230  pos.y += 2; // Skip other row.
231 }
232 
234  for (size_t line = 0; line < max_height_ - 1u; line += 2) {
235  std::memcpy(&pixels_[(line + 1) * max_width_], &pixels_[line * max_width_],
236  max_width_ * sizeof(RgbaColor));
237  }
238  bottom_pos_ = top_pos_;
239  if (max_height_ % 2 == 0)
240  bottom_pos_.y++;
241  else
242  bottom_pos_.y--; // Odd-height images don't end in odd-row, so move back.
243 }
244 
246  uint16_t* width,
247  uint16_t* height) const {
248  size_t max_y, min_y;
249  std::tie(min_y, max_y) = std::minmax(top_pos_.y, bottom_pos_.y);
250  if (max_y == 1 || max_y != min_y + 1) {
251  // 1. We should have at least one row.
252  // 2. Both top-rows and bottom-rows should have the same number of rows.
253  LOG(ERROR) << "Incomplete DVB-sub image";
254  return false;
255  }
256 
257  *width = width_;
258  // We skipped the other row in NewRow, so rollback.
259  *height = static_cast<uint16_t>(max_y - 1);
260  *pixels = pixels_.get();
261  if (*height > max_height_) {
262  LOG(ERROR) << "DVB-sub image cannot fit in region/window";
263  return false;
264  }
265 
266  return true;
267 }
268 
269 } // namespace media
270 } // namespace shaka
bool GetPixels(const RgbaColor **pixels, uint16_t *width, uint16_t *height) const
Definition: dvb_image.cc:245
void MirrorToBottomRows()
Copies the top-rows to the bottom rows.
Definition: dvb_image.cc:233
void Set4To8BitDepthMap(const uint8_t *map)
Must pass a 16-element array; elements are copied over.
Definition: dvb_image.cc:176
void Set2To8BitDepthMap(const uint8_t *map)
Must pass a 4-element array; elements are copied over.
Definition: dvb_image.cc:172
void Set2To4BitDepthMap(const uint8_t *map)
Must pass a 4-element array; elements are copied over.
Definition: dvb_image.cc:168
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66