diff --git a/core/changelog.txt b/core/changelog.txt index 107274be6..a4ce0955a 100644 --- a/core/changelog.txt +++ b/core/changelog.txt @@ -70,6 +70,7 @@ Please visit http://aegisub.net to download latest version - 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) - 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 =========================== diff --git a/core/subtitle_format_prs.cpp b/core/subtitle_format_prs.cpp index 1b864a616..cb729913e 100644 --- a/core/subtitle_format_prs.cpp +++ b/core/subtitle_format_prs.cpp @@ -38,6 +38,8 @@ // Headers #include #include +#include +#include #include "subtitle_format_prs.h" #include "ass_file.h" #include "ass_dialogue.h" @@ -98,112 +100,36 @@ void PRSSubtitleFormat::WriteFile(wxString filename,wxString encoding) { catch (AvisynthError &err) { throw _T("AviSynth error: ") + wxString(err.msg,wxConvLocal); } + PClip clip1 = script1.AsClip(); + PClip clip2 = script2.AsClip(); // Get range std::vector frames = GetFrameRanges(); - // Render all frames that were detected to contain subtitles - PClip clip1 = script1.AsClip(); - PClip clip2 = script2.AsClip(); + // Set variables int totalFrames = frames.size(); - int id = 0; - PRSDisplay *lastDisplay = NULL; + id = 0; + lastDisplay = NULL; + + // Render all frames that were detected to contain subtitles for (int framen=0;framenGetFrame(framen,env1); 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 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 - imgw = bmp.GetWidth(); - imgh = bmp.GetHeight(); - } + // Get wxImage + wxImage bmp = CalculateAlpha(frame1->GetReadPtr(),frame2->GetReadPtr(),frame1->GetRowSize(),frame1->GetHeight(),frame1->GetPitch(),&x,&y,&maxalpha); + if (!bmp.Ok()) continue; - // Create PRSImage - PRSImage *img = new PRSImage; - 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 (++framenid == 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); - } + // Add image to file + InsertFrame(file,framen,frames,bmp,x,y,maxalpha); } // 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 &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 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 (++framenid = 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 std::vector PRSSubtitleFormat::GetFrameRanges() { diff --git a/core/subtitle_format_prs.h b/core/subtitle_format_prs.h index 1daa1f07c..d965e5f18 100644 --- a/core/subtitle_format_prs.h +++ b/core/subtitle_format_prs.h @@ -43,10 +43,21 @@ #include +////////////// +// Prototypes +class PRSImage; +class PRSDisplay; +class PRSFile; + + ////////////// // PRS writer class PRSSubtitleFormat : public SubtitleFormat { private: + PRSDisplay *lastDisplay; + int id; + + void InsertFrame(PRSFile &file,int &framen,std::vector &frames,wxImage &bmp,int x,int y,int maxalpha); 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); std::vector GetFrameRanges(); diff --git a/core/video_display.cpp b/core/video_display.cpp index 467d8a5d6..f4999566f 100644 --- a/core/video_display.cpp +++ b/core/video_display.cpp @@ -905,6 +905,7 @@ void VideoDisplay::OnPlayTimer(wxTimerEvent &event) { wxString VideoDisplay::GetTempWorkFile () { if (tempfile.IsEmpty()) { tempfile = wxFileName::CreateTempFileName(_T("aegisub")); + wxRemoveFile(tempfile); tempfile += _T(".ass"); } return tempfile; diff --git a/prs/prs_specs.txt b/prs/prs_specs.txt index 07a78b39f..91d986044 100644 --- a/prs/prs_specs.txt +++ b/prs/prs_specs.txt @@ -6,6 +6,45 @@ WARNING: THIS FILE IS HEAVILY OUTDATED!!!!! 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 blocks) or instructions to do simple modifications (translation, clearing) to the current output.