Shaka Packager SDK
Loading...
Searching...
No Matches
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
16namespace shaka {
17namespace media {
18
19namespace {
20
21// See ETSI EN 300 743 Section 9.1.
22constexpr 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.
29constexpr 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.
41constexpr 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.
48constexpr 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.
70RgbaColor 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
97DvbImageColorSpace::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
106DvbImageColorSpace::~DvbImageColorSpace() {}
107
108RgbaColor 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
149void 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
169 memcpy(bit_depth_2_to_4_, map, sizeof(bit_depth_2_to_4_));
170}
171
173 memcpy(bit_depth_2_to_8_, map, sizeof(bit_depth_2_to_8_));
174}
175
177 memcpy(bit_depth_4_to_8_, map, sizeof(bit_depth_4_to_8_));
178}
179
180RgbaColor 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
194DvbImageBuilder::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
209DvbImageBuilder::~DvbImageBuilder() {}
210
211bool 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
227void 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.