Considerable improvements to PRS encoder, it will now prevent the generation of duplicate lines

Originally committed to SVN as r265.
This commit is contained in:
Rodrigo Braz Monteiro 2006-03-31 06:12:10 +00:00
parent 523f545484
commit 784ed8b82f
9 changed files with 214 additions and 72 deletions

View file

@ -98,6 +98,112 @@ void PRSSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal); throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal);
} }
// Get range
std::vector<int> frames = GetFrameRanges();
// Render all frames that were detected to contain subtitles
PClip clip1 = script1.AsClip();
PClip clip2 = script2.AsClip();
int totalFrames = frames.size();
int id = 0;
PRSDisplay *lastDisplay = NULL;
for (int framen=0;framen<totalFrames;framen++) {
// Render it?
if (frames[framen] == 0) continue;
// Read its image
PVideoFrame frame1 = clip1->GetFrame(framen,env1);
PVideoFrame frame2 = clip2->GetFrame(framen,env2);
// Convert to PNG (the block is there to force it to dealloc bmp earlier)
int x=0,y=0;
wxMemoryOutputStream stream;
{
wxImage bmp = CalculateAlpha(frame1->GetReadPtr(),frame2->GetReadPtr(),frame1->GetRowSize(),frame1->GetHeight(),frame1->GetPitch(),&x,&y);
if (!bmp.Ok()) continue;
bmp.SaveFile(stream,wxBITMAP_TYPE_PNG);
//bmp.SaveFile(filename + wxString::Format(_T("%i.png"),id),wxBITMAP_TYPE_PNG);
}
// Create PRSImage
PRSImage *img = new PRSImage;
img->id = id;
img->dataLen = stream.GetSize();
img->data = new char[img->dataLen];
img->imageType = PNG_IMG;
stream.CopyTo(img->data,img->dataLen);
// Hash the PRSImage data
md5_state_t state;
md5_init(&state);
md5_append(&state,(md5_byte_t*)img->data,img->dataLen);
md5_finish(&state,(md5_byte_t*)img->md5);
// Check for duplicates
PRSImage *dupe = file.FindDuplicateImage(img);
int useID = id;
// Dupe found, use that instead
if (dupe) {
useID = dupe->id;
delete img;
img = NULL;
}
// Frame is all OK, add it to file
else {
file.AddEntry(img);
id++;
}
// Find start and end times
int startf = framen;
while (++framen<totalFrames && frames[framen] == 1);
int endf = --framen;
int start = VFR_Output.GetTimeAtFrame(startf,true);
int end = VFR_Output.GetTimeAtFrame(endf,false);
// Set blend data
unsigned char alpha = 255;
unsigned char blend = 0;
// Check if it's just an extension of last display
if (lastDisplay && lastDisplay->id == useID && lastDisplay->endFrame == startf-1 &&
lastDisplay->x == x && lastDisplay->y == y && lastDisplay->alpha == alpha && lastDisplay->blend == blend)
{
lastDisplay->end = start;
lastDisplay->endFrame = startf;
}
// It isn't; needs a new display command
else {
// Create PRSDisplay
PRSDisplay *display = new PRSDisplay;
display->start = start;
display->end = end;
display->startFrame = startf;
display->endFrame = endf;
display->id = useID;
display->x = x;
display->y = y;
display->alpha = alpha;
display->blend = blend;
lastDisplay = display;
// Insert into list
file.AddEntry(display);
}
}
// Save file
file.Save((const char*)filename.mb_str(wxConvLocal));
#endif
}
////////////////////
// Get frame ranges
std::vector<int> PRSSubtitleFormat::GetFrameRanges() {
// Loop through subtitles in file // Loop through subtitles in file
AssFile *ass = AssFile::top; AssFile *ass = AssFile::top;
std::vector<int> frames; std::vector<int> frames;
@ -170,65 +276,8 @@ void PRSSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
} }
} }
// Render all frames that were detected to contain subtitles // Done
PClip clip1 = script1.AsClip(); return frames;
PClip clip2 = script2.AsClip();
int totalFrames = frames.size();
int id = 0;
for (int framen=0;framen<totalFrames;framen++) {
// Render it?
if (frames[framen] == 0) continue;
// Read its image
PVideoFrame frame1 = clip1->GetFrame(framen,env1);
PVideoFrame frame2 = clip2->GetFrame(framen,env2);
// Convert to PNG
int x=0,y=0;
wxImage bmp = CalculateAlpha(frame1->GetReadPtr(),frame2->GetReadPtr(),frame1->GetRowSize(),frame1->GetHeight(),frame1->GetPitch(),&x,&y);
if (!bmp.Ok()) continue;
wxMemoryOutputStream stream;
bmp.SaveFile(stream,wxBITMAP_TYPE_PNG);
bmp.SaveFile(filename + wxString::Format(_T("%i.png"),id),wxBITMAP_TYPE_PNG);
// Create PRSImage
PRSImage *img = new PRSImage;
img->id = id;
img->dataLen = stream.GetSize();
img->data = new char[img->dataLen];
img->imageType = PNG_IMG;
stream.CopyTo(img->data,img->dataLen);
// Hash the PRSImage data
md5_state_t state;
md5_init(&state);
md5_append(&state,(md5_byte_t*)img->data,img->dataLen);
md5_finish(&state,(md5_byte_t*)img->md5);
// Find start and end times
int start = framen;
while (++framen<totalFrames && frames[framen] == 1);
int end = framen-1;
// Create PRSDisplay
PRSDisplay *display = new PRSDisplay;
display->start = VFR_Output.GetTimeAtFrame(start,true);
display->end = VFR_Output.GetTimeAtFrame(end,false);
display->id = id;
display->x = x;
display->y = y;
display->alpha = 255;
display->blend = 0;
// Insert into list
file.AddEntry(img);
file.AddEntry(display);
id++;
}
// Save file
file.Save((const char*)filename.mb_str(wxConvLocal));
#endif
} }
@ -293,9 +342,7 @@ wxImage PRSSubtitleFormat::CalculateAlpha(const unsigned char* frame1, const uns
else if (y > maxy) maxy = y; else if (y > maxy) maxy = y;
// Calculate colour components // Calculate colour components
int mod; int mod = MAX(0,128/a-1);
if (a > 8) mod = 0;
else mod = 256 >> a;
r = MAX(0,r1-mod)*255 / a; r = MAX(0,r1-mod)*255 / a;
g = MAX(0,g1-mod)*255 / a; g = MAX(0,g1-mod)*255 / a;
b = MAX(0,b1-mod)*255 / a; b = MAX(0,b1-mod)*255 / a;

View file

@ -40,6 +40,7 @@
/////////// ///////////
// Headers // Headers
#include "subtitle_format.h" #include "subtitle_format.h"
#include <vector>
////////////// //////////////
@ -48,6 +49,7 @@ class PRSSubtitleFormat : public SubtitleFormat {
private: private:
wxImage SubImageWithAlpha(wxImage src,const wxRect &area); wxImage SubImageWithAlpha(wxImage src,const wxRect &area);
wxImage CalculateAlpha(const unsigned char* frame1, const unsigned char* frame2, int w, int h, int pitch, int *x=NULL, int *y=NULL); wxImage CalculateAlpha(const unsigned char* frame1, const unsigned char* frame2, int w, int h, int pitch, int *x=NULL, int *y=NULL);
std::vector<int> GetFrameRanges();
public: public:
bool CanWriteFile(wxString filename); bool CanWriteFile(wxString filename);

View file

@ -44,6 +44,8 @@
PRSDisplay::PRSDisplay() { PRSDisplay::PRSDisplay() {
start = -1; start = -1;
end = -1; end = -1;
startFrame = -1;
endFrame = -1;
id = -1; id = -1;
layer = 0; layer = 0;
x = 0; x = 0;

View file

@ -58,13 +58,15 @@ enum PRSBlendMode {
// Display class // Display class
class PRSDisplay : public PRSEntry { class PRSDisplay : public PRSEntry {
public: public:
unsigned int start; // First time to show this on (INCLUSIVE) (possible first frame?) unsigned __int32 start; // First time to show this on (INCLUSIVE)
unsigned int end; // Last time to show this on (EXCLUSIVE) (possible last frame?) unsigned __int32 end; // Last time to show this on (EXCLUSIVE)
unsigned int id; // ID of picture to be shown unsigned __int32 startFrame;// First frame to show this on (INCLUSIVE)
unsigned int layer; // Number of layer to draw this on unsigned __int32 endFrame; // Last frame to show this on (INCLUSIVE)
short x,y; // X and Y coordinates to draw picture on unsigned __int32 id; // ID of picture to be shown
unsigned char alpha; // Alpha blend of picture unsigned __int32 layer; // Number of layer to draw this on
unsigned char blend; // Blend mode to use signed __int16 x,y; // X and Y coordinates to draw picture on
unsigned __int8 alpha; // Alpha blend of picture
unsigned __int8 blend; // Blend mode to use
PRSDisplay(); PRSDisplay();
~PRSDisplay(); ~PRSDisplay();

57
prs/prs_entry.cpp Normal file
View file

@ -0,0 +1,57 @@
// Copyright (c) 2005, 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
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
///////////
// Headers
#include "prs_entry.h"
#include "prs_display.h"
#include "prs_image.h"
///////////////////
// Return as image
PRSImage* PRSEntry::GetImage(PRSEntry* entry) {
if (entry->GetType() == IMAGE_ENTRY) return ((PRSImage*) entry);
return NULL;
}
/////////////////////
// Return as display
PRSDisplay* PRSEntry::GetDisplay(PRSEntry* entry) {
if (entry->GetType() == DISPLAY_ENTRY) return ((PRSDisplay*) entry);
return NULL;
}

View file

@ -69,5 +69,5 @@ public:
virtual void WriteData(std::vector<char> &vec) { } virtual void WriteData(std::vector<char> &vec) { }
static PRSImage* GetImage(PRSEntry* entry); static PRSImage* GetImage(PRSEntry* entry);
static PRSDisplay* GetDisplay(PRSDisplay* entry); static PRSDisplay* GetDisplay(PRSEntry* entry);
}; };

View file

@ -209,3 +209,31 @@ void PRSFile::Load(std::string path, bool reset) {
void PRSFile::AddEntry(PRSEntry *entry) { void PRSFile::AddEntry(PRSEntry *entry) {
entryList.push_back(entry); entryList.push_back(entry);
} }
//////////////////////////////////////////////////
// Checks if there is any duplicate of this image
PRSImage *PRSFile::FindDuplicateImage(PRSImage *img) {
// Scan looking for duplicate hashes
PRSImage *orig;
std::list<PRSEntry*>::iterator cur;
for (cur=entryList.begin();cur!=entryList.end();cur++) {
orig = PRSEntry::GetImage(*cur);
if (orig) {
// Compare data lengths
if (orig->dataLen == img->dataLen) {
// Identical data lengths, compare hashes
if (memcmp(orig->md5,img->md5,16) == 0) {
// Identical hashes, compare image data to be sure
if (memcmp(orig->data,img->data,orig->dataLen) == 0) {
// Identical data, return
return orig;
}
}
}
}
}
// No duplicate found
return NULL;
}

View file

@ -40,6 +40,7 @@
////////////// //////////////
// Prototypes // Prototypes
class PRSEntry; class PRSEntry;
class PRSImage;
/////////// ///////////
@ -62,4 +63,6 @@ public:
void Save(std::string path); void Save(std::string path);
void Load(std::string path,bool reset=true); void Load(std::string path,bool reset=true);
PRSImage *FindDuplicateImage(PRSImage *img);
}; };

View file

@ -58,9 +58,10 @@ enum PRSImageType {
class PRSImage : public PRSEntry { class PRSImage : public PRSEntry {
public: public:
PRSImageType imageType; PRSImageType imageType;
unsigned int id; unsigned __int32 id;
unsigned int dataLen; unsigned __int32 dataLen;
void *data; void *data;
char md5[16];
PRSImage(); PRSImage();
~PRSImage(); ~PRSImage();