7 #include "packager/media/formats/dvb/subtitle_composer.h"
12 #include "packager/base/logging.h"
19 const uint16_t kDefaultWidth = 720;
20 const uint16_t kDefaultHeight = 576;
21 const RgbaColor kTransparent{0, 0, 0, 0};
23 struct PngFreeHelper {
24 PngFreeHelper(png_structp* png, png_infop* info) : png(png), info(info) {}
25 ~PngFreeHelper() { png_destroy_write_struct(png, info); }
31 void PngWriteData(png_structp png, png_bytep data, png_size_t length) {
32 auto* output =
reinterpret_cast<std::vector<uint8_t>*
>(png_get_io_ptr(png));
33 output->insert(output->end(), data, data + length);
36 void PngFlushData(png_structp png) {}
38 bool IsTransparent(
const RgbaColor* colors, uint16_t width, uint16_t height) {
39 for (
size_t i = 0; i < static_cast<size_t>(width) * height; i++) {
46 bool GetImageData(
const DvbImageBuilder* image,
47 std::vector<uint8_t>* data,
60 png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
61 auto info = png_create_info_struct(png);
62 PngFreeHelper helper(&png, &info);
64 LOG(ERROR) <<
"Error creating libpng struct";
67 if (setjmp(png_jmpbuf(png))) {
69 LOG(ERROR) <<
"Error writing PNG image";
72 png_set_write_fn(png, data, &PngWriteData, &PngFlushData);
74 const RgbaColor* pixels;
75 if (!image->GetPixels(&pixels, width, height))
77 if (IsTransparent(pixels, *width, *height))
79 png_set_IHDR(png, info, *width, *height, 8, PNG_COLOR_TYPE_RGBA,
80 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
81 PNG_FILTER_TYPE_BASE);
82 png_write_info(png, info);
84 const uint8_t* in_data =
reinterpret_cast<const uint8_t*
>(pixels);
85 for (
size_t y = 0; y < *height; y++) {
86 size_t offset = image->max_width() * y *
sizeof(RgbaColor);
87 png_write_row(png, in_data + offset);
89 png_write_end(png,
nullptr);
96 SubtitleComposer::SubtitleComposer()
97 : display_width_(kDefaultWidth), display_height_(kDefaultHeight) {}
99 SubtitleComposer::~SubtitleComposer() {}
101 void SubtitleComposer::SetDisplaySize(uint16_t width, uint16_t height) {
102 display_width_ = width;
103 display_height_ = height;
106 bool SubtitleComposer::SetRegionInfo(uint8_t region_id,
107 uint8_t color_space_id,
110 auto* region = ®ions_[region_id];
111 if (region->x + width > display_width_ ||
112 region->y + height > display_height_) {
113 LOG(ERROR) <<
"DVB-sub region won't fit within display";
116 if (width == 0 || height == 0) {
117 LOG(ERROR) <<
"DVB-sub width/height cannot be 0";
121 region->width = width;
122 region->height = height;
123 region->color_space = &color_spaces_[color_space_id];
127 bool SubtitleComposer::SetRegionPosition(uint8_t region_id,
130 auto* region = ®ions_[region_id];
131 if (x + region->width > display_width_ ||
132 y + region->height > display_height_) {
133 LOG(ERROR) <<
"DVB-sub region won't fit within display";
142 bool SubtitleComposer::SetObjectInfo(uint16_t object_id,
146 int default_color_code) {
147 auto region = regions_.find(region_id);
148 if (region == regions_.end()) {
149 LOG(ERROR) <<
"Unknown DVB-sub region: " << (int)region_id
150 <<
", in object: " << object_id;
153 if (x >= region->second.width || y >= region->second.height) {
154 LOG(ERROR) <<
"DVB-sub object is outside region: " << object_id;
158 auto*
object = &objects_[object_id];
159 object->region = ®ion->second;
160 object->default_color_code = default_color_code;
166 DvbImageColorSpace* SubtitleComposer::GetColorSpace(uint8_t color_space_id) {
167 return &color_spaces_[color_space_id];
170 DvbImageColorSpace* SubtitleComposer::GetColorSpaceForObject(
171 uint16_t object_id) {
172 auto info = objects_.find(object_id);
173 if (info == objects_.end()) {
174 LOG(ERROR) <<
"Unknown DVB-sub object: " << object_id;
177 return info->second.region->color_space;
180 DvbImageBuilder* SubtitleComposer::GetObjectImage(uint16_t object_id) {
181 auto it = images_.find(object_id);
182 if (it == images_.end()) {
183 auto info = objects_.find(object_id);
184 if (info == objects_.end()) {
185 LOG(ERROR) <<
"Unknown DVB-sub object: " << object_id;
189 auto color = info->second.default_color_code < 0
191 : info->second.region->color_space->GetColor(
192 BitDepth::k8Bit, info->second.default_color_code);
194 .emplace(std::piecewise_construct, std::make_tuple(object_id),
196 info->second.region->color_space, color,
197 info->second.region->width - info->second.region->x,
198 info->second.region->height - info->second.region->y))
204 bool SubtitleComposer::GetSamples(
207 std::vector<std::shared_ptr<TextSample>>* samples)
const {
208 for (
const auto& pair : objects_) {
209 auto it = images_.find(pair.first);
210 if (it == images_.end()) {
211 LOG(WARNING) <<
"DVB-sub object " << pair.first
212 <<
" doesn't include object data";
216 uint16_t width, height;
217 std::vector<uint8_t> image_data;
218 if (!GetImageData(&it->second, &image_data, &width, &height))
220 if (image_data.empty()) {
221 VLOG(1) <<
"Skipping transparent object";
224 TextFragment body({}, image_data);
225 DCHECK_LE(width, display_width_);
226 DCHECK_LE(height, display_height_);
228 TextSettings settings;
229 settings.position.emplace(
230 (pair.second.x + pair.second.region->x) * 100.0f / display_width_,
231 TextUnitType::kPercent);
232 settings.line.emplace(
233 (pair.second.y + pair.second.region->y) * 100.0f / display_height_,
234 TextUnitType::kPercent);
235 settings.width.emplace(width * 100.0f / display_width_,
236 TextUnitType::kPercent);
237 settings.height.emplace(height * 100.0f / display_height_,
238 TextUnitType::kPercent);
240 samples->emplace_back(
241 std::make_shared<TextSample>(
"", start, end, settings, body));
247 void SubtitleComposer::ClearObjects() {
All the methods that are virtual are virtual for mocking.