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