Aegisub/aegisub/src/subtitle_format_dvd.cpp
Amar Takhar 6ee2f98349 Note: This was done using a script! it's far from perfect but 95% of the work has been done already formatting-wise.
Document all functions, class, struct, union, enum, macro, variable, typedefs.  This isn't the actual document in itself but empty documentation using any old documentation if it was there.

This was done using exuberant ctags to get tag info, then a TCL script to parse/remove old comments and convert them into Doxygen-style.

Some notes:
 * Anything labeled 'DOCME' needs to be documented, @param and @return have been left blank as it would be annoying to delete the 'DOCME' from every one of those.
 * Some multiline comments may have been munged into single line comments
 * Leave the /// comments above global variables with a space, if they're harder to read then we'll be less likey to use them.
 * Enum comments can go after the enumeration itself '[value] /// comment'
 * include/aegisub/*.h haven't been converted yet, this will be done in a later commit
 * Some documentation blocks are in the wrong place, in the .h when it should be in the .cpp, or vice versa.

See http://devel.aegisub.org/wiki/Doxygen for some details on Doxygen and a 'style guide'.

Originally committed to SVN as r3312.
2009-07-29 22:59:22 +00:00

448 lines
11 KiB
C++

// Copyright (c) 2008, Rodrigo Braz Monteiro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
//
// $Id$
/// @file subtitle_format_dvd.cpp
/// @brief Writing of DVD-compatible sub-pictures
/// @ingroup subtitle_io vobsub
///
///////////
// Headers
#include "config.h"
#include "subtitle_format_dvd.h"
#include "video_provider_dummy.h"
#include "subtitles_provider_manager.h"
#include "ass_dialogue.h"
#include "ass_file.h"
/// DOCME
#undef _OPENMP
#ifdef _OPENMP
#include <omp.h>
#endif
#include <wx/file.h>
//#undef MAX_PATH
//#include <tessdll.h>
//
//#pragma comment(lib, "tessdll.lib")
/// @brief Format name
/// @return
///
wxString DVDSubtitleFormat::GetName() {
return _T("DVD Subpictures");
}
/// @brief Extensions
/// @return
///
wxArrayString DVDSubtitleFormat::GetWriteWildcards() {
wxArrayString results;
results.Add(_T("sup"));
return results;
}
/// @brief Can write
/// @param filename
/// @return
///
bool DVDSubtitleFormat::CanWriteFile(wxString filename) {
return (filename.Lower().EndsWith(_T(".sup")));
}
/// @brief Get subpicture list
/// @param pics
///
void DVDSubtitleFormat::GetSubPictureList(std::vector<SubPicture> &pics) {
// Create video frame
int w = 720;
int h = 480;
VideoProvider *video = new DummyVideoProvider(10,1,w,h,wxColour(255,0,0),false);
AegiVideoFrame srcFrame = video->GetFrame(0);
delete video;
// Count and index lines
using std::list;
int count = 0;
std::vector<AssDialogue*> diags;
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
AssDialogue *current = AssEntry::GetAsDialogue(*cur);
if (current) {
diags.push_back(current);
count++;
}
}
pics.resize(count);
SubtitlesProvider *provider = NULL;
provider = SubtitlesProviderFactoryManager::GetProvider();
provider->LoadSubtitles(GetAssFile());
//TessDllAPI tess;
// Write lines
int i=0;
#ifdef _OPENMP
#pragma omp parallel for shared(diags,pics,provider) private(i)
#endif
for (i=0;i<count;i++) {
// Dialogue
AssDialogue *current = diags[i];
// Get the image
AegiVideoFrame dst;
dst.CopyFrom(srcFrame);
double time = (current->Start.GetMS()/1000.0 + current->End.GetMS()/1000.0)/2.0;
#ifdef _OPENMP
#pragma omp critical
#endif
{
provider->DrawSubtitles(dst,time);
}
wxImage img = dst.GetImage();
img.SaveFile(_T("test.bmp"));
dst.Clear();
// Tesseract test
/*
tess.BeginPageUpright(img.GetWidth(),img.GetHeight(),img.GetData(),24);
ETEXT_DESC *output = tess.Recognize_all_Words();
wxString blah;
int j;
for (int cur = 0; cur < output->count; cur = j) {
const EANYCODE_CHAR* ch = &output->text[cur];
unsigned char unistr[8];
for (int b = 0; b < ch->blanks; ++b) blah += _T(" ");
for (j = cur; j < output->count; j++) {
const EANYCODE_CHAR* unich = &output->text[j];
if (ch->left != unich->left || ch->right != unich->right ||
ch->top != unich->top || ch->bottom != unich->bottom)
break;
unistr[j - cur] = static_cast<unsigned char>(unich->char_code);
}
unistr[j - cur] = '\0';
blah += wxString((char*)unistr,wxConvUTF8);
if (ch->formatting & 64) blah += _T("\n");
}
wxLogMessage(blah);
*/
// Perform colour reduction on image
unsigned char r,g,b;
unsigned char *data = img.GetData();
const unsigned char *dataRead = data;
unsigned char *dataWrite = data;
int startY = 0;
int endY = 0;
int startX = w;
int endX = 0;
// For each line
for (int y=h;--y>=0;) {
bool hasData = false;
int lineStartX = 0;
int lineEndX = 0;
// Scan line
for (int x=w;--x>=0;) {
// Read
r = *(dataRead++);
g = *(dataRead++);
b = *(dataRead++);
// Background
if (r > 127 && g < 20) {
r = 200;
g = 0;
b = 0;
}
// Text
else {
// Mark coordinates
hasData = true;
if (lineStartX == 0) lineStartX = w-x-1;
lineEndX = w-x-1;
// Set colour
if (r > 170 && g > 170) {
r = 255;
g = 255;
b = 255;
}
else if (r > 85 && g > 85) {
r = 127;
g = 127;
b = 127;
}
else {
r = 0;
g = 0;
b = 0;
}
}
// Write
*(dataWrite++) = r;
*(dataWrite++) = g;
*(dataWrite++) = b;
}
// Mark as last found so far
if (hasData) {
if (startY == 0) startY = h-y-1;
endY = h-y-1;
if (lineStartX < startX) startX = lineStartX;
if (lineEndX > endX) endX = lineEndX;
}
}
// Save image data
if (startX > endX) endX = startX;
if (startY > endY) endY = startY;
int sw = endX-startX+1;
int sh = endY-startY+1;
pics[i].x = startX;
pics[i].y = startY;
pics[i].w = sw;
pics[i].h = sh;
pics[i].start = current->Start.GetMS();
pics[i].end = current->End.GetMS();
// RLE to memory
for (int j=0;j<2;j++) {
int curCol = -1;
int col;
int temp;
int len = 0;
//wxImage subPic = img.GetSubImage(wxRect(startX,startY,sw,sh));
dataRead = data + ((startY+j)*w+startX)*3;
//dataRead = subPic.GetData();
std::vector<RLEGroup> groups;
groups.reserve(1024);
// Read this scanline
for (int y=startY+j;y<=endY;y+=2) {
for (int x=startX;x<=endX;x++) {
// Read current pixel colour
temp = *dataRead;
if (temp == 200) col = 0;
else if (temp == 255) col = 1;
else if (temp == 0) col = 2;
else col = 3;
// See if it matches
if (col == curCol) {
len++;
if (len == 255) {
groups.push_back(RLEGroup(curCol,len,false));
len = 0;
}
}
else {
if (len) groups.push_back(RLEGroup(curCol,len,false));
len = 1;
curCol = col;
}
dataRead += 3;
}
// Flush
if (len) groups.push_back(RLEGroup(curCol,0,true));
else {
groups.back().len = 0;
groups.back().eol = true;
}
curCol = -1;
len = 0;
// Advance
dataRead += (2*w-sw)*3;
//dataRead += sw*3;
}
// Encode into subpicture format
int nibble[4];
nibble[3] = 0;
bool off = false;
std::vector<unsigned char> &data = pics[i].data[j];
unsigned char last = 0;
for (size_t m=0;m<groups.size();m++) {
unsigned char len = groups[m].len;
int nibbles;
// End of line, write b000000cc
if (groups[m].eol) nibbles = 4;
// Get proper nibble count
else {
if (len < 4) nibbles = 1;
else if (len < 16) nibbles = 2;
else if (len < 64) nibbles = 3;
else nibbles = 4;
}
// Write nibbles
nibble[0] = groups[m].col | ((len & 0x3) << 2);
nibble[1] = (len & 0x3C) >> 2;
nibble[2] = (len & 0xC0) >> 6;
for (int n=nibbles;--n>=0;) {
wxASSERT(nibble[n] >= 0 && nibble[n] < 16);
wxASSERT(n >= 0 && n < 4);
if (!off) {
last = nibble[n] << 4;
data.push_back(last);
}
else data.back() = nibble[n] | last;
off = !off;
// Check if just wrote end of line
if (len == 0 && n == 0) {
last = 0;
off = false;
}
}
}
data.resize(data.size());
}
}
// Clean up
delete provider;
srcFrame.Clear();
}
/// @brief Actually write them
/// @param filename
/// @param encoding
///
void DVDSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
// Prepare subtitles
CreateCopy();
SortLines();
StripComments();
RecombineOverlaps();
MergeIdentical();
// Get subpictures
std::vector<SubPicture> pics;
GetSubPictureList(pics);
// Open file for writing
wxFile fp(filename,wxFile::write);
if (!fp.IsOpened()) throw _T("Could not open file for writing.");
// Write each subpicture
size_t pos = 0;
for (size_t i=0;i<pics.size();i++) {
// Write sup packet data
pos += fp.Write("SP",2);
wxUint32 temp = wxUINT32_SWAP_ON_BE(pics[i].start * 90);
pos += fp.Write(&temp,4);
temp = 0;
pos += fp.Write(&temp,4);
// Calculate lengths
size_t controlLen = 30;
size_t packetLen = 4 + pics[i].data[0].size() + pics[i].data[1].size() + controlLen;
size_t packetStart = pos;
// Write position of the next packet and control
unsigned short temp2;
temp2 = wxUINT16_SWAP_ON_LE(packetLen);
pos += fp.Write(&temp2,2);
temp2 = wxUINT16_SWAP_ON_LE(packetLen-controlLen);
pos += fp.Write(&temp2,2);
// Write RLE data
size_t line0pos = pos - packetStart;
pos += fp.Write(&pics[i].data[0][0],pics[i].data[0].size());
size_t line1pos = pos - packetStart;
pos += fp.Write(&pics[i].data[1][0],pics[i].data[1].size());
// Control group data
size_t comm2add = packetLen - 6;
unsigned char comm2_b1 = (comm2add & 0xFF00) >> 8;
unsigned char comm2_b2 = comm2add & 0xFF;
unsigned char pix0_b1 = (line0pos & 0xFF00) >> 8;
unsigned char pix0_b2 = line0pos & 0xFF;
unsigned char pix1_b1 = (line1pos & 0xFF00) >> 8;
unsigned char pix1_b2 = line1pos & 0xFF;
int delay = (pics[i].end - pics[i].start)*90/1024;
unsigned char delay_b1 = (delay & 0xFF00) >> 8;
unsigned char delay_b2 = delay & 0xFF;
int sx = pics[i].x;
int sy = pics[i].y;
int ex = pics[i].w + sx - 1;
int ey = pics[i].h + sy - 1;
unsigned char dispx_b1 = (sx & 0xFF0) >> 4;
unsigned char dispx_b2 = ((sx & 0x0F) << 4) | ((ex & 0xF00) >> 8);
unsigned char dispx_b3 = (ex & 0xFF);
unsigned char dispy_b1 = (sy & 0xFF0) >> 4;
unsigned char dispy_b2 = ((sy & 0x0F) << 4) | ((ey & 0xF00) >> 8);
unsigned char dispy_b3 = (ey & 0xFF);
// Write control group
unsigned char control[] = {
0x00, 0x00, // Delay
comm2_b1, comm2_b2, // Next command
0x01, // Start display
0x03, 0x82, 0x30, // Set colours
0x04, 0xFF, 0xF0, // Alpha blend
0x05, dispx_b1, dispx_b2, dispx_b3, dispy_b1, dispy_b2, dispy_b3, // Display area
0x06, pix0_b1, pix0_b2, pix1_b1, pix1_b2, // Pixel pointers
0xFF, // End block 1
delay_b1, delay_b2, // Delay
comm2_b1, comm2_b2, // This command
0x02, // Stop display
0xFF // End
};
pos += fp.Write(control,controlLen);
}
}