00001 #include <cassert>
00002 #include <iostream>
00003
00004 using namespace std;
00005
00006 #include <qapplication.h>
00007 #include <qpixmap.h>
00008 #include <qpainter.h>
00009 #include <qgl.h>
00010 #include <qcache.h>
00011 #include <qintcache.h>
00012
00013 #include "config.h"
00014 #ifdef CONFIG_DARWIN
00015 #include <OpenGL/glext.h>
00016 #endif
00017
00018 #ifdef _WIN32
00019 #include <GL/glext.h>
00020 #endif
00021
00022 #include "mythcontext.h"
00023 #include "mythpainter_ogl.h"
00024 #include "mythfontproperties.h"
00025
00026 #define MAX_GL_ITEMS 128
00027 #define MAX_STRING_ITEMS 48
00028
00029 #ifndef GL_TEXTURE_RECTANGLE_ARB
00030 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
00031 #endif
00032
00033 #ifndef GL_TEXTURE_RECTANGLE_EXT
00034 #define GL_TEXTURE_RECTANGLE_EXT 0x84F5
00035 #endif
00036
00037 #ifndef GL_TEXTURE_RECTANGLE_NV
00038 #define GL_TEXTURE_RECTANGLE_NV 0x84F5
00039 #endif
00040
00041 MythOpenGLPainter::MythOpenGLPainter()
00042 : MythPainter()
00043 {
00044 }
00045
00046 MythOpenGLPainter::~MythOpenGLPainter()
00047 {
00048 }
00049
00050 void MythOpenGLPainter::Begin(QWidget *parent)
00051 {
00052 assert(parent);
00053
00054 MythPainter::Begin(parent);
00055
00056 QGLWidget *realParent = dynamic_cast<QGLWidget *>(parent);
00057 assert(realParent);
00058
00059 realParent->makeCurrent();
00060 glClearColor(0.0, 0.0, 0.0, 0.0);
00061 glClear(GL_COLOR_BUFFER_BIT);
00062 glShadeModel(GL_FLAT);
00063 glViewport(0, 0, parent->width(), parent->height());
00064 glMatrixMode(GL_PROJECTION);
00065 glLoadIdentity();
00066 glOrtho(0, parent->width(), parent->height(), 0, -999999, 999999);
00067 glMatrixMode(GL_MODELVIEW);
00068 glLoadIdentity();
00069
00070
00071 GLint param;
00072 glGetIntegerv(GL_MAX_TEXTURE_SIZE, ¶m);
00073 m_maxTexDim = param;
00074 if (m_maxTexDim == 0)
00075 m_maxTexDim = 512;
00076 }
00077
00078 void MythOpenGLPainter::End(void)
00079 {
00080 QGLWidget *realParent = dynamic_cast<QGLWidget *>(m_Parent);
00081 assert(realParent);
00082
00083 realParent->makeCurrent();
00084 glFlush();
00085 realParent->swapBuffers();
00086
00087 MythPainter::End();
00088 }
00089
00090
00091
00092 int MythOpenGLPainter::NearestGLTextureSize(int v)
00093 {
00094 int n = 0, last = 0;
00095 int s;
00096
00097 for (s = 0; s < 32; ++s)
00098 {
00099 if (((v >> s) & 1) == 1)
00100 {
00101 ++n;
00102 last = s;
00103 }
00104 }
00105
00106 if (n > 1)
00107 s = 1 << (last + 1);
00108 else
00109 s = 1 << last;
00110
00111 return min(s, m_maxTexDim);
00112 }
00113
00114 void MythOpenGLPainter::RemoveImageFromCache(MythImage *im)
00115 {
00116 if (m_ImageIntMap.contains(im))
00117 {
00118 GLuint textures[1];
00119 textures[0] = m_ImageIntMap[im];
00120
00121 glDeleteTextures(1, textures);
00122 m_ImageIntMap.erase(im);
00123
00124 m_ImageExpireList.remove(im);
00125 }
00126 }
00127
00128 void MythOpenGLPainter::BindTextureFromCache(MythImage *im,
00129 bool alphaonly)
00130 {
00131 static bool init_extensions = true;
00132 static bool generate_mipmaps = false;
00133
00134 if (init_extensions)
00135 {
00136 QString extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
00137
00138 texture_rects = true;
00139 if (extensions.contains("GL_NV_texture_rectangle"))
00140 {
00141 VERBOSE(VB_GENERAL, "Using NV NPOT texture extension");
00142 q_gl_texture = GL_TEXTURE_RECTANGLE_NV;
00143 }
00144 else if (extensions.contains("GL_ARB_texture_rectangle"))
00145 {
00146 VERBOSE(VB_GENERAL, "Using ARB NPOT texture extension");
00147 q_gl_texture = GL_TEXTURE_RECTANGLE_ARB;
00148 }
00149 else if (extensions.contains("GL_EXT_texture_rectangle"))
00150 {
00151 VERBOSE(VB_GENERAL, "Using EXT NPOT texture extension");
00152 q_gl_texture = GL_TEXTURE_RECTANGLE_EXT;
00153 }
00154 else
00155 {
00156 texture_rects = false;
00157 q_gl_texture = GL_TEXTURE_2D;
00158 }
00159
00160 if (!texture_rects)
00161 generate_mipmaps = extensions.contains("GL_SGIS_generate_mipmap");
00162 else
00163 generate_mipmaps = false;
00164
00165 init_extensions = false;
00166 }
00167
00168 if (m_ImageIntMap.contains(im))
00169 {
00170 long val = m_ImageIntMap[im];
00171
00172 if (!im->IsChanged())
00173 {
00174 m_ImageExpireList.remove(im);
00175 m_ImageExpireList.push_back(im);
00176 glBindTexture(q_gl_texture, val);
00177 return;
00178 }
00179 else
00180 {
00181 RemoveImageFromCache(im);
00182 }
00183 }
00184
00185 im->SetChanged(false);
00186
00187
00188
00189 QImage tx;
00190
00191 if (!texture_rects)
00192 {
00193
00194
00195 int tx_w = NearestGLTextureSize(im->width());
00196 int tx_h = NearestGLTextureSize(im->height());
00197 if (tx_w != im->width() || tx_h != im->height())
00198 tx = QGLWidget::convertToGLFormat(im->scale(tx_w, tx_h));
00199 else
00200 tx = QGLWidget::convertToGLFormat(*im);
00201 }
00202 else
00203 tx = QGLWidget::convertToGLFormat(*im);
00204
00205 GLuint format = GL_RGBA8;
00206 if (alphaonly)
00207 format = GL_ALPHA;
00208
00209 GLuint tx_id;
00210 glGenTextures(1, &tx_id);
00211 glBindTexture(q_gl_texture, tx_id);
00212 glTexParameteri(q_gl_texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00213
00214 if (generate_mipmaps)
00215 {
00216 glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
00217 glTexParameteri(q_gl_texture, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
00218 glTexParameterf(q_gl_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
00219 }
00220 else
00221 glTexParameterf(q_gl_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00222
00223 glTexImage2D(q_gl_texture, 0, format, tx.width(), tx.height(), 0,
00224 GL_RGBA, GL_UNSIGNED_BYTE, tx.bits());
00225
00226 m_ImageIntMap[im] = tx_id;
00227 m_ImageExpireList.push_back(im);
00228
00229 if (m_ImageExpireList.size() > MAX_GL_ITEMS)
00230 {
00231 MythImage *im = m_ImageExpireList.front();
00232 m_ImageExpireList.pop_front();
00233 RemoveImageFromCache(im);
00234 }
00235 }
00236
00237 void MythOpenGLPainter::DrawImage(const QRect &r, MythImage *im,
00238 const QRect &src, int alpha)
00239 {
00240 double x1, y1, x2, y2;
00241
00242 glClearDepth(1.0f);
00243
00244
00245 BindTextureFromCache(im);
00246
00247 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00248
00249 glPushAttrib(GL_CURRENT_BIT);
00250
00251 glColor4f(1.0, 1.0, 1.0, alpha / 255.0);
00252 glEnable(q_gl_texture);
00253 glEnable(GL_BLEND);
00254
00255 glBegin(GL_QUADS);
00256 {
00257 if (!texture_rects)
00258 {
00259 x1 = src.x() / (double)im->width();
00260 x2 = x1 + src.width() / (double)im->width();
00261 y1 = src.y() / (double)im->height();
00262 y2 = y1 + src.height() / (double)im->height();
00263 }
00264 else
00265 {
00266 x1 = src.x();
00267 x2 = src.width();
00268 y1 = src.y();
00269 y2 = src.height();
00270 }
00271
00272 glTexCoord2f(x1, y2); glVertex2f(r.x(), r.y());
00273 glTexCoord2f(x2, y2); glVertex2f(r.x() + r.width(), r.y());
00274 glTexCoord2f(x2, y1); glVertex2f(r.x() + r.width(), r.y() + r.height());
00275 glTexCoord2f(x1, y1); glVertex2f(r.x(), r.y()+r.height());
00276 }
00277 glEnd();
00278
00279 glDisable(GL_BLEND);
00280 glDisable(q_gl_texture);
00281 glPopAttrib();
00282 }
00283
00284 int MythOpenGLPainter::CalcAlpha(int alpha1, int alpha2)
00285 {
00286 return (int)(alpha1 * (alpha2 / 255.0));
00287 }
00288
00289 MythImage *MythOpenGLPainter::GetImageFromString(const QString &msg,
00290 int flags, const QRect &r,
00291 const MythFontProperties &font)
00292 {
00293 QString incoming = font.GetHash() + QString::number(r.width()) +
00294 QString::number(r.height()) + QString::number(flags) +
00295 msg;
00296
00297 if (m_StringToImageMap.contains(incoming))
00298 {
00299 m_StringExpireList.remove(incoming);
00300 m_StringExpireList.push_back(incoming);
00301
00302 return m_StringToImageMap[incoming];
00303 }
00304
00305 MythImage *im = GetFormatImage();
00306
00307 qApp->lock();
00308
00309 int w, h;
00310
00311 if (!texture_rects)
00312 {
00313 w = NearestGLTextureSize(r.width());
00314 h = NearestGLTextureSize(r.height());
00315 }
00316 else
00317 {
00318 w = r.width();
00319 h = r.height();
00320 }
00321
00322 QPixmap pm(QSize(w, h));
00323 pm.fill();
00324
00325 QPainter tmp(&pm);
00326 tmp.setFont(font.face());
00327 tmp.setPen(Qt::black);
00328 tmp.drawText(0, 0, r.width(), r.height(), flags, msg);
00329 tmp.end();
00330
00331 im->Assign(pm.convertToImage().convertDepth(8, Qt::MonoOnly |
00332 Qt::ThresholdDither |
00333 Qt::AvoidDither));
00334
00335 qApp->unlock();
00336
00337 int numColors = im->numColors();
00338 QRgb *colorTable = im->colorTable();
00339
00340 for (int i = 0; i < numColors; i++)
00341 {
00342 int alpha = 255 - qRed(colorTable[i]);
00343 colorTable[i] = qRgba(0, 0, 0, alpha);
00344 }
00345
00346 m_StringToImageMap[incoming] = im;
00347 m_StringExpireList.push_back(incoming);
00348
00349 if (m_StringExpireList.size() > MAX_STRING_ITEMS)
00350 {
00351 QString oldmsg = m_StringExpireList.front();
00352 m_StringExpireList.pop_front();
00353
00354 MythImage *oldim = NULL;
00355 if (m_StringToImageMap.contains(oldmsg))
00356 oldim = m_StringToImageMap[oldmsg];
00357
00358 m_StringToImageMap.erase(oldmsg);
00359
00360 if (oldim)
00361 oldim->DownRef();
00362 }
00363
00364 return im;
00365 }
00366
00367 void MythOpenGLPainter::ReallyDrawText(QColor color, const QRect &r, int alpha)
00368 {
00369 double x1, y1, x2, y2;
00370
00371 glPushAttrib(GL_CURRENT_BIT);
00372
00373 alpha = CalcAlpha(qAlpha(color.rgb()), alpha);
00374
00375 glColor4f(color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0,
00376 alpha / 255.0);
00377 glEnable(q_gl_texture);
00378 glEnable(GL_BLEND);
00379
00380 glBegin(GL_QUADS);
00381 {
00382 if (!texture_rects)
00383 {
00384 x1 = y1 = 0;
00385 x2 = y2 = 1;
00386 }
00387 else
00388 {
00389 x1 = y1 = 0;
00390 x2 = r.width();
00391 y2 = r.height();
00392 }
00393
00394 glTexCoord2f(x1, y2); glVertex2f(r.x(), r.y());
00395 glTexCoord2f(x2, y2); glVertex2f(r.x() + r.width(), r.y());
00396 glTexCoord2f(x2, y1); glVertex2f(r.x() + r.width(), r.y() + r.height());
00397 glTexCoord2f(x1, y1); glVertex2f(r.x(), r.y()+r.height());
00398 }
00399 glEnd();
00400
00401 glDisable(GL_BLEND);
00402 glDisable(q_gl_texture);
00403 glPopAttrib();
00404 }
00405
00406 void MythOpenGLPainter::DrawText(const QRect &r, const QString &msg,
00407 int flags, const MythFontProperties &font,
00408 int alpha)
00409 {
00410 glClearDepth(1.0f);
00411
00412 MythImage *im = GetImageFromString(msg, flags, r, font);
00413
00414 if (!im)
00415 return;
00416
00417
00418 BindTextureFromCache(im, true);
00419
00420 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00421
00422 QRect newRect = r;
00423 newRect.setWidth(im->width());
00424 newRect.setHeight(im->height());
00425
00426 if (font.hasShadow())
00427 {
00428 QPoint shadowOffset;
00429 QColor shadowColor;
00430 int shadowAlpha;
00431
00432 font.GetShadow(shadowOffset, shadowColor, shadowAlpha);
00433
00434 QRect a = newRect;
00435 a.moveBy(shadowOffset.x(), shadowOffset.y());
00436
00437 ReallyDrawText(shadowColor, a, CalcAlpha(shadowAlpha, alpha));
00438 }
00439
00440 if (font.hasOutline())
00441 {
00442 QColor outlineColor;
00443 int outlineSize, outlineAlpha;
00444
00445 font.GetOutline(outlineColor, outlineSize, outlineAlpha);
00446
00447
00448 int outalpha = alpha;
00449
00450 outalpha /= 16;
00451
00452 QRect a = newRect;
00453 a.moveBy(0 - outlineSize, 0 - outlineSize);
00454 ReallyDrawText(outlineColor, a, outalpha);
00455
00456 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00457 {
00458 a.moveBy(1, 0);
00459 ReallyDrawText(outlineColor, a, outalpha);
00460 }
00461
00462 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00463 {
00464 a.moveBy(0, 1);
00465 ReallyDrawText(outlineColor, a, outalpha);
00466 }
00467
00468 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00469 {
00470 a.moveBy(-1, 0);
00471 ReallyDrawText(outlineColor, a, outalpha);
00472 }
00473
00474 for (int i = (0 - outlineSize + 1); i <= outlineSize; i++)
00475 {
00476 a.moveBy(0, -1);
00477 ReallyDrawText(outlineColor, a, outalpha);
00478 }
00479 }
00480
00481 ReallyDrawText(font.color(), newRect, alpha);
00482 }
00483
00484 MythImage *MythOpenGLPainter::GetFormatImage()
00485 {
00486 return new MythImage(this);
00487 }
00488
00489 void MythOpenGLPainter::DeleteFormatImage(MythImage *im)
00490 {
00491 RemoveImageFromCache(im);
00492 }
00493