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:
parent
523f545484
commit
784ed8b82f
9 changed files with 214 additions and 72 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
57
prs/prs_entry.cpp
Normal 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;
|
||||||
|
}
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue