Added a glitchy pngout optimizer, and added an introduction to the specs
Originally committed to SVN as r267.
This commit is contained in:
parent
99d7233474
commit
8eeb303eb3
5 changed files with 190 additions and 91 deletions
|
@ -70,6 +70,7 @@ Please visit http://aegisub.net to download latest version
|
||||||
- Removed the "Help" button from Find/Replace dialog. (AMZ)
|
- Removed the "Help" button from Find/Replace dialog. (AMZ)
|
||||||
- Added a bias slider to the "make times continuous" function in timing post-processor, which sets the point between the subs where the two lines will meet. (AMZ)
|
- Added a bias slider to the "make times continuous" function in timing post-processor, which sets the point between the subs where the two lines will meet. (AMZ)
|
||||||
- Separated keyframe thresholds on the timing post-processor in start and end. (AMZ)
|
- Separated keyframe thresholds on the timing post-processor in start and end. (AMZ)
|
||||||
|
- Aegisub no longer enjoys littering your temporary files folder with 0 kB files. (AMZ)
|
||||||
|
|
||||||
|
|
||||||
= 1.09 beta - 2006.01.16 ===========================
|
= 1.09 beta - 2006.01.16 ===========================
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
// Headers
|
// Headers
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
#include <wx/mstream.h>
|
#include <wx/mstream.h>
|
||||||
|
#include <wx/filename.h>
|
||||||
|
#include <wx/docview.h>
|
||||||
#include "subtitle_format_prs.h"
|
#include "subtitle_format_prs.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "ass_dialogue.h"
|
#include "ass_dialogue.h"
|
||||||
|
@ -98,112 +100,36 @@ void PRSSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
|
||||||
catch (AvisynthError &err) {
|
catch (AvisynthError &err) {
|
||||||
throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal);
|
throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal);
|
||||||
}
|
}
|
||||||
|
PClip clip1 = script1.AsClip();
|
||||||
|
PClip clip2 = script2.AsClip();
|
||||||
|
|
||||||
// Get range
|
// Get range
|
||||||
std::vector<int> frames = GetFrameRanges();
|
std::vector<int> frames = GetFrameRanges();
|
||||||
|
|
||||||
// Render all frames that were detected to contain subtitles
|
// Set variables
|
||||||
PClip clip1 = script1.AsClip();
|
|
||||||
PClip clip2 = script2.AsClip();
|
|
||||||
int totalFrames = frames.size();
|
int totalFrames = frames.size();
|
||||||
int id = 0;
|
id = 0;
|
||||||
PRSDisplay *lastDisplay = NULL;
|
lastDisplay = NULL;
|
||||||
|
|
||||||
|
// Render all frames that were detected to contain subtitles
|
||||||
for (int framen=0;framen<totalFrames;framen++) {
|
for (int framen=0;framen<totalFrames;framen++) {
|
||||||
// Render it?
|
// Is this frame supposed to be rendered?
|
||||||
if (frames[framen] == 0) continue;
|
if (frames[framen] == 0) continue;
|
||||||
|
|
||||||
// Read its image
|
// Read the frame image
|
||||||
PVideoFrame frame1 = clip1->GetFrame(framen,env1);
|
PVideoFrame frame1 = clip1->GetFrame(framen,env1);
|
||||||
PVideoFrame frame2 = clip2->GetFrame(framen,env2);
|
PVideoFrame frame2 = clip2->GetFrame(framen,env2);
|
||||||
|
|
||||||
// Convert to PNG (the block is there to force it to dealloc bmp earlier)
|
// Prepare to get wxImage
|
||||||
int x=0,y=0;
|
int x=0,y=0;
|
||||||
int maxalpha=0;
|
int maxalpha=0;
|
||||||
int imgw,imgh;
|
|
||||||
wxMemoryOutputStream stream;
|
|
||||||
{
|
|
||||||
// Get wxImage and convert to PNG
|
|
||||||
wxImage bmp = CalculateAlpha(frame1->GetReadPtr(),frame2->GetReadPtr(),frame1->GetRowSize(),frame1->GetHeight(),frame1->GetPitch(),&x,&y,&maxalpha);
|
|
||||||
if (!bmp.Ok()) continue;
|
|
||||||
bmp.SaveFile(stream,wxBITMAP_TYPE_PNG);
|
|
||||||
//bmp.SaveFile(filename + wxString::Format(_T("%i.png"),id),wxBITMAP_TYPE_PNG);
|
|
||||||
|
|
||||||
// Get size
|
// Get wxImage
|
||||||
imgw = bmp.GetWidth();
|
wxImage bmp = CalculateAlpha(frame1->GetReadPtr(),frame2->GetReadPtr(),frame1->GetRowSize(),frame1->GetHeight(),frame1->GetPitch(),&x,&y,&maxalpha);
|
||||||
imgh = bmp.GetHeight();
|
if (!bmp.Ok()) continue;
|
||||||
}
|
|
||||||
|
|
||||||
// Create PRSImage
|
// Add image to file
|
||||||
PRSImage *img = new PRSImage;
|
InsertFrame(file,framen,frames,bmp,x,y,maxalpha);
|
||||||
img->id = id;
|
|
||||||
img->imageType = PNG_IMG;
|
|
||||||
img->w = imgw;
|
|
||||||
img->h = imgh;
|
|
||||||
img->maxAlpha = maxalpha;
|
|
||||||
img->dataLen = stream.GetSize();
|
|
||||||
img->data = new char[img->dataLen];
|
|
||||||
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
|
// Save file
|
||||||
|
@ -212,6 +138,127 @@ void PRSSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Insert frame into file
|
||||||
|
void PRSSubtitleFormat::InsertFrame(PRSFile &file,int &framen,std::vector<int> &frames,wxImage &bmp,int x,int y,int maxalpha) {
|
||||||
|
// Generic data holder
|
||||||
|
//bmp.SaveFile(wxString::Format(_T("test_%i.png"),id),wxBITMAP_TYPE_PNG);
|
||||||
|
bool pngout = true;
|
||||||
|
size_t datasize = 0;
|
||||||
|
char *rawData = NULL;
|
||||||
|
std::vector<char> data;
|
||||||
|
|
||||||
|
// PNGout optimize
|
||||||
|
if (pngout) {
|
||||||
|
// Save temporary PNG
|
||||||
|
wxString tempFile = wxFileName::CreateTempFileName(_T("aegiprs"));
|
||||||
|
wxString tempOut = tempFile + _T("out.png");
|
||||||
|
bmp.SaveFile(tempFile,wxBITMAP_TYPE_PNG);
|
||||||
|
|
||||||
|
// Run PNGcrush on it
|
||||||
|
wxExecute(_T("pngout.exe ") + tempFile + _T(" ") + tempOut + _T(" /f0 /y /v"),wxEXEC_SYNC);
|
||||||
|
|
||||||
|
// Read file back
|
||||||
|
FILE *fp = fopen(tempOut.mb_str(wxConvLocal),"rb");
|
||||||
|
fseek(fp,0,SEEK_END);
|
||||||
|
datasize = ftell(fp);
|
||||||
|
data.resize(datasize);
|
||||||
|
rawData = &data[0];
|
||||||
|
rewind(fp);
|
||||||
|
fread(rawData,1,datasize,fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
// Destroy temporary files
|
||||||
|
wxRemoveFile(tempFile);
|
||||||
|
wxRemoveFile(tempOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No optimization (much faster)
|
||||||
|
else {
|
||||||
|
// Convert wxImage to PNG directly
|
||||||
|
wxMemoryOutputStream stream;
|
||||||
|
bmp.SaveFile(stream,wxBITMAP_TYPE_PNG);
|
||||||
|
datasize = stream.GetSize();
|
||||||
|
data.resize(datasize);
|
||||||
|
rawData = &data[0];
|
||||||
|
stream.CopyTo(rawData,datasize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find start and end times
|
||||||
|
int startf = framen;
|
||||||
|
int totalFrames = frames.size();
|
||||||
|
while (++framen<totalFrames && frames[framen] == 1);
|
||||||
|
int endf = --framen;
|
||||||
|
int start = VFR_Output.GetTimeAtFrame(startf,true);
|
||||||
|
int end = VFR_Output.GetTimeAtFrame(endf,false);
|
||||||
|
|
||||||
|
// Create PRSImage
|
||||||
|
PRSImage *img = new PRSImage;
|
||||||
|
img->id = id;
|
||||||
|
img->imageType = PNG_IMG;
|
||||||
|
img->w = bmp.GetWidth();
|
||||||
|
img->h = bmp.GetHeight();
|
||||||
|
img->maxAlpha = maxalpha;
|
||||||
|
img->dataLen = datasize;
|
||||||
|
img->data = new char[img->dataLen];
|
||||||
|
memcpy(img->data,rawData,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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// Get frame ranges
|
// Get frame ranges
|
||||||
std::vector<int> PRSSubtitleFormat::GetFrameRanges() {
|
std::vector<int> PRSSubtitleFormat::GetFrameRanges() {
|
||||||
|
|
|
@ -43,10 +43,21 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
//////////////
|
||||||
|
// Prototypes
|
||||||
|
class PRSImage;
|
||||||
|
class PRSDisplay;
|
||||||
|
class PRSFile;
|
||||||
|
|
||||||
|
|
||||||
//////////////
|
//////////////
|
||||||
// PRS writer
|
// PRS writer
|
||||||
class PRSSubtitleFormat : public SubtitleFormat {
|
class PRSSubtitleFormat : public SubtitleFormat {
|
||||||
private:
|
private:
|
||||||
|
PRSDisplay *lastDisplay;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
void InsertFrame(PRSFile &file,int &framen,std::vector<int> &frames,wxImage &bmp,int x,int y,int maxalpha);
|
||||||
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, int *maxalpha=NULL);
|
wxImage CalculateAlpha(const unsigned char* frame1, const unsigned char* frame2, int w, int h, int pitch, int *x=NULL, int *y=NULL, int *maxalpha=NULL);
|
||||||
std::vector<int> GetFrameRanges();
|
std::vector<int> GetFrameRanges();
|
||||||
|
|
|
@ -905,6 +905,7 @@ void VideoDisplay::OnPlayTimer(wxTimerEvent &event) {
|
||||||
wxString VideoDisplay::GetTempWorkFile () {
|
wxString VideoDisplay::GetTempWorkFile () {
|
||||||
if (tempfile.IsEmpty()) {
|
if (tempfile.IsEmpty()) {
|
||||||
tempfile = wxFileName::CreateTempFileName(_T("aegisub"));
|
tempfile = wxFileName::CreateTempFileName(_T("aegisub"));
|
||||||
|
wxRemoveFile(tempfile);
|
||||||
tempfile += _T(".ass");
|
tempfile += _T(".ass");
|
||||||
}
|
}
|
||||||
return tempfile;
|
return tempfile;
|
||||||
|
|
|
@ -6,6 +6,45 @@ WARNING: THIS FILE IS HEAVILY OUTDATED!!!!!
|
||||||
Pre-Rendered Subtitles
|
Pre-Rendered Subtitles
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
Specifications by Neils Martin Hansen
|
||||||
|
Modified and commented by Rodrigo Braz Monteiro
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
Pre-Rendered Subtitles (PRS) is a compact raster data format for subtitles
|
||||||
|
to be displayed into video files. It is designed to be simple, fast and small.
|
||||||
|
|
||||||
|
Compared to hardsubs, PRS has the following advantages:
|
||||||
|
- Most of the time, it is smaller (hardsubs often require 15 MB in a 24 minutes
|
||||||
|
video clip, whereas PRS often requires as little as 3 MB).
|
||||||
|
- Can be disabled, for the viewers who preffer to watch the original content.
|
||||||
|
- You can have multiple tracks, for multi-language files.
|
||||||
|
- PRS subtitles are easier to replace, making the Quality Control process much
|
||||||
|
faster.
|
||||||
|
|
||||||
|
Compared to softsubs, it has the following advantages:
|
||||||
|
- Decoding is much faster, which allows you to create every sort of advanced
|
||||||
|
effects that can still be displayed in realtime.
|
||||||
|
- Decoding is much simpler, making it possible to port to a wide range of
|
||||||
|
platforms.
|
||||||
|
- It is harder to "steal" the subtitles.
|
||||||
|
- Does not require the shipment of font files, which might be illegal.
|
||||||
|
|
||||||
|
Other advantages of the PRS format are:
|
||||||
|
- It allows effects that are not possible with any current subtitle format,
|
||||||
|
such as different blending modes.
|
||||||
|
- It makes the task of hardsubbing a complex karaoke several times much faster.
|
||||||
|
- It ensures that the encoder will encode the subtitles exactly as the
|
||||||
|
typesetter envisioned them.
|
||||||
|
- It allows conversion of video into subtitles, e.g. effects done in programs
|
||||||
|
such as Adobe After Effects.
|
||||||
|
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
PRS stores either full PNG images, partial PNG images (through a number of
|
PRS stores either full PNG images, partial PNG images (through a number of
|
||||||
blocks) or instructions to do simple modifications (translation, clearing)
|
blocks) or instructions to do simple modifications (translation, clearing)
|
||||||
to the current output.
|
to the current output.
|
||||||
|
|
Loading…
Reference in a new issue