mirror of https://github.com/odrling/Aegisub
Backport rewritten video display to 2.1.8. Merges revisions 3615,3617,3620,3623,3628-3631,3666,3711,3714,3717,3723,3726,3729,3739 (VideoOutGL); 3190,3201,3526,3613 (exception.h) from trunk.
Originally committed to SVN as r3741.
This commit is contained in:
parent
5f5c5569b8
commit
cb1127af2a
|
@ -1827,6 +1827,10 @@
|
|||
RelativePath="..\..\src\include\aegisub\audio_provider.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\include\aegisub\exception.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\include\aegisub\spellchecker.h"
|
||||
>
|
||||
|
@ -1903,6 +1907,14 @@
|
|||
RelativePath="..\..\src\video_display.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\video_out_gl.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\video_out_gl.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\video_slider.cpp"
|
||||
>
|
||||
|
|
|
@ -338,6 +338,7 @@ aegisub_2_1_SOURCES = \
|
|||
video_context.cpp \
|
||||
video_display.cpp \
|
||||
video_frame.cpp \
|
||||
video_out_gl.ccp \
|
||||
video_provider_cache.cpp \
|
||||
video_provider_dummy.cpp \
|
||||
video_provider_manager.cpp \
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
// Copyright (c) 2009, Niels Martin Hansen
|
||||
// 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.
|
||||
|
||||
|
||||
#include <wx/string.h>
|
||||
#pragma once
|
||||
|
||||
namespace Aegisub {
|
||||
|
||||
// Base class for exceptions
|
||||
// No public creators, all exceptions throws must be specific
|
||||
class Exception {
|
||||
wxString message;
|
||||
Exception *inner;
|
||||
|
||||
protected:
|
||||
Exception(const wxString &msg, const Exception *inr = 0)
|
||||
: message(msg)
|
||||
, inner(0)
|
||||
{
|
||||
if (inr)
|
||||
inner = inr->Copy();
|
||||
}
|
||||
Exception(); // not implemented, not wanted
|
||||
virtual ~Exception() { if (inner) delete inner; }
|
||||
public:
|
||||
// Error message for outer exception
|
||||
virtual wxString GetMessage() const { return message; }
|
||||
// Error message for outer exception, and chained message for inner exception
|
||||
wxString GetChainedMessage() const { if (inner) return inner->GetChainedMessage() + _T("\r\n") + GetMessage(); else return GetMessage(); }
|
||||
// Name of exception class, should only be implemented by specific classes
|
||||
virtual const wxChar * GetName() const = 0;
|
||||
|
||||
operator const wxChar * () { return GetMessage().c_str(); }
|
||||
operator wxString () { return GetMessage(); }
|
||||
|
||||
/// @brief Create a copy of the exception allocated on the heap
|
||||
/// @return A heap-allocated exception object
|
||||
///
|
||||
/// All deriving classes must implement this explicitly to avoid losing
|
||||
/// information in the duplication.
|
||||
virtual Exception *Copy() const = 0;
|
||||
};
|
||||
|
||||
|
||||
// Macro to quickly add location information to an error message
|
||||
#define AG_WHERE _T(" (at ") _T(__FILE__) _T(":") _T(#__LINE__) _T(")")
|
||||
|
||||
|
||||
// Macros to define basic exception classes that do nothing fancy
|
||||
// These should always be used inside the Aegisub namespace
|
||||
#define DEFINE_SIMPLE_EXCEPTION_NOINNER(classname,baseclass,displayname) \
|
||||
class classname : public baseclass { \
|
||||
public: \
|
||||
classname(const wxString &msg) : baseclass(msg) { } \
|
||||
const wxChar * GetName() const { return _T(displayname); } \
|
||||
Exception * Copy() const { return new classname(*this); } \
|
||||
};
|
||||
#define DEFINE_SIMPLE_EXCEPTION(classname,baseclass,displayname) \
|
||||
class classname : public baseclass { \
|
||||
public: \
|
||||
classname(const wxString &msg, Exception *inner) : baseclass(msg, inner) { } \
|
||||
const wxChar * GetName() const { return _T(displayname); } \
|
||||
Exception * Copy() const { return new classname(*this); } \
|
||||
};
|
||||
#define DEFINE_BASE_EXCEPTION_NOINNER(classname,baseclass) \
|
||||
class classname : public baseclass { \
|
||||
public: \
|
||||
classname(const wxString &msg) : baseclass(msg) { } \
|
||||
};
|
||||
#define DEFINE_BASE_EXCEPTION(classname,baseclass) \
|
||||
class classname : public baseclass { \
|
||||
public: \
|
||||
classname(const wxString &msg, Exception *inner) : baseclass(msg, inner) { } \
|
||||
};
|
||||
|
||||
|
||||
// Exception for "user cancel" events
|
||||
// I.e. when we want to abort an operation because the user requested that we do so
|
||||
// Not actually an error and should not be handled as such
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel")
|
||||
|
||||
|
||||
// Errors that should never happen and point to some invalid assumption in the code
|
||||
DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error")
|
||||
|
||||
|
||||
// Some error related to the filesystem
|
||||
// These should always be original causes and as such do not support inner exceptions
|
||||
DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError,Exception)
|
||||
|
||||
// A file can't be accessed for some reason
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(FileNotAccessibleError,FileSystemError,"filesystem/not_accessible")
|
||||
|
||||
// A file isn't accessible because it doesn't exist
|
||||
class FileNotFoundError : public FileNotAccessibleError {
|
||||
public:
|
||||
FileNotFoundError(const wxString &filename) : FileNotAccessibleError(wxString(_T("File not found: ")) + filename) { }
|
||||
const wxChar * GetName() const { return _T("filesystem/not_accessible/not_found"); }
|
||||
|
||||
// Not documented, see Aegisub::Exception class
|
||||
Exception * Copy() const { return new FileNotFoundError(*this); } \
|
||||
};
|
||||
|
||||
|
||||
// A problem with some input data
|
||||
DEFINE_BASE_EXCEPTION(InvalidInputException,Exception)
|
||||
|
||||
|
||||
// There is no "generic exception" class, everything must be a specific one
|
||||
// Define new classes if none fit the error you're reporting
|
||||
|
||||
};
|
|
@ -352,28 +352,13 @@ void VideoContext::RemoveDisplay(VideoDisplay *display) {
|
|||
// Update displays
|
||||
void VideoContext::UpdateDisplays(bool full) {
|
||||
for (std::list<VideoDisplay*>::iterator cur=displayList.begin();cur!=displayList.end();cur++) {
|
||||
// Get display
|
||||
VideoDisplay *display = *cur;
|
||||
|
||||
// Update slider
|
||||
if (full) {
|
||||
display->UpdateSize();
|
||||
display->ControlSlider->SetRange(0,GetLength()-1);
|
||||
}
|
||||
display->ControlSlider->SetValue(GetFrameN());
|
||||
//display->ControlSlider->Update();
|
||||
display->UpdatePositionDisplay();
|
||||
|
||||
// If not shown, don't update the display itself
|
||||
if (!display->IsShownOnScreen()) continue;
|
||||
|
||||
// Update visual controls
|
||||
if (display->visual) display->visual->Refresh();
|
||||
|
||||
// Update controls
|
||||
//display->Refresh();
|
||||
//display->Update();
|
||||
display->Render();
|
||||
display->SetFrame(GetFrameN());
|
||||
}
|
||||
|
||||
// Update audio display
|
||||
|
@ -384,7 +369,6 @@ void VideoContext::UpdateDisplays(bool full) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Refresh subtitles
|
||||
void VideoContext::Refresh (bool video, bool subtitles) {
|
||||
|
@ -434,7 +418,6 @@ void VideoContext::JumpToFrame(int n) {
|
|||
try {
|
||||
// Set frame number
|
||||
frame_n = n;
|
||||
GetFrameAsTexture(n);
|
||||
|
||||
// Display
|
||||
UpdateDisplays(false);
|
||||
|
@ -526,122 +509,9 @@ AegiVideoFrame VideoContext::GetFrame(int n,bool raw) {
|
|||
else return frame;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// Get GL Texture of frame
|
||||
GLuint VideoContext::GetFrameAsTexture(int n) {
|
||||
// Already uploaded
|
||||
if (n == lastFrame || n == -1) return lastTex;
|
||||
|
||||
// Get frame
|
||||
AegiVideoFrame frame = GetFrame(n);
|
||||
|
||||
// Set frame
|
||||
lastFrame = n;
|
||||
|
||||
// Set context
|
||||
#ifdef __APPLE__
|
||||
GetGLContext(displayList.front())->SetCurrent();
|
||||
#else
|
||||
GetGLContext(displayList.front())->SetCurrent(*displayList.front());
|
||||
#endif
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
if (glGetError() != 0) throw _T("Error enabling texture.");
|
||||
|
||||
// Image type
|
||||
GLenum format = GL_LUMINANCE;
|
||||
if (frame.format == FORMAT_RGB32) {
|
||||
if (frame.invertChannels) format = GL_BGRA_EXT;
|
||||
else format = GL_RGBA;
|
||||
}
|
||||
else if (frame.format == FORMAT_RGB24) {
|
||||
if (frame.invertChannels) format = GL_BGR_EXT;
|
||||
else format = GL_RGB;
|
||||
}
|
||||
else if (frame.format == FORMAT_YV12) {
|
||||
format = GL_LUMINANCE;
|
||||
}
|
||||
isInverted = frame.flipped;
|
||||
|
||||
if (lastTex == 0) {
|
||||
// Enable
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
// Generate texture with GL
|
||||
//glActiveTexture(GL_TEXTURE0);
|
||||
glGenTextures(1, &lastTex);
|
||||
if (glGetError() != 0) throw _T("Error generating texture.");
|
||||
glBindTexture(GL_TEXTURE_2D, lastTex);
|
||||
if (glGetError() != 0) throw _T("Error binding texture.");
|
||||
|
||||
// Texture parameters
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
|
||||
if (glGetError() != 0) throw _T("Error setting min_filter texture parameter.");
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
|
||||
if (glGetError() != 0) throw _T("Error setting mag_filter texture parameter.");
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
if (glGetError() != 0) throw _T("Error setting wrap_s texture parameter.");
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
if (glGetError() != 0) throw _T("Error setting wrap_t texture parameter.");
|
||||
|
||||
// Allocate texture
|
||||
int height = frame.h;
|
||||
if (frame.format == FORMAT_YV12) height = height * 3 / 2;
|
||||
int tw = SmallestPowerOf2(MAX(frame.pitch[0]/frame.GetBpp(0),frame.pitch[1]+frame.pitch[2]));
|
||||
int th = SmallestPowerOf2(height);
|
||||
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,tw,th,0,format,GL_UNSIGNED_BYTE,NULL);
|
||||
if (glGetError() != 0) {
|
||||
tw = MAX(tw,th);
|
||||
th = tw;
|
||||
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,tw,th,0,format,GL_UNSIGNED_BYTE,NULL);
|
||||
if (glGetError() != 0) {
|
||||
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,tw,th,0,format,GL_UNSIGNED_BYTE,NULL);
|
||||
if (glGetError() != 0) throw _T("Error allocating texture.");
|
||||
}
|
||||
}
|
||||
texW = float(frame.w)/float(tw);
|
||||
texH = float(frame.h)/float(th);
|
||||
|
||||
// Set texture
|
||||
//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
||||
//if (glGetError() != 0) throw _T("Error setting hinting.");
|
||||
|
||||
// Create shader
|
||||
if (frame.format == FORMAT_YV12 && yv12shader == 0 && OpenGLWrapper::UseShaders()) {
|
||||
yv12shader = OpenGLWrapper::CreateYV12Shader(texW,texH,float(frame.pitch[1])/float(tw));
|
||||
}
|
||||
|
||||
// Set priority
|
||||
float priority = 1.0f;
|
||||
glPrioritizeTextures(1,&lastTex,&priority);
|
||||
}
|
||||
|
||||
// Load texture data
|
||||
glBindTexture(GL_TEXTURE_2D, lastTex);
|
||||
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,frame.pitch[0]/frame.GetBpp(0),frame.h,format,GL_UNSIGNED_BYTE,frame.data[0]);
|
||||
if (glGetError() != 0) throw _T("Error uploading primary plane");
|
||||
|
||||
// UV planes for YV12
|
||||
if (frame.format == FORMAT_YV12) {
|
||||
int u = 1;
|
||||
int v = 2;
|
||||
if (frame.invertChannels) {
|
||||
u = 2;
|
||||
v = 1;
|
||||
}
|
||||
glTexSubImage2D(GL_TEXTURE_2D,0,0,frame.h,frame.pitch[1],frame.h/2,format,GL_UNSIGNED_BYTE,frame.data[u]);
|
||||
if (glGetError() != 0) throw _T("Error uploading U plane.");
|
||||
glTexSubImage2D(GL_TEXTURE_2D,0,frame.pitch[1],frame.h,frame.pitch[2],frame.h/2,format,GL_UNSIGNED_BYTE,frame.data[v]);
|
||||
if (glGetError() != 0) throw _T("Error uploadinv V plane.");
|
||||
}
|
||||
|
||||
// Return texture number
|
||||
return lastTex;
|
||||
}
|
||||
|
||||
|
||||
/////////////////
|
||||
// Save snapshot
|
||||
/// @brief Save snapshot
|
||||
/// @param raw
|
||||
///
|
||||
void VideoContext::SaveSnapshot(bool raw) {
|
||||
// Get folder
|
||||
wxString option = Options.AsText(_T("Video Screenshot Path"));
|
||||
|
@ -918,7 +788,6 @@ wxThread::ExitCode VideoContextThread::Entry() {
|
|||
// Get frame and set frame number
|
||||
{
|
||||
wxMutexLocker glLock(OpenGLWrapper::glMutex);
|
||||
parent->GetFrameAsTexture(frame);
|
||||
parent->frame_n = frame;
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,6 @@ public:
|
|||
void SaveSnapshot(bool raw);
|
||||
|
||||
wxGLContext *GetGLContext(wxGLCanvas *canvas);
|
||||
GLuint GetFrameAsTexture(int n);
|
||||
float GetTexW() { return texW; }
|
||||
float GetTexH() { return texH; }
|
||||
VideoFrameFormat GetFormat() { return vidFormat; }
|
||||
|
@ -217,3 +216,4 @@ public:
|
|||
VideoContextThread(VideoContext *parent);
|
||||
wxThread::ExitCode Entry();
|
||||
};
|
||||
|
||||
|
|
|
@ -63,12 +63,12 @@
|
|||
#include "vfw_wrap.h"
|
||||
#include "mkv_wrap.h"
|
||||
#include "options.h"
|
||||
#include "subs_edit_box.h"
|
||||
#include "audio_display.h"
|
||||
#include "main.h"
|
||||
#include "video_slider.h"
|
||||
#include "utils.h"
|
||||
#include "video_out_gl.h"
|
||||
#include "vfr.h"
|
||||
#include "video_box.h"
|
||||
#include "gl_wrap.h"
|
||||
#include "video_slider.h"
|
||||
#include "visual_tool.h"
|
||||
#include "visual_tool_cross.h"
|
||||
#include "visual_tool_rotatez.h"
|
||||
|
@ -119,6 +119,7 @@ VideoDisplay::VideoDisplay(wxWindow* parent, wxWindowID id, const wxPoint& pos,
|
|||
#else
|
||||
: wxGLCanvas (parent, id, attribList, pos, size, style, name)
|
||||
#endif
|
||||
, videoOut(new VideoOutGL())
|
||||
{
|
||||
// Set options
|
||||
box = NULL;
|
||||
|
@ -165,36 +166,77 @@ void VideoDisplay::ShowCursor(bool show) {
|
|||
}
|
||||
}
|
||||
|
||||
void VideoDisplay::SetFrame(int frameNumber) {
|
||||
VideoContext *context = VideoContext::Get();
|
||||
ControlSlider->SetValue(frameNumber);
|
||||
|
||||
// Get time for frame
|
||||
int time = VFR_Output.GetTimeAtFrame(frameNumber, true, true);
|
||||
int h = time / 3600000;
|
||||
int m = time % 3600000 / 60000;
|
||||
int s = time % 60000 / 1000;
|
||||
int ms = time % 1000;
|
||||
|
||||
// Set the text box for frame number and time
|
||||
PositionDisplay->SetValue(wxString::Format(_T("%01i:%02i:%02i.%03i - %i"), h, m, s, ms, frameNumber));
|
||||
if (context->GetKeyFrames().Index(frameNumber) != wxNOT_FOUND) {
|
||||
// Set the background color to indicate this is a keyframe
|
||||
PositionDisplay->SetBackgroundColour(Options.AsColour(_T("Grid selection background")));
|
||||
PositionDisplay->SetForegroundColour(Options.AsColour(_T("Grid selection foreground")));
|
||||
}
|
||||
else {
|
||||
PositionDisplay->SetBackgroundColour(wxNullColour);
|
||||
PositionDisplay->SetForegroundColour(wxNullColour);
|
||||
}
|
||||
|
||||
wxString startSign;
|
||||
wxString endSign;
|
||||
int startOff = 0;
|
||||
int endOff = 0;
|
||||
|
||||
if (AssDialogue *curLine = context->curLine) {
|
||||
startOff = time - curLine->Start.GetMS();
|
||||
endOff = time - curLine->End.GetMS();
|
||||
}
|
||||
|
||||
// Positive signs
|
||||
if (startOff > 0) startSign = _T("+");
|
||||
if (endOff > 0) endSign = _T("+");
|
||||
|
||||
// Set the text box for time relative to active subtitle line
|
||||
SubsPosition->SetValue(wxString::Format(_T("%s%ims; %s%ims"), startSign.c_str(), startOff, endSign.c_str(), endOff));
|
||||
|
||||
if (IsShownOnScreen() && visual) visual->Refresh();
|
||||
|
||||
// Render the new frame
|
||||
if (context->IsLoaded()) {
|
||||
AegiVideoFrame frame = context->GetFrame(frameNumber);
|
||||
videoOut->UploadFrameData(frame);
|
||||
}
|
||||
Render();
|
||||
|
||||
currentFrame = frameNumber;
|
||||
}
|
||||
|
||||
//////////
|
||||
// Render
|
||||
void VideoDisplay::Render()
|
||||
// Yes it's legal C++ to replace the body of a function with one huge try..catch statement
|
||||
try {
|
||||
|
||||
// Is shown?
|
||||
void VideoDisplay::Render() try {
|
||||
if (!IsShownOnScreen()) return;
|
||||
if (!wxIsMainThread()) throw _T("Error: trying to render from non-primary thread");
|
||||
|
||||
// Get video context
|
||||
VideoContext *context = VideoContext::Get();
|
||||
wxASSERT(context);
|
||||
if (!context->IsLoaded()) return;
|
||||
|
||||
// Set GL context
|
||||
//wxMutexLocker glLock(OpenGLWrapper::glMutex);
|
||||
#ifdef __WXMAC__
|
||||
SetCurrent();
|
||||
#else
|
||||
SetCurrent(*context->GetGLContext(this));
|
||||
#endif
|
||||
|
||||
// Get sizes
|
||||
int w,h,sw,sh,pw,ph;
|
||||
GetClientSize(&w,&h);
|
||||
int w, h, sw, sh, pw, ph;
|
||||
GetClientSize(&w, &h);
|
||||
wxASSERT(w > 0);
|
||||
wxASSERT(h > 0);
|
||||
context->GetScriptSize(sw,sh);
|
||||
context->GetScriptSize(sw, sh);
|
||||
pw = context->GetWidth();
|
||||
ph = context->GetHeight();
|
||||
wxASSERT(pw > 0);
|
||||
|
@ -216,9 +258,7 @@ try {
|
|||
if (freeSize) {
|
||||
// Set aspect ratio
|
||||
float thisAr = float(w)/float(h);
|
||||
float vidAr;
|
||||
if (context->GetAspectRatioType() == 0) vidAr = float(pw)/float(ph);
|
||||
else vidAr = context->GetAspectRatioValue();
|
||||
float vidAr = context->GetAspectRatioType() == 0 ? float(pw)/float(ph) : context->GetAspectRatioValue();
|
||||
|
||||
// Window is wider than video, blackbox left/right
|
||||
if (thisAr - vidAr > 0.01f) {
|
||||
|
@ -236,8 +276,6 @@ try {
|
|||
}
|
||||
|
||||
// Set viewport
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
if (glGetError()) throw _T("Error enabling texturing.");
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glViewport(dx1,dy1,dx2,dy2);
|
||||
|
@ -249,67 +287,34 @@ try {
|
|||
if (glGetError()) throw _T("Error setting up matrices (wtf?).");
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
// Texture mode
|
||||
if (w != pw || h != ph) {
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
||||
if (glGetError()) throw _T("Error setting texture parameter min filter.");
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
||||
if (glGetError()) throw _T("Error setting texture parameter mag filter.");
|
||||
}
|
||||
else {
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
|
||||
if (glGetError()) throw _T("Error setting texture parameter min filter.");
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
|
||||
if (glGetError()) throw _T("Error setting texture parameter mag filter.");
|
||||
}
|
||||
|
||||
// Texture coordinates
|
||||
float top = 0.0f;
|
||||
float bot = context->GetTexH();
|
||||
wxASSERT(bot != 0.0f);
|
||||
if (context->IsInverted()) {
|
||||
top = context->GetTexH();
|
||||
bot = 0.0f;
|
||||
}
|
||||
float left = 0.0;
|
||||
float right = context->GetTexW();
|
||||
wxASSERT(right != 0.0f);
|
||||
|
||||
// Draw frame
|
||||
glDisable(GL_BLEND);
|
||||
if (glGetError()) throw _T("Error disabling blending.");
|
||||
context->SetShader(true);
|
||||
glBindTexture(GL_TEXTURE_2D, VideoContext::Get()->GetFrameAsTexture(-1));
|
||||
if (glGetError()) throw _T("Error binding texture.");
|
||||
glColor4f(1.0f,1.0f,1.0f,1.0f);
|
||||
glBegin(GL_QUADS);
|
||||
// Top-left
|
||||
glTexCoord2f(left,top);
|
||||
glVertex2f(0,0);
|
||||
// Bottom-left
|
||||
glTexCoord2f(left,bot);
|
||||
glVertex2f(0,sh);
|
||||
// Bottom-right
|
||||
glTexCoord2f(right,bot);
|
||||
glVertex2f(sw,sh);
|
||||
// Top-right
|
||||
glTexCoord2f(right,top);
|
||||
glVertex2f(sw,0);
|
||||
glEnd();
|
||||
context->SetShader(false);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
// TV effects
|
||||
videoOut->Render(sw, sh);
|
||||
|
||||
DrawTVEffects();
|
||||
|
||||
// Draw overlay
|
||||
if (visual) visual->Draw();
|
||||
visual->Draw();
|
||||
|
||||
// Swap buffers
|
||||
glFinish();
|
||||
//if (glGetError()) throw _T("Error finishing gl operation.");
|
||||
SwapBuffers();
|
||||
}
|
||||
catch (const VideoOutUnsupportedException &err) {
|
||||
wxLogError(
|
||||
_T("An error occurred trying to render the video frame to screen.\n")
|
||||
_T("Your graphics card does not appear to have a functioning OpenGL driver.\n")
|
||||
_T("Error message reported: %s"),
|
||||
err.GetMessage());
|
||||
VideoContext::Get()->Reset();
|
||||
}
|
||||
catch (const VideoOutException &err) {
|
||||
wxLogError(
|
||||
_T("An error occurred trying to render the video frame to screen.\n")
|
||||
_T("If you get this error regardless of which video file you use, and also if you use dummy video, Aegisub might not work with your graphics card's OpenGL driver.\n")
|
||||
_T("Error message reported: %s"),
|
||||
err.GetMessage());
|
||||
VideoContext::Get()->Reset();
|
||||
}
|
||||
catch (const wxChar *err) {
|
||||
wxLogError(
|
||||
_T("An error occurred trying to render the video frame to screen.\n")
|
||||
|
@ -552,82 +557,6 @@ void VideoDisplay::SetZoomPos(int value) {
|
|||
if (zoomBox->GetSelection() != value) zoomBox->SetSelection(value);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////
|
||||
// Updates position display
|
||||
void VideoDisplay::UpdatePositionDisplay() {
|
||||
// Update position display control
|
||||
if (!PositionDisplay) {
|
||||
throw _T("Position Display not set!");
|
||||
}
|
||||
|
||||
// Get time
|
||||
int frame_n = VideoContext::Get()->GetFrameN();
|
||||
int time = VFR_Output.GetTimeAtFrame(frame_n,true,true);
|
||||
int temp = time;
|
||||
int h=0, m=0, s=0, ms=0;
|
||||
while (temp >= 3600000) {
|
||||
temp -= 3600000;
|
||||
h++;
|
||||
}
|
||||
while (temp >= 60000) {
|
||||
temp -= 60000;
|
||||
m++;
|
||||
}
|
||||
while (temp >= 1000) {
|
||||
temp -= 1000;
|
||||
s++;
|
||||
}
|
||||
ms = temp;
|
||||
|
||||
// Position display update
|
||||
PositionDisplay->SetValue(wxString::Format(_T("%01i:%02i:%02i.%03i - %i"),h,m,s,ms,frame_n));
|
||||
if (VideoContext::Get()->GetKeyFrames().Index(frame_n) != wxNOT_FOUND) {
|
||||
PositionDisplay->SetBackgroundColour(Options.AsColour(_T("Grid selection background")));
|
||||
PositionDisplay->SetForegroundColour(Options.AsColour(_T("Grid selection foreground")));
|
||||
}
|
||||
else {
|
||||
PositionDisplay->SetBackgroundColour(wxNullColour);
|
||||
PositionDisplay->SetForegroundColour(wxNullColour);
|
||||
}
|
||||
|
||||
// Subs position display update
|
||||
UpdateSubsRelativeTime();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Updates box with subs position relative to frame
|
||||
void VideoDisplay::UpdateSubsRelativeTime() {
|
||||
// Set variables
|
||||
wxString startSign;
|
||||
wxString endSign;
|
||||
int startOff,endOff;
|
||||
int frame_n = VideoContext::Get()->GetFrameN();
|
||||
AssDialogue *curLine = VideoContext::Get()->curLine;
|
||||
|
||||
// Set start/end
|
||||
if (curLine) {
|
||||
int time = VFR_Output.GetTimeAtFrame(frame_n,true,true);
|
||||
startOff = time - curLine->Start.GetMS();
|
||||
endOff = time - curLine->End.GetMS();
|
||||
}
|
||||
|
||||
// Fallback to zero
|
||||
else {
|
||||
startOff = 0;
|
||||
endOff = 0;
|
||||
}
|
||||
|
||||
// Positive signs
|
||||
if (startOff > 0) startSign = _T("+");
|
||||
if (endOff > 0) endSign = _T("+");
|
||||
|
||||
// Update line
|
||||
SubsPosition->SetValue(wxString::Format(_T("%s%ims; %s%ims"),startSign.c_str(),startOff,endSign.c_str(),endOff));
|
||||
}
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Copy to clipboard
|
||||
void VideoDisplay::OnCopyToClipboard(wxCommandEvent &event) {
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <time.h>
|
||||
#include <wx/glcanvas.h>
|
||||
#include <wx/combobox.h>
|
||||
#include <memory>
|
||||
#include "video_context.h"
|
||||
|
||||
|
||||
|
@ -59,6 +60,7 @@ class AssDialogue;
|
|||
class VideoProvider;
|
||||
class VisualTool;
|
||||
class VideoBox;
|
||||
class VideoOutGL;
|
||||
|
||||
|
||||
//////////////
|
||||
|
@ -74,6 +76,8 @@ private:
|
|||
int w,h;
|
||||
int dx1,dx2,dy1,dy2;
|
||||
int mouse_x,mouse_y;
|
||||
|
||||
int currentFrame;
|
||||
bool locked;
|
||||
|
||||
void DrawTVEffects();
|
||||
|
@ -97,6 +101,7 @@ public:
|
|||
|
||||
double zoomValue;
|
||||
bool freeSize;
|
||||
std::auto_ptr<VideoOutGL> videoOut;
|
||||
|
||||
VideoSlider *ControlSlider;
|
||||
wxComboBox *zoomBox;
|
||||
|
@ -107,15 +112,16 @@ public:
|
|||
~VideoDisplay();
|
||||
void Reset();
|
||||
|
||||
void SetFrame(int frameNumber);
|
||||
int GetFrame() const { return currentFrame; }
|
||||
|
||||
void Render();
|
||||
|
||||
void ShowCursor(bool show);
|
||||
void ConvertMouseCoords(int &x,int &y);
|
||||
void UpdatePositionDisplay();
|
||||
void UpdateSize();
|
||||
void SetZoom(double value);
|
||||
void SetZoomPos(int pos);
|
||||
void UpdateSubsRelativeTime();
|
||||
void SetVisualMode(int mode);
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
// Copyright (c) 2009, Thomas Goyne
|
||||
// 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 video_out_gl.cpp
|
||||
/// @brief OpenGL based video renderer
|
||||
/// @ingroup video
|
||||
///
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <wx/log.h>
|
||||
#endif
|
||||
|
||||
// These must be included before local headers.
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/GL.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
#include "video_out_gl.h"
|
||||
#include "utils.h"
|
||||
#include "video_frame.h"
|
||||
|
||||
// Windows only has headers for OpenGL 1.1 and GL_CLAMP_TO_EDGE is 1.2
|
||||
#ifndef GL_CLAMP_TO_EDGE
|
||||
#define GL_CLAMP_TO_EDGE 0x812F
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
/// @brief Structure tracking all precomputable information about a subtexture
|
||||
struct TextureInfo {
|
||||
/// The OpenGL texture id this is for
|
||||
GLuint textureID;
|
||||
/// The byte offset into the frame's data block
|
||||
int dataOffset;
|
||||
int sourceH;
|
||||
int sourceW;
|
||||
|
||||
int textureH;
|
||||
int textureW;
|
||||
|
||||
float destH;
|
||||
float destW;
|
||||
float destX;
|
||||
float destY;
|
||||
|
||||
float texTop;
|
||||
float texBottom;
|
||||
float texLeft;
|
||||
float texRight;
|
||||
};
|
||||
/// @brief Test if a texture can be created
|
||||
/// @param width The width of the texture
|
||||
/// @param height The height of the texture
|
||||
/// @param format The texture's format
|
||||
/// @return Whether the texture could be created.
|
||||
bool TestTexture(int width, int height, GLint format) {
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
|
||||
glDeleteTextures(1, &texture);
|
||||
while (glGetError()) { } // Silently swallow all errors as we don't care why it failed if it did
|
||||
|
||||
wxLogDebug(L"VideoOutGL::TestTexture: %dx%d\n", width, height);
|
||||
return format != 0;
|
||||
}
|
||||
}
|
||||
|
||||
VideoOutGL::VideoOutGL()
|
||||
: maxTextureSize(0),
|
||||
supportsRectangularTextures(false),
|
||||
supportsGlClampToEdge(false),
|
||||
internalFormat(0),
|
||||
frameWidth(0),
|
||||
frameHeight(0),
|
||||
frameFormat(0),
|
||||
frameFlipped(false),
|
||||
textureIdList(),
|
||||
textureList(),
|
||||
textureCount(0),
|
||||
textureRows(0),
|
||||
textureCols(0)
|
||||
{ }
|
||||
|
||||
/// @brief Runtime detection of required OpenGL capabilities
|
||||
void VideoOutGL::DetectOpenGLCapabilities() {
|
||||
if (maxTextureSize != 0) return;
|
||||
|
||||
// Test for supported internalformats
|
||||
if (TestTexture(64, 64, GL_RGBA8)) internalFormat = GL_RGBA8;
|
||||
else if (TestTexture(64, 64, GL_RGBA)) internalFormat = GL_RGBA;
|
||||
else throw VideoOutUnsupportedException(L"Could not create a 64x64 RGB texture in any format.");
|
||||
|
||||
// Test for the maximum supported texture size
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||
while (maxTextureSize > 64 && !TestTexture(maxTextureSize, maxTextureSize, internalFormat)) maxTextureSize >>= 1;
|
||||
wxLogDebug(L"VideoOutGL::DetectOpenGLCapabilities: Maximum texture size is %dx%d\n", maxTextureSize, maxTextureSize);
|
||||
|
||||
// Test for rectangular texture support
|
||||
supportsRectangularTextures = TestTexture(maxTextureSize, maxTextureSize >> 1, internalFormat);
|
||||
|
||||
// Test GL_CLAMP_TO_EDGE support
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
if (glGetError()) {
|
||||
supportsGlClampToEdge = false;
|
||||
wxLogDebug(L"VideoOutGL::DetectOpenGLCapabilities: Using GL_CLAMP\n");
|
||||
}
|
||||
else {
|
||||
supportsGlClampToEdge = true;
|
||||
wxLogDebug(L"VideoOutGL::DetectOpenGLCapabilities: Using GL_CLAMP_TO_EDGE\n");
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief If needed, create the grid of textures for displaying frames of the given format
|
||||
/// @param width The frame's width
|
||||
/// @param height The frame's height
|
||||
/// @param format The frame's format
|
||||
/// @param bpp The frame's bytes per pixel
|
||||
void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, bool flipped) {
|
||||
// Do nothing if the frame size and format are unchanged
|
||||
if (width == frameWidth && height == frameHeight && format == frameFormat && frameFlipped == flipped) return;
|
||||
wxLogDebug(L"VideoOutGL::InitTextures: Video size: %dx%d\n", width, height);
|
||||
|
||||
DetectOpenGLCapabilities();
|
||||
|
||||
// Clean up old textures
|
||||
if (textureIdList.size() > 0) {
|
||||
glDeleteTextures(textureIdList.size(), &textureIdList[0]);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glDeleteTextures", err);
|
||||
textureIdList.clear();
|
||||
textureList.clear();
|
||||
}
|
||||
|
||||
// Create the textures
|
||||
textureRows = (int)ceil(double(height) / maxTextureSize);
|
||||
textureCols = (int)ceil(double(width) / maxTextureSize);
|
||||
textureIdList.resize(textureRows * textureCols);
|
||||
textureList.resize(textureRows * textureCols);
|
||||
glGenTextures(textureIdList.size(), &textureIdList[0]);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glGenTextures", err);
|
||||
|
||||
// Calculate the position information for each texture
|
||||
int sourceY = 0;
|
||||
float destY = 0.0f;
|
||||
for (int i = 0; i < textureRows; i++) {
|
||||
int sourceX = 0;
|
||||
float destX = 0.0f;
|
||||
|
||||
int sourceH = maxTextureSize;
|
||||
int textureH = maxTextureSize;
|
||||
// If the last row doesn't need a full texture, shrink it to the smallest one possible
|
||||
if (i == textureRows - 1 && height % maxTextureSize > 0) {
|
||||
sourceH = height % maxTextureSize;
|
||||
textureH = SmallestPowerOf2(sourceH);
|
||||
}
|
||||
for (int j = 0; j < textureCols; j++) {
|
||||
TextureInfo& ti = textureList[i * textureCols + j];
|
||||
|
||||
// Copy the current position information into the struct
|
||||
ti.destX = destX;
|
||||
ti.destY = destY;
|
||||
ti.sourceH = sourceH;
|
||||
|
||||
ti.sourceW = maxTextureSize;
|
||||
int textureW = maxTextureSize;
|
||||
// If the last column doesn't need a full texture, shrink it to the smallest one possible
|
||||
if (j == textureCols - 1 && width % maxTextureSize > 0) {
|
||||
ti.sourceW = width % maxTextureSize;
|
||||
textureW = SmallestPowerOf2(ti.sourceW);
|
||||
}
|
||||
|
||||
int w = textureW;
|
||||
int h = textureH;
|
||||
if (!supportsRectangularTextures) w = h = MAX(w, h);
|
||||
|
||||
if (supportsGlClampToEdge) {
|
||||
ti.texLeft = 0.0f;
|
||||
ti.texTop = 0.0f;
|
||||
}
|
||||
else {
|
||||
// Stretch the texture a half pixel in each direction to eliminate the border
|
||||
ti.texLeft = 1.0f / (2 * w);
|
||||
ti.texTop = 1.0f / (2 * h);
|
||||
}
|
||||
|
||||
ti.destW = float(w) / width;
|
||||
ti.destH = float(h) / height;
|
||||
|
||||
ti.textureID = textureIdList[i * textureCols + j];
|
||||
ti.dataOffset = sourceY * width * bpp + sourceX * bpp;
|
||||
|
||||
ti.texRight = 1.0f - ti.texLeft;
|
||||
ti.texBottom = 1.0f - ti.texTop;
|
||||
if (flipped) {
|
||||
ti.texBottom = ti.texTop - float(h - ti.sourceH) / h;
|
||||
ti.texTop = 1.0f - ti.texTop - float(h - ti.sourceH) / h;
|
||||
|
||||
ti.dataOffset = (height - sourceY - ti.sourceH) * width * bpp + sourceX * bpp;
|
||||
}
|
||||
|
||||
|
||||
// Actually create the texture and set the scaling mode
|
||||
glBindTexture(GL_TEXTURE_2D, ti.textureID);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glBindTexture", err);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, NULL);
|
||||
wxLogDebug(L"VideoOutGL::InitTextures: Using texture size: %dx%d\n", w, h);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glTexImage2D", err);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glTexParameteri(GL_TEXTURE_MIN_FILTER)", err);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glTexParameteri(GL_TEXTURE_MAG_FILTER)", err);
|
||||
|
||||
GLint mode = supportsGlClampToEdge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glTexParameteri(GL_TEXTURE_WRAP_S)", err);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glTexParameteri(GL_TEXTURE_WRAP_T)", err);
|
||||
|
||||
destX += ti.destW;
|
||||
sourceX += ti.sourceW;
|
||||
}
|
||||
destY += float(sourceH) / height;
|
||||
sourceY += sourceH;
|
||||
}
|
||||
|
||||
// Store the information needed to know when the grid must be recreated
|
||||
frameWidth = width;
|
||||
frameHeight = height;
|
||||
frameFormat = format;
|
||||
frameFlipped = flipped;
|
||||
}
|
||||
void VideoOutGL::UploadFrameData(const AegiVideoFrame& frame) {
|
||||
if (frame.h == 0 || frame.w == 0) return;
|
||||
|
||||
GLuint format = frame.invertChannels ? GL_BGRA_EXT : GL_RGBA;
|
||||
InitTextures(frame.w, frame.h, format, frame.GetBpp(0), frame.flipped);
|
||||
|
||||
// Set the row length, needed to be able to upload partial rows
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.w);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glPixelStorei(GL_UNPACK_ROW_LENGTH, FrameWidth)", err);
|
||||
|
||||
for (unsigned i = 0; i < textureList.size(); i++) {
|
||||
TextureInfo& ti = textureList[i];
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, ti.textureID);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glBindTexture", err);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ti.sourceW, ti.sourceH, format, GL_UNSIGNED_BYTE, frame.data[0] + ti.dataOffset);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glTexSubImage2D", err);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glPixelStorei(GL_UNPACK_ROW_LENGTH, default)", err);
|
||||
}
|
||||
|
||||
void VideoOutGL::Render(int sw, int sh) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glEnable(GL_TEXTURE_2d)", err);
|
||||
|
||||
for (unsigned i = 0; i < textureList.size(); i++) {
|
||||
TextureInfo& ti = textureList[i];
|
||||
|
||||
float destX = ti.destX * sw;
|
||||
float destW = ti.destW * sw;
|
||||
float destY = ti.destY * sh;
|
||||
float destH = ti.destH * sh;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, ti.textureID);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glBindTexture", err);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glColor4f", err);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(ti.texLeft, ti.texTop); glVertex2f(destX, destY);
|
||||
glTexCoord2f(ti.texRight, ti.texTop); glVertex2f(destX + destW, destY);
|
||||
glTexCoord2f(ti.texRight, ti.texBottom); glVertex2f(destX + destW, destY + destH);
|
||||
glTexCoord2f(ti.texLeft, ti.texBottom); glVertex2f(destX, destY + destH);
|
||||
glEnd();
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"GL_QUADS", err);
|
||||
}
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
if (GLenum err = glGetError()) throw VideoOutOpenGLException(L"glDisable(GL_TEXTURE_2d)", err);
|
||||
}
|
||||
|
||||
VideoOutGL::~VideoOutGL() {
|
||||
if (textureIdList.size() > 0) {
|
||||
glDeleteTextures(textureIdList.size(), &textureIdList[0]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) 2009, Thomas Goyne
|
||||
// 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: video_out_gl.h 3613 2009-10-05 00:06:11Z plorkyeran $
|
||||
|
||||
/// @file video_out_gl.h
|
||||
/// @brief OpenGL based video renderer
|
||||
/// @ingroup video
|
||||
///
|
||||
|
||||
#include "include/aegisub/exception.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class AegiVideoFrame;
|
||||
namespace {
|
||||
struct TextureInfo;
|
||||
}
|
||||
|
||||
/// @class VideoOutGL
|
||||
/// @brief OpenGL based video renderer
|
||||
class VideoOutGL {
|
||||
private:
|
||||
/// The maximum texture size supported by the user's graphics card
|
||||
int maxTextureSize;
|
||||
/// Whether rectangular textures are supported by the user's graphics card
|
||||
bool supportsRectangularTextures;
|
||||
/// Whether GL_CLAMP_TO_EDGE is supported by the user's drivers
|
||||
bool supportsGlClampToEdge;
|
||||
/// The internalformat to use
|
||||
int internalFormat;
|
||||
|
||||
/// The frame height which the texture grid has been set up for
|
||||
int frameWidth;
|
||||
/// The frame width which the texture grid has been set up for
|
||||
int frameHeight;
|
||||
/// The frame format which the texture grid has been set up for
|
||||
GLenum frameFormat;
|
||||
/// Whether the grid is set up for flipped video
|
||||
bool frameFlipped;
|
||||
/// List of OpenGL texture ids used in the grid
|
||||
std::vector<GLuint> textureIdList;
|
||||
/// List of precalculated texture display information
|
||||
std::vector<TextureInfo> textureList;
|
||||
/// The total texture count
|
||||
int textureCount;
|
||||
/// The number of rows of textures
|
||||
int textureRows;
|
||||
/// The number of columns of textures
|
||||
int textureCols;
|
||||
|
||||
void DetectOpenGLCapabilities();
|
||||
void InitTextures(int width, int height, GLenum format, int bpp, bool flipped);
|
||||
|
||||
VideoOutGL(const VideoOutGL &);
|
||||
VideoOutGL& operator=(const VideoOutGL&);
|
||||
public:
|
||||
/// @brief Set the frame to be displayed when Render() is called
|
||||
/// @param frame The frame to be displayed
|
||||
void UploadFrameData(const AegiVideoFrame& frame);
|
||||
/// @brief Render a frame
|
||||
/// @param sw The current script width
|
||||
/// @param sh The current script height
|
||||
void Render(int sw, int sh);
|
||||
|
||||
/// @brief Constructor
|
||||
VideoOutGL();
|
||||
/// @brief Destructor
|
||||
~VideoOutGL();
|
||||
};
|
||||
|
||||
/// @class VideoOutException
|
||||
/// @extends Aegisub::Exception
|
||||
/// @brief Base class for all exceptions thrown by VideoOutGL
|
||||
DEFINE_BASE_EXCEPTION_NOINNER(VideoOutException, Aegisub::Exception)
|
||||
|
||||
/// @class VideoOutUnsupportedException
|
||||
/// @extends VideoOutException
|
||||
/// @brief The user's video card does not support OpenGL to any usable extent
|
||||
DEFINE_SIMPLE_EXCEPTION_NOINNER(VideoOutUnsupportedException, VideoOutException, "videoout/unsupported")
|
||||
|
||||
/// @class VideoOutOpenGLException
|
||||
/// @extends VideoOutException
|
||||
/// @brief An OpenGL error occured.
|
||||
///
|
||||
/// Unlike VideoOutUnsupportedException, these errors are likely to be video-specific
|
||||
/// and/or due to an Aegisub bug.
|
||||
class VideoOutOpenGLException : public VideoOutException {
|
||||
public:
|
||||
VideoOutOpenGLException(const wxChar *func, int err)
|
||||
: VideoOutException(wxString::Format(L"%s failed with error code %d", func, err))
|
||||
{ }
|
||||
const wxChar * GetName() const { return L"videoout/opengl"; }
|
||||
Exception * Copy() const { return new VideoOutOpenGLException(*this); }
|
||||
};
|
Loading…
Reference in New Issue